SpirvShader: Move GLSLstd450 handling to new cpp file

Bug: b/145336353
Change-Id: I39a5bffc1784c7b35ab9c4016658f4532d6ce5fb
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/38810
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
Reviewed-by: Alexis Hétu <sugoi@google.com>
Tested-by: Ben Clayton <bclayton@google.com>
diff --git a/src/Pipeline/BUILD.gn b/src/Pipeline/BUILD.gn
index e17084a..62f28cb 100644
--- a/src/Pipeline/BUILD.gn
+++ b/src/Pipeline/BUILD.gn
@@ -38,9 +38,10 @@
     "SamplerCore.cpp",
     "SetupRoutine.cpp",
     "ShaderCore.cpp",
-    "SpirvShader.cpp",
-    "SpirvShaderSampling.cpp",
     "SpirvShader_dbg.cpp",
+    "SpirvShader.cpp",
+    "SpirvShaderGLSLstd450.cpp",
+    "SpirvShaderSampling.cpp",
     "VertexProgram.cpp",
     "VertexRoutine.cpp",
   ]
diff --git a/src/Pipeline/SpirvShader.cpp b/src/Pipeline/SpirvShader.cpp
index 441ff43..ec916b5 100644
--- a/src/Pipeline/SpirvShader.cpp
+++ b/src/Pipeline/SpirvShader.cpp
@@ -33,8 +33,6 @@
 
 namespace
 {
-	constexpr float PI = 3.141592653589793f;
-
 	VkFormat SpirvFormatToVulkanFormat(spv::ImageFormat format)
 	{
 		switch (format)
@@ -3313,928 +3311,6 @@
 		return EmitResult::Continue;
 	}
 
-	SpirvShader::EmitResult SpirvShader::EmitExtendedInstruction(InsnIterator insn, EmitState *state) const
-	{
-		auto &type = getType(insn.word(1));
-		auto &dst = state->createIntermediate(insn.word(2), type.sizeInComponents);
-		auto extInstIndex = static_cast<GLSLstd450>(insn.word(4));
-
-		switch (extInstIndex)
-		{
-		case GLSLstd450FAbs:
-		{
-			auto src = GenericValue(this, state, insn.word(5));
-			for (auto i = 0u; i < type.sizeInComponents; i++)
-			{
-				dst.move(i, Abs(src.Float(i)));
-			}
-			break;
-		}
-		case GLSLstd450SAbs:
-		{
-			auto src = GenericValue(this, state, insn.word(5));
-			for (auto i = 0u; i < type.sizeInComponents; i++)
-			{
-				dst.move(i, Abs(src.Int(i)));
-			}
-			break;
-		}
-		case GLSLstd450Cross:
-		{
-			auto lhs = GenericValue(this, state, insn.word(5));
-			auto rhs = GenericValue(this, state, insn.word(6));
-			dst.move(0, lhs.Float(1) * rhs.Float(2) - rhs.Float(1) * lhs.Float(2));
-			dst.move(1, lhs.Float(2) * rhs.Float(0) - rhs.Float(2) * lhs.Float(0));
-			dst.move(2, lhs.Float(0) * rhs.Float(1) - rhs.Float(0) * lhs.Float(1));
-			break;
-		}
-		case GLSLstd450Floor:
-		{
-			auto src = GenericValue(this, state, insn.word(5));
-			for (auto i = 0u; i < type.sizeInComponents; i++)
-			{
-				dst.move(i, Floor(src.Float(i)));
-			}
-			break;
-		}
-		case GLSLstd450Trunc:
-		{
-			auto src = GenericValue(this, state, insn.word(5));
-			for (auto i = 0u; i < type.sizeInComponents; i++)
-			{
-				dst.move(i, Trunc(src.Float(i)));
-			}
-			break;
-		}
-		case GLSLstd450Ceil:
-		{
-			auto src = GenericValue(this, state, insn.word(5));
-			for (auto i = 0u; i < type.sizeInComponents; i++)
-			{
-				dst.move(i, Ceil(src.Float(i)));
-			}
-			break;
-		}
-		case GLSLstd450Fract:
-		{
-			auto src = GenericValue(this, state, insn.word(5));
-			for (auto i = 0u; i < type.sizeInComponents; i++)
-			{
-				dst.move(i, Frac(src.Float(i)));
-			}
-			break;
-		}
-		case GLSLstd450Round:
-		{
-			auto src = GenericValue(this, state, insn.word(5));
-			for (auto i = 0u; i < type.sizeInComponents; i++)
-			{
-				dst.move(i, Round(src.Float(i)));
-			}
-			break;
-		}
-		case GLSLstd450RoundEven:
-		{
-			auto src = GenericValue(this, state, insn.word(5));
-			for (auto i = 0u; i < type.sizeInComponents; i++)
-			{
-				auto x = Round(src.Float(i));
-				// dst = round(src) + ((round(src) < src) * 2 - 1) * (fract(src) == 0.5) * isOdd(round(src));
-				dst.move(i, x + ((SIMD::Float(CmpLT(x, src.Float(i)) & SIMD::Int(1)) * SIMD::Float(2.0f)) - SIMD::Float(1.0f)) *
-						SIMD::Float(CmpEQ(Frac(src.Float(i)), SIMD::Float(0.5f)) & SIMD::Int(1)) * SIMD::Float(Int4(x) & SIMD::Int(1)));
-			}
-			break;
-		}
-		case GLSLstd450FMin:
-		{
-			auto lhs = GenericValue(this, state, insn.word(5));
-			auto rhs = GenericValue(this, state, insn.word(6));
-			for (auto i = 0u; i < type.sizeInComponents; i++)
-			{
-				dst.move(i, Min(lhs.Float(i), rhs.Float(i)));
-			}
-			break;
-		}
-		case GLSLstd450FMax:
-		{
-			auto lhs = GenericValue(this, state, insn.word(5));
-			auto rhs = GenericValue(this, state, insn.word(6));
-			for (auto i = 0u; i < type.sizeInComponents; i++)
-			{
-				dst.move(i, Max(lhs.Float(i), rhs.Float(i)));
-			}
-			break;
-		}
-		case GLSLstd450SMin:
-		{
-			auto lhs = GenericValue(this, state, insn.word(5));
-			auto rhs = GenericValue(this, state, insn.word(6));
-			for (auto i = 0u; i < type.sizeInComponents; i++)
-			{
-				dst.move(i, Min(lhs.Int(i), rhs.Int(i)));
-			}
-			break;
-		}
-		case GLSLstd450SMax:
-		{
-			auto lhs = GenericValue(this, state, insn.word(5));
-			auto rhs = GenericValue(this, state, insn.word(6));
-			for (auto i = 0u; i < type.sizeInComponents; i++)
-			{
-				dst.move(i, Max(lhs.Int(i), rhs.Int(i)));
-			}
-			break;
-		}
-		case GLSLstd450UMin:
-		{
-			auto lhs = GenericValue(this, state, insn.word(5));
-			auto rhs = GenericValue(this, state, insn.word(6));
-			for (auto i = 0u; i < type.sizeInComponents; i++)
-			{
-				dst.move(i, Min(lhs.UInt(i), rhs.UInt(i)));
-			}
-			break;
-		}
-		case GLSLstd450UMax:
-		{
-			auto lhs = GenericValue(this, state, insn.word(5));
-			auto rhs = GenericValue(this, state, insn.word(6));
-			for (auto i = 0u; i < type.sizeInComponents; i++)
-			{
-				dst.move(i, Max(lhs.UInt(i), rhs.UInt(i)));
-			}
-			break;
-		}
-		case GLSLstd450Step:
-		{
-			auto edge = GenericValue(this, state, insn.word(5));
-			auto x = GenericValue(this, state, insn.word(6));
-			for (auto i = 0u; i < type.sizeInComponents; i++)
-			{
-				dst.move(i, CmpNLT(x.Float(i), edge.Float(i)) & As<SIMD::Int>(SIMD::Float(1.0f)));
-			}
-			break;
-		}
-		case GLSLstd450SmoothStep:
-		{
-			auto edge0 = GenericValue(this, state, insn.word(5));
-			auto edge1 = GenericValue(this, state, insn.word(6));
-			auto x = GenericValue(this, state, insn.word(7));
-			for (auto i = 0u; i < type.sizeInComponents; i++)
-			{
-				auto tx = Min(Max((x.Float(i) - edge0.Float(i)) /
-						(edge1.Float(i) - edge0.Float(i)), SIMD::Float(0.0f)), SIMD::Float(1.0f));
-				dst.move(i, tx * tx * (Float4(3.0f) - Float4(2.0f) * tx));
-			}
-			break;
-		}
-		case GLSLstd450FMix:
-		{
-			auto x = GenericValue(this, state, insn.word(5));
-			auto y = GenericValue(this, state, insn.word(6));
-			auto a = GenericValue(this, state, insn.word(7));
-			for (auto i = 0u; i < type.sizeInComponents; i++)
-			{
-				dst.move(i, a.Float(i) * (y.Float(i) - x.Float(i)) + x.Float(i));
-			}
-			break;
-		}
-		case GLSLstd450FClamp:
-		{
-			auto x = GenericValue(this, state, insn.word(5));
-			auto minVal = GenericValue(this, state, insn.word(6));
-			auto maxVal = GenericValue(this, state, insn.word(7));
-			for (auto i = 0u; i < type.sizeInComponents; i++)
-			{
-				dst.move(i, Min(Max(x.Float(i), minVal.Float(i)), maxVal.Float(i)));
-			}
-			break;
-		}
-		case GLSLstd450SClamp:
-		{
-			auto x = GenericValue(this, state, insn.word(5));
-			auto minVal = GenericValue(this, state, insn.word(6));
-			auto maxVal = GenericValue(this, state, insn.word(7));
-			for (auto i = 0u; i < type.sizeInComponents; i++)
-			{
-				dst.move(i, Min(Max(x.Int(i), minVal.Int(i)), maxVal.Int(i)));
-			}
-			break;
-		}
-		case GLSLstd450UClamp:
-		{
-			auto x = GenericValue(this, state, insn.word(5));
-			auto minVal = GenericValue(this, state, insn.word(6));
-			auto maxVal = GenericValue(this, state, insn.word(7));
-			for (auto i = 0u; i < type.sizeInComponents; i++)
-			{
-				dst.move(i, Min(Max(x.UInt(i), minVal.UInt(i)), maxVal.UInt(i)));
-			}
-			break;
-		}
-		case GLSLstd450FSign:
-		{
-			auto src = GenericValue(this, state, insn.word(5));
-			for (auto i = 0u; i < type.sizeInComponents; i++)
-			{
-				auto neg = As<SIMD::Int>(CmpLT(src.Float(i), SIMD::Float(-0.0f))) & As<SIMD::Int>(SIMD::Float(-1.0f));
-				auto pos = As<SIMD::Int>(CmpNLE(src.Float(i), SIMD::Float(+0.0f))) & As<SIMD::Int>(SIMD::Float(1.0f));
-				dst.move(i, neg | pos);
-			}
-			break;
-		}
-		case GLSLstd450SSign:
-		{
-			auto src = GenericValue(this, state, insn.word(5));
-			for (auto i = 0u; i < type.sizeInComponents; i++)
-			{
-				auto neg = CmpLT(src.Int(i), SIMD::Int(0)) & SIMD::Int(-1);
-				auto pos = CmpNLE(src.Int(i), SIMD::Int(0)) & SIMD::Int(1);
-				dst.move(i, neg | pos);
-			}
-			break;
-		}
-		case GLSLstd450Reflect:
-		{
-			auto I = GenericValue(this, state, insn.word(5));
-			auto N = GenericValue(this, state, insn.word(6));
-
-			SIMD::Float d = Dot(type.sizeInComponents, I, N);
-
-			for (auto i = 0u; i < type.sizeInComponents; i++)
-			{
-				dst.move(i, I.Float(i) - SIMD::Float(2.0f) * d * N.Float(i));
-			}
-			break;
-		}
-		case GLSLstd450Refract:
-		{
-			auto I = GenericValue(this, state, insn.word(5));
-			auto N = GenericValue(this, state, insn.word(6));
-			auto eta = GenericValue(this, state, insn.word(7));
-
-			SIMD::Float d = Dot(type.sizeInComponents, I, N);
-			SIMD::Float k = SIMD::Float(1.0f) - eta.Float(0) * eta.Float(0) * (SIMD::Float(1.0f) - d * d);
-			SIMD::Int pos = CmpNLT(k, SIMD::Float(0.0f));
-			SIMD::Float t = (eta.Float(0) * d + Sqrt(k));
-
-			for (auto i = 0u; i < type.sizeInComponents; i++)
-			{
-				dst.move(i, pos & As<SIMD::Int>(eta.Float(0) * I.Float(i) - t * N.Float(i)));
-			}
-			break;
-		}
-		case GLSLstd450FaceForward:
-		{
-			auto N = GenericValue(this, state, insn.word(5));
-			auto I = GenericValue(this, state, insn.word(6));
-			auto Nref = GenericValue(this, state, insn.word(7));
-
-			SIMD::Float d = Dot(type.sizeInComponents, I, Nref);
-			SIMD::Int neg = CmpLT(d, SIMD::Float(0.0f));
-
-			for (auto i = 0u; i < type.sizeInComponents; i++)
-			{
-				auto n = N.Float(i);
-				dst.move(i, (neg & As<SIMD::Int>(n)) | (~neg & As<SIMD::Int>(-n)));
-			}
-			break;
-		}
-		case GLSLstd450Length:
-		{
-			auto x = GenericValue(this, state, insn.word(5));
-			SIMD::Float d = Dot(getType(getObject(insn.word(5)).type).sizeInComponents, x, x);
-
-			dst.move(0, Sqrt(d));
-			break;
-		}
-		case GLSLstd450Normalize:
-		{
-			auto x = GenericValue(this, state, insn.word(5));
-			SIMD::Float d = Dot(getType(getObject(insn.word(5)).type).sizeInComponents, x, x);
-			SIMD::Float invLength = SIMD::Float(1.0f) / Sqrt(d);
-
-			for (auto i = 0u; i < type.sizeInComponents; i++)
-			{
-				dst.move(i, invLength * x.Float(i));
-			}
-			break;
-		}
-		case GLSLstd450Distance:
-		{
-			auto p0 = GenericValue(this, state, insn.word(5));
-			auto p1 = GenericValue(this, state, insn.word(6));
-			auto p0Type = getType(p0.type);
-
-			// sqrt(dot(p0-p1, p0-p1))
-			SIMD::Float d = (p0.Float(0) - p1.Float(0)) * (p0.Float(0) - p1.Float(0));
-
-			for (auto i = 1u; i < p0Type.sizeInComponents; i++)
-			{
-				d += (p0.Float(i) - p1.Float(i)) * (p0.Float(i) - p1.Float(i));
-			}
-
-			dst.move(0, Sqrt(d));
-			break;
-		}
-		case GLSLstd450Modf:
-		{
-			auto val = GenericValue(this, state, insn.word(5));
-			auto ptrId = Object::ID(insn.word(6));
-			auto ptrTy = getType(getObject(ptrId).type);
-			auto ptr = GetPointerToData(ptrId, 0, state);
-			bool interleavedByLane = IsStorageInterleavedByLane(ptrTy.storageClass);
-			// TODO: GLSL modf() takes an output parameter and thus the pointer is assumed
-			// to be in bounds even for inactive lanes.
-			// - Clarify the SPIR-V spec.
-			// - Eliminate lane masking and assume interleaving.
-			auto robustness = OutOfBoundsBehavior::UndefinedBehavior;
-
-			for (auto i = 0u; i < type.sizeInComponents; i++)
-			{
-				SIMD::Float whole, frac;
-				std::tie(whole, frac) = Modf(val.Float(i));
-				dst.move(i, frac);
-				auto p = ptr + (i * sizeof(float));
-				if (interleavedByLane) { p = InterleaveByLane(p); }
-				p.Store(whole, robustness, state->activeLaneMask());
-			}
-			break;
-		}
-		case GLSLstd450ModfStruct:
-		{
-			auto val = GenericValue(this, state, insn.word(5));
-			auto valTy = getType(val.type);
-
-			for (auto i = 0u; i < valTy.sizeInComponents; i++)
-			{
-				SIMD::Float whole, frac;
-				std::tie(whole, frac) = Modf(val.Float(i));
-				dst.move(i, frac);
-				dst.move(i + valTy.sizeInComponents, whole);
-			}
-			break;
-		}
-		case GLSLstd450PackSnorm4x8:
-		{
-			auto val = GenericValue(this, state, insn.word(5));
-			dst.move(0, (SIMD::Int(Round(Min(Max(val.Float(0), SIMD::Float(-1.0f)), SIMD::Float(1.0f)) * SIMD::Float(127.0f))) &
-						 SIMD::Int(0xFF)) |
-						((SIMD::Int(Round(Min(Max(val.Float(1), SIMD::Float(-1.0f)), SIMD::Float(1.0f)) * SIMD::Float(127.0f))) &
-						  SIMD::Int(0xFF)) << 8) |
-						((SIMD::Int(Round(Min(Max(val.Float(2), SIMD::Float(-1.0f)), SIMD::Float(1.0f)) * SIMD::Float(127.0f))) &
-						  SIMD::Int(0xFF)) << 16) |
-						((SIMD::Int(Round(Min(Max(val.Float(3), SIMD::Float(-1.0f)), SIMD::Float(1.0f)) * SIMD::Float(127.0f))) &
-						  SIMD::Int(0xFF)) << 24));
-			break;
-		}
-		case GLSLstd450PackUnorm4x8:
-		{
-			auto val = GenericValue(this, state, insn.word(5));
-			dst.move(0, (SIMD::UInt(Round(Min(Max(val.Float(0), SIMD::Float(0.0f)), SIMD::Float(1.0f)) * SIMD::Float(255.0f)))) |
-						((SIMD::UInt(Round(Min(Max(val.Float(1), SIMD::Float(0.0f)), SIMD::Float(1.0f)) * SIMD::Float(255.0f)))) << 8) |
-						((SIMD::UInt(Round(Min(Max(val.Float(2), SIMD::Float(0.0f)), SIMD::Float(1.0f)) * SIMD::Float(255.0f)))) << 16) |
-						((SIMD::UInt(Round(Min(Max(val.Float(3), SIMD::Float(0.0f)), SIMD::Float(1.0f)) * SIMD::Float(255.0f)))) << 24));
-			break;
-		}
-		case GLSLstd450PackSnorm2x16:
-		{
-			auto val = GenericValue(this, state, insn.word(5));
-			dst.move(0, (SIMD::Int(Round(Min(Max(val.Float(0), SIMD::Float(-1.0f)), SIMD::Float(1.0f)) * SIMD::Float(32767.0f))) &
-						 SIMD::Int(0xFFFF)) |
-						((SIMD::Int(Round(Min(Max(val.Float(1), SIMD::Float(-1.0f)), SIMD::Float(1.0f)) * SIMD::Float(32767.0f))) &
-						  SIMD::Int(0xFFFF)) << 16));
-			break;
-		}
-		case GLSLstd450PackUnorm2x16:
-		{
-			auto val = GenericValue(this, state, insn.word(5));
-			dst.move(0, (SIMD::UInt(Round(Min(Max(val.Float(0), SIMD::Float(0.0f)), SIMD::Float(1.0f)) * SIMD::Float(65535.0f))) &
-						 SIMD::UInt(0xFFFF)) |
-						((SIMD::UInt(Round(Min(Max(val.Float(1), SIMD::Float(0.0f)), SIMD::Float(1.0f)) * SIMD::Float(65535.0f))) &
-						  SIMD::UInt(0xFFFF)) << 16));
-			break;
-		}
-		case GLSLstd450PackHalf2x16:
-		{
-			auto val = GenericValue(this, state, insn.word(5));
-			dst.move(0, FloatToHalfBits(val.UInt(0), false) | FloatToHalfBits(val.UInt(1), true));
-			break;
-		}
-		case GLSLstd450UnpackSnorm4x8:
-		{
-			auto val = GenericValue(this, state, insn.word(5));
-			dst.move(0, Min(Max(SIMD::Float(((val.Int(0)<<24) & SIMD::Int(0xFF000000))) * SIMD::Float(1.0f / float(0x7f000000)), SIMD::Float(-1.0f)), SIMD::Float(1.0f)));
-			dst.move(1, Min(Max(SIMD::Float(((val.Int(0)<<16) & SIMD::Int(0xFF000000))) * SIMD::Float(1.0f / float(0x7f000000)), SIMD::Float(-1.0f)), SIMD::Float(1.0f)));
-			dst.move(2, Min(Max(SIMD::Float(((val.Int(0)<<8) & SIMD::Int(0xFF000000))) * SIMD::Float(1.0f / float(0x7f000000)), SIMD::Float(-1.0f)), SIMD::Float(1.0f)));
-			dst.move(3, Min(Max(SIMD::Float(((val.Int(0)) & SIMD::Int(0xFF000000))) * SIMD::Float(1.0f / float(0x7f000000)), SIMD::Float(-1.0f)), SIMD::Float(1.0f)));
-			break;
-		}
-		case GLSLstd450UnpackUnorm4x8:
-		{
-			auto val = GenericValue(this, state, insn.word(5));
-			dst.move(0, SIMD::Float((val.UInt(0) & SIMD::UInt(0xFF))) * SIMD::Float(1.0f / 255.f));
-			dst.move(1, SIMD::Float(((val.UInt(0)>>8) & SIMD::UInt(0xFF))) * SIMD::Float(1.0f / 255.f));
-			dst.move(2, SIMD::Float(((val.UInt(0)>>16) & SIMD::UInt(0xFF))) * SIMD::Float(1.0f / 255.f));
-			dst.move(3, SIMD::Float(((val.UInt(0)>>24) & SIMD::UInt(0xFF))) * SIMD::Float(1.0f / 255.f));
-			break;
-		}
-		case GLSLstd450UnpackSnorm2x16:
-		{
-			auto val = GenericValue(this, state, insn.word(5));
-			// clamp(f / 32767.0, -1.0, 1.0)
-			dst.move(0, Min(Max(SIMD::Float(As<SIMD::Int>((val.UInt(0) & SIMD::UInt(0x0000FFFF)) << 16)) *
-								SIMD::Float(1.0f / float(0x7FFF0000)), SIMD::Float(-1.0f)), SIMD::Float(1.0f)));
-			dst.move(1, Min(Max(SIMD::Float(As<SIMD::Int>(val.UInt(0) & SIMD::UInt(0xFFFF0000))) * SIMD::Float(1.0f / float(0x7FFF0000)),
-								SIMD::Float(-1.0f)), SIMD::Float(1.0f)));
-			break;
-		}
-		case GLSLstd450UnpackUnorm2x16:
-		{
-			auto val = GenericValue(this, state, insn.word(5));
-			// f / 65535.0
-			dst.move(0, SIMD::Float((val.UInt(0) & SIMD::UInt(0x0000FFFF)) << 16) * SIMD::Float(1.0f / float(0xFFFF0000)));
-			dst.move(1, SIMD::Float(val.UInt(0) & SIMD::UInt(0xFFFF0000)) * SIMD::Float(1.0f / float(0xFFFF0000)));
-			break;
-		}
-		case GLSLstd450UnpackHalf2x16:
-		{
-			auto val = GenericValue(this, state, insn.word(5));
-			dst.move(0, halfToFloatBits(val.UInt(0) & SIMD::UInt(0x0000FFFF)));
-			dst.move(1, halfToFloatBits((val.UInt(0) & SIMD::UInt(0xFFFF0000)) >> 16));
-			break;
-		}
-		case GLSLstd450Fma:
-		{
-			auto a = GenericValue(this, state, insn.word(5));
-			auto b = GenericValue(this, state, insn.word(6));
-			auto c = GenericValue(this, state, insn.word(7));
-			for (auto i = 0u; i < type.sizeInComponents; i++)
-			{
-				dst.move(i, FMA(a.Float(i), b.Float(i), c.Float(i)));
-			}
-			break;
-		}
-		case GLSLstd450Frexp:
-		{
-			auto val = GenericValue(this, state, insn.word(5));
-			auto ptrId = Object::ID(insn.word(6));
-			auto ptrTy = getType(getObject(ptrId).type);
-			auto ptr = GetPointerToData(ptrId, 0, state);
-			bool interleavedByLane = IsStorageInterleavedByLane(ptrTy.storageClass);
-			// TODO: GLSL frexp() takes an output parameter and thus the pointer is assumed
-			// to be in bounds even for inactive lanes.
-			// - Clarify the SPIR-V spec.
-			// - Eliminate lane masking and assume interleaving.
-			auto robustness = OutOfBoundsBehavior::UndefinedBehavior;
-
-			for (auto i = 0u; i < type.sizeInComponents; i++)
-			{
-				SIMD::Float significand;
-				SIMD::Int exponent;
-				std::tie(significand, exponent) = Frexp(val.Float(i));
-
-				dst.move(i, significand);
-
-				auto p = ptr + (i * sizeof(float));
-				if (interleavedByLane) { p = InterleaveByLane(p); }
-				p.Store(exponent, robustness, state->activeLaneMask());
-			}
-			break;
-		}
-		case GLSLstd450FrexpStruct:
-		{
-			auto val = GenericValue(this, state, insn.word(5));
-			auto numComponents = getType(val.type).sizeInComponents;
-			for (auto i = 0u; i < numComponents; i++)
-			{
-				auto significandAndExponent = Frexp(val.Float(i));
-				dst.move(i, significandAndExponent.first);
-				dst.move(i + numComponents, significandAndExponent.second);
-			}
-			break;
-		}
-		case GLSLstd450Ldexp:
-		{
-			auto significand = GenericValue(this, state, insn.word(5));
-			auto exponent = GenericValue(this, state, insn.word(6));
-			for (auto i = 0u; i < type.sizeInComponents; i++)
-			{
-				// Assumes IEEE 754
-				auto in = significand.Float(i);
-				auto significandExponent = Exponent(in);
-				auto combinedExponent = exponent.Int(i) + significandExponent;
-				auto isSignificandZero     = SIMD::UInt(CmpEQ(significand.Int(i), SIMD::Int(0)));
-				auto isSignificandInf      = SIMD::UInt(IsInf(in));
-				auto isSignificandNaN      = SIMD::UInt(IsNan(in));
-				auto isExponentNotTooSmall = SIMD::UInt(CmpGE(combinedExponent, SIMD::Int(-126)));
-				auto isExponentNotTooLarge = SIMD::UInt(CmpLE(combinedExponent, SIMD::Int(128)));
-				auto isExponentInBounds    = isExponentNotTooSmall & isExponentNotTooLarge;
-
-				SIMD::UInt v;
-				v  = significand.UInt(i) & SIMD::UInt(0x7FFFFF); // Add significand.
-				v |= (SIMD::UInt(combinedExponent + SIMD::Int(126)) << SIMD::UInt(23)); // Add exponent.
-				v &= isExponentInBounds; // Clear v if the exponent is OOB.
-
-				v |= significand.UInt(i) & SIMD::UInt(0x80000000); // Add sign bit.
-				v |= ~isExponentNotTooLarge & SIMD::UInt(0x7F800000); // Mark as inf if the exponent is too great.
-
-				// If the input significand is zero, inf or nan, just return the
-				// input significand.
-				auto passthrough = isSignificandZero | isSignificandInf | isSignificandNaN;
-				v = (v & ~passthrough) | (significand.UInt(i) & passthrough);
-
-				dst.move(i, As<SIMD::Float>(v));
-			}
-			break;
-		}
-		case GLSLstd450Radians:
-		{
-			auto degrees = GenericValue(this, state, insn.word(5));
-			for (auto i = 0u; i < type.sizeInComponents; i++)
-			{
-				dst.move(i, degrees.Float(i) * SIMD::Float(PI / 180.0f));
-			}
-			break;
-		}
-		case GLSLstd450Degrees:
-		{
-			auto radians = GenericValue(this, state, insn.word(5));
-			for (auto i = 0u; i < type.sizeInComponents; i++)
-			{
-				dst.move(i, radians.Float(i) * SIMD::Float(180.0f / PI));
-			}
-			break;
-		}
-		case GLSLstd450Sin:
-		{
-			auto radians = GenericValue(this, state, insn.word(5));
-			for (auto i = 0u; i < type.sizeInComponents; i++)
-			{
-				dst.move(i, Sin(radians.Float(i)));
-			}
-			break;
-		}
-		case GLSLstd450Cos:
-		{
-			auto radians = GenericValue(this, state, insn.word(5));
-			for (auto i = 0u; i < type.sizeInComponents; i++)
-			{
-				dst.move(i, Cos(radians.Float(i)));
-			}
-			break;
-		}
-		case GLSLstd450Tan:
-		{
-			auto radians = GenericValue(this, state, insn.word(5));
-			for (auto i = 0u; i < type.sizeInComponents; i++)
-			{
-				dst.move(i, Tan(radians.Float(i)));
-			}
-			break;
-		}
-		case GLSLstd450Asin:
-		{
-			auto val = GenericValue(this, state, insn.word(5));
-			for (auto i = 0u; i < type.sizeInComponents; i++)
-			{
-				dst.move(i, Asin(val.Float(i)));
-			}
-			break;
-		}
-		case GLSLstd450Acos:
-		{
-			auto val = GenericValue(this, state, insn.word(5));
-			for (auto i = 0u; i < type.sizeInComponents; i++)
-			{
-				dst.move(i, Acos(val.Float(i)));
-			}
-			break;
-		}
-		case GLSLstd450Atan:
-		{
-			auto val = GenericValue(this, state, insn.word(5));
-			for (auto i = 0u; i < type.sizeInComponents; i++)
-			{
-				dst.move(i, Atan(val.Float(i)));
-			}
-			break;
-		}
-		case GLSLstd450Sinh:
-		{
-			auto val = GenericValue(this, state, insn.word(5));
-			for (auto i = 0u; i < type.sizeInComponents; i++)
-			{
-				dst.move(i, Sinh(val.Float(i)));
-			}
-			break;
-		}
-		case GLSLstd450Cosh:
-		{
-			auto val = GenericValue(this, state, insn.word(5));
-			for (auto i = 0u; i < type.sizeInComponents; i++)
-			{
-				dst.move(i, Cosh(val.Float(i)));
-			}
-			break;
-		}
-		case GLSLstd450Tanh:
-		{
-			auto val = GenericValue(this, state, insn.word(5));
-			for (auto i = 0u; i < type.sizeInComponents; i++)
-			{
-				dst.move(i, Tanh(val.Float(i)));
-			}
-			break;
-		}
-		case GLSLstd450Asinh:
-		{
-			auto val = GenericValue(this, state, insn.word(5));
-			for (auto i = 0u; i < type.sizeInComponents; i++)
-			{
-				dst.move(i, Asinh(val.Float(i)));
-			}
-			break;
-		}
-		case GLSLstd450Acosh:
-		{
-			auto val = GenericValue(this, state, insn.word(5));
-			for (auto i = 0u; i < type.sizeInComponents; i++)
-			{
-				dst.move(i, Acosh(val.Float(i)));
-			}
-			break;
-		}
-		case GLSLstd450Atanh:
-		{
-			auto val = GenericValue(this, state, insn.word(5));
-			for (auto i = 0u; i < type.sizeInComponents; i++)
-			{
-				dst.move(i, Atanh(val.Float(i)));
-			}
-			break;
-		}
-		case GLSLstd450Atan2:
-		{
-			auto x = GenericValue(this, state, insn.word(5));
-			auto y = GenericValue(this, state, insn.word(6));
-			for (auto i = 0u; i < type.sizeInComponents; i++)
-			{
-				dst.move(i, Atan2(x.Float(i), y.Float(i)));
-			}
-			break;
-		}
-		case GLSLstd450Pow:
-		{
-			auto x = GenericValue(this, state, insn.word(5));
-			auto y = GenericValue(this, state, insn.word(6));
-			for (auto i = 0u; i < type.sizeInComponents; i++)
-			{
-				dst.move(i, Pow(x.Float(i), y.Float(i)));
-			}
-			break;
-		}
-		case GLSLstd450Exp:
-		{
-			auto val = GenericValue(this, state, insn.word(5));
-			for (auto i = 0u; i < type.sizeInComponents; i++)
-			{
-				dst.move(i, Exp(val.Float(i)));
-			}
-			break;
-		}
-		case GLSLstd450Log:
-		{
-			auto val = GenericValue(this, state, insn.word(5));
-			for (auto i = 0u; i < type.sizeInComponents; i++)
-			{
-				dst.move(i, Log(val.Float(i)));
-			}
-			break;
-		}
-		case GLSLstd450Exp2:
-		{
-			auto val = GenericValue(this, state, insn.word(5));
-			for (auto i = 0u; i < type.sizeInComponents; i++)
-			{
-				dst.move(i, Exp2(val.Float(i)));
-			}
-			break;
-		}
-		case GLSLstd450Log2:
-		{
-			auto val = GenericValue(this, state, insn.word(5));
-			for (auto i = 0u; i < type.sizeInComponents; i++)
-			{
-				dst.move(i, Log2(val.Float(i)));
-			}
-			break;
-		}
-		case GLSLstd450Sqrt:
-		{
-			auto val = GenericValue(this, state, insn.word(5));
-			for (auto i = 0u; i < type.sizeInComponents; i++)
-			{
-				dst.move(i, Sqrt(val.Float(i)));
-			}
-			break;
-		}
-		case GLSLstd450InverseSqrt:
-		{
-			auto val = GenericValue(this, state, insn.word(5));
-			Decorations d;
-			ApplyDecorationsForId(&d, insn.word(5));
-			if (d.RelaxedPrecision)
-			{
-				for (auto i = 0u; i < type.sizeInComponents; i++)
-				{
-					dst.move(i, RcpSqrt_pp(val.Float(i)));
-				}
-			}
-			else
-			{
-				for (auto i = 0u; i < type.sizeInComponents; i++)
-				{
-					dst.move(i, SIMD::Float(1.0f) / Sqrt(val.Float(i)));
-				}
-			}
-			break;
-		}
-		case GLSLstd450Determinant:
-		{
-			auto mat = GenericValue(this, state, insn.word(5));
-			auto numComponents = getType(mat.type).sizeInComponents;
-			switch (numComponents)
-			{
-			case 4: // 2x2
-				dst.move(0, Determinant(
-					mat.Float(0), mat.Float(1),
-					mat.Float(2), mat.Float(3)));
-				break;
-			case 9: // 3x3
-				dst.move(0, Determinant(
-					mat.Float(0), mat.Float(1), mat.Float(2),
-					mat.Float(3), mat.Float(4), mat.Float(5),
-					mat.Float(6), mat.Float(7), mat.Float(8)));
-				break;
-			case 16: // 4x4
-				dst.move(0, Determinant(
-					mat.Float(0),  mat.Float(1),  mat.Float(2),  mat.Float(3),
-					mat.Float(4),  mat.Float(5),  mat.Float(6),  mat.Float(7),
-					mat.Float(8),  mat.Float(9),  mat.Float(10), mat.Float(11),
-					mat.Float(12), mat.Float(13), mat.Float(14), mat.Float(15)));
-				break;
-			default:
-				UNREACHABLE("GLSLstd450Determinant can only operate with square matrices. Got %d elements", int(numComponents));
-			}
-			break;
-		}
-		case GLSLstd450MatrixInverse:
-		{
-			auto mat = GenericValue(this, state, insn.word(5));
-			auto numComponents = getType(mat.type).sizeInComponents;
-			switch (numComponents)
-			{
-			case 4: // 2x2
-			{
-				auto inv = MatrixInverse(
-					mat.Float(0), mat.Float(1),
-					mat.Float(2), mat.Float(3));
-				for (uint32_t i = 0; i < inv.size(); i++)
-				{
-					dst.move(i, inv[i]);
-				}
-				break;
-			}
-			case 9: // 3x3
-			{
-				auto inv = MatrixInverse(
-					mat.Float(0), mat.Float(1), mat.Float(2),
-					mat.Float(3), mat.Float(4), mat.Float(5),
-					mat.Float(6), mat.Float(7), mat.Float(8));
-				for (uint32_t i = 0; i < inv.size(); i++)
-				{
-					dst.move(i, inv[i]);
-				}
-				break;
-			}
-			case 16: // 4x4
-			{
-				auto inv = MatrixInverse(
-					mat.Float(0),  mat.Float(1),  mat.Float(2),  mat.Float(3),
-					mat.Float(4),  mat.Float(5),  mat.Float(6),  mat.Float(7),
-					mat.Float(8),  mat.Float(9),  mat.Float(10), mat.Float(11),
-					mat.Float(12), mat.Float(13), mat.Float(14), mat.Float(15));
-				for (uint32_t i = 0; i < inv.size(); i++)
-				{
-					dst.move(i, inv[i]);
-				}
-				break;
-			}
-			default:
-				UNREACHABLE("GLSLstd450MatrixInverse can only operate with square matrices. Got %d elements", int(numComponents));
-			}
-			break;
-		}
-		case GLSLstd450IMix:
-		{
-			UNREACHABLE("GLSLstd450IMix has been removed from the specification");
-			break;
-		}
-		case GLSLstd450PackDouble2x32:
-		{
-			UNSUPPORTED("SPIR-V Float64 Capability (GLSLstd450PackDouble2x32)");
-			break;
-		}
-		case GLSLstd450UnpackDouble2x32:
-		{
-			UNSUPPORTED("SPIR-V Float64 Capability (GLSLstd450UnpackDouble2x32)");
-			break;
-		}
-		case GLSLstd450FindILsb:
-		{
-			auto val = GenericValue(this, state, insn.word(5));
-			for (auto i = 0u; i < type.sizeInComponents; i++)
-			{
-				auto v = val.UInt(i);
-				dst.move(i, Cttz(v, true) | CmpEQ(v, SIMD::UInt(0)));
-			}
-			break;
-		}
-		case GLSLstd450FindSMsb:
-		{
-			auto val = GenericValue(this, state, insn.word(5));
-			for (auto i = 0u; i < type.sizeInComponents; i++)
-			{
-				auto v = val.UInt(i) ^ As<SIMD::UInt>(CmpLT(val.Int(i), SIMD::Int(0)));
-				dst.move(i, SIMD::UInt(31) - Ctlz(v, false));
-			}
-			break;
-		}
-		case GLSLstd450FindUMsb:
-		{
-			auto val = GenericValue(this, state, insn.word(5));
-			for (auto i = 0u; i < type.sizeInComponents; i++)
-			{
-				dst.move(i, SIMD::UInt(31) - Ctlz(val.UInt(i), false));
-			}
-			break;
-		}
-		case GLSLstd450InterpolateAtCentroid:
-		{
-			UNSUPPORTED("SPIR-V SampleRateShading Capability (GLSLstd450InterpolateAtCentroid)");
-			break;
-		}
-		case GLSLstd450InterpolateAtSample:
-		{
-			UNSUPPORTED("SPIR-V SampleRateShading Capability (GLSLstd450InterpolateAtCentroid)");
-			break;
-		}
-		case GLSLstd450InterpolateAtOffset:
-		{
-			UNSUPPORTED("SPIR-V SampleRateShading Capability (GLSLstd450InterpolateAtCentroid)");
-			break;
-		}
-		case GLSLstd450NMin:
-		{
-			auto x = GenericValue(this, state, insn.word(5));
-			auto y = GenericValue(this, state, insn.word(6));
-			for (auto i = 0u; i < type.sizeInComponents; i++)
-			{
-				dst.move(i, NMin(x.Float(i), y.Float(i)));
-			}
-			break;
-		}
-		case GLSLstd450NMax:
-		{
-			auto x = GenericValue(this, state, insn.word(5));
-			auto y = GenericValue(this, state, insn.word(6));
-			for (auto i = 0u; i < type.sizeInComponents; i++)
-			{
-				dst.move(i, NMax(x.Float(i), y.Float(i)));
-			}
-			break;
-		}
-		case GLSLstd450NClamp:
-		{
-			auto x = GenericValue(this, state, insn.word(5));
-			auto minVal = GenericValue(this, state, insn.word(6));
-			auto maxVal = GenericValue(this, state, insn.word(7));
-			for (auto i = 0u; i < type.sizeInComponents; i++)
-			{
-				auto clamp = NMin(NMax(x.Float(i), minVal.Float(i)), maxVal.Float(i));
-				dst.move(i, clamp);
-			}
-			break;
-		}
-		default:
-			UNREACHABLE("ExtInst %d", int(extInstIndex));
-			break;
-		}
-
-		return EmitResult::Continue;
-	}
-
 	std::memory_order SpirvShader::MemoryOrder(spv::MemorySemanticsMask memorySemantics)
 	{
 		auto control = static_cast<uint32_t>(memorySemantics) & static_cast<uint32_t>(
diff --git a/src/Pipeline/SpirvShaderGLSLstd450.cpp b/src/Pipeline/SpirvShaderGLSLstd450.cpp
new file mode 100644
index 0000000..f9485ca
--- /dev/null
+++ b/src/Pipeline/SpirvShaderGLSLstd450.cpp
@@ -0,0 +1,951 @@
+// Copyright 2019 The SwiftShader Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "SpirvShader.hpp"
+
+#include "ShaderCore.hpp"
+
+#include <spirv/unified1/spirv.hpp>
+#include <spirv/unified1/GLSL.std.450.h>
+
+namespace
+{
+	constexpr float PI = 3.141592653589793f;
+}
+
+namespace sw {
+
+SpirvShader::EmitResult SpirvShader::EmitExtendedInstruction(InsnIterator insn, EmitState *state) const
+{
+	auto &type = getType(insn.word(1));
+	auto &dst = state->createIntermediate(insn.word(2), type.sizeInComponents);
+	auto extInstIndex = static_cast<GLSLstd450>(insn.word(4));
+
+	switch (extInstIndex)
+	{
+	case GLSLstd450FAbs:
+	{
+		auto src = GenericValue(this, state, insn.word(5));
+		for (auto i = 0u; i < type.sizeInComponents; i++)
+		{
+			dst.move(i, Abs(src.Float(i)));
+		}
+		break;
+	}
+	case GLSLstd450SAbs:
+	{
+		auto src = GenericValue(this, state, insn.word(5));
+		for (auto i = 0u; i < type.sizeInComponents; i++)
+		{
+			dst.move(i, Abs(src.Int(i)));
+		}
+		break;
+	}
+	case GLSLstd450Cross:
+	{
+		auto lhs = GenericValue(this, state, insn.word(5));
+		auto rhs = GenericValue(this, state, insn.word(6));
+		dst.move(0, lhs.Float(1) * rhs.Float(2) - rhs.Float(1) * lhs.Float(2));
+		dst.move(1, lhs.Float(2) * rhs.Float(0) - rhs.Float(2) * lhs.Float(0));
+		dst.move(2, lhs.Float(0) * rhs.Float(1) - rhs.Float(0) * lhs.Float(1));
+		break;
+	}
+	case GLSLstd450Floor:
+	{
+		auto src = GenericValue(this, state, insn.word(5));
+		for (auto i = 0u; i < type.sizeInComponents; i++)
+		{
+			dst.move(i, Floor(src.Float(i)));
+		}
+		break;
+	}
+	case GLSLstd450Trunc:
+	{
+		auto src = GenericValue(this, state, insn.word(5));
+		for (auto i = 0u; i < type.sizeInComponents; i++)
+		{
+			dst.move(i, Trunc(src.Float(i)));
+		}
+		break;
+	}
+	case GLSLstd450Ceil:
+	{
+		auto src = GenericValue(this, state, insn.word(5));
+		for (auto i = 0u; i < type.sizeInComponents; i++)
+		{
+			dst.move(i, Ceil(src.Float(i)));
+		}
+		break;
+	}
+	case GLSLstd450Fract:
+	{
+		auto src = GenericValue(this, state, insn.word(5));
+		for (auto i = 0u; i < type.sizeInComponents; i++)
+		{
+			dst.move(i, Frac(src.Float(i)));
+		}
+		break;
+	}
+	case GLSLstd450Round:
+	{
+		auto src = GenericValue(this, state, insn.word(5));
+		for (auto i = 0u; i < type.sizeInComponents; i++)
+		{
+			dst.move(i, Round(src.Float(i)));
+		}
+		break;
+	}
+	case GLSLstd450RoundEven:
+	{
+		auto src = GenericValue(this, state, insn.word(5));
+		for (auto i = 0u; i < type.sizeInComponents; i++)
+		{
+			auto x = Round(src.Float(i));
+			// dst = round(src) + ((round(src) < src) * 2 - 1) * (fract(src) == 0.5) * isOdd(round(src));
+			dst.move(i, x + ((SIMD::Float(CmpLT(x, src.Float(i)) & SIMD::Int(1)) * SIMD::Float(2.0f)) - SIMD::Float(1.0f)) *
+					SIMD::Float(CmpEQ(Frac(src.Float(i)), SIMD::Float(0.5f)) & SIMD::Int(1)) * SIMD::Float(Int4(x) & SIMD::Int(1)));
+		}
+		break;
+	}
+	case GLSLstd450FMin:
+	{
+		auto lhs = GenericValue(this, state, insn.word(5));
+		auto rhs = GenericValue(this, state, insn.word(6));
+		for (auto i = 0u; i < type.sizeInComponents; i++)
+		{
+			dst.move(i, Min(lhs.Float(i), rhs.Float(i)));
+		}
+		break;
+	}
+	case GLSLstd450FMax:
+	{
+		auto lhs = GenericValue(this, state, insn.word(5));
+		auto rhs = GenericValue(this, state, insn.word(6));
+		for (auto i = 0u; i < type.sizeInComponents; i++)
+		{
+			dst.move(i, Max(lhs.Float(i), rhs.Float(i)));
+		}
+		break;
+	}
+	case GLSLstd450SMin:
+	{
+		auto lhs = GenericValue(this, state, insn.word(5));
+		auto rhs = GenericValue(this, state, insn.word(6));
+		for (auto i = 0u; i < type.sizeInComponents; i++)
+		{
+			dst.move(i, Min(lhs.Int(i), rhs.Int(i)));
+		}
+		break;
+	}
+	case GLSLstd450SMax:
+	{
+		auto lhs = GenericValue(this, state, insn.word(5));
+		auto rhs = GenericValue(this, state, insn.word(6));
+		for (auto i = 0u; i < type.sizeInComponents; i++)
+		{
+			dst.move(i, Max(lhs.Int(i), rhs.Int(i)));
+		}
+		break;
+	}
+	case GLSLstd450UMin:
+	{
+		auto lhs = GenericValue(this, state, insn.word(5));
+		auto rhs = GenericValue(this, state, insn.word(6));
+		for (auto i = 0u; i < type.sizeInComponents; i++)
+		{
+			dst.move(i, Min(lhs.UInt(i), rhs.UInt(i)));
+		}
+		break;
+	}
+	case GLSLstd450UMax:
+	{
+		auto lhs = GenericValue(this, state, insn.word(5));
+		auto rhs = GenericValue(this, state, insn.word(6));
+		for (auto i = 0u; i < type.sizeInComponents; i++)
+		{
+			dst.move(i, Max(lhs.UInt(i), rhs.UInt(i)));
+		}
+		break;
+	}
+	case GLSLstd450Step:
+	{
+		auto edge = GenericValue(this, state, insn.word(5));
+		auto x = GenericValue(this, state, insn.word(6));
+		for (auto i = 0u; i < type.sizeInComponents; i++)
+		{
+			dst.move(i, CmpNLT(x.Float(i), edge.Float(i)) & As<SIMD::Int>(SIMD::Float(1.0f)));
+		}
+		break;
+	}
+	case GLSLstd450SmoothStep:
+	{
+		auto edge0 = GenericValue(this, state, insn.word(5));
+		auto edge1 = GenericValue(this, state, insn.word(6));
+		auto x = GenericValue(this, state, insn.word(7));
+		for (auto i = 0u; i < type.sizeInComponents; i++)
+		{
+			auto tx = Min(Max((x.Float(i) - edge0.Float(i)) /
+					(edge1.Float(i) - edge0.Float(i)), SIMD::Float(0.0f)), SIMD::Float(1.0f));
+			dst.move(i, tx * tx * (Float4(3.0f) - Float4(2.0f) * tx));
+		}
+		break;
+	}
+	case GLSLstd450FMix:
+	{
+		auto x = GenericValue(this, state, insn.word(5));
+		auto y = GenericValue(this, state, insn.word(6));
+		auto a = GenericValue(this, state, insn.word(7));
+		for (auto i = 0u; i < type.sizeInComponents; i++)
+		{
+			dst.move(i, a.Float(i) * (y.Float(i) - x.Float(i)) + x.Float(i));
+		}
+		break;
+	}
+	case GLSLstd450FClamp:
+	{
+		auto x = GenericValue(this, state, insn.word(5));
+		auto minVal = GenericValue(this, state, insn.word(6));
+		auto maxVal = GenericValue(this, state, insn.word(7));
+		for (auto i = 0u; i < type.sizeInComponents; i++)
+		{
+			dst.move(i, Min(Max(x.Float(i), minVal.Float(i)), maxVal.Float(i)));
+		}
+		break;
+	}
+	case GLSLstd450SClamp:
+	{
+		auto x = GenericValue(this, state, insn.word(5));
+		auto minVal = GenericValue(this, state, insn.word(6));
+		auto maxVal = GenericValue(this, state, insn.word(7));
+		for (auto i = 0u; i < type.sizeInComponents; i++)
+		{
+			dst.move(i, Min(Max(x.Int(i), minVal.Int(i)), maxVal.Int(i)));
+		}
+		break;
+	}
+	case GLSLstd450UClamp:
+	{
+		auto x = GenericValue(this, state, insn.word(5));
+		auto minVal = GenericValue(this, state, insn.word(6));
+		auto maxVal = GenericValue(this, state, insn.word(7));
+		for (auto i = 0u; i < type.sizeInComponents; i++)
+		{
+			dst.move(i, Min(Max(x.UInt(i), minVal.UInt(i)), maxVal.UInt(i)));
+		}
+		break;
+	}
+	case GLSLstd450FSign:
+	{
+		auto src = GenericValue(this, state, insn.word(5));
+		for (auto i = 0u; i < type.sizeInComponents; i++)
+		{
+			auto neg = As<SIMD::Int>(CmpLT(src.Float(i), SIMD::Float(-0.0f))) & As<SIMD::Int>(SIMD::Float(-1.0f));
+			auto pos = As<SIMD::Int>(CmpNLE(src.Float(i), SIMD::Float(+0.0f))) & As<SIMD::Int>(SIMD::Float(1.0f));
+			dst.move(i, neg | pos);
+		}
+		break;
+	}
+	case GLSLstd450SSign:
+	{
+		auto src = GenericValue(this, state, insn.word(5));
+		for (auto i = 0u; i < type.sizeInComponents; i++)
+		{
+			auto neg = CmpLT(src.Int(i), SIMD::Int(0)) & SIMD::Int(-1);
+			auto pos = CmpNLE(src.Int(i), SIMD::Int(0)) & SIMD::Int(1);
+			dst.move(i, neg | pos);
+		}
+		break;
+	}
+	case GLSLstd450Reflect:
+	{
+		auto I = GenericValue(this, state, insn.word(5));
+		auto N = GenericValue(this, state, insn.word(6));
+
+		SIMD::Float d = Dot(type.sizeInComponents, I, N);
+
+		for (auto i = 0u; i < type.sizeInComponents; i++)
+		{
+			dst.move(i, I.Float(i) - SIMD::Float(2.0f) * d * N.Float(i));
+		}
+		break;
+	}
+	case GLSLstd450Refract:
+	{
+		auto I = GenericValue(this, state, insn.word(5));
+		auto N = GenericValue(this, state, insn.word(6));
+		auto eta = GenericValue(this, state, insn.word(7));
+
+		SIMD::Float d = Dot(type.sizeInComponents, I, N);
+		SIMD::Float k = SIMD::Float(1.0f) - eta.Float(0) * eta.Float(0) * (SIMD::Float(1.0f) - d * d);
+		SIMD::Int pos = CmpNLT(k, SIMD::Float(0.0f));
+		SIMD::Float t = (eta.Float(0) * d + Sqrt(k));
+
+		for (auto i = 0u; i < type.sizeInComponents; i++)
+		{
+			dst.move(i, pos & As<SIMD::Int>(eta.Float(0) * I.Float(i) - t * N.Float(i)));
+		}
+		break;
+	}
+	case GLSLstd450FaceForward:
+	{
+		auto N = GenericValue(this, state, insn.word(5));
+		auto I = GenericValue(this, state, insn.word(6));
+		auto Nref = GenericValue(this, state, insn.word(7));
+
+		SIMD::Float d = Dot(type.sizeInComponents, I, Nref);
+		SIMD::Int neg = CmpLT(d, SIMD::Float(0.0f));
+
+		for (auto i = 0u; i < type.sizeInComponents; i++)
+		{
+			auto n = N.Float(i);
+			dst.move(i, (neg & As<SIMD::Int>(n)) | (~neg & As<SIMD::Int>(-n)));
+		}
+		break;
+	}
+	case GLSLstd450Length:
+	{
+		auto x = GenericValue(this, state, insn.word(5));
+		SIMD::Float d = Dot(getType(getObject(insn.word(5)).type).sizeInComponents, x, x);
+
+		dst.move(0, Sqrt(d));
+		break;
+	}
+	case GLSLstd450Normalize:
+	{
+		auto x = GenericValue(this, state, insn.word(5));
+		SIMD::Float d = Dot(getType(getObject(insn.word(5)).type).sizeInComponents, x, x);
+		SIMD::Float invLength = SIMD::Float(1.0f) / Sqrt(d);
+
+		for (auto i = 0u; i < type.sizeInComponents; i++)
+		{
+			dst.move(i, invLength * x.Float(i));
+		}
+		break;
+	}
+	case GLSLstd450Distance:
+	{
+		auto p0 = GenericValue(this, state, insn.word(5));
+		auto p1 = GenericValue(this, state, insn.word(6));
+		auto p0Type = getType(p0.type);
+
+		// sqrt(dot(p0-p1, p0-p1))
+		SIMD::Float d = (p0.Float(0) - p1.Float(0)) * (p0.Float(0) - p1.Float(0));
+
+		for (auto i = 1u; i < p0Type.sizeInComponents; i++)
+		{
+			d += (p0.Float(i) - p1.Float(i)) * (p0.Float(i) - p1.Float(i));
+		}
+
+		dst.move(0, Sqrt(d));
+		break;
+	}
+	case GLSLstd450Modf:
+	{
+		auto val = GenericValue(this, state, insn.word(5));
+		auto ptrId = Object::ID(insn.word(6));
+		auto ptrTy = getType(getObject(ptrId).type);
+		auto ptr = GetPointerToData(ptrId, 0, state);
+		bool interleavedByLane = IsStorageInterleavedByLane(ptrTy.storageClass);
+		// TODO: GLSL modf() takes an output parameter and thus the pointer is assumed
+		// to be in bounds even for inactive lanes.
+		// - Clarify the SPIR-V spec.
+		// - Eliminate lane masking and assume interleaving.
+		auto robustness = OutOfBoundsBehavior::UndefinedBehavior;
+
+		for (auto i = 0u; i < type.sizeInComponents; i++)
+		{
+			SIMD::Float whole, frac;
+			std::tie(whole, frac) = Modf(val.Float(i));
+			dst.move(i, frac);
+			auto p = ptr + (i * sizeof(float));
+			if (interleavedByLane) { p = InterleaveByLane(p); }
+			p.Store(whole, robustness, state->activeLaneMask());
+		}
+		break;
+	}
+	case GLSLstd450ModfStruct:
+	{
+		auto val = GenericValue(this, state, insn.word(5));
+		auto valTy = getType(val.type);
+
+		for (auto i = 0u; i < valTy.sizeInComponents; i++)
+		{
+			SIMD::Float whole, frac;
+			std::tie(whole, frac) = Modf(val.Float(i));
+			dst.move(i, frac);
+			dst.move(i + valTy.sizeInComponents, whole);
+		}
+		break;
+	}
+	case GLSLstd450PackSnorm4x8:
+	{
+		auto val = GenericValue(this, state, insn.word(5));
+		dst.move(0, (SIMD::Int(Round(Min(Max(val.Float(0), SIMD::Float(-1.0f)), SIMD::Float(1.0f)) * SIMD::Float(127.0f))) &
+						SIMD::Int(0xFF)) |
+					((SIMD::Int(Round(Min(Max(val.Float(1), SIMD::Float(-1.0f)), SIMD::Float(1.0f)) * SIMD::Float(127.0f))) &
+						SIMD::Int(0xFF)) << 8) |
+					((SIMD::Int(Round(Min(Max(val.Float(2), SIMD::Float(-1.0f)), SIMD::Float(1.0f)) * SIMD::Float(127.0f))) &
+						SIMD::Int(0xFF)) << 16) |
+					((SIMD::Int(Round(Min(Max(val.Float(3), SIMD::Float(-1.0f)), SIMD::Float(1.0f)) * SIMD::Float(127.0f))) &
+						SIMD::Int(0xFF)) << 24));
+		break;
+	}
+	case GLSLstd450PackUnorm4x8:
+	{
+		auto val = GenericValue(this, state, insn.word(5));
+		dst.move(0, (SIMD::UInt(Round(Min(Max(val.Float(0), SIMD::Float(0.0f)), SIMD::Float(1.0f)) * SIMD::Float(255.0f)))) |
+					((SIMD::UInt(Round(Min(Max(val.Float(1), SIMD::Float(0.0f)), SIMD::Float(1.0f)) * SIMD::Float(255.0f)))) << 8) |
+					((SIMD::UInt(Round(Min(Max(val.Float(2), SIMD::Float(0.0f)), SIMD::Float(1.0f)) * SIMD::Float(255.0f)))) << 16) |
+					((SIMD::UInt(Round(Min(Max(val.Float(3), SIMD::Float(0.0f)), SIMD::Float(1.0f)) * SIMD::Float(255.0f)))) << 24));
+		break;
+	}
+	case GLSLstd450PackSnorm2x16:
+	{
+		auto val = GenericValue(this, state, insn.word(5));
+		dst.move(0, (SIMD::Int(Round(Min(Max(val.Float(0), SIMD::Float(-1.0f)), SIMD::Float(1.0f)) * SIMD::Float(32767.0f))) &
+						SIMD::Int(0xFFFF)) |
+					((SIMD::Int(Round(Min(Max(val.Float(1), SIMD::Float(-1.0f)), SIMD::Float(1.0f)) * SIMD::Float(32767.0f))) &
+						SIMD::Int(0xFFFF)) << 16));
+		break;
+	}
+	case GLSLstd450PackUnorm2x16:
+	{
+		auto val = GenericValue(this, state, insn.word(5));
+		dst.move(0, (SIMD::UInt(Round(Min(Max(val.Float(0), SIMD::Float(0.0f)), SIMD::Float(1.0f)) * SIMD::Float(65535.0f))) &
+						SIMD::UInt(0xFFFF)) |
+					((SIMD::UInt(Round(Min(Max(val.Float(1), SIMD::Float(0.0f)), SIMD::Float(1.0f)) * SIMD::Float(65535.0f))) &
+						SIMD::UInt(0xFFFF)) << 16));
+		break;
+	}
+	case GLSLstd450PackHalf2x16:
+	{
+		auto val = GenericValue(this, state, insn.word(5));
+		dst.move(0, FloatToHalfBits(val.UInt(0), false) | FloatToHalfBits(val.UInt(1), true));
+		break;
+	}
+	case GLSLstd450UnpackSnorm4x8:
+	{
+		auto val = GenericValue(this, state, insn.word(5));
+		dst.move(0, Min(Max(SIMD::Float(((val.Int(0)<<24) & SIMD::Int(0xFF000000))) * SIMD::Float(1.0f / float(0x7f000000)), SIMD::Float(-1.0f)), SIMD::Float(1.0f)));
+		dst.move(1, Min(Max(SIMD::Float(((val.Int(0)<<16) & SIMD::Int(0xFF000000))) * SIMD::Float(1.0f / float(0x7f000000)), SIMD::Float(-1.0f)), SIMD::Float(1.0f)));
+		dst.move(2, Min(Max(SIMD::Float(((val.Int(0)<<8) & SIMD::Int(0xFF000000))) * SIMD::Float(1.0f / float(0x7f000000)), SIMD::Float(-1.0f)), SIMD::Float(1.0f)));
+		dst.move(3, Min(Max(SIMD::Float(((val.Int(0)) & SIMD::Int(0xFF000000))) * SIMD::Float(1.0f / float(0x7f000000)), SIMD::Float(-1.0f)), SIMD::Float(1.0f)));
+		break;
+	}
+	case GLSLstd450UnpackUnorm4x8:
+	{
+		auto val = GenericValue(this, state, insn.word(5));
+		dst.move(0, SIMD::Float((val.UInt(0) & SIMD::UInt(0xFF))) * SIMD::Float(1.0f / 255.f));
+		dst.move(1, SIMD::Float(((val.UInt(0)>>8) & SIMD::UInt(0xFF))) * SIMD::Float(1.0f / 255.f));
+		dst.move(2, SIMD::Float(((val.UInt(0)>>16) & SIMD::UInt(0xFF))) * SIMD::Float(1.0f / 255.f));
+		dst.move(3, SIMD::Float(((val.UInt(0)>>24) & SIMD::UInt(0xFF))) * SIMD::Float(1.0f / 255.f));
+		break;
+	}
+	case GLSLstd450UnpackSnorm2x16:
+	{
+		auto val = GenericValue(this, state, insn.word(5));
+		// clamp(f / 32767.0, -1.0, 1.0)
+		dst.move(0, Min(Max(SIMD::Float(As<SIMD::Int>((val.UInt(0) & SIMD::UInt(0x0000FFFF)) << 16)) *
+							SIMD::Float(1.0f / float(0x7FFF0000)), SIMD::Float(-1.0f)), SIMD::Float(1.0f)));
+		dst.move(1, Min(Max(SIMD::Float(As<SIMD::Int>(val.UInt(0) & SIMD::UInt(0xFFFF0000))) * SIMD::Float(1.0f / float(0x7FFF0000)),
+							SIMD::Float(-1.0f)), SIMD::Float(1.0f)));
+		break;
+	}
+	case GLSLstd450UnpackUnorm2x16:
+	{
+		auto val = GenericValue(this, state, insn.word(5));
+		// f / 65535.0
+		dst.move(0, SIMD::Float((val.UInt(0) & SIMD::UInt(0x0000FFFF)) << 16) * SIMD::Float(1.0f / float(0xFFFF0000)));
+		dst.move(1, SIMD::Float(val.UInt(0) & SIMD::UInt(0xFFFF0000)) * SIMD::Float(1.0f / float(0xFFFF0000)));
+		break;
+	}
+	case GLSLstd450UnpackHalf2x16:
+	{
+		auto val = GenericValue(this, state, insn.word(5));
+		dst.move(0, halfToFloatBits(val.UInt(0) & SIMD::UInt(0x0000FFFF)));
+		dst.move(1, halfToFloatBits((val.UInt(0) & SIMD::UInt(0xFFFF0000)) >> 16));
+		break;
+	}
+	case GLSLstd450Fma:
+	{
+		auto a = GenericValue(this, state, insn.word(5));
+		auto b = GenericValue(this, state, insn.word(6));
+		auto c = GenericValue(this, state, insn.word(7));
+		for (auto i = 0u; i < type.sizeInComponents; i++)
+		{
+			dst.move(i, FMA(a.Float(i), b.Float(i), c.Float(i)));
+		}
+		break;
+	}
+	case GLSLstd450Frexp:
+	{
+		auto val = GenericValue(this, state, insn.word(5));
+		auto ptrId = Object::ID(insn.word(6));
+		auto ptrTy = getType(getObject(ptrId).type);
+		auto ptr = GetPointerToData(ptrId, 0, state);
+		bool interleavedByLane = IsStorageInterleavedByLane(ptrTy.storageClass);
+		// TODO: GLSL frexp() takes an output parameter and thus the pointer is assumed
+		// to be in bounds even for inactive lanes.
+		// - Clarify the SPIR-V spec.
+		// - Eliminate lane masking and assume interleaving.
+		auto robustness = OutOfBoundsBehavior::UndefinedBehavior;
+
+		for (auto i = 0u; i < type.sizeInComponents; i++)
+		{
+			SIMD::Float significand;
+			SIMD::Int exponent;
+			std::tie(significand, exponent) = Frexp(val.Float(i));
+
+			dst.move(i, significand);
+
+			auto p = ptr + (i * sizeof(float));
+			if (interleavedByLane) { p = InterleaveByLane(p); }
+			p.Store(exponent, robustness, state->activeLaneMask());
+		}
+		break;
+	}
+	case GLSLstd450FrexpStruct:
+	{
+		auto val = GenericValue(this, state, insn.word(5));
+		auto numComponents = getType(val.type).sizeInComponents;
+		for (auto i = 0u; i < numComponents; i++)
+		{
+			auto significandAndExponent = Frexp(val.Float(i));
+			dst.move(i, significandAndExponent.first);
+			dst.move(i + numComponents, significandAndExponent.second);
+		}
+		break;
+	}
+	case GLSLstd450Ldexp:
+	{
+		auto significand = GenericValue(this, state, insn.word(5));
+		auto exponent = GenericValue(this, state, insn.word(6));
+		for (auto i = 0u; i < type.sizeInComponents; i++)
+		{
+			// Assumes IEEE 754
+			auto in = significand.Float(i);
+			auto significandExponent = Exponent(in);
+			auto combinedExponent = exponent.Int(i) + significandExponent;
+			auto isSignificandZero	 = SIMD::UInt(CmpEQ(significand.Int(i), SIMD::Int(0)));
+			auto isSignificandInf	  = SIMD::UInt(IsInf(in));
+			auto isSignificandNaN	  = SIMD::UInt(IsNan(in));
+			auto isExponentNotTooSmall = SIMD::UInt(CmpGE(combinedExponent, SIMD::Int(-126)));
+			auto isExponentNotTooLarge = SIMD::UInt(CmpLE(combinedExponent, SIMD::Int(128)));
+			auto isExponentInBounds	= isExponentNotTooSmall & isExponentNotTooLarge;
+
+			SIMD::UInt v;
+			v  = significand.UInt(i) & SIMD::UInt(0x7FFFFF); // Add significand.
+			v |= (SIMD::UInt(combinedExponent + SIMD::Int(126)) << SIMD::UInt(23)); // Add exponent.
+			v &= isExponentInBounds; // Clear v if the exponent is OOB.
+
+			v |= significand.UInt(i) & SIMD::UInt(0x80000000); // Add sign bit.
+			v |= ~isExponentNotTooLarge & SIMD::UInt(0x7F800000); // Mark as inf if the exponent is too great.
+
+			// If the input significand is zero, inf or nan, just return the
+			// input significand.
+			auto passthrough = isSignificandZero | isSignificandInf | isSignificandNaN;
+			v = (v & ~passthrough) | (significand.UInt(i) & passthrough);
+
+			dst.move(i, As<SIMD::Float>(v));
+		}
+		break;
+	}
+	case GLSLstd450Radians:
+	{
+		auto degrees = GenericValue(this, state, insn.word(5));
+		for (auto i = 0u; i < type.sizeInComponents; i++)
+		{
+			dst.move(i, degrees.Float(i) * SIMD::Float(PI / 180.0f));
+		}
+		break;
+	}
+	case GLSLstd450Degrees:
+	{
+		auto radians = GenericValue(this, state, insn.word(5));
+		for (auto i = 0u; i < type.sizeInComponents; i++)
+		{
+			dst.move(i, radians.Float(i) * SIMD::Float(180.0f / PI));
+		}
+		break;
+	}
+	case GLSLstd450Sin:
+	{
+		auto radians = GenericValue(this, state, insn.word(5));
+		for (auto i = 0u; i < type.sizeInComponents; i++)
+		{
+			dst.move(i, Sin(radians.Float(i)));
+		}
+		break;
+	}
+	case GLSLstd450Cos:
+	{
+		auto radians = GenericValue(this, state, insn.word(5));
+		for (auto i = 0u; i < type.sizeInComponents; i++)
+		{
+			dst.move(i, Cos(radians.Float(i)));
+		}
+		break;
+	}
+	case GLSLstd450Tan:
+	{
+		auto radians = GenericValue(this, state, insn.word(5));
+		for (auto i = 0u; i < type.sizeInComponents; i++)
+		{
+			dst.move(i, Tan(radians.Float(i)));
+		}
+		break;
+	}
+	case GLSLstd450Asin:
+	{
+		auto val = GenericValue(this, state, insn.word(5));
+		for (auto i = 0u; i < type.sizeInComponents; i++)
+		{
+			dst.move(i, Asin(val.Float(i)));
+		}
+		break;
+	}
+	case GLSLstd450Acos:
+	{
+		auto val = GenericValue(this, state, insn.word(5));
+		for (auto i = 0u; i < type.sizeInComponents; i++)
+		{
+			dst.move(i, Acos(val.Float(i)));
+		}
+		break;
+	}
+	case GLSLstd450Atan:
+	{
+		auto val = GenericValue(this, state, insn.word(5));
+		for (auto i = 0u; i < type.sizeInComponents; i++)
+		{
+			dst.move(i, Atan(val.Float(i)));
+		}
+		break;
+	}
+	case GLSLstd450Sinh:
+	{
+		auto val = GenericValue(this, state, insn.word(5));
+		for (auto i = 0u; i < type.sizeInComponents; i++)
+		{
+			dst.move(i, Sinh(val.Float(i)));
+		}
+		break;
+	}
+	case GLSLstd450Cosh:
+	{
+		auto val = GenericValue(this, state, insn.word(5));
+		for (auto i = 0u; i < type.sizeInComponents; i++)
+		{
+			dst.move(i, Cosh(val.Float(i)));
+		}
+		break;
+	}
+	case GLSLstd450Tanh:
+	{
+		auto val = GenericValue(this, state, insn.word(5));
+		for (auto i = 0u; i < type.sizeInComponents; i++)
+		{
+			dst.move(i, Tanh(val.Float(i)));
+		}
+		break;
+	}
+	case GLSLstd450Asinh:
+	{
+		auto val = GenericValue(this, state, insn.word(5));
+		for (auto i = 0u; i < type.sizeInComponents; i++)
+		{
+			dst.move(i, Asinh(val.Float(i)));
+		}
+		break;
+	}
+	case GLSLstd450Acosh:
+	{
+		auto val = GenericValue(this, state, insn.word(5));
+		for (auto i = 0u; i < type.sizeInComponents; i++)
+		{
+			dst.move(i, Acosh(val.Float(i)));
+		}
+		break;
+	}
+	case GLSLstd450Atanh:
+	{
+		auto val = GenericValue(this, state, insn.word(5));
+		for (auto i = 0u; i < type.sizeInComponents; i++)
+		{
+			dst.move(i, Atanh(val.Float(i)));
+		}
+		break;
+	}
+	case GLSLstd450Atan2:
+	{
+		auto x = GenericValue(this, state, insn.word(5));
+		auto y = GenericValue(this, state, insn.word(6));
+		for (auto i = 0u; i < type.sizeInComponents; i++)
+		{
+			dst.move(i, Atan2(x.Float(i), y.Float(i)));
+		}
+		break;
+	}
+	case GLSLstd450Pow:
+	{
+		auto x = GenericValue(this, state, insn.word(5));
+		auto y = GenericValue(this, state, insn.word(6));
+		for (auto i = 0u; i < type.sizeInComponents; i++)
+		{
+			dst.move(i, Pow(x.Float(i), y.Float(i)));
+		}
+		break;
+	}
+	case GLSLstd450Exp:
+	{
+		auto val = GenericValue(this, state, insn.word(5));
+		for (auto i = 0u; i < type.sizeInComponents; i++)
+		{
+			dst.move(i, Exp(val.Float(i)));
+		}
+		break;
+	}
+	case GLSLstd450Log:
+	{
+		auto val = GenericValue(this, state, insn.word(5));
+		for (auto i = 0u; i < type.sizeInComponents; i++)
+		{
+			dst.move(i, Log(val.Float(i)));
+		}
+		break;
+	}
+	case GLSLstd450Exp2:
+	{
+		auto val = GenericValue(this, state, insn.word(5));
+		for (auto i = 0u; i < type.sizeInComponents; i++)
+		{
+			dst.move(i, Exp2(val.Float(i)));
+		}
+		break;
+	}
+	case GLSLstd450Log2:
+	{
+		auto val = GenericValue(this, state, insn.word(5));
+		for (auto i = 0u; i < type.sizeInComponents; i++)
+		{
+			dst.move(i, Log2(val.Float(i)));
+		}
+		break;
+	}
+	case GLSLstd450Sqrt:
+	{
+		auto val = GenericValue(this, state, insn.word(5));
+		for (auto i = 0u; i < type.sizeInComponents; i++)
+		{
+			dst.move(i, Sqrt(val.Float(i)));
+		}
+		break;
+	}
+	case GLSLstd450InverseSqrt:
+	{
+		auto val = GenericValue(this, state, insn.word(5));
+		Decorations d;
+		ApplyDecorationsForId(&d, insn.word(5));
+		if (d.RelaxedPrecision)
+		{
+			for (auto i = 0u; i < type.sizeInComponents; i++)
+			{
+				dst.move(i, RcpSqrt_pp(val.Float(i)));
+			}
+		}
+		else
+		{
+			for (auto i = 0u; i < type.sizeInComponents; i++)
+			{
+				dst.move(i, SIMD::Float(1.0f) / Sqrt(val.Float(i)));
+			}
+		}
+		break;
+	}
+	case GLSLstd450Determinant:
+	{
+		auto mat = GenericValue(this, state, insn.word(5));
+		auto numComponents = getType(mat.type).sizeInComponents;
+		switch (numComponents)
+		{
+		case 4: // 2x2
+			dst.move(0, Determinant(
+				mat.Float(0), mat.Float(1),
+				mat.Float(2), mat.Float(3)));
+			break;
+		case 9: // 3x3
+			dst.move(0, Determinant(
+				mat.Float(0), mat.Float(1), mat.Float(2),
+				mat.Float(3), mat.Float(4), mat.Float(5),
+				mat.Float(6), mat.Float(7), mat.Float(8)));
+			break;
+		case 16: // 4x4
+			dst.move(0, Determinant(
+				mat.Float(0),  mat.Float(1),  mat.Float(2),  mat.Float(3),
+				mat.Float(4),  mat.Float(5),  mat.Float(6),  mat.Float(7),
+				mat.Float(8),  mat.Float(9),  mat.Float(10), mat.Float(11),
+				mat.Float(12), mat.Float(13), mat.Float(14), mat.Float(15)));
+			break;
+		default:
+			UNREACHABLE("GLSLstd450Determinant can only operate with square matrices. Got %d elements", int(numComponents));
+		}
+		break;
+	}
+	case GLSLstd450MatrixInverse:
+	{
+		auto mat = GenericValue(this, state, insn.word(5));
+		auto numComponents = getType(mat.type).sizeInComponents;
+		switch (numComponents)
+		{
+		case 4: // 2x2
+		{
+			auto inv = MatrixInverse(
+				mat.Float(0), mat.Float(1),
+				mat.Float(2), mat.Float(3));
+			for (uint32_t i = 0; i < inv.size(); i++)
+			{
+				dst.move(i, inv[i]);
+			}
+			break;
+		}
+		case 9: // 3x3
+		{
+			auto inv = MatrixInverse(
+				mat.Float(0), mat.Float(1), mat.Float(2),
+				mat.Float(3), mat.Float(4), mat.Float(5),
+				mat.Float(6), mat.Float(7), mat.Float(8));
+			for (uint32_t i = 0; i < inv.size(); i++)
+			{
+				dst.move(i, inv[i]);
+			}
+			break;
+		}
+		case 16: // 4x4
+		{
+			auto inv = MatrixInverse(
+				mat.Float(0),  mat.Float(1),  mat.Float(2),  mat.Float(3),
+				mat.Float(4),  mat.Float(5),  mat.Float(6),  mat.Float(7),
+				mat.Float(8),  mat.Float(9),  mat.Float(10), mat.Float(11),
+				mat.Float(12), mat.Float(13), mat.Float(14), mat.Float(15));
+			for (uint32_t i = 0; i < inv.size(); i++)
+			{
+				dst.move(i, inv[i]);
+			}
+			break;
+		}
+		default:
+			UNREACHABLE("GLSLstd450MatrixInverse can only operate with square matrices. Got %d elements", int(numComponents));
+		}
+		break;
+	}
+	case GLSLstd450IMix:
+	{
+		UNREACHABLE("GLSLstd450IMix has been removed from the specification");
+		break;
+	}
+	case GLSLstd450PackDouble2x32:
+	{
+		UNSUPPORTED("SPIR-V Float64 Capability (GLSLstd450PackDouble2x32)");
+		break;
+	}
+	case GLSLstd450UnpackDouble2x32:
+	{
+		UNSUPPORTED("SPIR-V Float64 Capability (GLSLstd450UnpackDouble2x32)");
+		break;
+	}
+	case GLSLstd450FindILsb:
+	{
+		auto val = GenericValue(this, state, insn.word(5));
+		for (auto i = 0u; i < type.sizeInComponents; i++)
+		{
+			auto v = val.UInt(i);
+			dst.move(i, Cttz(v, true) | CmpEQ(v, SIMD::UInt(0)));
+		}
+		break;
+	}
+	case GLSLstd450FindSMsb:
+	{
+		auto val = GenericValue(this, state, insn.word(5));
+		for (auto i = 0u; i < type.sizeInComponents; i++)
+		{
+			auto v = val.UInt(i) ^ As<SIMD::UInt>(CmpLT(val.Int(i), SIMD::Int(0)));
+			dst.move(i, SIMD::UInt(31) - Ctlz(v, false));
+		}
+		break;
+	}
+	case GLSLstd450FindUMsb:
+	{
+		auto val = GenericValue(this, state, insn.word(5));
+		for (auto i = 0u; i < type.sizeInComponents; i++)
+		{
+			dst.move(i, SIMD::UInt(31) - Ctlz(val.UInt(i), false));
+		}
+		break;
+	}
+	case GLSLstd450InterpolateAtCentroid:
+	{
+		UNSUPPORTED("SPIR-V SampleRateShading Capability (GLSLstd450InterpolateAtCentroid)");
+		break;
+	}
+	case GLSLstd450InterpolateAtSample:
+	{
+		UNSUPPORTED("SPIR-V SampleRateShading Capability (GLSLstd450InterpolateAtCentroid)");
+		break;
+	}
+	case GLSLstd450InterpolateAtOffset:
+	{
+		UNSUPPORTED("SPIR-V SampleRateShading Capability (GLSLstd450InterpolateAtCentroid)");
+		break;
+	}
+	case GLSLstd450NMin:
+	{
+		auto x = GenericValue(this, state, insn.word(5));
+		auto y = GenericValue(this, state, insn.word(6));
+		for (auto i = 0u; i < type.sizeInComponents; i++)
+		{
+			dst.move(i, NMin(x.Float(i), y.Float(i)));
+		}
+		break;
+	}
+	case GLSLstd450NMax:
+	{
+		auto x = GenericValue(this, state, insn.word(5));
+		auto y = GenericValue(this, state, insn.word(6));
+		for (auto i = 0u; i < type.sizeInComponents; i++)
+		{
+			dst.move(i, NMax(x.Float(i), y.Float(i)));
+		}
+		break;
+	}
+	case GLSLstd450NClamp:
+	{
+		auto x = GenericValue(this, state, insn.word(5));
+		auto minVal = GenericValue(this, state, insn.word(6));
+		auto maxVal = GenericValue(this, state, insn.word(7));
+		for (auto i = 0u; i < type.sizeInComponents; i++)
+		{
+			auto clamp = NMin(NMax(x.Float(i), minVal.Float(i)), maxVal.Float(i));
+			dst.move(i, clamp);
+		}
+		break;
+	}
+	default:
+		UNREACHABLE("ExtInst %d", int(extInstIndex));
+		break;
+	}
+
+	return EmitResult::Continue;
+}
+
+}  // namespace sw
\ No newline at end of file
diff --git a/src/Vulkan/vulkan.vcxproj b/src/Vulkan/vulkan.vcxproj
index 7ee6982..3b6c30e 100644
--- a/src/Vulkan/vulkan.vcxproj
+++ b/src/Vulkan/vulkan.vcxproj
@@ -165,6 +165,7 @@
     <ClCompile Include="..\Pipeline\SetupRoutine.cpp" />

     <ClCompile Include="..\Pipeline\ShaderCore.cpp" />

     <ClCompile Include="..\Pipeline\SpirvShader.cpp" />

+    <ClCompile Include="..\Pipeline\SpirvShaderGLSLstd450.cpp" />

     <ClCompile Include="..\Pipeline\SpirvShaderSampling.cpp" />

     <ClCompile Include="..\Pipeline\SpirvShader_dbg.cpp" />

     <ClCompile Include="..\Pipeline\VertexProgram.cpp" />

diff --git a/src/Vulkan/vulkan.vcxproj.filters b/src/Vulkan/vulkan.vcxproj.filters
index 34ed4c7..7a0e176 100644
--- a/src/Vulkan/vulkan.vcxproj.filters
+++ b/src/Vulkan/vulkan.vcxproj.filters
@@ -246,6 +246,9 @@
     <ClCompile Include="..\Pipeline\SpirvShader.cpp">

       <Filter>Source Files\Pipeline</Filter>

     </ClCompile>

+    <ClCompile Include="..\Pipeline\SpirvShaderGLSLstd450.cpp">

+      <Filter>Source Files\Pipeline</Filter>

+    </ClCompile>

     <ClCompile Include="..\Pipeline\SpirvShaderSampling.cpp">

       <Filter>Source Files\Pipeline</Filter>

     </ClCompile>