Rename SpirvShader to Spirv

SpirvShader currently holds both the parsed SPIR-V code and pipeline
state which will affect code emission. This state is provided at
pipeline creation time but we perform code emission at draw time.

This change essentially renames SpirvShader to Spirv, in preparation of
making the latter a purely parsing-only class, and a new SpirvShader
class was added which derives from Spirv and is meant to store the
state variables in a subsequent refactoring.

Bug: b/247020580
Bug: b/253234336
Change-Id: I582353a587d5911a05f6cbb3129f18f27f23ca20
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/68970
Tested-by: Nicolas Capens <nicolascapens@google.com>
Reviewed-by: Alexis Hétu <sugoi@google.com>
Kokoro-Result: kokoro <noreply+kokoro@google.com>
diff --git a/src/Pipeline/ComputeProgram.cpp b/src/Pipeline/ComputeProgram.cpp
index bb38c13..415f2a0 100644
--- a/src/Pipeline/ComputeProgram.cpp
+++ b/src/Pipeline/ComputeProgram.cpp
@@ -58,29 +58,29 @@
 	routine->subgroupsPerWorkgroup = *Pointer<Int>(data + OFFSET(Data, subgroupsPerWorkgroup));
 	routine->invocationsPerSubgroup = *Pointer<Int>(data + OFFSET(Data, invocationsPerSubgroup));
 
-	routine->setInputBuiltin(shader.get(), spv::BuiltInNumWorkgroups, [&](const SpirvShader::BuiltinMapping &builtin, Array<SIMD::Float> &value) {
+	routine->setInputBuiltin(shader.get(), spv::BuiltInNumWorkgroups, [&](const Spirv::BuiltinMapping &builtin, Array<SIMD::Float> &value) {
 		value[builtin.FirstComponent + 0] = As<SIMD::Float>(SIMD::Int(routine->numWorkgroups.x));
 		value[builtin.FirstComponent + 1] = As<SIMD::Float>(SIMD::Int(routine->numWorkgroups.y));
 		value[builtin.FirstComponent + 2] = As<SIMD::Float>(SIMD::Int(routine->numWorkgroups.z));
 	});
 
-	routine->setInputBuiltin(shader.get(), spv::BuiltInWorkgroupId, [&](const SpirvShader::BuiltinMapping &builtin, Array<SIMD::Float> &value) {
+	routine->setInputBuiltin(shader.get(), spv::BuiltInWorkgroupId, [&](const Spirv::BuiltinMapping &builtin, Array<SIMD::Float> &value) {
 		value[builtin.FirstComponent + 0] = As<SIMD::Float>(SIMD::Int(workgroupID[0]));
 		value[builtin.FirstComponent + 1] = As<SIMD::Float>(SIMD::Int(workgroupID[1]));
 		value[builtin.FirstComponent + 2] = As<SIMD::Float>(SIMD::Int(workgroupID[2]));
 	});
 
-	routine->setInputBuiltin(shader.get(), spv::BuiltInWorkgroupSize, [&](const SpirvShader::BuiltinMapping &builtin, Array<SIMD::Float> &value) {
+	routine->setInputBuiltin(shader.get(), spv::BuiltInWorkgroupSize, [&](const Spirv::BuiltinMapping &builtin, Array<SIMD::Float> &value) {
 		value[builtin.FirstComponent + 0] = As<SIMD::Float>(SIMD::Int(routine->workgroupSize.x));
 		value[builtin.FirstComponent + 1] = As<SIMD::Float>(SIMD::Int(routine->workgroupSize.y));
 		value[builtin.FirstComponent + 2] = As<SIMD::Float>(SIMD::Int(routine->workgroupSize.z));
 	});
 
-	routine->setInputBuiltin(shader.get(), spv::BuiltInNumSubgroups, [&](const SpirvShader::BuiltinMapping &builtin, Array<SIMD::Float> &value) {
+	routine->setInputBuiltin(shader.get(), spv::BuiltInNumSubgroups, [&](const Spirv::BuiltinMapping &builtin, Array<SIMD::Float> &value) {
 		value[builtin.FirstComponent] = As<SIMD::Float>(SIMD::Int(routine->subgroupsPerWorkgroup));
 	});
 
-	routine->setInputBuiltin(shader.get(), spv::BuiltInSubgroupSize, [&](const SpirvShader::BuiltinMapping &builtin, Array<SIMD::Float> &value) {
+	routine->setInputBuiltin(shader.get(), spv::BuiltInSubgroupSize, [&](const Spirv::BuiltinMapping &builtin, Array<SIMD::Float> &value) {
 		value[builtin.FirstComponent] = As<SIMD::Float>(SIMD::Int(routine->invocationsPerSubgroup));
 	});
 
@@ -121,17 +121,17 @@
 	routine->globalInvocationID[1] = globalInvocationID[1];
 	routine->globalInvocationID[2] = globalInvocationID[2];
 
-	routine->setInputBuiltin(shader.get(), spv::BuiltInLocalInvocationIndex, [&](const SpirvShader::BuiltinMapping &builtin, Array<SIMD::Float> &value) {
+	routine->setInputBuiltin(shader.get(), spv::BuiltInLocalInvocationIndex, [&](const Spirv::BuiltinMapping &builtin, Array<SIMD::Float> &value) {
 		ASSERT(builtin.SizeInComponents == 1);
 		value[builtin.FirstComponent] = As<SIMD::Float>(localInvocationIndex);
 	});
 
-	routine->setInputBuiltin(shader.get(), spv::BuiltInSubgroupId, [&](const SpirvShader::BuiltinMapping &builtin, Array<SIMD::Float> &value) {
+	routine->setInputBuiltin(shader.get(), spv::BuiltInSubgroupId, [&](const Spirv::BuiltinMapping &builtin, Array<SIMD::Float> &value) {
 		ASSERT(builtin.SizeInComponents == 1);
 		value[builtin.FirstComponent] = As<SIMD::Float>(SIMD::Int(subgroupIndex));
 	});
 
-	routine->setInputBuiltin(shader.get(), spv::BuiltInLocalInvocationId, [&](const SpirvShader::BuiltinMapping &builtin, Array<SIMD::Float> &value) {
+	routine->setInputBuiltin(shader.get(), spv::BuiltInLocalInvocationId, [&](const Spirv::BuiltinMapping &builtin, Array<SIMD::Float> &value) {
 		for(uint32_t component = 0; component < builtin.SizeInComponents; component++)
 		{
 			value[builtin.FirstComponent + component] =
@@ -139,7 +139,7 @@
 		}
 	});
 
-	routine->setInputBuiltin(shader.get(), spv::BuiltInGlobalInvocationId, [&](const SpirvShader::BuiltinMapping &builtin, Array<SIMD::Float> &value) {
+	routine->setInputBuiltin(shader.get(), spv::BuiltInGlobalInvocationId, [&](const Spirv::BuiltinMapping &builtin, Array<SIMD::Float> &value) {
 		for(uint32_t component = 0; component < builtin.SizeInComponents; component++)
 		{
 			value[builtin.FirstComponent + component] =
diff --git a/src/Pipeline/PixelProgram.cpp b/src/Pipeline/PixelProgram.cpp
index df4f273..c73bb42 100644
--- a/src/Pipeline/PixelProgram.cpp
+++ b/src/Pipeline/PixelProgram.cpp
@@ -104,12 +104,12 @@
 	routine.pointCoord[0] = SIMD::Float(0.5f) + pointSizeInv * (((SIMD::Float(Float(x)) + SIMD::Float(0.0f, 1.0f, 0.0f, 1.0f)) - SIMD::Float(*Pointer<Float>(primitive + OFFSET(Primitive, x0)))));
 	routine.pointCoord[1] = SIMD::Float(0.5f) + pointSizeInv * (((SIMD::Float(Float(y)) + SIMD::Float(0.0f, 0.0f, 1.0f, 1.0f)) - SIMD::Float(*Pointer<Float>(primitive + OFFSET(Primitive, y0)))));
 
-	routine.setInputBuiltin(spirvShader, spv::BuiltInViewIndex, [&](const SpirvShader::BuiltinMapping &builtin, Array<SIMD::Float> &value) {
+	routine.setInputBuiltin(spirvShader, spv::BuiltInViewIndex, [&](const Spirv::BuiltinMapping &builtin, Array<SIMD::Float> &value) {
 		assert(builtin.SizeInComponents == 1);
 		value[builtin.FirstComponent] = As<SIMD::Float>(SIMD::Int(routine.layer));
 	});
 
-	routine.setInputBuiltin(spirvShader, spv::BuiltInFragCoord, [&](const SpirvShader::BuiltinMapping &builtin, Array<SIMD::Float> &value) {
+	routine.setInputBuiltin(spirvShader, spv::BuiltInFragCoord, [&](const Spirv::BuiltinMapping &builtin, Array<SIMD::Float> &value) {
 		assert(builtin.SizeInComponents == 4);
 		value[builtin.FirstComponent + 0] = routine.fragCoord[0];
 		value[builtin.FirstComponent + 1] = routine.fragCoord[1];
@@ -117,18 +117,18 @@
 		value[builtin.FirstComponent + 3] = routine.fragCoord[3];
 	});
 
-	routine.setInputBuiltin(spirvShader, spv::BuiltInPointCoord, [&](const SpirvShader::BuiltinMapping &builtin, Array<SIMD::Float> &value) {
+	routine.setInputBuiltin(spirvShader, spv::BuiltInPointCoord, [&](const Spirv::BuiltinMapping &builtin, Array<SIMD::Float> &value) {
 		assert(builtin.SizeInComponents == 2);
 		value[builtin.FirstComponent + 0] = routine.pointCoord[0];
 		value[builtin.FirstComponent + 1] = routine.pointCoord[1];
 	});
 
-	routine.setInputBuiltin(spirvShader, spv::BuiltInSubgroupSize, [&](const SpirvShader::BuiltinMapping &builtin, Array<SIMD::Float> &value) {
+	routine.setInputBuiltin(spirvShader, spv::BuiltInSubgroupSize, [&](const Spirv::BuiltinMapping &builtin, Array<SIMD::Float> &value) {
 		assert(builtin.SizeInComponents == 1);
 		value[builtin.FirstComponent] = As<SIMD::Float>(SIMD::Int(SIMD::Width));
 	});
 
-	routine.setInputBuiltin(spirvShader, spv::BuiltInHelperInvocation, [&](const SpirvShader::BuiltinMapping &builtin, Array<SIMD::Float> &value) {
+	routine.setInputBuiltin(spirvShader, spv::BuiltInHelperInvocation, [&](const Spirv::BuiltinMapping &builtin, Array<SIMD::Float> &value) {
 		assert(builtin.SizeInComponents == 1);
 		value[builtin.FirstComponent] = As<SIMD::Float>(routine.helperInvocation);
 	});
diff --git a/src/Pipeline/PixelRoutine.cpp b/src/Pipeline/PixelRoutine.cpp
index 68e13f3..1f01e61 100644
--- a/src/Pipeline/PixelRoutine.cpp
+++ b/src/Pipeline/PixelRoutine.cpp
@@ -206,7 +206,7 @@
 				for(int interfaceInterpolant = 0; interfaceInterpolant < MAX_INTERFACE_COMPONENTS; interfaceInterpolant++)
 				{
 					const auto &input = spirvShader->inputs[interfaceInterpolant];
-					if(input.Type != SpirvShader::ATTRIBTYPE_UNUSED)
+					if(input.Type != Spirv::ATTRIBTYPE_UNUSED)
 					{
 						routine.inputsInterpolation[packedInterpolant] = input.Flat ? SpirvRoutine::Flat : (input.NoPerspective ? SpirvRoutine::Linear : SpirvRoutine::Perspective);
 						if(input.Centroid && state.enableMultiSampling)
diff --git a/src/Pipeline/SpirvShader.cpp b/src/Pipeline/SpirvShader.cpp
index 4399bbd..69b9922 100644
--- a/src/Pipeline/SpirvShader.cpp
+++ b/src/Pipeline/SpirvShader.cpp
@@ -27,7 +27,7 @@
 
 namespace sw {
 
-SpirvShader::SpirvShader(
+Spirv::Spirv(
     VkShaderStageFlagBits pipelineStage,
     const char *entryPointName,
     const SpirvBinary &insns,
@@ -845,11 +845,11 @@
 #endif
 }
 
-SpirvShader::~SpirvShader()
+Spirv::~Spirv()
 {
 }
 
-void SpirvShader::DeclareType(InsnIterator insn)
+void Spirv::DeclareType(InsnIterator insn)
 {
 	Type::ID resultId = insn.word(1);
 
@@ -901,7 +901,7 @@
 	}
 }
 
-SpirvShader::Object &SpirvShader::CreateConstant(InsnIterator insn)
+Spirv::Object &Spirv::CreateConstant(InsnIterator insn)
 {
 	Type::ID typeId = insn.word(1);
 	Object::ID resultId = insn.word(2);
@@ -914,7 +914,7 @@
 	return object;
 }
 
-void SpirvShader::ProcessInterfaceVariable(Object &object)
+void Spirv::ProcessInterfaceVariable(Object &object)
 {
 	auto &objectTy = getType(object);
 	ASSERT(objectTy.storageClass == spv::StorageClassInput || objectTy.storageClass == spv::StorageClassOutput);
@@ -977,7 +977,7 @@
 	}
 }
 
-uint32_t SpirvShader::GetNumInputComponents(int32_t location) const
+uint32_t Spirv::GetNumInputComponents(int32_t location) const
 {
 	ASSERT(location >= 0);
 
@@ -996,7 +996,7 @@
 	return num_components_per_input;
 }
 
-uint32_t SpirvShader::GetPackedInterpolant(int32_t location) const
+uint32_t Spirv::GetPackedInterpolant(int32_t location) const
 {
 	ASSERT(location >= 0);
 	const uint32_t maxInterpolant = (location << 2);
@@ -1014,7 +1014,7 @@
 	return packedInterpolant;
 }
 
-void SpirvShader::ProcessExecutionMode(InsnIterator insn)
+void Spirv::ProcessExecutionMode(InsnIterator insn)
 {
 	Function::ID function = insn.word(1);
 	if(function != entryPoint)
@@ -1064,22 +1064,22 @@
 	}
 }
 
-uint32_t SpirvShader::getWorkgroupSizeX() const
+uint32_t Spirv::getWorkgroupSizeX() const
 {
 	return executionModes.useWorkgroupSizeId ? getObject(executionModes.WorkgroupSizeX).constantValue[0] : executionModes.WorkgroupSizeX.value();
 }
 
-uint32_t SpirvShader::getWorkgroupSizeY() const
+uint32_t Spirv::getWorkgroupSizeY() const
 {
 	return executionModes.useWorkgroupSizeId ? getObject(executionModes.WorkgroupSizeY).constantValue[0] : executionModes.WorkgroupSizeY.value();
 }
 
-uint32_t SpirvShader::getWorkgroupSizeZ() const
+uint32_t Spirv::getWorkgroupSizeZ() const
 {
 	return executionModes.useWorkgroupSizeId ? getObject(executionModes.WorkgroupSizeZ).constantValue[0] : executionModes.WorkgroupSizeZ.value();
 }
 
-uint32_t SpirvShader::ComputeTypeSize(InsnIterator insn)
+uint32_t Spirv::ComputeTypeSize(InsnIterator insn)
 {
 	// Types are always built from the bottom up (with the exception of forward ptrs, which
 	// don't appear in Vulkan shaders. Therefore, we can always assume our component parts have
@@ -1138,7 +1138,7 @@
 	}
 }
 
-int SpirvShader::VisitInterfaceInner(Type::ID id, Decorations d, const InterfaceVisitor &f) const
+int Spirv::VisitInterfaceInner(Type::ID id, Decorations d, const InterfaceVisitor &f) const
 {
 	// Recursively walks variable definition and its type tree, taking into account
 	// any explicit Location or Component decorations encountered; where explicit
@@ -1209,7 +1209,7 @@
 	}
 }
 
-void SpirvShader::VisitInterface(Object::ID id, const InterfaceVisitor &f) const
+void Spirv::VisitInterface(Object::ID id, const InterfaceVisitor &f) const
 {
 	// Walk a variable definition and call f for each component in it.
 	Decorations d = GetDecorationsForId(id);
@@ -1219,7 +1219,7 @@
 	VisitInterfaceInner(def.word(1), d, f);
 }
 
-void SpirvShader::ApplyDecorationsForAccessChain(Decorations *d, DescriptorDecorations *dd, Object::ID baseId, const Span &indexIds) const
+void Spirv::ApplyDecorationsForAccessChain(Decorations *d, DescriptorDecorations *dd, Object::ID baseId, const Span &indexIds) const
 {
 	ApplyDecorationsForId(d, baseId);
 	auto &baseObject = getObject(baseId);
@@ -1487,7 +1487,7 @@
 	return ptr;
 }
 
-uint32_t SpirvShader::WalkLiteralAccessChain(Type::ID typeId, const Span &indexes) const
+uint32_t Spirv::WalkLiteralAccessChain(Type::ID typeId, const Span &indexes) const
 {
 	uint32_t componentOffset = 0;
 
@@ -1529,7 +1529,7 @@
 	return componentOffset;
 }
 
-void SpirvShader::Decorations::Apply(spv::Decoration decoration, uint32_t arg)
+void Spirv::Decorations::Apply(spv::Decoration decoration, uint32_t arg)
 {
 	switch(decoration)
 	{
@@ -1592,7 +1592,7 @@
 	}
 }
 
-void SpirvShader::Decorations::Apply(const Decorations &src)
+void Spirv::Decorations::Apply(const Decorations &src)
 {
 	// Apply a decoration group to this set of decorations
 	if(src.HasBuiltIn)
@@ -1647,7 +1647,7 @@
 	NonUniform |= src.NonUniform;
 }
 
-void SpirvShader::DescriptorDecorations::Apply(const sw::SpirvShader::DescriptorDecorations &src)
+void Spirv::DescriptorDecorations::Apply(const sw::Spirv::DescriptorDecorations &src)
 {
 	if(src.DescriptorSet >= 0)
 	{
@@ -1665,7 +1665,7 @@
 	}
 }
 
-SpirvShader::Decorations SpirvShader::GetDecorationsForId(TypeOrObjectID id) const
+Spirv::Decorations Spirv::GetDecorationsForId(TypeOrObjectID id) const
 {
 	Decorations d;
 	ApplyDecorationsForId(&d, id);
@@ -1673,7 +1673,7 @@
 	return d;
 }
 
-void SpirvShader::ApplyDecorationsForId(Decorations *d, TypeOrObjectID id) const
+void Spirv::ApplyDecorationsForId(Decorations *d, TypeOrObjectID id) const
 {
 	auto it = decorations.find(id);
 	if(it != decorations.end())
@@ -1682,7 +1682,7 @@
 	}
 }
 
-void SpirvShader::ApplyDecorationsForIdMember(Decorations *d, Type::ID id, uint32_t member) const
+void Spirv::ApplyDecorationsForIdMember(Decorations *d, Type::ID id, uint32_t member) const
 {
 	auto it = memberDecorations.find(id);
 	if(it != memberDecorations.end() && member < it->second.size())
@@ -1691,7 +1691,7 @@
 	}
 }
 
-void SpirvShader::DefineResult(const InsnIterator &insn)
+void Spirv::DefineResult(const InsnIterator &insn)
 {
 	Type::ID typeId = insn.word(1);
 	Object::ID resultId = insn.word(2);
@@ -1716,7 +1716,7 @@
 	object.definition = insn;
 }
 
-OutOfBoundsBehavior SpirvShader::getOutOfBoundsBehavior(Object::ID pointerId, const vk::PipelineLayout *pipelineLayout) const
+OutOfBoundsBehavior Spirv::getOutOfBoundsBehavior(Object::ID pointerId, const vk::PipelineLayout *pipelineLayout) const
 {
 	auto it = descriptorDecorations.find(pointerId);
 	if(it != descriptorDecorations.end())
@@ -1770,7 +1770,7 @@
 
 // emit-time
 
-void SpirvShader::emitProlog(SpirvRoutine *routine) const
+void Spirv::emitProlog(SpirvRoutine *routine) const
 {
 	for(auto insn : *this)
 	{
@@ -1815,14 +1815,28 @@
 	}
 }
 
-void SpirvShader::emit(SpirvRoutine *routine, const RValue<SIMD::Int> &activeLaneMask, const RValue<SIMD::Int> &storesAndAtomicsMask, const vk::DescriptorSet::Bindings &descriptorSets, unsigned int multiSampleCount) const
+void Spirv::emit(SpirvRoutine *routine, const RValue<SIMD::Int> &activeLaneMask, const RValue<SIMD::Int> &storesAndAtomicsMask, const vk::DescriptorSet::Bindings &descriptorSets, unsigned int multiSampleCount) const
 {
 	SpirvEmitter::emit(*this, routine, entryPoint, activeLaneMask, storesAndAtomicsMask, descriptorSets, multiSampleCount);
 }
 
-SpirvEmitter::SpirvEmitter(const SpirvShader &shader,
+SpirvShader::SpirvShader(VkShaderStageFlagBits stage,
+                         const char *entryPointName,
+                         const SpirvBinary &insns,
+                         const vk::RenderPass *renderPass,
+                         uint32_t subpassIndex,
+                         bool robustBufferAccess)
+    : Spirv(stage, entryPointName, insns, renderPass, subpassIndex, robustBufferAccess)
+{
+}
+
+SpirvShader::~SpirvShader()
+{
+}
+
+SpirvEmitter::SpirvEmitter(const Spirv &shader,
                            SpirvRoutine *routine,
-                           SpirvShader::Function::ID entryPoint,
+                           Spirv::Function::ID entryPoint,
                            RValue<SIMD::Int> activeLaneMask,
                            RValue<SIMD::Int> storesAndAtomicsMask,
                            const vk::DescriptorSet::Bindings &descriptorSets,
@@ -1837,9 +1851,9 @@
 {
 }
 
-void SpirvEmitter::emit(const SpirvShader &shader,
+void SpirvEmitter::emit(const Spirv &shader,
                         SpirvRoutine *routine,
-                        SpirvShader::Function::ID entryPoint,
+                        Spirv::Function::ID entryPoint,
                         RValue<SIMD::Int> activeLaneMask,
                         RValue<SIMD::Int> storesAndAtomicsMask,
                         const vk::DescriptorSet::Bindings &descriptorSets,
@@ -2687,9 +2701,9 @@
 	auto ext = shader.getExtension(insn.word(3));
 	switch(ext.name)
 	{
-	case SpirvShader::Extension::GLSLstd450:
+	case Spirv::Extension::GLSLstd450:
 		return EmitExtGLSLstd450(insn);
-	case SpirvShader::Extension::NonSemanticInfo:
+	case Spirv::Extension::NonSemanticInfo:
 		// An extended set name which is prefixed with "NonSemantic." is
 		// guaranteed to contain only non-semantic instructions and all
 		// OpExtInst instructions referencing this set can be ignored.
@@ -2699,7 +2713,7 @@
 	}
 }
 
-uint32_t SpirvShader::GetConstScalarInt(Object::ID id) const
+uint32_t Spirv::GetConstScalarInt(Object::ID id) const
 {
 	auto &scopeObj = getObject(id);
 	ASSERT(scopeObj.kind == Object::Kind::Constant);
@@ -2708,7 +2722,7 @@
 	return scopeObj.constantValue[0];
 }
 
-void SpirvShader::emitEpilog(SpirvRoutine *routine) const
+void Spirv::emitEpilog(SpirvRoutine *routine) const
 {
 	for(auto insn : *this)
 	{
@@ -2732,7 +2746,7 @@
 	}
 }
 
-VkShaderStageFlagBits SpirvShader::executionModelToStage(spv::ExecutionModel model)
+VkShaderStageFlagBits Spirv::executionModelToStage(spv::ExecutionModel model)
 {
 	switch(model)
 	{
@@ -2757,7 +2771,7 @@
 	}
 }
 
-SpirvEmitter::Operand::Operand(const SpirvShader &shader, const SpirvEmitter &state, Object::ID objectId)
+SpirvEmitter::Operand::Operand(const Spirv &shader, const SpirvEmitter &state, Object::ID objectId)
     : Operand(state, shader.getObject(objectId))
 {}
 
@@ -2777,7 +2791,7 @@
 {
 }
 
-bool SpirvShader::Object::isConstantZero() const
+bool Spirv::Object::isConstantZero() const
 {
 	if(kind != Kind::Constant)
 	{
@@ -2802,12 +2816,12 @@
 
 void SpirvRoutine::setImmutableInputBuiltins(const SpirvShader *shader)
 {
-	setInputBuiltin(shader, spv::BuiltInSubgroupLocalInvocationId, [&](const SpirvShader::BuiltinMapping &builtin, Array<SIMD::Float> &value) {
+	setInputBuiltin(shader, spv::BuiltInSubgroupLocalInvocationId, [&](const Spirv::BuiltinMapping &builtin, Array<SIMD::Float> &value) {
 		ASSERT(builtin.SizeInComponents == 1);
 		value[builtin.FirstComponent] = As<SIMD::Float>(SIMD::Int(0, 1, 2, 3));
 	});
 
-	setInputBuiltin(shader, spv::BuiltInSubgroupEqMask, [&](const SpirvShader::BuiltinMapping &builtin, Array<SIMD::Float> &value) {
+	setInputBuiltin(shader, spv::BuiltInSubgroupEqMask, [&](const Spirv::BuiltinMapping &builtin, Array<SIMD::Float> &value) {
 		ASSERT(builtin.SizeInComponents == 4);
 		value[builtin.FirstComponent + 0] = As<SIMD::Float>(SIMD::Int(1, 2, 4, 8));
 		value[builtin.FirstComponent + 1] = As<SIMD::Float>(SIMD::Int(0, 0, 0, 0));
@@ -2815,7 +2829,7 @@
 		value[builtin.FirstComponent + 3] = As<SIMD::Float>(SIMD::Int(0, 0, 0, 0));
 	});
 
-	setInputBuiltin(shader, spv::BuiltInSubgroupGeMask, [&](const SpirvShader::BuiltinMapping &builtin, Array<SIMD::Float> &value) {
+	setInputBuiltin(shader, spv::BuiltInSubgroupGeMask, [&](const Spirv::BuiltinMapping &builtin, Array<SIMD::Float> &value) {
 		ASSERT(builtin.SizeInComponents == 4);
 		value[builtin.FirstComponent + 0] = As<SIMD::Float>(SIMD::Int(15, 14, 12, 8));
 		value[builtin.FirstComponent + 1] = As<SIMD::Float>(SIMD::Int(0, 0, 0, 0));
@@ -2823,7 +2837,7 @@
 		value[builtin.FirstComponent + 3] = As<SIMD::Float>(SIMD::Int(0, 0, 0, 0));
 	});
 
-	setInputBuiltin(shader, spv::BuiltInSubgroupGtMask, [&](const SpirvShader::BuiltinMapping &builtin, Array<SIMD::Float> &value) {
+	setInputBuiltin(shader, spv::BuiltInSubgroupGtMask, [&](const Spirv::BuiltinMapping &builtin, Array<SIMD::Float> &value) {
 		ASSERT(builtin.SizeInComponents == 4);
 		value[builtin.FirstComponent + 0] = As<SIMD::Float>(SIMD::Int(14, 12, 8, 0));
 		value[builtin.FirstComponent + 1] = As<SIMD::Float>(SIMD::Int(0, 0, 0, 0));
@@ -2831,7 +2845,7 @@
 		value[builtin.FirstComponent + 3] = As<SIMD::Float>(SIMD::Int(0, 0, 0, 0));
 	});
 
-	setInputBuiltin(shader, spv::BuiltInSubgroupLeMask, [&](const SpirvShader::BuiltinMapping &builtin, Array<SIMD::Float> &value) {
+	setInputBuiltin(shader, spv::BuiltInSubgroupLeMask, [&](const Spirv::BuiltinMapping &builtin, Array<SIMD::Float> &value) {
 		ASSERT(builtin.SizeInComponents == 4);
 		value[builtin.FirstComponent + 0] = As<SIMD::Float>(SIMD::Int(1, 3, 7, 15));
 		value[builtin.FirstComponent + 1] = As<SIMD::Float>(SIMD::Int(0, 0, 0, 0));
@@ -2839,7 +2853,7 @@
 		value[builtin.FirstComponent + 3] = As<SIMD::Float>(SIMD::Int(0, 0, 0, 0));
 	});
 
-	setInputBuiltin(shader, spv::BuiltInSubgroupLtMask, [&](const SpirvShader::BuiltinMapping &builtin, Array<SIMD::Float> &value) {
+	setInputBuiltin(shader, spv::BuiltInSubgroupLtMask, [&](const Spirv::BuiltinMapping &builtin, Array<SIMD::Float> &value) {
 		ASSERT(builtin.SizeInComponents == 4);
 		value[builtin.FirstComponent + 0] = As<SIMD::Float>(SIMD::Int(0, 1, 3, 7));
 		value[builtin.FirstComponent + 1] = As<SIMD::Float>(SIMD::Int(0, 0, 0, 0));
@@ -2847,7 +2861,7 @@
 		value[builtin.FirstComponent + 3] = As<SIMD::Float>(SIMD::Int(0, 0, 0, 0));
 	});
 
-	setInputBuiltin(shader, spv::BuiltInDeviceIndex, [&](const SpirvShader::BuiltinMapping &builtin, Array<SIMD::Float> &value) {
+	setInputBuiltin(shader, spv::BuiltInDeviceIndex, [&](const Spirv::BuiltinMapping &builtin, Array<SIMD::Float> &value) {
 		ASSERT(builtin.SizeInComponents == 1);
 		// Only a single physical device is supported.
 		value[builtin.FirstComponent] = As<SIMD::Float>(SIMD::Int(0, 0, 0, 0));
diff --git a/src/Pipeline/SpirvShader.hpp b/src/Pipeline/SpirvShader.hpp
index 6043714..7555aba 100644
--- a/src/Pipeline/SpirvShader.hpp
+++ b/src/Pipeline/SpirvShader.hpp
@@ -146,17 +146,17 @@
 #endif  // ENABLE_RR_PRINT
 };
 
-class SpirvShader
+class Spirv
 {
 public:
-	SpirvShader(VkShaderStageFlagBits stage,
-	            const char *entryPointName,
-	            const SpirvBinary &insns,
-	            const vk::RenderPass *renderPass,
-	            uint32_t subpassIndex,
-	            bool robustBufferAccess);
+	Spirv(VkShaderStageFlagBits stage,
+	      const char *entryPointName,
+	      const SpirvBinary &insns,
+	      const vk::RenderPass *renderPass,
+	      uint32_t subpassIndex,
+	      bool robustBufferAccess);
 
-	~SpirvShader();
+	~Spirv();
 
 	SpirvBinary insns;
 
@@ -979,20 +979,34 @@
 	static bool IsTerminator(spv::Op opcode);
 };
 
+// TODO(b/247020580): Move state from Spirv to SpirvShader
+class SpirvShader : public Spirv
+{
+public:
+	SpirvShader(VkShaderStageFlagBits stage,
+	            const char *entryPointName,
+	            const SpirvBinary &insns,
+	            const vk::RenderPass *renderPass,
+	            uint32_t subpassIndex,
+	            bool robustBufferAccess);
+
+	~SpirvShader();
+};
+
 // The SpirvEmitter class translates the parsed SPIR-V shader into Reactor code.
 class SpirvEmitter
 {
-	using Type = SpirvShader::Type;
-	using Object = SpirvShader::Object;
-	using Block = SpirvShader::Block;
-	using InsnIterator = SpirvShader::InsnIterator;
-	using Decorations = SpirvShader::Decorations;
-	using Span = SpirvShader::Span;
+	using Type = Spirv::Type;
+	using Object = Spirv::Object;
+	using Block = Spirv::Block;
+	using InsnIterator = Spirv::InsnIterator;
+	using Decorations = Spirv::Decorations;
+	using Span = Spirv::Span;
 
 public:
-	static void emit(const SpirvShader &shader,
+	static void emit(const Spirv &shader,
 	                 SpirvRoutine *routine,
-	                 SpirvShader::Function::ID entryPoint,
+	                 Spirv::Function::ID entryPoint,
 	                 RValue<SIMD::Int> activeLaneMask,
 	                 RValue<SIMD::Int> storesAndAtomicsMask,
 	                 const vk::DescriptorSet::Bindings &descriptorSets,
@@ -1005,9 +1019,9 @@
 	};
 
 private:
-	SpirvEmitter(const SpirvShader &shader,
+	SpirvEmitter(const Spirv &shader,
 	             SpirvRoutine *routine,
-	             SpirvShader::Function::ID entryPoint,
+	             Spirv::Function::ID entryPoint,
 	             RValue<SIMD::Int> activeLaneMask,
 	             RValue<SIMD::Int> storesAndAtomicsMask,
 	             const vk::DescriptorSet::Bindings &descriptorSets,
@@ -1135,7 +1149,7 @@
 
 	struct ImageInstruction : public ImageInstructionSignature
 	{
-		ImageInstruction(InsnIterator insn, const SpirvShader &shader, const SpirvEmitter &state);
+		ImageInstruction(InsnIterator insn, const Spirv &shader, const SpirvEmitter &state);
 
 		const uint32_t position;
 
@@ -1175,7 +1189,7 @@
 	class Operand
 	{
 	public:
-		Operand(const SpirvShader &shader, const SpirvEmitter &state, Object::ID objectId);
+		Operand(const Spirv &shader, const SpirvEmitter &state, Object::ID objectId);
 		Operand(const Intermediate &value);
 
 		RValue<SIMD::Float> Float(uint32_t i) const
@@ -1505,13 +1519,13 @@
 	static sw::MipmapType convertMipmapMode(const vk::SamplerState *samplerState);
 	static sw::AddressingMode convertAddressingMode(int coordinateIndex, const vk::SamplerState *samplerState, VkImageViewType imageViewType);
 
-	const SpirvShader &shader;
+	const Spirv &shader;
 	SpirvRoutine *const routine;                     // The current routine being built.
-	SpirvShader::Function::ID function;              // The current function being built.
+	Spirv::Function::ID function;                    // The current function being built.
 	Block::ID block;                                 // The current block being built.
 	rr::Value *activeLaneMaskValue = nullptr;        // The current active lane mask.
 	rr::Value *storesAndAtomicsMaskValue = nullptr;  // The current atomics mask.
-	SpirvShader::Block::Set visited;                 // Blocks already built.
+	Spirv::Block::Set visited;                       // Blocks already built.
 	std::unordered_map<Block::Edge, RValue<SIMD::Int>, Block::Edge::Hash> edgeActiveLaneMasks;
 	std::deque<Block::ID> *pending;
 
@@ -1527,7 +1541,7 @@
 
 class SpirvRoutine
 {
-	using Object = SpirvShader::Object;
+	using Object = Spirv::Object;
 
 public:
 	SpirvRoutine(const vk::PipelineLayout *pipelineLayout);
@@ -1622,7 +1636,7 @@
 	// setInputBuiltin() calls f() with the builtin and value if the shader
 	// uses the input builtin, otherwise the call is a no-op.
 	// F is a function with the signature:
-	// void(const SpirvShader::BuiltinMapping& builtin, Array<SIMD::Float>& value)
+	// void(const Spirv::BuiltinMapping& builtin, Array<SIMD::Float>& value)
 	template<typename F>
 	inline void setInputBuiltin(const SpirvShader *shader, spv::BuiltIn id, F &&f)
 	{
diff --git a/src/Pipeline/SpirvShaderArithmetic.cpp b/src/Pipeline/SpirvShaderArithmetic.cpp
index 5176837..031bbcb 100644
--- a/src/Pipeline/SpirvShaderArithmetic.cpp
+++ b/src/Pipeline/SpirvShaderArithmetic.cpp
@@ -23,7 +23,7 @@
 
 namespace sw {
 
-void SpirvEmitter::EmitVectorTimesScalar(SpirvShader::InsnIterator insn)
+void SpirvEmitter::EmitVectorTimesScalar(Spirv::InsnIterator insn)
 {
 	auto &type = shader.getType(insn.resultTypeId());
 	auto &dst = createIntermediate(insn.resultId(), type.componentCount);
@@ -36,7 +36,7 @@
 	}
 }
 
-void SpirvEmitter::EmitMatrixTimesVector(SpirvShader::InsnIterator insn)
+void SpirvEmitter::EmitMatrixTimesVector(Spirv::InsnIterator insn)
 {
 	auto &type = shader.getType(insn.resultTypeId());
 	auto &dst = createIntermediate(insn.resultId(), type.componentCount);
@@ -54,7 +54,7 @@
 	}
 }
 
-void SpirvEmitter::EmitVectorTimesMatrix(SpirvShader::InsnIterator insn)
+void SpirvEmitter::EmitVectorTimesMatrix(Spirv::InsnIterator insn)
 {
 	auto &type = shader.getType(insn.resultTypeId());
 	auto &dst = createIntermediate(insn.resultId(), type.componentCount);
@@ -72,7 +72,7 @@
 	}
 }
 
-void SpirvEmitter::EmitMatrixTimesMatrix(SpirvShader::InsnIterator insn)
+void SpirvEmitter::EmitMatrixTimesMatrix(Spirv::InsnIterator insn)
 {
 	auto &type = shader.getType(insn.resultTypeId());
 	auto &dst = createIntermediate(insn.resultId(), type.componentCount);
@@ -97,7 +97,7 @@
 	}
 }
 
-void SpirvEmitter::EmitOuterProduct(SpirvShader::InsnIterator insn)
+void SpirvEmitter::EmitOuterProduct(Spirv::InsnIterator insn)
 {
 	auto &type = shader.getType(insn.resultTypeId());
 	auto &dst = createIntermediate(insn.resultId(), type.componentCount);
@@ -116,7 +116,7 @@
 	}
 }
 
-void SpirvEmitter::EmitTranspose(SpirvShader::InsnIterator insn)
+void SpirvEmitter::EmitTranspose(Spirv::InsnIterator insn)
 {
 	auto &type = shader.getType(insn.resultTypeId());
 	auto &dst = createIntermediate(insn.resultId(), type.componentCount);
@@ -134,7 +134,7 @@
 	}
 }
 
-void SpirvEmitter::EmitBitcastPointer(SpirvShader::Object::ID resultID, Operand &src)
+void SpirvEmitter::EmitBitcastPointer(Spirv::Object::ID resultID, Operand &src)
 {
 	if(src.isPointer())  // Pointer -> Integer bits
 	{
@@ -174,12 +174,12 @@
 	}
 }
 
-void SpirvEmitter::EmitUnaryOp(SpirvShader::InsnIterator insn)
+void SpirvEmitter::EmitUnaryOp(Spirv::InsnIterator insn)
 {
 	auto &type = shader.getType(insn.resultTypeId());
 	auto src = Operand(shader, *this, insn.word(3));
 
-	bool dstIsPointer = shader.getObject(insn.resultId()).kind == SpirvShader::Object::Kind::Pointer;
+	bool dstIsPointer = shader.getObject(insn.resultId()).kind == Spirv::Object::Kind::Pointer;
 	bool srcIsPointer = src.isPointer();
 	if(srcIsPointer || dstIsPointer)
 	{
@@ -345,7 +345,7 @@
 	}
 }
 
-void SpirvEmitter::EmitBinaryOp(SpirvShader::InsnIterator insn)
+void SpirvEmitter::EmitBinaryOp(Spirv::InsnIterator insn)
 {
 	auto &type = shader.getType(insn.resultTypeId());
 	auto &dst = createIntermediate(insn.resultId(), type.componentCount);
@@ -556,7 +556,7 @@
 	SPIRV_SHADER_DBG("{0}: {1}", insn.word(4), rhs);
 }
 
-void SpirvEmitter::EmitDot(SpirvShader::InsnIterator insn)
+void SpirvEmitter::EmitDot(Spirv::InsnIterator insn)
 {
 	auto &type = shader.getType(insn.resultTypeId());
 	ASSERT(type.componentCount == 1);
diff --git a/src/Pipeline/SpirvShaderControlFlow.cpp b/src/Pipeline/SpirvShaderControlFlow.cpp
index 63b6b13..2cdefe0 100644
--- a/src/Pipeline/SpirvShaderControlFlow.cpp
+++ b/src/Pipeline/SpirvShaderControlFlow.cpp
@@ -28,7 +28,7 @@
 
 namespace sw {
 
-SpirvShader::Block::Block(InsnIterator begin, InsnIterator end)
+Spirv::Block::Block(InsnIterator begin, InsnIterator end)
     : begin_(begin)
     , end_(end)
 {
@@ -117,7 +117,7 @@
 	}
 }
 
-void SpirvShader::Function::TraverseReachableBlocks(Block::ID id, Block::Set &reachable) const
+void Spirv::Function::TraverseReachableBlocks(Block::ID id, Block::Set &reachable) const
 {
 	if(reachable.count(id) == 0)
 	{
@@ -129,7 +129,7 @@
 	}
 }
 
-void SpirvShader::Function::AssignBlockFields()
+void Spirv::Function::AssignBlockFields()
 {
 	Block::Set reachable;
 	TraverseReachableBlocks(entry, reachable);
@@ -157,7 +157,7 @@
 	}
 }
 
-void SpirvShader::Function::ForeachBlockDependency(Block::ID blockId, std::function<void(Block::ID)> f) const
+void Spirv::Function::ForeachBlockDependency(Block::ID blockId, std::function<void(Block::ID)> f) const
 {
 	auto block = getBlock(blockId);
 	for(auto dep : block.ins)
@@ -170,7 +170,7 @@
 	}
 }
 
-bool SpirvShader::Function::ExistsPath(Block::ID from, Block::ID to, Block::ID notPassingThrough) const
+bool Spirv::Function::ExistsPath(Block::ID from, Block::ID to, Block::ID notPassingThrough) const
 {
 	// TODO: Optimize: This can be cached on the block.
 	Block::Set seen;
@@ -589,7 +589,7 @@
 
 void SpirvEmitter::EmitFunctionCall(InsnIterator insn)
 {
-	auto functionId = SpirvShader::Function::ID(insn.word(3));
+	auto functionId = Spirv::Function::ID(insn.word(3));
 	const auto &functionIt = shader.functions.find(functionId);
 	ASSERT(functionIt != shader.functions.end());
 	auto &function = functionIt->second;
@@ -732,7 +732,7 @@
 	storesAndAtomicsMaskValue = mask.value();
 }
 
-void SpirvShader::WriteCFGGraphVizDotFile(const char *path) const
+void Spirv::WriteCFGGraphVizDotFile(const char *path) const
 {
 	std::ofstream file(path);
 	file << "digraph D {" << std::endl;
diff --git a/src/Pipeline/SpirvShaderDebug.hpp b/src/Pipeline/SpirvShaderDebug.hpp
index b8eecd8..66a7ca3 100644
--- a/src/Pipeline/SpirvShaderDebug.hpp
+++ b/src/Pipeline/SpirvShaderDebug.hpp
@@ -50,22 +50,22 @@
 #ifdef ENABLE_RR_PRINT
 namespace rr {
 template<>
-struct PrintValue::Ty<sw::SpirvShader::Object::ID>
+struct PrintValue::Ty<sw::Spirv::Object::ID>
 {
-	static inline std::string fmt(sw::SpirvShader::Object::ID v) { return "Object<" + std::to_string(v.value()) + ">"; }
-	static inline std::vector<Value *> val(sw::SpirvShader::Object::ID v) { return {}; }
+	static inline std::string fmt(sw::Spirv::Object::ID v) { return "Object<" + std::to_string(v.value()) + ">"; }
+	static inline std::vector<Value *> val(sw::Spirv::Object::ID v) { return {}; }
 };
 template<>
-struct PrintValue::Ty<sw::SpirvShader::Type::ID>
+struct PrintValue::Ty<sw::Spirv::Type::ID>
 {
-	static inline std::string fmt(sw::SpirvShader::Type::ID v) { return "Type<" + std::to_string(v.value()) + ">"; }
-	static inline std::vector<Value *> val(sw::SpirvShader::Type::ID v) { return {}; }
+	static inline std::string fmt(sw::Spirv::Type::ID v) { return "Type<" + std::to_string(v.value()) + ">"; }
+	static inline std::vector<Value *> val(sw::Spirv::Type::ID v) { return {}; }
 };
 template<>
-struct PrintValue::Ty<sw::SpirvShader::Block::ID>
+struct PrintValue::Ty<sw::Spirv::Block::ID>
 {
-	static inline std::string fmt(sw::SpirvShader::Block::ID v) { return "Block<" + std::to_string(v.value()) + ">"; }
-	static inline std::vector<Value *> val(sw::SpirvShader::Block::ID v) { return {}; }
+	static inline std::string fmt(sw::Spirv::Block::ID v) { return "Block<" + std::to_string(v.value()) + ">"; }
+	static inline std::vector<Value *> val(sw::Spirv::Block::ID v) { return {}; }
 };
 
 template<>
diff --git a/src/Pipeline/SpirvShaderGLSLstd450.cpp b/src/Pipeline/SpirvShaderGLSLstd450.cpp
index 87a2522..3a5a6ca 100644
--- a/src/Pipeline/SpirvShaderGLSLstd450.cpp
+++ b/src/Pipeline/SpirvShaderGLSLstd450.cpp
@@ -25,7 +25,7 @@
 
 static constexpr float PI = 3.141592653589793f;
 
-void SpirvEmitter::EmitExtGLSLstd450(SpirvShader::InsnIterator insn)
+void SpirvEmitter::EmitExtGLSLstd450(Spirv::InsnIterator insn)
 {
 	auto &type = shader.getType(insn.resultTypeId());
 	auto &dst = createIntermediate(insn.resultId(), type.componentCount);
@@ -283,7 +283,7 @@
 			auto I = Operand(shader, *this, insn.word(5));
 			auto N = Operand(shader, *this, insn.word(6));
 			auto eta = Operand(shader, *this, insn.word(7));
-			SpirvShader::Decorations r = shader.GetDecorationsForId(insn.resultId());
+			Spirv::Decorations r = shader.GetDecorationsForId(insn.resultId());
 
 			SIMD::Float d = FDot(type.componentCount, I, N);
 			SIMD::Float k = SIMD::Float(1.0f) - eta.Float(0) * eta.Float(0) * (SIMD::Float(1.0f) - d * d);
@@ -316,7 +316,7 @@
 		{
 			auto x = Operand(shader, *this, insn.word(5));
 			SIMD::Float d = FDot(shader.getObjectType(insn.word(5)).componentCount, x, x);
-			SpirvShader::Decorations r = shader.GetDecorationsForId(insn.resultId());
+			Spirv::Decorations r = shader.GetDecorationsForId(insn.resultId());
 
 			dst.move(0, Sqrt(d, r.RelaxedPrecision));
 		}
@@ -324,7 +324,7 @@
 	case GLSLstd450Normalize:
 		{
 			auto x = Operand(shader, *this, insn.word(5));
-			SpirvShader::Decorations r = shader.GetDecorationsForId(insn.resultId());
+			Spirv::Decorations r = shader.GetDecorationsForId(insn.resultId());
 
 			SIMD::Float d = FDot(shader.getObjectType(insn.word(5)).componentCount, x, x);
 			SIMD::Float invLength = SIMD::Float(1.0f) / Sqrt(d, r.RelaxedPrecision);
@@ -339,7 +339,7 @@
 		{
 			auto p0 = Operand(shader, *this, insn.word(5));
 			auto p1 = Operand(shader, *this, insn.word(6));
-			SpirvShader::Decorations r = shader.GetDecorationsForId(insn.resultId());
+			Spirv::Decorations r = shader.GetDecorationsForId(insn.resultId());
 
 			// sqrt(dot(p0-p1, p0-p1))
 			SIMD::Float d = (p0.Float(0) - p1.Float(0)) * (p0.Float(0) - p1.Float(0));
@@ -355,7 +355,7 @@
 	case GLSLstd450Modf:
 		{
 			auto val = Operand(shader, *this, insn.word(5));
-			auto ptrId = SpirvShader::Object::ID(insn.word(6));
+			auto ptrId = Spirv::Object::ID(insn.word(6));
 
 			Intermediate whole(type.componentCount);
 
@@ -492,7 +492,7 @@
 	case GLSLstd450Frexp:
 		{
 			auto val = Operand(shader, *this, insn.word(5));
-			auto ptrId = SpirvShader::Object::ID(insn.word(6));
+			auto ptrId = Spirv::Object::ID(insn.word(6));
 
 			Intermediate exp(type.componentCount);
 
@@ -550,7 +550,7 @@
 	case GLSLstd450Sin:
 		{
 			auto radians = Operand(shader, *this, insn.word(5));
-			SpirvShader::Decorations d = shader.GetDecorationsForId(insn.resultId());
+			Spirv::Decorations d = shader.GetDecorationsForId(insn.resultId());
 
 			for(auto i = 0u; i < type.componentCount; i++)
 			{
@@ -561,7 +561,7 @@
 	case GLSLstd450Cos:
 		{
 			auto radians = Operand(shader, *this, insn.word(5));
-			SpirvShader::Decorations d = shader.GetDecorationsForId(insn.resultId());
+			Spirv::Decorations d = shader.GetDecorationsForId(insn.resultId());
 
 			for(auto i = 0u; i < type.componentCount; i++)
 			{
@@ -572,7 +572,7 @@
 	case GLSLstd450Tan:
 		{
 			auto radians = Operand(shader, *this, insn.word(5));
-			SpirvShader::Decorations d = shader.GetDecorationsForId(insn.resultId());
+			Spirv::Decorations d = shader.GetDecorationsForId(insn.resultId());
 
 			for(auto i = 0u; i < type.componentCount; i++)
 			{
@@ -583,7 +583,7 @@
 	case GLSLstd450Asin:
 		{
 			auto val = Operand(shader, *this, insn.word(5));
-			SpirvShader::Decorations d = shader.GetDecorationsForId(insn.resultId());
+			Spirv::Decorations d = shader.GetDecorationsForId(insn.resultId());
 
 			for(auto i = 0u; i < type.componentCount; i++)
 			{
@@ -594,7 +594,7 @@
 	case GLSLstd450Acos:
 		{
 			auto val = Operand(shader, *this, insn.word(5));
-			SpirvShader::Decorations d = shader.GetDecorationsForId(insn.resultId());
+			Spirv::Decorations d = shader.GetDecorationsForId(insn.resultId());
 
 			for(auto i = 0u; i < type.componentCount; i++)
 			{
@@ -605,7 +605,7 @@
 	case GLSLstd450Atan:
 		{
 			auto val = Operand(shader, *this, insn.word(5));
-			SpirvShader::Decorations d = shader.GetDecorationsForId(insn.resultId());
+			Spirv::Decorations d = shader.GetDecorationsForId(insn.resultId());
 
 			for(auto i = 0u; i < type.componentCount; i++)
 			{
@@ -616,7 +616,7 @@
 	case GLSLstd450Sinh:
 		{
 			auto val = Operand(shader, *this, insn.word(5));
-			SpirvShader::Decorations d = shader.GetDecorationsForId(insn.resultId());
+			Spirv::Decorations d = shader.GetDecorationsForId(insn.resultId());
 
 			for(auto i = 0u; i < type.componentCount; i++)
 			{
@@ -627,7 +627,7 @@
 	case GLSLstd450Cosh:
 		{
 			auto val = Operand(shader, *this, insn.word(5));
-			SpirvShader::Decorations d = shader.GetDecorationsForId(insn.resultId());
+			Spirv::Decorations d = shader.GetDecorationsForId(insn.resultId());
 
 			for(auto i = 0u; i < type.componentCount; i++)
 			{
@@ -638,7 +638,7 @@
 	case GLSLstd450Tanh:
 		{
 			auto val = Operand(shader, *this, insn.word(5));
-			SpirvShader::Decorations d = shader.GetDecorationsForId(insn.resultId());
+			Spirv::Decorations d = shader.GetDecorationsForId(insn.resultId());
 
 			for(auto i = 0u; i < type.componentCount; i++)
 			{
@@ -649,7 +649,7 @@
 	case GLSLstd450Asinh:
 		{
 			auto val = Operand(shader, *this, insn.word(5));
-			SpirvShader::Decorations d = shader.GetDecorationsForId(insn.resultId());
+			Spirv::Decorations d = shader.GetDecorationsForId(insn.resultId());
 
 			for(auto i = 0u; i < type.componentCount; i++)
 			{
@@ -660,7 +660,7 @@
 	case GLSLstd450Acosh:
 		{
 			auto val = Operand(shader, *this, insn.word(5));
-			SpirvShader::Decorations d = shader.GetDecorationsForId(insn.resultId());
+			Spirv::Decorations d = shader.GetDecorationsForId(insn.resultId());
 
 			for(auto i = 0u; i < type.componentCount; i++)
 			{
@@ -671,7 +671,7 @@
 	case GLSLstd450Atanh:
 		{
 			auto val = Operand(shader, *this, insn.word(5));
-			SpirvShader::Decorations d = shader.GetDecorationsForId(insn.resultId());
+			Spirv::Decorations d = shader.GetDecorationsForId(insn.resultId());
 
 			for(auto i = 0u; i < type.componentCount; i++)
 			{
@@ -683,7 +683,7 @@
 		{
 			auto x = Operand(shader, *this, insn.word(5));
 			auto y = Operand(shader, *this, insn.word(6));
-			SpirvShader::Decorations d = shader.GetDecorationsForId(insn.resultId());
+			Spirv::Decorations d = shader.GetDecorationsForId(insn.resultId());
 
 			for(auto i = 0u; i < type.componentCount; i++)
 			{
@@ -695,7 +695,7 @@
 		{
 			auto x = Operand(shader, *this, insn.word(5));
 			auto y = Operand(shader, *this, insn.word(6));
-			SpirvShader::Decorations d = shader.GetDecorationsForId(insn.resultId());
+			Spirv::Decorations d = shader.GetDecorationsForId(insn.resultId());
 
 			for(auto i = 0u; i < type.componentCount; i++)
 			{
@@ -706,7 +706,7 @@
 	case GLSLstd450Exp:
 		{
 			auto val = Operand(shader, *this, insn.word(5));
-			SpirvShader::Decorations d = shader.GetDecorationsForId(insn.resultId());
+			Spirv::Decorations d = shader.GetDecorationsForId(insn.resultId());
 
 			for(auto i = 0u; i < type.componentCount; i++)
 			{
@@ -717,7 +717,7 @@
 	case GLSLstd450Log:
 		{
 			auto val = Operand(shader, *this, insn.word(5));
-			SpirvShader::Decorations d = shader.GetDecorationsForId(insn.resultId());
+			Spirv::Decorations d = shader.GetDecorationsForId(insn.resultId());
 
 			for(auto i = 0u; i < type.componentCount; i++)
 			{
@@ -728,7 +728,7 @@
 	case GLSLstd450Exp2:
 		{
 			auto val = Operand(shader, *this, insn.word(5));
-			SpirvShader::Decorations d = shader.GetDecorationsForId(insn.resultId());
+			Spirv::Decorations d = shader.GetDecorationsForId(insn.resultId());
 
 			for(auto i = 0u; i < type.componentCount; i++)
 			{
@@ -739,7 +739,7 @@
 	case GLSLstd450Log2:
 		{
 			auto val = Operand(shader, *this, insn.word(5));
-			SpirvShader::Decorations d = shader.GetDecorationsForId(insn.resultId());
+			Spirv::Decorations d = shader.GetDecorationsForId(insn.resultId());
 
 			for(auto i = 0u; i < type.componentCount; i++)
 			{
@@ -750,7 +750,7 @@
 	case GLSLstd450Sqrt:
 		{
 			auto val = Operand(shader, *this, insn.word(5));
-			SpirvShader::Decorations d = shader.GetDecorationsForId(insn.resultId());
+			Spirv::Decorations d = shader.GetDecorationsForId(insn.resultId());
 
 			for(auto i = 0u; i < type.componentCount; i++)
 			{
@@ -761,7 +761,7 @@
 	case GLSLstd450InverseSqrt:
 		{
 			auto val = Operand(shader, *this, insn.word(5));
-			SpirvShader::Decorations d = shader.GetDecorationsForId(insn.resultId());
+			Spirv::Decorations d = shader.GetDecorationsForId(insn.resultId());
 
 			for(auto i = 0u; i < type.componentCount; i++)
 			{
@@ -891,7 +891,7 @@
 		break;
 	case GLSLstd450InterpolateAtCentroid:
 		{
-			SpirvShader::Decorations d = shader.GetDecorationsForId(insn.word(5));
+			Spirv::Decorations d = shader.GetDecorationsForId(insn.word(5));
 			auto ptr = getPointer(insn.word(5));
 			for(auto i = 0u; i < type.componentCount; i++)
 			{
@@ -901,7 +901,7 @@
 		break;
 	case GLSLstd450InterpolateAtSample:
 		{
-			SpirvShader::Decorations d = shader.GetDecorationsForId(insn.word(5));
+			Spirv::Decorations d = shader.GetDecorationsForId(insn.word(5));
 			auto ptr = getPointer(insn.word(5));
 			for(auto i = 0u; i < type.componentCount; i++)
 			{
@@ -911,7 +911,7 @@
 		break;
 	case GLSLstd450InterpolateAtOffset:
 		{
-			SpirvShader::Decorations d = shader.GetDecorationsForId(insn.word(5));
+			Spirv::Decorations d = shader.GetDecorationsForId(insn.word(5));
 			auto ptr = getPointer(insn.word(5));
 			for(auto i = 0u; i < type.componentCount; i++)
 			{
@@ -976,7 +976,7 @@
 	return interpolant;
 }
 
-SIMD::Float SpirvEmitter::EmitInterpolate(const SIMD::Pointer &ptr, int32_t location, SpirvShader::Object::ID paramId,
+SIMD::Float SpirvEmitter::EmitInterpolate(const SIMD::Pointer &ptr, int32_t location, Spirv::Object::ID paramId,
                                           uint32_t component, InterpolationType type) const
 {
 	uint32_t interpolant = (location * 4);
diff --git a/src/Pipeline/SpirvShaderImage.cpp b/src/Pipeline/SpirvShaderImage.cpp
index 1292b9b..ef2d30e 100644
--- a/src/Pipeline/SpirvShaderImage.cpp
+++ b/src/Pipeline/SpirvShaderImage.cpp
@@ -74,7 +74,7 @@
 	}
 }
 
-SpirvEmitter::ImageInstruction::ImageInstruction(InsnIterator insn, const SpirvShader &shader, const SpirvEmitter &state)
+SpirvEmitter::ImageInstruction::ImageInstruction(InsnIterator insn, const Spirv &shader, const SpirvEmitter &state)
     : ImageInstructionSignature(parseVariantAndMethod(insn))
     , position(insn.distanceFrom(shader.begin()))
 {
@@ -529,7 +529,7 @@
 	bool isArrayed = imageType.definition.word(5) != 0;
 	uint32_t dimensions = resultTy.componentCount - (isArrayed ? 1 : 0);
 
-	const SpirvShader::DescriptorDecorations &d = shader.descriptorDecorations.at(imageId);
+	const Spirv::DescriptorDecorations &d = shader.descriptorDecorations.at(imageId);
 	auto descriptorType = routine->pipelineLayout->getDescriptorType(d.DescriptorSet, d.Binding);
 
 	Pointer<Byte> descriptor = getPointer(imageId).getUniformPointer();
@@ -588,7 +588,7 @@
 	ASSERT(resultTy.componentCount == 1);
 	auto imageId = Object::ID(insn.word(3));
 
-	const SpirvShader::DescriptorDecorations &d = shader.descriptorDecorations.at(imageId);
+	const Spirv::DescriptorDecorations &d = shader.descriptorDecorations.at(imageId);
 	auto descriptorType = routine->pipelineLayout->getDescriptorType(d.DescriptorSet, d.Binding);
 
 	Pointer<Byte> descriptor = getPointer(imageId).getUniformPointer();
@@ -618,7 +618,7 @@
 	ASSERT(imageTy.definition.word(3) == spv::Dim2D);
 	ASSERT(imageTy.definition.word(6 /* MS */) == 1);
 
-	const SpirvShader::DescriptorDecorations &d = shader.descriptorDecorations.at(imageId);
+	const Spirv::DescriptorDecorations &d = shader.descriptorDecorations.at(imageId);
 	auto descriptorType = routine->pipelineLayout->getDescriptorType(d.DescriptorSet, d.Binding);
 
 	Pointer<Byte> descriptor = getPointer(imageId).getUniformPointer();
@@ -830,7 +830,7 @@
 	auto dim = static_cast<spv::Dim>(instruction.dim);
 
 	auto coordinate = Operand(shader, *this, instruction.coordinateId);
-	const SpirvShader::DescriptorDecorations &d = shader.descriptorDecorations.at(instruction.imageId);
+	const Spirv::DescriptorDecorations &d = shader.descriptorDecorations.at(instruction.imageId);
 
 	// For subpass data, format in the instruction is spv::ImageFormatUnknown. Get it from
 	// the renderpass data instead. In all other cases, we can use the format in the instruction.
diff --git a/src/Pipeline/SpirvShaderInstructions.cpp b/src/Pipeline/SpirvShaderInstructions.cpp
index 7675665..fdc8704 100644
--- a/src/Pipeline/SpirvShaderInstructions.cpp
+++ b/src/Pipeline/SpirvShaderInstructions.cpp
@@ -20,13 +20,13 @@
 
 namespace sw {
 
-const char *SpirvShader::OpcodeName(spv::Op opcode)
+const char *Spirv::OpcodeName(spv::Op opcode)
 {
 	return spvOpcodeString(opcode);
 }
 
 // This function is used by the shader debugger to determine whether an instruction is steppable.
-bool SpirvShader::IsStatement(spv::Op opcode)
+bool Spirv::IsStatement(spv::Op opcode)
 {
 	switch(opcode)
 	{
@@ -91,7 +91,7 @@
 	}
 }
 
-bool SpirvShader::IsTerminator(spv::Op opcode)
+bool Spirv::IsTerminator(spv::Op opcode)
 {
 	switch(opcode)
 	{
diff --git a/src/Pipeline/SpirvShaderMemory.cpp b/src/Pipeline/SpirvShaderMemory.cpp
index bcbff15..f7fd2dc 100644
--- a/src/Pipeline/SpirvShaderMemory.cpp
+++ b/src/Pipeline/SpirvShaderMemory.cpp
@@ -57,7 +57,7 @@
 
 	if(result.kind == Object::Kind::Pointer)
 	{
-		shader.VisitMemoryObject(pointerId, true, [&](const SpirvShader::MemoryElement &el) {
+		shader.VisitMemoryObject(pointerId, true, [&](const Spirv::MemoryElement &el) {
 			ASSERT(el.index == 0);
 			auto p = GetElementPointer(ptr, el.offset, pointerTy.storageClass);
 			createPointer(resultId, p.Load<SIMD::Pointer>(robustness, activeLaneMask(), atomic, memoryOrder, sizeof(void *)));
@@ -68,7 +68,7 @@
 	else
 	{
 		auto &dst = createIntermediate(resultId, resultTy.componentCount);
-		shader.VisitMemoryObject(pointerId, false, [&](const SpirvShader::MemoryElement &el) {
+		shader.VisitMemoryObject(pointerId, false, [&](const Spirv::MemoryElement &el) {
 			auto p = GetElementPointer(ptr, el.offset, pointerTy.storageClass);
 			dst.move(el.index, p.Load<SIMD::Float>(robustness, activeLaneMask(), atomic, memoryOrder));
 		});
@@ -117,7 +117,7 @@
 
 	if(value.isPointer())
 	{
-		shader.VisitMemoryObject(pointerId, true, [&](const SpirvShader::MemoryElement &el) {
+		shader.VisitMemoryObject(pointerId, true, [&](const Spirv::MemoryElement &el) {
 			ASSERT(el.index == 0);
 			auto p = GetElementPointer(ptr, el.offset, pointerTy.storageClass);
 			p.Store(value.Pointer(), robustness, mask, atomic, memoryOrder);
@@ -125,7 +125,7 @@
 	}
 	else
 	{
-		shader.VisitMemoryObject(pointerId, false, [&](const SpirvShader::MemoryElement &el) {
+		shader.VisitMemoryObject(pointerId, false, [&](const Spirv::MemoryElement &el) {
 			auto p = GetElementPointer(ptr, el.offset, pointerTy.storageClass);
 			p.Store(value.Float(el.index), robustness, mask, atomic, memoryOrder);
 		});
@@ -166,7 +166,7 @@
 				auto &dst = routine->getVariable(resultId);
 				int offset = 0;
 				shader.VisitInterface(resultId,
-				                      [&](const Decorations &d, SpirvShader::AttribType type) {
+				                      [&](const Decorations &d, Spirv::AttribType type) {
 					                      auto scalarSlot = d.Location << 2 | d.Component;
 					                      dst[offset++] = routine->inputs[scalarSlot];
 				                      });
@@ -239,7 +239,7 @@
 				auto ptr = GetPointerToData(resultId, 0, false);
 				Operand initialValue(shader, *this, initializerId);
 
-				shader.VisitMemoryObject(resultId, false, [&](const SpirvShader::MemoryElement &el) {
+				shader.VisitMemoryObject(resultId, false, [&](const Spirv::MemoryElement &el) {
 					auto p = GetElementPointer(ptr, el.offset, objectTy.storageClass);
 					auto robustness = OutOfBoundsBehavior::UndefinedBehavior;  // Local variables are always within bounds.
 					p.Store(initialValue.Float(el.index), robustness, activeLaneMask());
@@ -272,9 +272,9 @@
 
 	std::unordered_map<uint32_t, uint32_t> srcOffsets;
 
-	shader.VisitMemoryObject(srcPtrId, false, [&](const SpirvShader::MemoryElement &el) { srcOffsets[el.index] = el.offset; });
+	shader.VisitMemoryObject(srcPtrId, false, [&](const Spirv::MemoryElement &el) { srcOffsets[el.index] = el.offset; });
 
-	shader.VisitMemoryObject(dstPtrId, false, [&](const SpirvShader::MemoryElement &el) {
+	shader.VisitMemoryObject(dstPtrId, false, [&](const Spirv::MemoryElement &el) {
 		auto it = srcOffsets.find(el.index);
 		ASSERT(it != srcOffsets.end());
 		auto srcOffset = it->second;
@@ -299,7 +299,7 @@
 	Fence(semantics);
 }
 
-void SpirvShader::VisitMemoryObjectInner(Type::ID id, Decorations d, uint32_t &index, uint32_t offset, bool resultIsPointer, const MemoryVisitor &f) const
+void Spirv::VisitMemoryObjectInner(Type::ID id, Decorations d, uint32_t &index, uint32_t offset, bool resultIsPointer, const MemoryVisitor &f) const
 {
 	ApplyDecorationsForId(&d, id);
 	const auto &type = getType(id);
@@ -370,7 +370,7 @@
 	}
 }
 
-void SpirvShader::VisitMemoryObject(Object::ID id, bool resultIsPointer, const MemoryVisitor &f) const
+void Spirv::VisitMemoryObject(Object::ID id, bool resultIsPointer, const MemoryVisitor &f) const
 {
 	auto typeId = getObject(id).typeId();
 	const auto &type = getType(typeId);
@@ -505,7 +505,7 @@
 	}
 }
 
-std::memory_order SpirvShader::MemoryOrder(spv::MemorySemanticsMask memorySemantics)
+std::memory_order Spirv::MemoryOrder(spv::MemorySemanticsMask memorySemantics)
 {
 	uint32_t control = static_cast<uint32_t>(memorySemantics) & static_cast<uint32_t>(
 	                                                                spv::MemorySemanticsAcquireMask |
@@ -527,7 +527,7 @@
 	}
 }
 
-bool SpirvShader::StoresInHelperInvocationsHaveNoEffect(spv::StorageClass storageClass)
+bool Spirv::StoresInHelperInvocationsHaveNoEffect(spv::StorageClass storageClass)
 {
 	switch(storageClass)
 	{
@@ -542,7 +542,7 @@
 	}
 }
 
-bool SpirvShader::IsExplicitLayout(spv::StorageClass storageClass)
+bool Spirv::IsExplicitLayout(spv::StorageClass storageClass)
 {
 	// From the Vulkan spec:
 	// "Composite objects in the StorageBuffer, PhysicalStorageBuffer, Uniform,
diff --git a/src/Pipeline/SpirvShaderSpec.cpp b/src/Pipeline/SpirvShaderSpec.cpp
index 15cd385..da89dd3 100644
--- a/src/Pipeline/SpirvShaderSpec.cpp
+++ b/src/Pipeline/SpirvShaderSpec.cpp
@@ -18,7 +18,7 @@
 
 namespace sw {
 
-void SpirvShader::EvalSpecConstantOp(InsnIterator insn)
+void Spirv::EvalSpecConstantOp(InsnIterator insn)
 {
 	auto opcode = static_cast<spv::Op>(insn.word(3));
 
@@ -153,7 +153,7 @@
 	}
 }
 
-void SpirvShader::EvalSpecConstantUnaryOp(InsnIterator insn)
+void Spirv::EvalSpecConstantUnaryOp(InsnIterator insn)
 {
 	auto &result = CreateConstant(insn);
 
@@ -203,7 +203,7 @@
 	}
 }
 
-void SpirvShader::EvalSpecConstantBinaryOp(InsnIterator insn)
+void Spirv::EvalSpecConstantBinaryOp(InsnIterator insn)
 {
 	auto &result = CreateConstant(insn);
 
diff --git a/src/Pipeline/VertexProgram.cpp b/src/Pipeline/VertexProgram.cpp
index 3ffa499..23d40c7 100644
--- a/src/Pipeline/VertexProgram.cpp
+++ b/src/Pipeline/VertexProgram.cpp
@@ -39,18 +39,18 @@
 	routine.layer = *Pointer<Int>(data + OFFSET(DrawData, layer));
 	routine.instanceID = *Pointer<Int>(data + OFFSET(DrawData, instanceID));
 
-	routine.setInputBuiltin(spirvShader, spv::BuiltInViewIndex, [&](const SpirvShader::BuiltinMapping &builtin, Array<SIMD::Float> &value) {
+	routine.setInputBuiltin(spirvShader, spv::BuiltInViewIndex, [&](const Spirv::BuiltinMapping &builtin, Array<SIMD::Float> &value) {
 		assert(builtin.SizeInComponents == 1);
 		value[builtin.FirstComponent] = As<SIMD::Float>(SIMD::Int(routine.layer));
 	});
 
-	routine.setInputBuiltin(spirvShader, spv::BuiltInInstanceIndex, [&](const SpirvShader::BuiltinMapping &builtin, Array<SIMD::Float> &value) {
+	routine.setInputBuiltin(spirvShader, spv::BuiltInInstanceIndex, [&](const Spirv::BuiltinMapping &builtin, Array<SIMD::Float> &value) {
 		// TODO: we could do better here; we know InstanceIndex is uniform across all lanes
 		assert(builtin.SizeInComponents == 1);
 		value[builtin.FirstComponent] = As<SIMD::Float>(SIMD::Int(routine.instanceID));
 	});
 
-	routine.setInputBuiltin(spirvShader, spv::BuiltInSubgroupSize, [&](const SpirvShader::BuiltinMapping &builtin, Array<SIMD::Float> &value) {
+	routine.setInputBuiltin(spirvShader, spv::BuiltInSubgroupSize, [&](const Spirv::BuiltinMapping &builtin, Array<SIMD::Float> &value) {
 		ASSERT(builtin.SizeInComponents == 1);
 		value[builtin.FirstComponent] = As<SIMD::Float>(SIMD::Int(SIMD::Width));
 	});
diff --git a/src/Pipeline/VertexRoutine.cpp b/src/Pipeline/VertexRoutine.cpp
index 6f30f0f..eb556c9 100644
--- a/src/Pipeline/VertexRoutine.cpp
+++ b/src/Pipeline/VertexRoutine.cpp
@@ -90,10 +90,10 @@
 {
 	for(int i = 0; i < MAX_INTERFACE_COMPONENTS; i += 4)
 	{
-		if(spirvShader->inputs[i + 0].Type != SpirvShader::ATTRIBTYPE_UNUSED ||
-		   spirvShader->inputs[i + 1].Type != SpirvShader::ATTRIBTYPE_UNUSED ||
-		   spirvShader->inputs[i + 2].Type != SpirvShader::ATTRIBTYPE_UNUSED ||
-		   spirvShader->inputs[i + 3].Type != SpirvShader::ATTRIBTYPE_UNUSED)
+		if(spirvShader->inputs[i + 0].Type != Spirv::ATTRIBTYPE_UNUSED ||
+		   spirvShader->inputs[i + 1].Type != Spirv::ATTRIBTYPE_UNUSED ||
+		   spirvShader->inputs[i + 2].Type != Spirv::ATTRIBTYPE_UNUSED ||
+		   spirvShader->inputs[i + 3].Type != Spirv::ATTRIBTYPE_UNUSED)
 		{
 			Pointer<Byte> input = *Pointer<Pointer<Byte>>(data + OFFSET(DrawData, input) + sizeof(void *) * (i / 4));
 			UInt stride = *Pointer<UInt>(data + OFFSET(DrawData, stride) + sizeof(uint32_t) * (i / 4));
@@ -208,7 +208,7 @@
 
 	int componentCount = format.componentCount();
 	bool normalized = !format.isUnnormalizedInteger();
-	bool isNativeFloatAttrib = (stream.attribType == SpirvShader::ATTRIBTYPE_FLOAT) || normalized;
+	bool isNativeFloatAttrib = (stream.attribType == Spirv::ATTRIBTYPE_FLOAT) || normalized;
 	bool bgra = false;
 
 	switch(stream.format)
@@ -685,10 +685,10 @@
 
 	for(int i = 0; i < MAX_INTERFACE_COMPONENTS; i += 4)
 	{
-		if(spirvShader->outputs[i + 0].Type != SpirvShader::ATTRIBTYPE_UNUSED ||
-		   spirvShader->outputs[i + 1].Type != SpirvShader::ATTRIBTYPE_UNUSED ||
-		   spirvShader->outputs[i + 2].Type != SpirvShader::ATTRIBTYPE_UNUSED ||
-		   spirvShader->outputs[i + 3].Type != SpirvShader::ATTRIBTYPE_UNUSED)
+		if(spirvShader->outputs[i + 0].Type != Spirv::ATTRIBTYPE_UNUSED ||
+		   spirvShader->outputs[i + 1].Type != Spirv::ATTRIBTYPE_UNUSED ||
+		   spirvShader->outputs[i + 2].Type != Spirv::ATTRIBTYPE_UNUSED ||
+		   spirvShader->outputs[i + 3].Type != Spirv::ATTRIBTYPE_UNUSED)
 		{
 			Vector4f v;
 			v.x = Extract128(routine.outputs[i + 0], 0);
@@ -717,7 +717,7 @@
 
 	for(int i = 0; i < MAX_INTERFACE_COMPONENTS; i++)
 	{
-		if(spirvShader->outputs[i].Type != SpirvShader::ATTRIBTYPE_UNUSED)
+		if(spirvShader->outputs[i].Type != Spirv::ATTRIBTYPE_UNUSED)
 		{
 			*Pointer<Int>(vertex + OFFSET(Vertex, v[i]), 4) = *Pointer<Int>(cacheEntry + OFFSET(Vertex, v[i]), 4);
 		}
diff --git a/third_party/llvm-project b/third_party/llvm-project
index 4bf84e4..fc3b34c 160000
--- a/third_party/llvm-project
+++ b/third_party/llvm-project
@@ -1 +1 @@
-Subproject commit 4bf84e433d921c8d4d5dd9640662a816df42a531
+Subproject commit fc3b34c50803274b8ba3b8a30df9177b7d29063c