clang-format the src/Pipeline directory

Bug: b/144825072

Change-Id: I869aef91d6318bf6955581e5dad762800bd46296
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/39655
Tested-by: Ben Clayton <bclayton@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
diff --git a/src/Pipeline/SpirvShader.cpp b/src/Pipeline/SpirvShader.cpp
index 11b3770..2350195 100644
--- a/src/Pipeline/SpirvShader.cpp
+++ b/src/Pipeline/SpirvShader.cpp
@@ -23,17 +23,18 @@
 namespace sw {
 
 SpirvShader::SpirvShader(
-		uint32_t codeSerialID,
-		VkShaderStageFlagBits pipelineStage,
-		const char *entryPointName,
-		InsnStore const &insns,
-		const vk::RenderPass *renderPass,
-		uint32_t subpassIndex,
-		bool robustBufferAccess)
-			: insns{insns}, inputs{MAX_INTERFACE_COMPONENTS},
-			  outputs{MAX_INTERFACE_COMPONENTS},
-			  codeSerialID(codeSerialID),
-			  robustBufferAccess(robustBufferAccess)
+    uint32_t codeSerialID,
+    VkShaderStageFlagBits pipelineStage,
+    const char *entryPointName,
+    InsnStore const &insns,
+    const vk::RenderPass *renderPass,
+    uint32_t subpassIndex,
+    bool robustBufferAccess)
+    : insns{ insns }
+    , inputs{ MAX_INTERFACE_COMPONENTS }
+    , outputs{ MAX_INTERFACE_COMPONENTS }
+    , codeSerialID(codeSerialID)
+    , robustBufferAccess(robustBufferAccess)
 {
 	ASSERT(insns.size() > 0);
 
@@ -46,7 +47,8 @@
 		{
 			auto attachmentIndex = subpass.pInputAttachments[i].attachment;
 			inputAttachmentFormats.push_back(attachmentIndex != VK_ATTACHMENT_UNUSED
-											 ? renderPass->getAttachment(attachmentIndex).format : VK_FORMAT_UNDEFINED);
+			                                     ? renderPass->getAttachment(attachmentIndex).format
+			                                     : VK_FORMAT_UNDEFINED);
 		}
 	}
 
@@ -63,403 +65,404 @@
 
 		switch(opcode)
 		{
-		case spv::OpEntryPoint:
-		{
-			executionModel = spv::ExecutionModel(insn.word(1));
-			auto id = Function::ID(insn.word(2));
-			auto name = insn.string(3);
-			auto stage = executionModelToStage(executionModel);
-			if(stage == pipelineStage && strcmp(name, entryPointName) == 0)
+			case spv::OpEntryPoint:
 			{
-				ASSERT_MSG(entryPoint == 0, "Duplicate entry point with name '%s' and stage %d", name, int(stage));
-				entryPoint = id;
-			}
-			break;
-		}
-
-		case spv::OpExecutionMode:
-			ProcessExecutionMode(insn);
-			break;
-
-		case spv::OpDecorate:
-		{
-			TypeOrObjectID targetId = insn.word(1);
-			auto decoration = static_cast<spv::Decoration>(insn.word(2));
-			uint32_t value = insn.wordCount() > 3 ? insn.word(3) : 0;
-
-			decorations[targetId].Apply(decoration, value);
-
-			switch(decoration)
-			{
-			case spv::DecorationDescriptorSet:
-				descriptorDecorations[targetId].DescriptorSet = value;
-				break;
-			case spv::DecorationBinding:
-				descriptorDecorations[targetId].Binding = value;
-				break;
-			case spv::DecorationInputAttachmentIndex:
-				descriptorDecorations[targetId].InputAttachmentIndex = value;
-				break;
-			default:
-				// Only handling descriptor decorations here.
+				executionModel = spv::ExecutionModel(insn.word(1));
+				auto id = Function::ID(insn.word(2));
+				auto name = insn.string(3);
+				auto stage = executionModelToStage(executionModel);
+				if(stage == pipelineStage && strcmp(name, entryPointName) == 0)
+				{
+					ASSERT_MSG(entryPoint == 0, "Duplicate entry point with name '%s' and stage %d", name, int(stage));
+					entryPoint = id;
+				}
 				break;
 			}
 
-			if(decoration == spv::DecorationCentroid)
-				modes.NeedsCentroid = true;
-			break;
-		}
+			case spv::OpExecutionMode:
+				ProcessExecutionMode(insn);
+				break;
 
-		case spv::OpMemberDecorate:
-		{
-			Type::ID targetId = insn.word(1);
-			auto memberIndex = insn.word(2);
-			auto decoration = static_cast<spv::Decoration>(insn.word(3));
-			uint32_t value = insn.wordCount() > 4 ? insn.word(4) : 0;
-
-			auto &d = memberDecorations[targetId];
-			if(memberIndex >= d.size())
-				d.resize(memberIndex + 1);    // on demand; exact size would require another pass...
-
-			d[memberIndex].Apply(decoration, value);
-
-			if(decoration == spv::DecorationCentroid)
-				modes.NeedsCentroid = true;
-			break;
-		}
-
-		case spv::OpDecorationGroup:
-			// Nothing to do here. We don't need to record the definition of the group; we'll just have
-			// the bundle of decorations float around. If we were to ever walk the decorations directly,
-			// we might think about introducing this as a real Object.
-			break;
-
-		case spv::OpGroupDecorate:
-		{
-			uint32_t group = insn.word(1);
-			auto const &groupDecorations = decorations[group];
-			auto const &descriptorGroupDecorations = descriptorDecorations[group];
-			for(auto i = 2u; i < insn.wordCount(); i++)
+			case spv::OpDecorate:
 			{
-				// Remaining operands are targets to apply the group to.
-				uint32_t target = insn.word(i);
-				decorations[target].Apply(groupDecorations);
-				descriptorDecorations[target].Apply(descriptorGroupDecorations);
+				TypeOrObjectID targetId = insn.word(1);
+				auto decoration = static_cast<spv::Decoration>(insn.word(2));
+				uint32_t value = insn.wordCount() > 3 ? insn.word(3) : 0;
+
+				decorations[targetId].Apply(decoration, value);
+
+				switch(decoration)
+				{
+					case spv::DecorationDescriptorSet:
+						descriptorDecorations[targetId].DescriptorSet = value;
+						break;
+					case spv::DecorationBinding:
+						descriptorDecorations[targetId].Binding = value;
+						break;
+					case spv::DecorationInputAttachmentIndex:
+						descriptorDecorations[targetId].InputAttachmentIndex = value;
+						break;
+					default:
+						// Only handling descriptor decorations here.
+						break;
+				}
+
+				if(decoration == spv::DecorationCentroid)
+					modes.NeedsCentroid = true;
+				break;
 			}
 
-			break;
-		}
-
-		case spv::OpGroupMemberDecorate:
-		{
-			auto const &srcDecorations = decorations[insn.word(1)];
-			for(auto i = 2u; i < insn.wordCount(); i += 2)
+			case spv::OpMemberDecorate:
 			{
-				// remaining operands are pairs of <id>, literal for members to apply to.
-				auto &d = memberDecorations[insn.word(i)];
-				auto memberIndex = insn.word(i + 1);
+				Type::ID targetId = insn.word(1);
+				auto memberIndex = insn.word(2);
+				auto decoration = static_cast<spv::Decoration>(insn.word(3));
+				uint32_t value = insn.wordCount() > 4 ? insn.word(4) : 0;
+
+				auto &d = memberDecorations[targetId];
 				if(memberIndex >= d.size())
-					d.resize(memberIndex + 1);    // on demand resize, see above...
-				d[memberIndex].Apply(srcDecorations);
+					d.resize(memberIndex + 1);  // on demand; exact size would require another pass...
+
+				d[memberIndex].Apply(decoration, value);
+
+				if(decoration == spv::DecorationCentroid)
+					modes.NeedsCentroid = true;
+				break;
 			}
-			break;
-		}
 
-		case spv::OpLabel:
-		{
-			ASSERT(currentBlock.value() == 0);
-			currentBlock = Block::ID(insn.word(1));
-			blockStart = insn;
-			break;
-		}
-
-		// Branch Instructions (subset of Termination Instructions):
-		case spv::OpBranch:
-		case spv::OpBranchConditional:
-		case spv::OpSwitch:
-		case spv::OpReturn:
-		// fallthrough
-
-		// Termination instruction:
-		case spv::OpKill:
-		case spv::OpUnreachable:
-		{
-			ASSERT(currentBlock.value() != 0);
-			ASSERT(currentFunction.value() != 0);
-
-			auto blockEnd = insn; blockEnd++;
-			functions[currentFunction].blocks[currentBlock] = Block(blockStart, blockEnd);
-			currentBlock = Block::ID(0);
-
-			if(opcode == spv::OpKill)
-			{
-				modes.ContainsKill = true;
-			}
-			break;
-		}
-
-		case spv::OpLoopMerge:
-		case spv::OpSelectionMerge:
-			break; // Nothing to do in analysis pass.
-
-		case spv::OpTypeVoid:
-		case spv::OpTypeBool:
-		case spv::OpTypeInt:
-		case spv::OpTypeFloat:
-		case spv::OpTypeVector:
-		case spv::OpTypeMatrix:
-		case spv::OpTypeImage:
-		case spv::OpTypeSampler:
-		case spv::OpTypeSampledImage:
-		case spv::OpTypeArray:
-		case spv::OpTypeRuntimeArray:
-		case spv::OpTypeStruct:
-		case spv::OpTypePointer:
-		case spv::OpTypeFunction:
-			DeclareType(insn);
-			break;
-
-		case spv::OpVariable:
-		{
-			Type::ID typeId = insn.word(1);
-			Object::ID resultId = insn.word(2);
-			auto storageClass = static_cast<spv::StorageClass>(insn.word(3));
-
-			auto &object = defs[resultId];
-			object.kind = Object::Kind::Pointer;
-			object.definition = insn;
-			object.type = typeId;
-
-			ASSERT(getType(typeId).definition.opcode() == spv::OpTypePointer);
-			ASSERT(getType(typeId).storageClass == storageClass);
-
-			switch(storageClass)
-			{
-			case spv::StorageClassInput:
-			case spv::StorageClassOutput:
-				ProcessInterfaceVariable(object);
+			case spv::OpDecorationGroup:
+				// Nothing to do here. We don't need to record the definition of the group; we'll just have
+				// the bundle of decorations float around. If we were to ever walk the decorations directly,
+				// we might think about introducing this as a real Object.
 				break;
 
-			case spv::StorageClassUniform:
-			case spv::StorageClassStorageBuffer:
-				object.kind = Object::Kind::DescriptorSet;
+			case spv::OpGroupDecorate:
+			{
+				uint32_t group = insn.word(1);
+				auto const &groupDecorations = decorations[group];
+				auto const &descriptorGroupDecorations = descriptorDecorations[group];
+				for(auto i = 2u; i < insn.wordCount(); i++)
+				{
+					// Remaining operands are targets to apply the group to.
+					uint32_t target = insn.word(i);
+					decorations[target].Apply(groupDecorations);
+					descriptorDecorations[target].Apply(descriptorGroupDecorations);
+				}
+
+				break;
+			}
+
+			case spv::OpGroupMemberDecorate:
+			{
+				auto const &srcDecorations = decorations[insn.word(1)];
+				for(auto i = 2u; i < insn.wordCount(); i += 2)
+				{
+					// remaining operands are pairs of <id>, literal for members to apply to.
+					auto &d = memberDecorations[insn.word(i)];
+					auto memberIndex = insn.word(i + 1);
+					if(memberIndex >= d.size())
+						d.resize(memberIndex + 1);  // on demand resize, see above...
+					d[memberIndex].Apply(srcDecorations);
+				}
+				break;
+			}
+
+			case spv::OpLabel:
+			{
+				ASSERT(currentBlock.value() == 0);
+				currentBlock = Block::ID(insn.word(1));
+				blockStart = insn;
+				break;
+			}
+
+			// Branch Instructions (subset of Termination Instructions):
+			case spv::OpBranch:
+			case spv::OpBranchConditional:
+			case spv::OpSwitch:
+			case spv::OpReturn:
+			// fallthrough
+
+			// Termination instruction:
+			case spv::OpKill:
+			case spv::OpUnreachable:
+			{
+				ASSERT(currentBlock.value() != 0);
+				ASSERT(currentFunction.value() != 0);
+
+				auto blockEnd = insn;
+				blockEnd++;
+				functions[currentFunction].blocks[currentBlock] = Block(blockStart, blockEnd);
+				currentBlock = Block::ID(0);
+
+				if(opcode == spv::OpKill)
+				{
+					modes.ContainsKill = true;
+				}
+				break;
+			}
+
+			case spv::OpLoopMerge:
+			case spv::OpSelectionMerge:
+				break;  // Nothing to do in analysis pass.
+
+			case spv::OpTypeVoid:
+			case spv::OpTypeBool:
+			case spv::OpTypeInt:
+			case spv::OpTypeFloat:
+			case spv::OpTypeVector:
+			case spv::OpTypeMatrix:
+			case spv::OpTypeImage:
+			case spv::OpTypeSampler:
+			case spv::OpTypeSampledImage:
+			case spv::OpTypeArray:
+			case spv::OpTypeRuntimeArray:
+			case spv::OpTypeStruct:
+			case spv::OpTypePointer:
+			case spv::OpTypeFunction:
+				DeclareType(insn);
 				break;
 
-			case spv::StorageClassPushConstant:
-			case spv::StorageClassPrivate:
-			case spv::StorageClassFunction:
-			case spv::StorageClassUniformConstant:
-				break; // Correctly handled.
-
-			case spv::StorageClassWorkgroup:
+			case spv::OpVariable:
 			{
-				auto &elTy = getType(getType(typeId).element);
-				auto sizeInBytes = elTy.sizeInComponents * static_cast<uint32_t>(sizeof(float));
-				workgroupMemory.allocate(resultId, sizeInBytes);
+				Type::ID typeId = insn.word(1);
+				Object::ID resultId = insn.word(2);
+				auto storageClass = static_cast<spv::StorageClass>(insn.word(3));
+
+				auto &object = defs[resultId];
 				object.kind = Object::Kind::Pointer;
-				break;
-			}
-			case spv::StorageClassAtomicCounter:
-			case spv::StorageClassImage:
-				UNIMPLEMENTED("StorageClass %d not yet implemented", (int)storageClass);
-				break;
+				object.definition = insn;
+				object.type = typeId;
 
-			case spv::StorageClassCrossWorkgroup:
-				UNSUPPORTED("SPIR-V OpenCL Execution Model (StorageClassCrossWorkgroup)");
-				break;
+				ASSERT(getType(typeId).definition.opcode() == spv::OpTypePointer);
+				ASSERT(getType(typeId).storageClass == storageClass);
 
-			case spv::StorageClassGeneric:
-				UNSUPPORTED("SPIR-V GenericPointer Capability (StorageClassGeneric)");
-				break;
-
-			default:
-				UNREACHABLE("Unexpected StorageClass %d", storageClass); // See Appendix A of the Vulkan spec.
-				break;
-			}
-			break;
-		}
-
-		case spv::OpConstant:
-		case spv::OpSpecConstant:
-			CreateConstant(insn).constantValue[0] = insn.word(3);
-			break;
-		case spv::OpConstantFalse:
-		case spv::OpSpecConstantFalse:
-			CreateConstant(insn).constantValue[0] = 0;    // Represent Boolean false as zero.
-			break;
-		case spv::OpConstantTrue:
-		case spv::OpSpecConstantTrue:
-			CreateConstant(insn).constantValue[0] = ~0u;  // Represent Boolean true as all bits set.
-			break;
-		case spv::OpConstantNull:
-		case spv::OpUndef:
-		{
-			// TODO: consider a real LLVM-level undef. For now, zero is a perfectly good value.
-			// OpConstantNull forms a constant of arbitrary type, all zeros.
-			auto &object = CreateConstant(insn);
-			auto &objectTy = getType(object.type);
-			for(auto i = 0u; i < objectTy.sizeInComponents; i++)
-			{
-				object.constantValue[i] = 0;
-			}
-			break;
-		}
-		case spv::OpConstantComposite:
-		case spv::OpSpecConstantComposite:
-		{
-			auto &object = CreateConstant(insn);
-			auto offset = 0u;
-			for(auto i = 0u; i < insn.wordCount() - 3; i++)
-			{
-				auto &constituent = getObject(insn.word(i + 3));
-				auto &constituentTy = getType(constituent.type);
-				for(auto j = 0u; j < constituentTy.sizeInComponents; j++)
+				switch(storageClass)
 				{
-					object.constantValue[offset++] = constituent.constantValue[j];
+					case spv::StorageClassInput:
+					case spv::StorageClassOutput:
+						ProcessInterfaceVariable(object);
+						break;
+
+					case spv::StorageClassUniform:
+					case spv::StorageClassStorageBuffer:
+						object.kind = Object::Kind::DescriptorSet;
+						break;
+
+					case spv::StorageClassPushConstant:
+					case spv::StorageClassPrivate:
+					case spv::StorageClassFunction:
+					case spv::StorageClassUniformConstant:
+						break;  // Correctly handled.
+
+					case spv::StorageClassWorkgroup:
+					{
+						auto &elTy = getType(getType(typeId).element);
+						auto sizeInBytes = elTy.sizeInComponents * static_cast<uint32_t>(sizeof(float));
+						workgroupMemory.allocate(resultId, sizeInBytes);
+						object.kind = Object::Kind::Pointer;
+						break;
+					}
+					case spv::StorageClassAtomicCounter:
+					case spv::StorageClassImage:
+						UNIMPLEMENTED("StorageClass %d not yet implemented", (int)storageClass);
+						break;
+
+					case spv::StorageClassCrossWorkgroup:
+						UNSUPPORTED("SPIR-V OpenCL Execution Model (StorageClassCrossWorkgroup)");
+						break;
+
+					case spv::StorageClassGeneric:
+						UNSUPPORTED("SPIR-V GenericPointer Capability (StorageClassGeneric)");
+						break;
+
+					default:
+						UNREACHABLE("Unexpected StorageClass %d", storageClass);  // See Appendix A of the Vulkan spec.
+						break;
 				}
+				break;
 			}
 
-			auto objectId = Object::ID(insn.word(2));
-			auto decorationsIt = decorations.find(objectId);
-			if(decorationsIt != decorations.end() &&
-				decorationsIt->second.BuiltIn == spv::BuiltInWorkgroupSize)
+			case spv::OpConstant:
+			case spv::OpSpecConstant:
+				CreateConstant(insn).constantValue[0] = insn.word(3);
+				break;
+			case spv::OpConstantFalse:
+			case spv::OpSpecConstantFalse:
+				CreateConstant(insn).constantValue[0] = 0;  // Represent Boolean false as zero.
+				break;
+			case spv::OpConstantTrue:
+			case spv::OpSpecConstantTrue:
+				CreateConstant(insn).constantValue[0] = ~0u;  // Represent Boolean true as all bits set.
+				break;
+			case spv::OpConstantNull:
+			case spv::OpUndef:
 			{
-				// https://www.khronos.org/registry/vulkan/specs/1.1/html/vkspec.html#interfaces-builtin-variables :
-				// Decorating an object with the WorkgroupSize built-in
-				// decoration will make that object contain the dimensions
-				// of a local workgroup. If an object is decorated with the
-				// WorkgroupSize decoration, this must take precedence over
-				// any execution mode set for LocalSize.
-				// The object decorated with WorkgroupSize must be declared
-				// as a three-component vector of 32-bit integers.
-				ASSERT(getType(object.type).sizeInComponents == 3);
-				modes.WorkgroupSizeX = object.constantValue[0];
-				modes.WorkgroupSizeY = object.constantValue[1];
-				modes.WorkgroupSizeZ = object.constantValue[2];
-			}
-			break;
-		}
-		case spv::OpSpecConstantOp:
-			EvalSpecConstantOp(insn);
-			break;
-
-		case spv::OpCapability:
-		{
-			auto capability = static_cast<spv::Capability>(insn.word(1));
-			switch(capability)
-			{
-			case spv::CapabilityMatrix: capabilities.Matrix = true; break;
-			case spv::CapabilityShader: capabilities.Shader = true; break;
-			case spv::CapabilityClipDistance: capabilities.ClipDistance = true; break;
-			case spv::CapabilityCullDistance: capabilities.CullDistance = true; break;
-			case spv::CapabilityInputAttachment: capabilities.InputAttachment = true; break;
-			case spv::CapabilitySampled1D: capabilities.Sampled1D = true; break;
-			case spv::CapabilityImage1D: capabilities.Image1D = true; break;
-			case spv::CapabilityImageCubeArray: capabilities.ImageCubeArray = true; break;
-			case spv::CapabilitySampledBuffer: capabilities.SampledBuffer = true; break;
-			case spv::CapabilitySampledCubeArray: capabilities.SampledCubeArray = true; break;
-			case spv::CapabilityImageBuffer: capabilities.ImageBuffer = true; break;
-			case spv::CapabilityStorageImageExtendedFormats: capabilities.StorageImageExtendedFormats = true; break;
-			case spv::CapabilityImageQuery: capabilities.ImageQuery = true; break;
-			case spv::CapabilityDerivativeControl: capabilities.DerivativeControl = true; break;
-			case spv::CapabilityGroupNonUniform: capabilities.GroupNonUniform = true; break;
-			case spv::CapabilityGroupNonUniformVote: capabilities.GroupNonUniformVote = true; break;
-			case spv::CapabilityGroupNonUniformArithmetic: capabilities.GroupNonUniformArithmetic = true; break;
-			case spv::CapabilityGroupNonUniformBallot: capabilities.GroupNonUniformBallot = true; break;
-			case spv::CapabilityGroupNonUniformShuffle: capabilities.GroupNonUniformShuffle = true; break;
-			case spv::CapabilityGroupNonUniformShuffleRelative: capabilities.GroupNonUniformShuffleRelative = true; break;
-			case spv::CapabilityDeviceGroup: capabilities.DeviceGroup = true; break;
-			case spv::CapabilityMultiView: capabilities.MultiView = true; break;
-			default:
-				UNSUPPORTED("Unsupported capability %u", insn.word(1));
-			}
-			break; // Various capabilities will be declared, but none affect our code generation at this point.
-		}
-
-		case spv::OpMemoryModel:
-			break; // Memory model does not affect our code generation until we decide to do Vulkan Memory Model support.
-
-		case spv::OpFunction:
-		{
-			auto functionId = Function::ID(insn.word(2));
-			ASSERT_MSG(currentFunction == 0, "Functions %d and %d overlap", currentFunction.value(), functionId.value());
-			currentFunction = functionId;
-			auto &function = functions[functionId];
-			function.result = Type::ID(insn.word(1));
-			function.type = Type::ID(insn.word(4));
-			// Scan forward to find the function's label.
-			for(auto it = insn; it != end() && function.entry == 0; it++)
-			{
-				switch(it.opcode())
+				// TODO: consider a real LLVM-level undef. For now, zero is a perfectly good value.
+				// OpConstantNull forms a constant of arbitrary type, all zeros.
+				auto &object = CreateConstant(insn);
+				auto &objectTy = getType(object.type);
+				for(auto i = 0u; i < objectTy.sizeInComponents; i++)
 				{
-				case spv::OpFunction:
-				case spv::OpFunctionParameter:
-					break;
-				case spv::OpLabel:
-					function.entry = Block::ID(it.word(1));
-					break;
-				default:
-					WARN("Unexpected opcode '%s' following OpFunction", OpcodeName(it.opcode()).c_str());
+					object.constantValue[i] = 0;
 				}
+				break;
 			}
-			ASSERT_MSG(function.entry != 0, "Function<%d> has no label", currentFunction.value());
-			break;
-		}
-
-		case spv::OpFunctionEnd:
-			currentFunction = 0;
-			break;
-
-		case spv::OpExtInstImport:
-		{
-			// We will only support the GLSL 450 extended instruction set, so no point in tracking the ID we assign it.
-			// Valid shaders will not attempt to import any other instruction sets.
-			auto ext = insn.string(2);
-			if(0 != strcmp("GLSL.std.450", ext))
+			case spv::OpConstantComposite:
+			case spv::OpSpecConstantComposite:
 			{
-				UNSUPPORTED("SPIR-V Extension: %s", ext);
+				auto &object = CreateConstant(insn);
+				auto offset = 0u;
+				for(auto i = 0u; i < insn.wordCount() - 3; i++)
+				{
+					auto &constituent = getObject(insn.word(i + 3));
+					auto &constituentTy = getType(constituent.type);
+					for(auto j = 0u; j < constituentTy.sizeInComponents; j++)
+					{
+						object.constantValue[offset++] = constituent.constantValue[j];
+					}
+				}
+
+				auto objectId = Object::ID(insn.word(2));
+				auto decorationsIt = decorations.find(objectId);
+				if(decorationsIt != decorations.end() &&
+				   decorationsIt->second.BuiltIn == spv::BuiltInWorkgroupSize)
+				{
+					// https://www.khronos.org/registry/vulkan/specs/1.1/html/vkspec.html#interfaces-builtin-variables :
+					// Decorating an object with the WorkgroupSize built-in
+					// decoration will make that object contain the dimensions
+					// of a local workgroup. If an object is decorated with the
+					// WorkgroupSize decoration, this must take precedence over
+					// any execution mode set for LocalSize.
+					// The object decorated with WorkgroupSize must be declared
+					// as a three-component vector of 32-bit integers.
+					ASSERT(getType(object.type).sizeInComponents == 3);
+					modes.WorkgroupSizeX = object.constantValue[0];
+					modes.WorkgroupSizeY = object.constantValue[1];
+					modes.WorkgroupSizeZ = object.constantValue[2];
+				}
+				break;
 			}
-			break;
-		}
-		case spv::OpName:
-		case spv::OpMemberName:
-		case spv::OpSource:
-		case spv::OpSourceContinued:
-		case spv::OpSourceExtension:
-		case spv::OpLine:
-		case spv::OpNoLine:
-		case spv::OpModuleProcessed:
-		case spv::OpString:
-			// No semantic impact
-			break;
+			case spv::OpSpecConstantOp:
+				EvalSpecConstantOp(insn);
+				break;
 
-		case spv::OpFunctionParameter:
-			// These should have all been removed by preprocessing passes. If we see them here,
-			// our assumptions are wrong and we will probably generate wrong code.
-			UNREACHABLE("%s should have already been lowered.", OpcodeName(opcode).c_str());
-			break;
+			case spv::OpCapability:
+			{
+				auto capability = static_cast<spv::Capability>(insn.word(1));
+				switch(capability)
+				{
+					case spv::CapabilityMatrix: capabilities.Matrix = true; break;
+					case spv::CapabilityShader: capabilities.Shader = true; break;
+					case spv::CapabilityClipDistance: capabilities.ClipDistance = true; break;
+					case spv::CapabilityCullDistance: capabilities.CullDistance = true; break;
+					case spv::CapabilityInputAttachment: capabilities.InputAttachment = true; break;
+					case spv::CapabilitySampled1D: capabilities.Sampled1D = true; break;
+					case spv::CapabilityImage1D: capabilities.Image1D = true; break;
+					case spv::CapabilityImageCubeArray: capabilities.ImageCubeArray = true; break;
+					case spv::CapabilitySampledBuffer: capabilities.SampledBuffer = true; break;
+					case spv::CapabilitySampledCubeArray: capabilities.SampledCubeArray = true; break;
+					case spv::CapabilityImageBuffer: capabilities.ImageBuffer = true; break;
+					case spv::CapabilityStorageImageExtendedFormats: capabilities.StorageImageExtendedFormats = true; break;
+					case spv::CapabilityImageQuery: capabilities.ImageQuery = true; break;
+					case spv::CapabilityDerivativeControl: capabilities.DerivativeControl = true; break;
+					case spv::CapabilityGroupNonUniform: capabilities.GroupNonUniform = true; break;
+					case spv::CapabilityGroupNonUniformVote: capabilities.GroupNonUniformVote = true; break;
+					case spv::CapabilityGroupNonUniformArithmetic: capabilities.GroupNonUniformArithmetic = true; break;
+					case spv::CapabilityGroupNonUniformBallot: capabilities.GroupNonUniformBallot = true; break;
+					case spv::CapabilityGroupNonUniformShuffle: capabilities.GroupNonUniformShuffle = true; break;
+					case spv::CapabilityGroupNonUniformShuffleRelative: capabilities.GroupNonUniformShuffleRelative = true; break;
+					case spv::CapabilityDeviceGroup: capabilities.DeviceGroup = true; break;
+					case spv::CapabilityMultiView: capabilities.MultiView = true; break;
+					default:
+						UNSUPPORTED("Unsupported capability %u", insn.word(1));
+				}
+				break;  // Various capabilities will be declared, but none affect our code generation at this point.
+			}
 
-		case spv::OpFunctionCall:
-			// TODO(b/141246700): Add full support for spv::OpFunctionCall
-			break;
+			case spv::OpMemoryModel:
+				break;  // Memory model does not affect our code generation until we decide to do Vulkan Memory Model support.
 
-		case spv::OpFConvert:
-			UNSUPPORTED("SPIR-V Float16 or Float64 Capability (OpFConvert)");
-			break;
+			case spv::OpFunction:
+			{
+				auto functionId = Function::ID(insn.word(2));
+				ASSERT_MSG(currentFunction == 0, "Functions %d and %d overlap", currentFunction.value(), functionId.value());
+				currentFunction = functionId;
+				auto &function = functions[functionId];
+				function.result = Type::ID(insn.word(1));
+				function.type = Type::ID(insn.word(4));
+				// Scan forward to find the function's label.
+				for(auto it = insn; it != end() && function.entry == 0; it++)
+				{
+					switch(it.opcode())
+					{
+						case spv::OpFunction:
+						case spv::OpFunctionParameter:
+							break;
+						case spv::OpLabel:
+							function.entry = Block::ID(it.word(1));
+							break;
+						default:
+							WARN("Unexpected opcode '%s' following OpFunction", OpcodeName(it.opcode()).c_str());
+					}
+				}
+				ASSERT_MSG(function.entry != 0, "Function<%d> has no label", currentFunction.value());
+				break;
+			}
 
-		case spv::OpSConvert:
-			UNSUPPORTED("SPIR-V Int16 or Int64 Capability (OpSConvert)");
-			break;
+			case spv::OpFunctionEnd:
+				currentFunction = 0;
+				break;
 
-		case spv::OpUConvert:
-			UNSUPPORTED("SPIR-V Int16 or Int64 Capability (OpUConvert)");
-			break;
+			case spv::OpExtInstImport:
+			{
+				// We will only support the GLSL 450 extended instruction set, so no point in tracking the ID we assign it.
+				// Valid shaders will not attempt to import any other instruction sets.
+				auto ext = insn.string(2);
+				if(0 != strcmp("GLSL.std.450", ext))
+				{
+					UNSUPPORTED("SPIR-V Extension: %s", ext);
+				}
+				break;
+			}
+			case spv::OpName:
+			case spv::OpMemberName:
+			case spv::OpSource:
+			case spv::OpSourceContinued:
+			case spv::OpSourceExtension:
+			case spv::OpLine:
+			case spv::OpNoLine:
+			case spv::OpModuleProcessed:
+			case spv::OpString:
+				// No semantic impact
+				break;
 
-		case spv::OpLoad:
-		case spv::OpAccessChain:
-		case spv::OpInBoundsAccessChain:
-		case spv::OpSampledImage:
-		case spv::OpImage:
+			case spv::OpFunctionParameter:
+				// These should have all been removed by preprocessing passes. If we see them here,
+				// our assumptions are wrong and we will probably generate wrong code.
+				UNREACHABLE("%s should have already been lowered.", OpcodeName(opcode).c_str());
+				break;
+
+			case spv::OpFunctionCall:
+				// TODO(b/141246700): Add full support for spv::OpFunctionCall
+				break;
+
+			case spv::OpFConvert:
+				UNSUPPORTED("SPIR-V Float16 or Float64 Capability (OpFConvert)");
+				break;
+
+			case spv::OpSConvert:
+				UNSUPPORTED("SPIR-V Int16 or Int64 Capability (OpSConvert)");
+				break;
+
+			case spv::OpUConvert:
+				UNSUPPORTED("SPIR-V Int16 or Int64 Capability (OpUConvert)");
+				break;
+
+			case spv::OpLoad:
+			case spv::OpAccessChain:
+			case spv::OpInBoundsAccessChain:
+			case spv::OpSampledImage:
+			case spv::OpImage:
 			{
 				// Propagate the descriptor decorations to the result.
 				Object::ID resultId = insn.word(2);
@@ -484,202 +487,202 @@
 			}
 			break;
 
-		case spv::OpCompositeConstruct:
-		case spv::OpCompositeInsert:
-		case spv::OpCompositeExtract:
-		case spv::OpVectorShuffle:
-		case spv::OpVectorTimesScalar:
-		case spv::OpMatrixTimesScalar:
-		case spv::OpMatrixTimesVector:
-		case spv::OpVectorTimesMatrix:
-		case spv::OpMatrixTimesMatrix:
-		case spv::OpOuterProduct:
-		case spv::OpTranspose:
-		case spv::OpVectorExtractDynamic:
-		case spv::OpVectorInsertDynamic:
-		// Unary ops
-		case spv::OpNot:
-		case spv::OpBitFieldInsert:
-		case spv::OpBitFieldSExtract:
-		case spv::OpBitFieldUExtract:
-		case spv::OpBitReverse:
-		case spv::OpBitCount:
-		case spv::OpSNegate:
-		case spv::OpFNegate:
-		case spv::OpLogicalNot:
-		case spv::OpQuantizeToF16:
-		// Binary ops
-		case spv::OpIAdd:
-		case spv::OpISub:
-		case spv::OpIMul:
-		case spv::OpSDiv:
-		case spv::OpUDiv:
-		case spv::OpFAdd:
-		case spv::OpFSub:
-		case spv::OpFMul:
-		case spv::OpFDiv:
-		case spv::OpFMod:
-		case spv::OpFRem:
-		case spv::OpFOrdEqual:
-		case spv::OpFUnordEqual:
-		case spv::OpFOrdNotEqual:
-		case spv::OpFUnordNotEqual:
-		case spv::OpFOrdLessThan:
-		case spv::OpFUnordLessThan:
-		case spv::OpFOrdGreaterThan:
-		case spv::OpFUnordGreaterThan:
-		case spv::OpFOrdLessThanEqual:
-		case spv::OpFUnordLessThanEqual:
-		case spv::OpFOrdGreaterThanEqual:
-		case spv::OpFUnordGreaterThanEqual:
-		case spv::OpSMod:
-		case spv::OpSRem:
-		case spv::OpUMod:
-		case spv::OpIEqual:
-		case spv::OpINotEqual:
-		case spv::OpUGreaterThan:
-		case spv::OpSGreaterThan:
-		case spv::OpUGreaterThanEqual:
-		case spv::OpSGreaterThanEqual:
-		case spv::OpULessThan:
-		case spv::OpSLessThan:
-		case spv::OpULessThanEqual:
-		case spv::OpSLessThanEqual:
-		case spv::OpShiftRightLogical:
-		case spv::OpShiftRightArithmetic:
-		case spv::OpShiftLeftLogical:
-		case spv::OpBitwiseOr:
-		case spv::OpBitwiseXor:
-		case spv::OpBitwiseAnd:
-		case spv::OpLogicalOr:
-		case spv::OpLogicalAnd:
-		case spv::OpLogicalEqual:
-		case spv::OpLogicalNotEqual:
-		case spv::OpUMulExtended:
-		case spv::OpSMulExtended:
-		case spv::OpIAddCarry:
-		case spv::OpISubBorrow:
-		case spv::OpDot:
-		case spv::OpConvertFToU:
-		case spv::OpConvertFToS:
-		case spv::OpConvertSToF:
-		case spv::OpConvertUToF:
-		case spv::OpBitcast:
-		case spv::OpSelect:
-		case spv::OpExtInst:
-		case spv::OpIsInf:
-		case spv::OpIsNan:
-		case spv::OpAny:
-		case spv::OpAll:
-		case spv::OpDPdx:
-		case spv::OpDPdxCoarse:
-		case spv::OpDPdy:
-		case spv::OpDPdyCoarse:
-		case spv::OpFwidth:
-		case spv::OpFwidthCoarse:
-		case spv::OpDPdxFine:
-		case spv::OpDPdyFine:
-		case spv::OpFwidthFine:
-		case spv::OpAtomicLoad:
-		case spv::OpAtomicIAdd:
-		case spv::OpAtomicISub:
-		case spv::OpAtomicSMin:
-		case spv::OpAtomicSMax:
-		case spv::OpAtomicUMin:
-		case spv::OpAtomicUMax:
-		case spv::OpAtomicAnd:
-		case spv::OpAtomicOr:
-		case spv::OpAtomicXor:
-		case spv::OpAtomicIIncrement:
-		case spv::OpAtomicIDecrement:
-		case spv::OpAtomicExchange:
-		case spv::OpAtomicCompareExchange:
-		case spv::OpPhi:
-		case spv::OpImageSampleImplicitLod:
-		case spv::OpImageSampleExplicitLod:
-		case spv::OpImageSampleDrefImplicitLod:
-		case spv::OpImageSampleDrefExplicitLod:
-		case spv::OpImageSampleProjImplicitLod:
-		case spv::OpImageSampleProjExplicitLod:
-		case spv::OpImageSampleProjDrefImplicitLod:
-		case spv::OpImageSampleProjDrefExplicitLod:
-		case spv::OpImageGather:
-		case spv::OpImageDrefGather:
-		case spv::OpImageFetch:
-		case spv::OpImageQuerySizeLod:
-		case spv::OpImageQuerySize:
-		case spv::OpImageQueryLod:
-		case spv::OpImageQueryLevels:
-		case spv::OpImageQuerySamples:
-		case spv::OpImageRead:
-		case spv::OpImageTexelPointer:
-		case spv::OpGroupNonUniformElect:
-		case spv::OpGroupNonUniformAll:
-		case spv::OpGroupNonUniformAny:
-		case spv::OpGroupNonUniformAllEqual:
-		case spv::OpGroupNonUniformBroadcast:
-		case spv::OpGroupNonUniformBroadcastFirst:
-		case spv::OpGroupNonUniformBallot:
-		case spv::OpGroupNonUniformInverseBallot:
-		case spv::OpGroupNonUniformBallotBitExtract:
-		case spv::OpGroupNonUniformBallotBitCount:
-		case spv::OpGroupNonUniformBallotFindLSB:
-		case spv::OpGroupNonUniformBallotFindMSB:
-		case spv::OpGroupNonUniformShuffle:
-		case spv::OpGroupNonUniformShuffleXor:
-		case spv::OpGroupNonUniformShuffleUp:
-		case spv::OpGroupNonUniformShuffleDown:
-		case spv::OpGroupNonUniformIAdd:
-		case spv::OpGroupNonUniformFAdd:
-		case spv::OpGroupNonUniformIMul:
-		case spv::OpGroupNonUniformFMul:
-		case spv::OpGroupNonUniformSMin:
-		case spv::OpGroupNonUniformUMin:
-		case spv::OpGroupNonUniformFMin:
-		case spv::OpGroupNonUniformSMax:
-		case spv::OpGroupNonUniformUMax:
-		case spv::OpGroupNonUniformFMax:
-		case spv::OpGroupNonUniformBitwiseAnd:
-		case spv::OpGroupNonUniformBitwiseOr:
-		case spv::OpGroupNonUniformBitwiseXor:
-		case spv::OpGroupNonUniformLogicalAnd:
-		case spv::OpGroupNonUniformLogicalOr:
-		case spv::OpGroupNonUniformLogicalXor:
-		case spv::OpCopyObject:
-		case spv::OpArrayLength:
-			// Instructions that yield an intermediate value or divergent pointer
-			DefineResult(insn);
-			break;
+			case spv::OpCompositeConstruct:
+			case spv::OpCompositeInsert:
+			case spv::OpCompositeExtract:
+			case spv::OpVectorShuffle:
+			case spv::OpVectorTimesScalar:
+			case spv::OpMatrixTimesScalar:
+			case spv::OpMatrixTimesVector:
+			case spv::OpVectorTimesMatrix:
+			case spv::OpMatrixTimesMatrix:
+			case spv::OpOuterProduct:
+			case spv::OpTranspose:
+			case spv::OpVectorExtractDynamic:
+			case spv::OpVectorInsertDynamic:
+			// Unary ops
+			case spv::OpNot:
+			case spv::OpBitFieldInsert:
+			case spv::OpBitFieldSExtract:
+			case spv::OpBitFieldUExtract:
+			case spv::OpBitReverse:
+			case spv::OpBitCount:
+			case spv::OpSNegate:
+			case spv::OpFNegate:
+			case spv::OpLogicalNot:
+			case spv::OpQuantizeToF16:
+			// Binary ops
+			case spv::OpIAdd:
+			case spv::OpISub:
+			case spv::OpIMul:
+			case spv::OpSDiv:
+			case spv::OpUDiv:
+			case spv::OpFAdd:
+			case spv::OpFSub:
+			case spv::OpFMul:
+			case spv::OpFDiv:
+			case spv::OpFMod:
+			case spv::OpFRem:
+			case spv::OpFOrdEqual:
+			case spv::OpFUnordEqual:
+			case spv::OpFOrdNotEqual:
+			case spv::OpFUnordNotEqual:
+			case spv::OpFOrdLessThan:
+			case spv::OpFUnordLessThan:
+			case spv::OpFOrdGreaterThan:
+			case spv::OpFUnordGreaterThan:
+			case spv::OpFOrdLessThanEqual:
+			case spv::OpFUnordLessThanEqual:
+			case spv::OpFOrdGreaterThanEqual:
+			case spv::OpFUnordGreaterThanEqual:
+			case spv::OpSMod:
+			case spv::OpSRem:
+			case spv::OpUMod:
+			case spv::OpIEqual:
+			case spv::OpINotEqual:
+			case spv::OpUGreaterThan:
+			case spv::OpSGreaterThan:
+			case spv::OpUGreaterThanEqual:
+			case spv::OpSGreaterThanEqual:
+			case spv::OpULessThan:
+			case spv::OpSLessThan:
+			case spv::OpULessThanEqual:
+			case spv::OpSLessThanEqual:
+			case spv::OpShiftRightLogical:
+			case spv::OpShiftRightArithmetic:
+			case spv::OpShiftLeftLogical:
+			case spv::OpBitwiseOr:
+			case spv::OpBitwiseXor:
+			case spv::OpBitwiseAnd:
+			case spv::OpLogicalOr:
+			case spv::OpLogicalAnd:
+			case spv::OpLogicalEqual:
+			case spv::OpLogicalNotEqual:
+			case spv::OpUMulExtended:
+			case spv::OpSMulExtended:
+			case spv::OpIAddCarry:
+			case spv::OpISubBorrow:
+			case spv::OpDot:
+			case spv::OpConvertFToU:
+			case spv::OpConvertFToS:
+			case spv::OpConvertSToF:
+			case spv::OpConvertUToF:
+			case spv::OpBitcast:
+			case spv::OpSelect:
+			case spv::OpExtInst:
+			case spv::OpIsInf:
+			case spv::OpIsNan:
+			case spv::OpAny:
+			case spv::OpAll:
+			case spv::OpDPdx:
+			case spv::OpDPdxCoarse:
+			case spv::OpDPdy:
+			case spv::OpDPdyCoarse:
+			case spv::OpFwidth:
+			case spv::OpFwidthCoarse:
+			case spv::OpDPdxFine:
+			case spv::OpDPdyFine:
+			case spv::OpFwidthFine:
+			case spv::OpAtomicLoad:
+			case spv::OpAtomicIAdd:
+			case spv::OpAtomicISub:
+			case spv::OpAtomicSMin:
+			case spv::OpAtomicSMax:
+			case spv::OpAtomicUMin:
+			case spv::OpAtomicUMax:
+			case spv::OpAtomicAnd:
+			case spv::OpAtomicOr:
+			case spv::OpAtomicXor:
+			case spv::OpAtomicIIncrement:
+			case spv::OpAtomicIDecrement:
+			case spv::OpAtomicExchange:
+			case spv::OpAtomicCompareExchange:
+			case spv::OpPhi:
+			case spv::OpImageSampleImplicitLod:
+			case spv::OpImageSampleExplicitLod:
+			case spv::OpImageSampleDrefImplicitLod:
+			case spv::OpImageSampleDrefExplicitLod:
+			case spv::OpImageSampleProjImplicitLod:
+			case spv::OpImageSampleProjExplicitLod:
+			case spv::OpImageSampleProjDrefImplicitLod:
+			case spv::OpImageSampleProjDrefExplicitLod:
+			case spv::OpImageGather:
+			case spv::OpImageDrefGather:
+			case spv::OpImageFetch:
+			case spv::OpImageQuerySizeLod:
+			case spv::OpImageQuerySize:
+			case spv::OpImageQueryLod:
+			case spv::OpImageQueryLevels:
+			case spv::OpImageQuerySamples:
+			case spv::OpImageRead:
+			case spv::OpImageTexelPointer:
+			case spv::OpGroupNonUniformElect:
+			case spv::OpGroupNonUniformAll:
+			case spv::OpGroupNonUniformAny:
+			case spv::OpGroupNonUniformAllEqual:
+			case spv::OpGroupNonUniformBroadcast:
+			case spv::OpGroupNonUniformBroadcastFirst:
+			case spv::OpGroupNonUniformBallot:
+			case spv::OpGroupNonUniformInverseBallot:
+			case spv::OpGroupNonUniformBallotBitExtract:
+			case spv::OpGroupNonUniformBallotBitCount:
+			case spv::OpGroupNonUniformBallotFindLSB:
+			case spv::OpGroupNonUniformBallotFindMSB:
+			case spv::OpGroupNonUniformShuffle:
+			case spv::OpGroupNonUniformShuffleXor:
+			case spv::OpGroupNonUniformShuffleUp:
+			case spv::OpGroupNonUniformShuffleDown:
+			case spv::OpGroupNonUniformIAdd:
+			case spv::OpGroupNonUniformFAdd:
+			case spv::OpGroupNonUniformIMul:
+			case spv::OpGroupNonUniformFMul:
+			case spv::OpGroupNonUniformSMin:
+			case spv::OpGroupNonUniformUMin:
+			case spv::OpGroupNonUniformFMin:
+			case spv::OpGroupNonUniformSMax:
+			case spv::OpGroupNonUniformUMax:
+			case spv::OpGroupNonUniformFMax:
+			case spv::OpGroupNonUniformBitwiseAnd:
+			case spv::OpGroupNonUniformBitwiseOr:
+			case spv::OpGroupNonUniformBitwiseXor:
+			case spv::OpGroupNonUniformLogicalAnd:
+			case spv::OpGroupNonUniformLogicalOr:
+			case spv::OpGroupNonUniformLogicalXor:
+			case spv::OpCopyObject:
+			case spv::OpArrayLength:
+				// Instructions that yield an intermediate value or divergent pointer
+				DefineResult(insn);
+				break;
 
-		case spv::OpStore:
-		case spv::OpAtomicStore:
-		case spv::OpImageWrite:
-		case spv::OpCopyMemory:
-		case spv::OpMemoryBarrier:
-			// Don't need to do anything during analysis pass
-			break;
+			case spv::OpStore:
+			case spv::OpAtomicStore:
+			case spv::OpImageWrite:
+			case spv::OpCopyMemory:
+			case spv::OpMemoryBarrier:
+				// Don't need to do anything during analysis pass
+				break;
 
-		case spv::OpControlBarrier:
-			modes.ContainsControlBarriers = true;
-			break;
+			case spv::OpControlBarrier:
+				modes.ContainsControlBarriers = true;
+				break;
 
-		case spv::OpExtension:
-		{
-			auto ext = insn.string(1);
-			// Part of core SPIR-V 1.3. Vulkan 1.1 implementations must also accept the pre-1.3
-			// extension per Appendix A, `Vulkan Environment for SPIR-V`.
-			if(!strcmp(ext, "SPV_KHR_storage_buffer_storage_class")) break;
-			if(!strcmp(ext, "SPV_KHR_shader_draw_parameters")) break;
-			if(!strcmp(ext, "SPV_KHR_16bit_storage")) break;
-			if(!strcmp(ext, "SPV_KHR_variable_pointers")) break;
-			if(!strcmp(ext, "SPV_KHR_device_group")) break;
-			if(!strcmp(ext, "SPV_KHR_multiview")) break;
-			UNSUPPORTED("SPIR-V Extension: %s", ext);
-			break;
-		}
+			case spv::OpExtension:
+			{
+				auto ext = insn.string(1);
+				// Part of core SPIR-V 1.3. Vulkan 1.1 implementations must also accept the pre-1.3
+				// extension per Appendix A, `Vulkan Environment for SPIR-V`.
+				if(!strcmp(ext, "SPV_KHR_storage_buffer_storage_class")) break;
+				if(!strcmp(ext, "SPV_KHR_shader_draw_parameters")) break;
+				if(!strcmp(ext, "SPV_KHR_16bit_storage")) break;
+				if(!strcmp(ext, "SPV_KHR_variable_pointers")) break;
+				if(!strcmp(ext, "SPV_KHR_device_group")) break;
+				if(!strcmp(ext, "SPV_KHR_multiview")) break;
+				UNSUPPORTED("SPIR-V Extension: %s", ext);
+				break;
+			}
 
-		default:
-			UNIMPLEMENTED("%s", OpcodeName(opcode).c_str());
+			default:
+				UNIMPLEMENTED("%s", OpcodeName(opcode).c_str());
 		}
 	}
 
@@ -702,45 +705,45 @@
 	// member. All members of such a structure are builtins.
 	switch(insn.opcode())
 	{
-	case spv::OpTypeStruct:
-	{
-		auto d = memberDecorations.find(resultId);
-		if(d != memberDecorations.end())
+		case spv::OpTypeStruct:
 		{
-			for(auto &m : d->second)
+			auto d = memberDecorations.find(resultId);
+			if(d != memberDecorations.end())
 			{
-				if(m.HasBuiltIn)
+				for(auto &m : d->second)
 				{
-					type.isBuiltInBlock = true;
-					break;
+					if(m.HasBuiltIn)
+					{
+						type.isBuiltInBlock = true;
+						break;
+					}
 				}
 			}
+			break;
 		}
-		break;
-	}
-	case spv::OpTypePointer:
-	{
-		Type::ID elementTypeId = insn.word(3);
-		type.element = elementTypeId;
-		type.isBuiltInBlock = getType(elementTypeId).isBuiltInBlock;
-		type.storageClass = static_cast<spv::StorageClass>(insn.word(2));
-		break;
-	}
-	case spv::OpTypeVector:
-	case spv::OpTypeMatrix:
-	case spv::OpTypeArray:
-	case spv::OpTypeRuntimeArray:
-	{
-		Type::ID elementTypeId = insn.word(2);
-		type.element = elementTypeId;
-		break;
-	}
-	default:
-		break;
+		case spv::OpTypePointer:
+		{
+			Type::ID elementTypeId = insn.word(3);
+			type.element = elementTypeId;
+			type.isBuiltInBlock = getType(elementTypeId).isBuiltInBlock;
+			type.storageClass = static_cast<spv::StorageClass>(insn.word(2));
+			break;
+		}
+		case spv::OpTypeVector:
+		case spv::OpTypeMatrix:
+		case spv::OpTypeArray:
+		case spv::OpTypeRuntimeArray:
+		{
+			Type::ID elementTypeId = insn.word(2);
+			type.element = elementTypeId;
+			break;
+		}
+		default:
+			break;
 	}
 }
 
-SpirvShader::Object& SpirvShader::CreateConstant(InsnIterator insn)
+SpirvShader::Object &SpirvShader::CreateConstant(InsnIterator insn)
 {
 	Type::ID typeId = insn.word(1);
 	Object::ID resultId = insn.word(2);
@@ -771,7 +774,7 @@
 	{
 		// walk the builtin block, registering each of its members separately.
 		auto m = memberDecorations.find(objectTy.element);
-		ASSERT(m != memberDecorations.end());        // otherwise we wouldn't have marked the type chain
+		ASSERT(m != memberDecorations.end());  // otherwise we wouldn't have marked the type chain
 		auto &structType = pointeeTy.definition;
 		auto offset = 0u;
 		auto word = 2u;
@@ -781,7 +784,7 @@
 
 			if(member.HasBuiltIn)
 			{
-				builtinInterface[member.BuiltIn] = {resultId, offset, memberType.sizeInComponents};
+				builtinInterface[member.BuiltIn] = { resultId, offset, memberType.sizeInComponents };
 			}
 
 			offset += memberType.sizeInComponents;
@@ -793,24 +796,24 @@
 	auto d = decorations.find(resultId);
 	if(d != decorations.end() && d->second.HasBuiltIn)
 	{
-		builtinInterface[d->second.BuiltIn] = {resultId, 0, pointeeTy.sizeInComponents};
+		builtinInterface[d->second.BuiltIn] = { resultId, 0, pointeeTy.sizeInComponents };
 	}
 	else
 	{
 		object.kind = Object::Kind::InterfaceVariable;
 		VisitInterface(resultId,
-					   [&userDefinedInterface](Decorations const &d, AttribType type) {
-						   // Populate a single scalar slot in the interface from a collection of decorations and the intended component type.
-						   auto scalarSlot = (d.Location << 2) | d.Component;
-						   ASSERT(scalarSlot >= 0 &&
-								  scalarSlot < static_cast<int32_t>(userDefinedInterface.size()));
+		               [&userDefinedInterface](Decorations const &d, AttribType type) {
+			               // Populate a single scalar slot in the interface from a collection of decorations and the intended component type.
+			               auto scalarSlot = (d.Location << 2) | d.Component;
+			               ASSERT(scalarSlot >= 0 &&
+			                      scalarSlot < static_cast<int32_t>(userDefinedInterface.size()));
 
-						   auto &slot = userDefinedInterface[scalarSlot];
-						   slot.Type = type;
-						   slot.Flat = d.Flat;
-						   slot.NoPerspective = d.NoPerspective;
-						   slot.Centroid = d.Centroid;
-					   });
+			               auto &slot = userDefinedInterface[scalarSlot];
+			               slot.Type = type;
+			               slot.Flat = d.Flat;
+			               slot.NoPerspective = d.NoPerspective;
+			               slot.Centroid = d.Centroid;
+		               });
 	}
 }
 
@@ -819,31 +822,31 @@
 	auto mode = static_cast<spv::ExecutionMode>(insn.word(2));
 	switch(mode)
 	{
-	case spv::ExecutionModeEarlyFragmentTests:
-		modes.EarlyFragmentTests = true;
-		break;
-	case spv::ExecutionModeDepthReplacing:
-		modes.DepthReplacing = true;
-		break;
-	case spv::ExecutionModeDepthGreater:
-		modes.DepthGreater = true;
-		break;
-	case spv::ExecutionModeDepthLess:
-		modes.DepthLess = true;
-		break;
-	case spv::ExecutionModeDepthUnchanged:
-		modes.DepthUnchanged = true;
-		break;
-	case spv::ExecutionModeLocalSize:
-		modes.WorkgroupSizeX = insn.word(3);
-		modes.WorkgroupSizeY = insn.word(4);
-		modes.WorkgroupSizeZ = insn.word(5);
-		break;
-	case spv::ExecutionModeOriginUpperLeft:
-		// This is always the case for a Vulkan shader. Do nothing.
-		break;
-	default:
-		UNREACHABLE("Execution mode: %d", int(mode));
+		case spv::ExecutionModeEarlyFragmentTests:
+			modes.EarlyFragmentTests = true;
+			break;
+		case spv::ExecutionModeDepthReplacing:
+			modes.DepthReplacing = true;
+			break;
+		case spv::ExecutionModeDepthGreater:
+			modes.DepthGreater = true;
+			break;
+		case spv::ExecutionModeDepthLess:
+			modes.DepthLess = true;
+			break;
+		case spv::ExecutionModeDepthUnchanged:
+			modes.DepthUnchanged = true;
+			break;
+		case spv::ExecutionModeLocalSize:
+			modes.WorkgroupSizeX = insn.word(3);
+			modes.WorkgroupSizeY = insn.word(4);
+			modes.WorkgroupSizeZ = insn.word(5);
+			break;
+		case spv::ExecutionModeOriginUpperLeft:
+			// This is always the case for a Vulkan shader. Do nothing.
+			break;
+		default:
+			UNREACHABLE("Execution mode: %d", int(mode));
 	}
 }
 
@@ -854,54 +857,54 @@
 	// already been described (and so their sizes determined)
 	switch(insn.opcode())
 	{
-	case spv::OpTypeVoid:
-	case spv::OpTypeSampler:
-	case spv::OpTypeImage:
-	case spv::OpTypeSampledImage:
-	case spv::OpTypeFunction:
-	case spv::OpTypeRuntimeArray:
-		// Objects that don't consume any space.
-		// Descriptor-backed objects currently only need exist at compile-time.
-		// Runtime arrays don't appear in places where their size would be interesting
-		return 0;
+		case spv::OpTypeVoid:
+		case spv::OpTypeSampler:
+		case spv::OpTypeImage:
+		case spv::OpTypeSampledImage:
+		case spv::OpTypeFunction:
+		case spv::OpTypeRuntimeArray:
+			// Objects that don't consume any space.
+			// Descriptor-backed objects currently only need exist at compile-time.
+			// Runtime arrays don't appear in places where their size would be interesting
+			return 0;
 
-	case spv::OpTypeBool:
-	case spv::OpTypeFloat:
-	case spv::OpTypeInt:
-		// All the fundamental types are 1 component. If we ever add support for 8/16/64-bit components,
-		// we might need to change this, but only 32 bit components are required for Vulkan 1.1.
-		return 1;
+		case spv::OpTypeBool:
+		case spv::OpTypeFloat:
+		case spv::OpTypeInt:
+			// All the fundamental types are 1 component. If we ever add support for 8/16/64-bit components,
+			// we might need to change this, but only 32 bit components are required for Vulkan 1.1.
+			return 1;
 
-	case spv::OpTypeVector:
-	case spv::OpTypeMatrix:
-		// Vectors and matrices both consume element count * element size.
-		return getType(insn.word(2)).sizeInComponents * insn.word(3);
+		case spv::OpTypeVector:
+		case spv::OpTypeMatrix:
+			// Vectors and matrices both consume element count * element size.
+			return getType(insn.word(2)).sizeInComponents * insn.word(3);
 
-	case spv::OpTypeArray:
-	{
-		// Element count * element size. Array sizes come from constant ids.
-		auto arraySize = GetConstScalarInt(insn.word(3));
-		return getType(insn.word(2)).sizeInComponents * arraySize;
-	}
-
-	case spv::OpTypeStruct:
-	{
-		uint32_t size = 0;
-		for(uint32_t i = 2u; i < insn.wordCount(); i++)
+		case spv::OpTypeArray:
 		{
-			size += getType(insn.word(i)).sizeInComponents;
+			// Element count * element size. Array sizes come from constant ids.
+			auto arraySize = GetConstScalarInt(insn.word(3));
+			return getType(insn.word(2)).sizeInComponents * arraySize;
 		}
-		return size;
-	}
 
-	case spv::OpTypePointer:
-		// Runtime representation of a pointer is a per-lane index.
-		// Note: clients are expected to look through the pointer if they want the pointee size instead.
-		return 1;
+		case spv::OpTypeStruct:
+		{
+			uint32_t size = 0;
+			for(uint32_t i = 2u; i < insn.wordCount(); i++)
+			{
+				size += getType(insn.word(i)).sizeInComponents;
+			}
+			return size;
+		}
 
-	default:
-		UNREACHABLE("%s", OpcodeName(insn.opcode()).c_str());
-		return 0;
+		case spv::OpTypePointer:
+			// Runtime representation of a pointer is a per-lane index.
+			// Note: clients are expected to look through the pointer if they want the pointee size instead.
+			return 1;
+
+		default:
+			UNREACHABLE("%s", OpcodeName(insn.opcode()).c_str());
+			return 0;
 	}
 }
 
@@ -924,54 +927,54 @@
 	auto const &obj = getType(id);
 	switch(obj.opcode())
 	{
-	case spv::OpTypePointer:
-		return VisitInterfaceInner(obj.definition.word(3), d, f);
-	case spv::OpTypeMatrix:
-		for(auto i = 0u; i < obj.definition.word(3); i++, d.Location++)
+		case spv::OpTypePointer:
+			return VisitInterfaceInner(obj.definition.word(3), d, f);
+		case spv::OpTypeMatrix:
+			for(auto i = 0u; i < obj.definition.word(3); i++, d.Location++)
+			{
+				// consumes same components of N consecutive locations
+				VisitInterfaceInner(obj.definition.word(2), d, f);
+			}
+			return d.Location;
+		case spv::OpTypeVector:
+			for(auto i = 0u; i < obj.definition.word(3); i++, d.Component++)
+			{
+				// consumes N consecutive components in the same location
+				VisitInterfaceInner(obj.definition.word(2), d, f);
+			}
+			return d.Location + 1;
+		case spv::OpTypeFloat:
+			f(d, ATTRIBTYPE_FLOAT);
+			return d.Location + 1;
+		case spv::OpTypeInt:
+			f(d, obj.definition.word(3) ? ATTRIBTYPE_INT : ATTRIBTYPE_UINT);
+			return d.Location + 1;
+		case spv::OpTypeBool:
+			f(d, ATTRIBTYPE_UINT);
+			return d.Location + 1;
+		case spv::OpTypeStruct:
 		{
-			// consumes same components of N consecutive locations
-			VisitInterfaceInner(obj.definition.word(2), d, f);
+			// iterate over members, which may themselves have Location/Component decorations
+			for(auto i = 0u; i < obj.definition.wordCount() - 2; i++)
+			{
+				ApplyDecorationsForIdMember(&d, id, i);
+				d.Location = VisitInterfaceInner(obj.definition.word(i + 2), d, f);
+				d.Component = 0;  // Implicit locations always have component=0
+			}
+			return d.Location;
 		}
-		return d.Location;
-	case spv::OpTypeVector:
-		for(auto i = 0u; i < obj.definition.word(3); i++, d.Component++)
+		case spv::OpTypeArray:
 		{
-			// consumes N consecutive components in the same location
-			VisitInterfaceInner(obj.definition.word(2), d, f);
+			auto arraySize = GetConstScalarInt(obj.definition.word(3));
+			for(auto i = 0u; i < arraySize; i++)
+			{
+				d.Location = VisitInterfaceInner(obj.definition.word(2), d, f);
+			}
+			return d.Location;
 		}
-		return d.Location + 1;
-	case spv::OpTypeFloat:
-		f(d, ATTRIBTYPE_FLOAT);
-		return d.Location + 1;
-	case spv::OpTypeInt:
-		f(d, obj.definition.word(3) ? ATTRIBTYPE_INT : ATTRIBTYPE_UINT);
-		return d.Location + 1;
-	case spv::OpTypeBool:
-		f(d, ATTRIBTYPE_UINT);
-		return d.Location + 1;
-	case spv::OpTypeStruct:
-	{
-		// iterate over members, which may themselves have Location/Component decorations
-		for(auto i = 0u; i < obj.definition.wordCount() - 2; i++)
-		{
-			ApplyDecorationsForIdMember(&d, id, i);
-			d.Location = VisitInterfaceInner(obj.definition.word(i + 2), d, f);
-			d.Component = 0;    // Implicit locations always have component=0
-		}
-		return d.Location;
-	}
-	case spv::OpTypeArray:
-	{
-		auto arraySize = GetConstScalarInt(obj.definition.word(3));
-		for(auto i = 0u; i < arraySize; i++)
-		{
-			d.Location = VisitInterfaceInner(obj.definition.word(2), d, f);
-		}
-		return d.Location;
-	}
-	default:
-		// Intentionally partial; most opcodes do not participate in type hierarchies
-		return 0;
+		default:
+			// Intentionally partial; most opcodes do not participate in type hierarchies
+			return 0;
 	}
 }
 
@@ -996,33 +999,33 @@
 	for(auto i = 0u; i < numIndexes; i++)
 	{
 		ApplyDecorationsForId(d, typeId);
-		auto & type = getType(typeId);
+		auto &type = getType(typeId);
 		switch(type.opcode())
 		{
-		case spv::OpTypeStruct:
-		{
-			int memberIndex = GetConstScalarInt(indexIds[i]);
-			ApplyDecorationsForIdMember(d, typeId, memberIndex);
-			typeId = type.definition.word(2u + memberIndex);
-			break;
-		}
-		case spv::OpTypeArray:
-		case spv::OpTypeRuntimeArray:
-			if(dd->InputAttachmentIndex >= 0)
+			case spv::OpTypeStruct:
 			{
-				dd->InputAttachmentIndex += GetConstScalarInt(indexIds[i]);
+				int memberIndex = GetConstScalarInt(indexIds[i]);
+				ApplyDecorationsForIdMember(d, typeId, memberIndex);
+				typeId = type.definition.word(2u + memberIndex);
+				break;
 			}
-			typeId = type.element;
-			break;
-		case spv::OpTypeVector:
-			typeId = type.element;
-			break;
-		case spv::OpTypeMatrix:
-			typeId = type.element;
-			d->InsideMatrix = true;
-			break;
-		default:
-			UNREACHABLE("%s", OpcodeName(type.definition.opcode()).c_str());
+			case spv::OpTypeArray:
+			case spv::OpTypeRuntimeArray:
+				if(dd->InputAttachmentIndex >= 0)
+				{
+					dd->InputAttachmentIndex += GetConstScalarInt(indexIds[i]);
+				}
+				typeId = type.element;
+				break;
+			case spv::OpTypeVector:
+				typeId = type.element;
+				break;
+			case spv::OpTypeMatrix:
+				typeId = type.element;
+				d->InsideMatrix = true;
+				break;
+			default:
+				UNREACHABLE("%s", OpcodeName(type.definition.opcode()).c_str());
 		}
 	}
 }
@@ -1057,72 +1060,72 @@
 
 	for(auto i = 0u; i < numIndexes; i++)
 	{
-		auto & type = getType(typeId);
+		auto &type = getType(typeId);
 		ApplyDecorationsForId(&d, typeId);
 
 		switch(type.definition.opcode())
 		{
-		case spv::OpTypeStruct:
-		{
-			int memberIndex = GetConstScalarInt(indexIds[i]);
-			ApplyDecorationsForIdMember(&d, typeId, memberIndex);
-			ASSERT(d.HasOffset);
-			constantOffset += d.Offset;
-			typeId = type.definition.word(2u + memberIndex);
-			break;
-		}
-		case spv::OpTypeArray:
-		case spv::OpTypeRuntimeArray:
-		{
-			// TODO: b/127950082: Check bounds.
-			ASSERT(d.HasArrayStride);
-			auto & obj = getObject(indexIds[i]);
-			if(obj.kind == Object::Kind::Constant)
+			case spv::OpTypeStruct:
 			{
-				constantOffset += d.ArrayStride * GetConstScalarInt(indexIds[i]);
+				int memberIndex = GetConstScalarInt(indexIds[i]);
+				ApplyDecorationsForIdMember(&d, typeId, memberIndex);
+				ASSERT(d.HasOffset);
+				constantOffset += d.Offset;
+				typeId = type.definition.word(2u + memberIndex);
+				break;
 			}
-			else
+			case spv::OpTypeArray:
+			case spv::OpTypeRuntimeArray:
 			{
-				ptr += SIMD::Int(d.ArrayStride) * state->getIntermediate(indexIds[i]).Int(0);
+				// TODO: b/127950082: Check bounds.
+				ASSERT(d.HasArrayStride);
+				auto &obj = getObject(indexIds[i]);
+				if(obj.kind == Object::Kind::Constant)
+				{
+					constantOffset += d.ArrayStride * GetConstScalarInt(indexIds[i]);
+				}
+				else
+				{
+					ptr += SIMD::Int(d.ArrayStride) * state->getIntermediate(indexIds[i]).Int(0);
+				}
+				typeId = type.element;
+				break;
 			}
-			typeId = type.element;
-			break;
-		}
-		case spv::OpTypeMatrix:
-		{
-			// TODO: b/127950082: Check bounds.
-			ASSERT(d.HasMatrixStride);
-			d.InsideMatrix = true;
-			auto columnStride = (d.HasRowMajor && d.RowMajor) ? static_cast<int32_t>(sizeof(float)) : d.MatrixStride;
-			auto & obj = getObject(indexIds[i]);
-			if(obj.kind == Object::Kind::Constant)
+			case spv::OpTypeMatrix:
 			{
-				constantOffset += columnStride * GetConstScalarInt(indexIds[i]);
+				// TODO: b/127950082: Check bounds.
+				ASSERT(d.HasMatrixStride);
+				d.InsideMatrix = true;
+				auto columnStride = (d.HasRowMajor && d.RowMajor) ? static_cast<int32_t>(sizeof(float)) : d.MatrixStride;
+				auto &obj = getObject(indexIds[i]);
+				if(obj.kind == Object::Kind::Constant)
+				{
+					constantOffset += columnStride * GetConstScalarInt(indexIds[i]);
+				}
+				else
+				{
+					ptr += SIMD::Int(columnStride) * state->getIntermediate(indexIds[i]).Int(0);
+				}
+				typeId = type.element;
+				break;
 			}
-			else
+			case spv::OpTypeVector:
 			{
-				ptr += SIMD::Int(columnStride) * state->getIntermediate(indexIds[i]).Int(0);
+				auto elemStride = (d.InsideMatrix && d.HasRowMajor && d.RowMajor) ? d.MatrixStride : static_cast<int32_t>(sizeof(float));
+				auto &obj = getObject(indexIds[i]);
+				if(obj.kind == Object::Kind::Constant)
+				{
+					constantOffset += elemStride * GetConstScalarInt(indexIds[i]);
+				}
+				else
+				{
+					ptr += SIMD::Int(elemStride) * state->getIntermediate(indexIds[i]).Int(0);
+				}
+				typeId = type.element;
+				break;
 			}
-			typeId = type.element;
-			break;
-		}
-		case spv::OpTypeVector:
-		{
-			auto elemStride = (d.InsideMatrix && d.HasRowMajor && d.RowMajor) ? d.MatrixStride : static_cast<int32_t>(sizeof(float));
-			auto & obj = getObject(indexIds[i]);
-			if(obj.kind == Object::Kind::Constant)
-			{
-				constantOffset += elemStride * GetConstScalarInt(indexIds[i]);
-			}
-			else
-			{
-				ptr += SIMD::Int(elemStride) * state->getIntermediate(indexIds[i]).Int(0);
-			}
-			typeId = type.element;
-			break;
-		}
-		default:
-			UNREACHABLE("%s", OpcodeName(type.definition.opcode()).c_str());
+			default:
+				UNREACHABLE("%s", OpcodeName(type.definition.opcode()).c_str());
 		}
 	}
 
@@ -1143,63 +1146,64 @@
 
 	for(auto i = 0u; i < numIndexes; i++)
 	{
-		auto & type = getType(typeId);
+		auto &type = getType(typeId);
 		switch(type.opcode())
 		{
-		case spv::OpTypeStruct:
-		{
-			int memberIndex = GetConstScalarInt(indexIds[i]);
-			int offsetIntoStruct = 0;
-			for(auto j = 0; j < memberIndex; j++) {
-				auto memberType = type.definition.word(2u + j);
-				offsetIntoStruct += getType(memberType).sizeInComponents * sizeof(float);
-			}
-			constantOffset += offsetIntoStruct;
-			typeId = type.definition.word(2u + memberIndex);
-			break;
-		}
-
-		case spv::OpTypeVector:
-		case spv::OpTypeMatrix:
-		case spv::OpTypeArray:
-		case spv::OpTypeRuntimeArray:
-		{
-			// TODO: b/127950082: Check bounds.
-			if(getType(baseObject.type).storageClass == spv::StorageClassUniformConstant)
+			case spv::OpTypeStruct:
 			{
-				// indexing into an array of descriptors.
-				auto &obj = getObject(indexIds[i]);
-				if(obj.kind != Object::Kind::Constant)
+				int memberIndex = GetConstScalarInt(indexIds[i]);
+				int offsetIntoStruct = 0;
+				for(auto j = 0; j < memberIndex; j++)
 				{
-					UNSUPPORTED("SPIR-V SampledImageArrayDynamicIndexing Capability");
+					auto memberType = type.definition.word(2u + j);
+					offsetIntoStruct += getType(memberType).sizeInComponents * sizeof(float);
 				}
-
-				auto d = descriptorDecorations.at(baseId);
-				ASSERT(d.DescriptorSet >= 0);
-				ASSERT(d.Binding >= 0);
-				auto setLayout = routine->pipelineLayout->getDescriptorSetLayout(d.DescriptorSet);
-				auto stride = static_cast<uint32_t>(setLayout->getBindingStride(d.Binding));
-				ptr.base += stride * GetConstScalarInt(indexIds[i]);
+				constantOffset += offsetIntoStruct;
+				typeId = type.definition.word(2u + memberIndex);
+				break;
 			}
-			else
+
+			case spv::OpTypeVector:
+			case spv::OpTypeMatrix:
+			case spv::OpTypeArray:
+			case spv::OpTypeRuntimeArray:
 			{
-				auto stride = getType(type.element).sizeInComponents * static_cast<uint32_t>(sizeof(float));
-				auto & obj = getObject(indexIds[i]);
-				if(obj.kind == Object::Kind::Constant)
+				// TODO: b/127950082: Check bounds.
+				if(getType(baseObject.type).storageClass == spv::StorageClassUniformConstant)
 				{
-					ptr += stride * GetConstScalarInt(indexIds[i]);
+					// indexing into an array of descriptors.
+					auto &obj = getObject(indexIds[i]);
+					if(obj.kind != Object::Kind::Constant)
+					{
+						UNSUPPORTED("SPIR-V SampledImageArrayDynamicIndexing Capability");
+					}
+
+					auto d = descriptorDecorations.at(baseId);
+					ASSERT(d.DescriptorSet >= 0);
+					ASSERT(d.Binding >= 0);
+					auto setLayout = routine->pipelineLayout->getDescriptorSetLayout(d.DescriptorSet);
+					auto stride = static_cast<uint32_t>(setLayout->getBindingStride(d.Binding));
+					ptr.base += stride * GetConstScalarInt(indexIds[i]);
 				}
 				else
 				{
-					ptr += SIMD::Int(stride) * state->getIntermediate(indexIds[i]).Int(0);
+					auto stride = getType(type.element).sizeInComponents * static_cast<uint32_t>(sizeof(float));
+					auto &obj = getObject(indexIds[i]);
+					if(obj.kind == Object::Kind::Constant)
+					{
+						ptr += stride * GetConstScalarInt(indexIds[i]);
+					}
+					else
+					{
+						ptr += SIMD::Int(stride) * state->getIntermediate(indexIds[i]).Int(0);
+					}
 				}
+				typeId = type.element;
+				break;
 			}
-			typeId = type.element;
-			break;
-		}
 
-		default:
-			UNREACHABLE("%s", OpcodeName(type.opcode()).c_str());
+			default:
+				UNREACHABLE("%s", OpcodeName(type.opcode()).c_str());
 		}
 	}
 
@@ -1216,35 +1220,36 @@
 
 	for(auto i = 0u; i < numIndexes; i++)
 	{
-		auto & type = getType(typeId);
+		auto &type = getType(typeId);
 		switch(type.opcode())
 		{
-		case spv::OpTypeStruct:
-		{
-			int memberIndex = indexes[i];
-			int offsetIntoStruct = 0;
-			for(auto j = 0; j < memberIndex; j++) {
-				auto memberType = type.definition.word(2u + j);
-				offsetIntoStruct += getType(memberType).sizeInComponents;
+			case spv::OpTypeStruct:
+			{
+				int memberIndex = indexes[i];
+				int offsetIntoStruct = 0;
+				for(auto j = 0; j < memberIndex; j++)
+				{
+					auto memberType = type.definition.word(2u + j);
+					offsetIntoStruct += getType(memberType).sizeInComponents;
+				}
+				componentOffset += offsetIntoStruct;
+				typeId = type.definition.word(2u + memberIndex);
+				break;
 			}
-			componentOffset += offsetIntoStruct;
-			typeId = type.definition.word(2u + memberIndex);
-			break;
-		}
 
-		case spv::OpTypeVector:
-		case spv::OpTypeMatrix:
-		case spv::OpTypeArray:
-		{
-			auto elementType = type.definition.word(2);
-			auto stride = getType(elementType).sizeInComponents;
-			componentOffset += stride * indexes[i];
-			typeId = elementType;
-			break;
-		}
+			case spv::OpTypeVector:
+			case spv::OpTypeMatrix:
+			case spv::OpTypeArray:
+			{
+				auto elementType = type.definition.word(2);
+				auto stride = getType(elementType).sizeInComponents;
+				componentOffset += stride * indexes[i];
+				typeId = elementType;
+				break;
+			}
 
-		default:
-			UNREACHABLE("%s", OpcodeName(type.opcode()).c_str());
+			default:
+				UNREACHABLE("%s", OpcodeName(type.opcode()).c_str());
 		}
 	}
 
@@ -1255,58 +1260,58 @@
 {
 	switch(decoration)
 	{
-	case spv::DecorationLocation:
-		HasLocation = true;
-		Location = static_cast<int32_t>(arg);
-		break;
-	case spv::DecorationComponent:
-		HasComponent = true;
-		Component = arg;
-		break;
-	case spv::DecorationBuiltIn:
-		HasBuiltIn = true;
-		BuiltIn = static_cast<spv::BuiltIn>(arg);
-		break;
-	case spv::DecorationFlat:
-		Flat = true;
-		break;
-	case spv::DecorationNoPerspective:
-		NoPerspective = true;
-		break;
-	case spv::DecorationCentroid:
-		Centroid = true;
-		break;
-	case spv::DecorationBlock:
-		Block = true;
-		break;
-	case spv::DecorationBufferBlock:
-		BufferBlock = true;
-		break;
-	case spv::DecorationOffset:
-		HasOffset = true;
-		Offset = static_cast<int32_t>(arg);
-		break;
-	case spv::DecorationArrayStride:
-		HasArrayStride = true;
-		ArrayStride = static_cast<int32_t>(arg);
-		break;
-	case spv::DecorationMatrixStride:
-		HasMatrixStride = true;
-		MatrixStride = static_cast<int32_t>(arg);
-		break;
-	case spv::DecorationRelaxedPrecision:
-		RelaxedPrecision = true;
-		break;
-	case spv::DecorationRowMajor:
-		HasRowMajor = true;
-		RowMajor = true;
-		break;
-	case spv::DecorationColMajor:
-		HasRowMajor = true;
-		RowMajor = false;
-	default:
-		// Intentionally partial, there are many decorations we just don't care about.
-		break;
+		case spv::DecorationLocation:
+			HasLocation = true;
+			Location = static_cast<int32_t>(arg);
+			break;
+		case spv::DecorationComponent:
+			HasComponent = true;
+			Component = arg;
+			break;
+		case spv::DecorationBuiltIn:
+			HasBuiltIn = true;
+			BuiltIn = static_cast<spv::BuiltIn>(arg);
+			break;
+		case spv::DecorationFlat:
+			Flat = true;
+			break;
+		case spv::DecorationNoPerspective:
+			NoPerspective = true;
+			break;
+		case spv::DecorationCentroid:
+			Centroid = true;
+			break;
+		case spv::DecorationBlock:
+			Block = true;
+			break;
+		case spv::DecorationBufferBlock:
+			BufferBlock = true;
+			break;
+		case spv::DecorationOffset:
+			HasOffset = true;
+			Offset = static_cast<int32_t>(arg);
+			break;
+		case spv::DecorationArrayStride:
+			HasArrayStride = true;
+			ArrayStride = static_cast<int32_t>(arg);
+			break;
+		case spv::DecorationMatrixStride:
+			HasMatrixStride = true;
+			MatrixStride = static_cast<int32_t>(arg);
+			break;
+		case spv::DecorationRelaxedPrecision:
+			RelaxedPrecision = true;
+			break;
+		case spv::DecorationRowMajor:
+			HasRowMajor = true;
+			RowMajor = true;
+			break;
+		case spv::DecorationColMajor:
+			HasRowMajor = true;
+			RowMajor = false;
+		default:
+			// Intentionally partial, there are many decorations we just don't care about.
+			break;
 	}
 }
 
@@ -1407,15 +1412,15 @@
 
 	switch(getType(typeId).opcode())
 	{
-	case spv::OpTypePointer:
-	case spv::OpTypeImage:
-	case spv::OpTypeSampledImage:
-	case spv::OpTypeSampler:
-		object.kind = Object::Kind::Pointer;
-		break;
+		case spv::OpTypePointer:
+		case spv::OpTypeImage:
+		case spv::OpTypeSampledImage:
+		case spv::OpTypeSampler:
+			object.kind = Object::Kind::Pointer;
+			break;
 
-	default:
-		object.kind = Object::Kind::Intermediate;
+		default:
+			object.kind = Object::Kind::Intermediate;
 	}
 
 	object.definition = insn;
@@ -1425,27 +1430,27 @@
 {
 	switch(storageClass)
 	{
-	case spv::StorageClassUniform:
-	case spv::StorageClassStorageBuffer:
-		// Buffer resource access. robustBufferAccess feature applies.
-		return robustBufferAccess ? OutOfBoundsBehavior::RobustBufferAccess
-		                          : OutOfBoundsBehavior::UndefinedBehavior;
-
-	case spv::StorageClassImage:
-		return OutOfBoundsBehavior::UndefinedValue;  // "The value returned by a read of an invalid texel is undefined"
-
-	case spv::StorageClassInput:
-		if(executionModel == spv::ExecutionModelVertex)
-		{
-			// Vertex attributes follow robustBufferAccess rules.
+		case spv::StorageClassUniform:
+		case spv::StorageClassStorageBuffer:
+			// Buffer resource access. robustBufferAccess feature applies.
 			return robustBufferAccess ? OutOfBoundsBehavior::RobustBufferAccess
 			                          : OutOfBoundsBehavior::UndefinedBehavior;
-		}
-		// Fall through to default case.
-	default:
-		// TODO(b/137183137): Optimize if the pointer resulted from OpInBoundsAccessChain.
-		// TODO(b/131224163): Optimize cases statically known to be within bounds.
-		return OutOfBoundsBehavior::UndefinedValue;
+
+		case spv::StorageClassImage:
+			return OutOfBoundsBehavior::UndefinedValue;  // "The value returned by a read of an invalid texel is undefined"
+
+		case spv::StorageClassInput:
+			if(executionModel == spv::ExecutionModelVertex)
+			{
+				// Vertex attributes follow robustBufferAccess rules.
+				return robustBufferAccess ? OutOfBoundsBehavior::RobustBufferAccess
+				                          : OutOfBoundsBehavior::UndefinedBehavior;
+			}
+			// Fall through to default case.
+		default:
+			// TODO(b/137183137): Optimize if the pointer resulted from OpInBoundsAccessChain.
+			// TODO(b/131224163): Optimize cases statically known to be within bounds.
+			return OutOfBoundsBehavior::UndefinedValue;
 	}
 
 	return OutOfBoundsBehavior::Nullify;
@@ -1459,48 +1464,48 @@
 	{
 		switch(insn.opcode())
 		{
-		case spv::OpVariable:
-		{
-			Type::ID resultPointerTypeId = insn.word(1);
-			auto resultPointerType = getType(resultPointerTypeId);
-			auto pointeeType = getType(resultPointerType.element);
+			case spv::OpVariable:
+			{
+				Type::ID resultPointerTypeId = insn.word(1);
+				auto resultPointerType = getType(resultPointerTypeId);
+				auto pointeeType = getType(resultPointerType.element);
 
-			if(pointeeType.sizeInComponents > 0)  // TODO: what to do about zero-slot objects?
+				if(pointeeType.sizeInComponents > 0)  // TODO: what to do about zero-slot objects?
+				{
+					Object::ID resultId = insn.word(2);
+					routine->createVariable(resultId, pointeeType.sizeInComponents);
+				}
+				break;
+			}
+			case spv::OpPhi:
+			{
+				auto type = getType(insn.word(1));
+				Object::ID resultId = insn.word(2);
+				routine->phis.emplace(resultId, SpirvRoutine::Variable(type.sizeInComponents));
+				break;
+			}
+
+			case spv::OpImageDrefGather:
+			case spv::OpImageFetch:
+			case spv::OpImageGather:
+			case spv::OpImageQueryLod:
+			case spv::OpImageSampleDrefExplicitLod:
+			case spv::OpImageSampleDrefImplicitLod:
+			case spv::OpImageSampleExplicitLod:
+			case spv::OpImageSampleImplicitLod:
+			case spv::OpImageSampleProjDrefExplicitLod:
+			case spv::OpImageSampleProjDrefImplicitLod:
+			case spv::OpImageSampleProjExplicitLod:
+			case spv::OpImageSampleProjImplicitLod:
 			{
 				Object::ID resultId = insn.word(2);
-				routine->createVariable(resultId, pointeeType.sizeInComponents);
+				routine->samplerCache.emplace(resultId, SpirvRoutine::SamplerCache{});
+				break;
 			}
-			break;
-		}
-		case spv::OpPhi:
-		{
-			auto type = getType(insn.word(1));
-			Object::ID resultId = insn.word(2);
-			routine->phis.emplace(resultId, SpirvRoutine::Variable(type.sizeInComponents));
-			break;
-		}
 
-		case spv::OpImageDrefGather:
-		case spv::OpImageFetch:
-		case spv::OpImageGather:
-		case spv::OpImageQueryLod:
-		case spv::OpImageSampleDrefExplicitLod:
-		case spv::OpImageSampleDrefImplicitLod:
-		case spv::OpImageSampleExplicitLod:
-		case spv::OpImageSampleImplicitLod:
-		case spv::OpImageSampleProjDrefExplicitLod:
-		case spv::OpImageSampleProjDrefImplicitLod:
-		case spv::OpImageSampleProjExplicitLod:
-		case spv::OpImageSampleProjImplicitLod:
-		{
-			Object::ID resultId = insn.word(2);
-			routine->samplerCache.emplace(resultId, SpirvRoutine::SamplerCache{});
-			break;
-		}
-
-		default:
-			// Nothing else produces interface variables, so can all be safely ignored.
-			break;
+			default:
+				// Nothing else produces interface variables, so can all be safely ignored.
+				break;
 		}
 	}
 }
@@ -1531,13 +1536,13 @@
 		auto res = EmitInstruction(insn, state);
 		switch(res)
 		{
-		case EmitResult::Continue:
-			continue;
-		case EmitResult::Terminator:
-			break;
-		default:
-			UNREACHABLE("Unexpected EmitResult %d", int(res));
-			break;
+			case EmitResult::Continue:
+				continue;
+			case EmitResult::Terminator:
+				break;
+			default:
+				UNREACHABLE("Unexpected EmitResult %d", int(res));
+				break;
 		}
 	}
 }
@@ -1548,365 +1553,365 @@
 
 	switch(opcode)
 	{
-	case spv::OpTypeVoid:
-	case spv::OpTypeInt:
-	case spv::OpTypeFloat:
-	case spv::OpTypeBool:
-	case spv::OpTypeVector:
-	case spv::OpTypeArray:
-	case spv::OpTypeRuntimeArray:
-	case spv::OpTypeMatrix:
-	case spv::OpTypeStruct:
-	case spv::OpTypePointer:
-	case spv::OpTypeFunction:
-	case spv::OpTypeImage:
-	case spv::OpTypeSampledImage:
-	case spv::OpTypeSampler:
-	case spv::OpExecutionMode:
-	case spv::OpMemoryModel:
-	case spv::OpFunction:
-	case spv::OpFunctionEnd:
-	case spv::OpConstant:
-	case spv::OpConstantNull:
-	case spv::OpConstantTrue:
-	case spv::OpConstantFalse:
-	case spv::OpConstantComposite:
-	case spv::OpSpecConstant:
-	case spv::OpSpecConstantTrue:
-	case spv::OpSpecConstantFalse:
-	case spv::OpSpecConstantComposite:
-	case spv::OpSpecConstantOp:
-	case spv::OpUndef:
-	case spv::OpExtension:
-	case spv::OpCapability:
-	case spv::OpEntryPoint:
-	case spv::OpExtInstImport:
-	case spv::OpDecorate:
-	case spv::OpMemberDecorate:
-	case spv::OpGroupDecorate:
-	case spv::OpGroupMemberDecorate:
-	case spv::OpDecorationGroup:
-	case spv::OpName:
-	case spv::OpMemberName:
-	case spv::OpSource:
-	case spv::OpSourceContinued:
-	case spv::OpSourceExtension:
-	case spv::OpLine:
-	case spv::OpNoLine:
-	case spv::OpModuleProcessed:
-	case spv::OpString:
-		// Nothing to do at emit time. These are either fully handled at analysis time,
-		// or don't require any work at all.
-		return EmitResult::Continue;
+		case spv::OpTypeVoid:
+		case spv::OpTypeInt:
+		case spv::OpTypeFloat:
+		case spv::OpTypeBool:
+		case spv::OpTypeVector:
+		case spv::OpTypeArray:
+		case spv::OpTypeRuntimeArray:
+		case spv::OpTypeMatrix:
+		case spv::OpTypeStruct:
+		case spv::OpTypePointer:
+		case spv::OpTypeFunction:
+		case spv::OpTypeImage:
+		case spv::OpTypeSampledImage:
+		case spv::OpTypeSampler:
+		case spv::OpExecutionMode:
+		case spv::OpMemoryModel:
+		case spv::OpFunction:
+		case spv::OpFunctionEnd:
+		case spv::OpConstant:
+		case spv::OpConstantNull:
+		case spv::OpConstantTrue:
+		case spv::OpConstantFalse:
+		case spv::OpConstantComposite:
+		case spv::OpSpecConstant:
+		case spv::OpSpecConstantTrue:
+		case spv::OpSpecConstantFalse:
+		case spv::OpSpecConstantComposite:
+		case spv::OpSpecConstantOp:
+		case spv::OpUndef:
+		case spv::OpExtension:
+		case spv::OpCapability:
+		case spv::OpEntryPoint:
+		case spv::OpExtInstImport:
+		case spv::OpDecorate:
+		case spv::OpMemberDecorate:
+		case spv::OpGroupDecorate:
+		case spv::OpGroupMemberDecorate:
+		case spv::OpDecorationGroup:
+		case spv::OpName:
+		case spv::OpMemberName:
+		case spv::OpSource:
+		case spv::OpSourceContinued:
+		case spv::OpSourceExtension:
+		case spv::OpLine:
+		case spv::OpNoLine:
+		case spv::OpModuleProcessed:
+		case spv::OpString:
+			// Nothing to do at emit time. These are either fully handled at analysis time,
+			// or don't require any work at all.
+			return EmitResult::Continue;
 
-	case spv::OpLabel:
-		return EmitResult::Continue;
+		case spv::OpLabel:
+			return EmitResult::Continue;
 
-	case spv::OpVariable:
-		return EmitVariable(insn, state);
+		case spv::OpVariable:
+			return EmitVariable(insn, state);
 
-	case spv::OpLoad:
-	case spv::OpAtomicLoad:
-		return EmitLoad(insn, state);
+		case spv::OpLoad:
+		case spv::OpAtomicLoad:
+			return EmitLoad(insn, state);
 
-	case spv::OpStore:
-	case spv::OpAtomicStore:
-		return EmitStore(insn, state);
+		case spv::OpStore:
+		case spv::OpAtomicStore:
+			return EmitStore(insn, state);
 
-	case spv::OpAtomicIAdd:
-	case spv::OpAtomicISub:
-	case spv::OpAtomicSMin:
-	case spv::OpAtomicSMax:
-	case spv::OpAtomicUMin:
-	case spv::OpAtomicUMax:
-	case spv::OpAtomicAnd:
-	case spv::OpAtomicOr:
-	case spv::OpAtomicXor:
-	case spv::OpAtomicIIncrement:
-	case spv::OpAtomicIDecrement:
-	case spv::OpAtomicExchange:
-		return EmitAtomicOp(insn, state);
+		case spv::OpAtomicIAdd:
+		case spv::OpAtomicISub:
+		case spv::OpAtomicSMin:
+		case spv::OpAtomicSMax:
+		case spv::OpAtomicUMin:
+		case spv::OpAtomicUMax:
+		case spv::OpAtomicAnd:
+		case spv::OpAtomicOr:
+		case spv::OpAtomicXor:
+		case spv::OpAtomicIIncrement:
+		case spv::OpAtomicIDecrement:
+		case spv::OpAtomicExchange:
+			return EmitAtomicOp(insn, state);
 
-	case spv::OpAtomicCompareExchange:
-		return EmitAtomicCompareExchange(insn, state);
+		case spv::OpAtomicCompareExchange:
+			return EmitAtomicCompareExchange(insn, state);
 
-	case spv::OpAccessChain:
-	case spv::OpInBoundsAccessChain:
-		return EmitAccessChain(insn, state);
+		case spv::OpAccessChain:
+		case spv::OpInBoundsAccessChain:
+			return EmitAccessChain(insn, state);
 
-	case spv::OpCompositeConstruct:
-		return EmitCompositeConstruct(insn, state);
+		case spv::OpCompositeConstruct:
+			return EmitCompositeConstruct(insn, state);
 
-	case spv::OpCompositeInsert:
-		return EmitCompositeInsert(insn, state);
+		case spv::OpCompositeInsert:
+			return EmitCompositeInsert(insn, state);
 
-	case spv::OpCompositeExtract:
-		return EmitCompositeExtract(insn, state);
+		case spv::OpCompositeExtract:
+			return EmitCompositeExtract(insn, state);
 
-	case spv::OpVectorShuffle:
-		return EmitVectorShuffle(insn, state);
+		case spv::OpVectorShuffle:
+			return EmitVectorShuffle(insn, state);
 
-	case spv::OpVectorExtractDynamic:
-		return EmitVectorExtractDynamic(insn, state);
+		case spv::OpVectorExtractDynamic:
+			return EmitVectorExtractDynamic(insn, state);
 
-	case spv::OpVectorInsertDynamic:
-		return EmitVectorInsertDynamic(insn, state);
+		case spv::OpVectorInsertDynamic:
+			return EmitVectorInsertDynamic(insn, state);
 
-	case spv::OpVectorTimesScalar:
-	case spv::OpMatrixTimesScalar:
-		return EmitVectorTimesScalar(insn, state);
+		case spv::OpVectorTimesScalar:
+		case spv::OpMatrixTimesScalar:
+			return EmitVectorTimesScalar(insn, state);
 
-	case spv::OpMatrixTimesVector:
-		return EmitMatrixTimesVector(insn, state);
+		case spv::OpMatrixTimesVector:
+			return EmitMatrixTimesVector(insn, state);
 
-	case spv::OpVectorTimesMatrix:
-		return EmitVectorTimesMatrix(insn, state);
+		case spv::OpVectorTimesMatrix:
+			return EmitVectorTimesMatrix(insn, state);
 
-	case spv::OpMatrixTimesMatrix:
-		return EmitMatrixTimesMatrix(insn, state);
+		case spv::OpMatrixTimesMatrix:
+			return EmitMatrixTimesMatrix(insn, state);
 
-	case spv::OpOuterProduct:
-		return EmitOuterProduct(insn, state);
+		case spv::OpOuterProduct:
+			return EmitOuterProduct(insn, state);
 
-	case spv::OpTranspose:
-		return EmitTranspose(insn, state);
+		case spv::OpTranspose:
+			return EmitTranspose(insn, state);
 
-	case spv::OpNot:
-	case spv::OpBitFieldInsert:
-	case spv::OpBitFieldSExtract:
-	case spv::OpBitFieldUExtract:
-	case spv::OpBitReverse:
-	case spv::OpBitCount:
-	case spv::OpSNegate:
-	case spv::OpFNegate:
-	case spv::OpLogicalNot:
-	case spv::OpConvertFToU:
-	case spv::OpConvertFToS:
-	case spv::OpConvertSToF:
-	case spv::OpConvertUToF:
-	case spv::OpBitcast:
-	case spv::OpIsInf:
-	case spv::OpIsNan:
-	case spv::OpDPdx:
-	case spv::OpDPdxCoarse:
-	case spv::OpDPdy:
-	case spv::OpDPdyCoarse:
-	case spv::OpFwidth:
-	case spv::OpFwidthCoarse:
-	case spv::OpDPdxFine:
-	case spv::OpDPdyFine:
-	case spv::OpFwidthFine:
-	case spv::OpQuantizeToF16:
-		return EmitUnaryOp(insn, state);
+		case spv::OpNot:
+		case spv::OpBitFieldInsert:
+		case spv::OpBitFieldSExtract:
+		case spv::OpBitFieldUExtract:
+		case spv::OpBitReverse:
+		case spv::OpBitCount:
+		case spv::OpSNegate:
+		case spv::OpFNegate:
+		case spv::OpLogicalNot:
+		case spv::OpConvertFToU:
+		case spv::OpConvertFToS:
+		case spv::OpConvertSToF:
+		case spv::OpConvertUToF:
+		case spv::OpBitcast:
+		case spv::OpIsInf:
+		case spv::OpIsNan:
+		case spv::OpDPdx:
+		case spv::OpDPdxCoarse:
+		case spv::OpDPdy:
+		case spv::OpDPdyCoarse:
+		case spv::OpFwidth:
+		case spv::OpFwidthCoarse:
+		case spv::OpDPdxFine:
+		case spv::OpDPdyFine:
+		case spv::OpFwidthFine:
+		case spv::OpQuantizeToF16:
+			return EmitUnaryOp(insn, state);
 
-	case spv::OpIAdd:
-	case spv::OpISub:
-	case spv::OpIMul:
-	case spv::OpSDiv:
-	case spv::OpUDiv:
-	case spv::OpFAdd:
-	case spv::OpFSub:
-	case spv::OpFMul:
-	case spv::OpFDiv:
-	case spv::OpFMod:
-	case spv::OpFRem:
-	case spv::OpFOrdEqual:
-	case spv::OpFUnordEqual:
-	case spv::OpFOrdNotEqual:
-	case spv::OpFUnordNotEqual:
-	case spv::OpFOrdLessThan:
-	case spv::OpFUnordLessThan:
-	case spv::OpFOrdGreaterThan:
-	case spv::OpFUnordGreaterThan:
-	case spv::OpFOrdLessThanEqual:
-	case spv::OpFUnordLessThanEqual:
-	case spv::OpFOrdGreaterThanEqual:
-	case spv::OpFUnordGreaterThanEqual:
-	case spv::OpSMod:
-	case spv::OpSRem:
-	case spv::OpUMod:
-	case spv::OpIEqual:
-	case spv::OpINotEqual:
-	case spv::OpUGreaterThan:
-	case spv::OpSGreaterThan:
-	case spv::OpUGreaterThanEqual:
-	case spv::OpSGreaterThanEqual:
-	case spv::OpULessThan:
-	case spv::OpSLessThan:
-	case spv::OpULessThanEqual:
-	case spv::OpSLessThanEqual:
-	case spv::OpShiftRightLogical:
-	case spv::OpShiftRightArithmetic:
-	case spv::OpShiftLeftLogical:
-	case spv::OpBitwiseOr:
-	case spv::OpBitwiseXor:
-	case spv::OpBitwiseAnd:
-	case spv::OpLogicalOr:
-	case spv::OpLogicalAnd:
-	case spv::OpLogicalEqual:
-	case spv::OpLogicalNotEqual:
-	case spv::OpUMulExtended:
-	case spv::OpSMulExtended:
-	case spv::OpIAddCarry:
-	case spv::OpISubBorrow:
-		return EmitBinaryOp(insn, state);
+		case spv::OpIAdd:
+		case spv::OpISub:
+		case spv::OpIMul:
+		case spv::OpSDiv:
+		case spv::OpUDiv:
+		case spv::OpFAdd:
+		case spv::OpFSub:
+		case spv::OpFMul:
+		case spv::OpFDiv:
+		case spv::OpFMod:
+		case spv::OpFRem:
+		case spv::OpFOrdEqual:
+		case spv::OpFUnordEqual:
+		case spv::OpFOrdNotEqual:
+		case spv::OpFUnordNotEqual:
+		case spv::OpFOrdLessThan:
+		case spv::OpFUnordLessThan:
+		case spv::OpFOrdGreaterThan:
+		case spv::OpFUnordGreaterThan:
+		case spv::OpFOrdLessThanEqual:
+		case spv::OpFUnordLessThanEqual:
+		case spv::OpFOrdGreaterThanEqual:
+		case spv::OpFUnordGreaterThanEqual:
+		case spv::OpSMod:
+		case spv::OpSRem:
+		case spv::OpUMod:
+		case spv::OpIEqual:
+		case spv::OpINotEqual:
+		case spv::OpUGreaterThan:
+		case spv::OpSGreaterThan:
+		case spv::OpUGreaterThanEqual:
+		case spv::OpSGreaterThanEqual:
+		case spv::OpULessThan:
+		case spv::OpSLessThan:
+		case spv::OpULessThanEqual:
+		case spv::OpSLessThanEqual:
+		case spv::OpShiftRightLogical:
+		case spv::OpShiftRightArithmetic:
+		case spv::OpShiftLeftLogical:
+		case spv::OpBitwiseOr:
+		case spv::OpBitwiseXor:
+		case spv::OpBitwiseAnd:
+		case spv::OpLogicalOr:
+		case spv::OpLogicalAnd:
+		case spv::OpLogicalEqual:
+		case spv::OpLogicalNotEqual:
+		case spv::OpUMulExtended:
+		case spv::OpSMulExtended:
+		case spv::OpIAddCarry:
+		case spv::OpISubBorrow:
+			return EmitBinaryOp(insn, state);
 
-	case spv::OpDot:
-		return EmitDot(insn, state);
+		case spv::OpDot:
+			return EmitDot(insn, state);
 
-	case spv::OpSelect:
-		return EmitSelect(insn, state);
+		case spv::OpSelect:
+			return EmitSelect(insn, state);
 
-	case spv::OpExtInst:
-		return EmitExtendedInstruction(insn, state);
+		case spv::OpExtInst:
+			return EmitExtendedInstruction(insn, state);
 
-	case spv::OpAny:
-		return EmitAny(insn, state);
+		case spv::OpAny:
+			return EmitAny(insn, state);
 
-	case spv::OpAll:
-		return EmitAll(insn, state);
+		case spv::OpAll:
+			return EmitAll(insn, state);
 
-	case spv::OpBranch:
-		return EmitBranch(insn, state);
+		case spv::OpBranch:
+			return EmitBranch(insn, state);
 
-	case spv::OpPhi:
-		return EmitPhi(insn, state);
+		case spv::OpPhi:
+			return EmitPhi(insn, state);
 
-	case spv::OpSelectionMerge:
-	case spv::OpLoopMerge:
-		return EmitResult::Continue;
+		case spv::OpSelectionMerge:
+		case spv::OpLoopMerge:
+			return EmitResult::Continue;
 
-	case spv::OpBranchConditional:
-		return EmitBranchConditional(insn, state);
+		case spv::OpBranchConditional:
+			return EmitBranchConditional(insn, state);
 
-	case spv::OpSwitch:
-		return EmitSwitch(insn, state);
+		case spv::OpSwitch:
+			return EmitSwitch(insn, state);
 
-	case spv::OpUnreachable:
-		return EmitUnreachable(insn, state);
+		case spv::OpUnreachable:
+			return EmitUnreachable(insn, state);
 
-	case spv::OpReturn:
-		return EmitReturn(insn, state);
+		case spv::OpReturn:
+			return EmitReturn(insn, state);
 
-	case spv::OpFunctionCall:
-		return EmitFunctionCall(insn, state);
+		case spv::OpFunctionCall:
+			return EmitFunctionCall(insn, state);
 
-	case spv::OpKill:
-		return EmitKill(insn, state);
+		case spv::OpKill:
+			return EmitKill(insn, state);
 
-	case spv::OpImageSampleImplicitLod:
-		return EmitImageSampleImplicitLod(None, insn, state);
+		case spv::OpImageSampleImplicitLod:
+			return EmitImageSampleImplicitLod(None, insn, state);
 
-	case spv::OpImageSampleExplicitLod:
-		return EmitImageSampleExplicitLod(None, insn, state);
+		case spv::OpImageSampleExplicitLod:
+			return EmitImageSampleExplicitLod(None, insn, state);
 
-	case spv::OpImageSampleDrefImplicitLod:
-		return EmitImageSampleImplicitLod(Dref, insn, state);
+		case spv::OpImageSampleDrefImplicitLod:
+			return EmitImageSampleImplicitLod(Dref, insn, state);
 
-	case spv::OpImageSampleDrefExplicitLod:
-		return EmitImageSampleExplicitLod(Dref, insn, state);
+		case spv::OpImageSampleDrefExplicitLod:
+			return EmitImageSampleExplicitLod(Dref, insn, state);
 
-	case spv::OpImageSampleProjImplicitLod:
-		return EmitImageSampleImplicitLod(Proj, insn, state);
+		case spv::OpImageSampleProjImplicitLod:
+			return EmitImageSampleImplicitLod(Proj, insn, state);
 
-	case spv::OpImageSampleProjExplicitLod:
-		return EmitImageSampleExplicitLod(Proj, insn, state);
+		case spv::OpImageSampleProjExplicitLod:
+			return EmitImageSampleExplicitLod(Proj, insn, state);
 
-	case spv::OpImageSampleProjDrefImplicitLod:
-		return EmitImageSampleImplicitLod(ProjDref, insn, state);
+		case spv::OpImageSampleProjDrefImplicitLod:
+			return EmitImageSampleImplicitLod(ProjDref, insn, state);
 
-	case spv::OpImageSampleProjDrefExplicitLod:
-		return EmitImageSampleExplicitLod(ProjDref, insn, state);
+		case spv::OpImageSampleProjDrefExplicitLod:
+			return EmitImageSampleExplicitLod(ProjDref, insn, state);
 
-	case spv::OpImageGather:
-		return EmitImageGather(None, insn, state);
+		case spv::OpImageGather:
+			return EmitImageGather(None, insn, state);
 
-	case spv::OpImageDrefGather:
-		return EmitImageGather(Dref, insn, state);
+		case spv::OpImageDrefGather:
+			return EmitImageGather(Dref, insn, state);
 
-	case spv::OpImageFetch:
-		return EmitImageFetch(insn, state);
+		case spv::OpImageFetch:
+			return EmitImageFetch(insn, state);
 
-	case spv::OpImageQuerySizeLod:
-		return EmitImageQuerySizeLod(insn, state);
+		case spv::OpImageQuerySizeLod:
+			return EmitImageQuerySizeLod(insn, state);
 
-	case spv::OpImageQuerySize:
-		return EmitImageQuerySize(insn, state);
+		case spv::OpImageQuerySize:
+			return EmitImageQuerySize(insn, state);
 
-	case spv::OpImageQueryLod:
-		return EmitImageQueryLod(insn, state);
+		case spv::OpImageQueryLod:
+			return EmitImageQueryLod(insn, state);
 
-	case spv::OpImageQueryLevels:
-		return EmitImageQueryLevels(insn, state);
+		case spv::OpImageQueryLevels:
+			return EmitImageQueryLevels(insn, state);
 
-	case spv::OpImageQuerySamples:
-		return EmitImageQuerySamples(insn, state);
+		case spv::OpImageQuerySamples:
+			return EmitImageQuerySamples(insn, state);
 
-	case spv::OpImageRead:
-		return EmitImageRead(insn, state);
+		case spv::OpImageRead:
+			return EmitImageRead(insn, state);
 
-	case spv::OpImageWrite:
-		return EmitImageWrite(insn, state);
+		case spv::OpImageWrite:
+			return EmitImageWrite(insn, state);
 
-	case spv::OpImageTexelPointer:
-		return EmitImageTexelPointer(insn, state);
+		case spv::OpImageTexelPointer:
+			return EmitImageTexelPointer(insn, state);
 
-	case spv::OpSampledImage:
-	case spv::OpImage:
-		return EmitSampledImageCombineOrSplit(insn, state);
+		case spv::OpSampledImage:
+		case spv::OpImage:
+			return EmitSampledImageCombineOrSplit(insn, state);
 
-	case spv::OpCopyObject:
-		return EmitCopyObject(insn, state);
+		case spv::OpCopyObject:
+			return EmitCopyObject(insn, state);
 
-	case spv::OpCopyMemory:
-		return EmitCopyMemory(insn, state);
+		case spv::OpCopyMemory:
+			return EmitCopyMemory(insn, state);
 
-	case spv::OpControlBarrier:
-		return EmitControlBarrier(insn, state);
+		case spv::OpControlBarrier:
+			return EmitControlBarrier(insn, state);
 
-	case spv::OpMemoryBarrier:
-		return EmitMemoryBarrier(insn, state);
+		case spv::OpMemoryBarrier:
+			return EmitMemoryBarrier(insn, state);
 
-	case spv::OpGroupNonUniformElect:
-	case spv::OpGroupNonUniformAll:
-	case spv::OpGroupNonUniformAny:
-	case spv::OpGroupNonUniformAllEqual:
-	case spv::OpGroupNonUniformBroadcast:
-	case spv::OpGroupNonUniformBroadcastFirst:
-	case spv::OpGroupNonUniformBallot:
-	case spv::OpGroupNonUniformInverseBallot:
-	case spv::OpGroupNonUniformBallotBitExtract:
-	case spv::OpGroupNonUniformBallotBitCount:
-	case spv::OpGroupNonUniformBallotFindLSB:
-	case spv::OpGroupNonUniformBallotFindMSB:
-	case spv::OpGroupNonUniformShuffle:
-	case spv::OpGroupNonUniformShuffleXor:
-	case spv::OpGroupNonUniformShuffleUp:
-	case spv::OpGroupNonUniformShuffleDown:
-	case spv::OpGroupNonUniformIAdd:
-	case spv::OpGroupNonUniformFAdd:
-	case spv::OpGroupNonUniformIMul:
-	case spv::OpGroupNonUniformFMul:
-	case spv::OpGroupNonUniformSMin:
-	case spv::OpGroupNonUniformUMin:
-	case spv::OpGroupNonUniformFMin:
-	case spv::OpGroupNonUniformSMax:
-	case spv::OpGroupNonUniformUMax:
-	case spv::OpGroupNonUniformFMax:
-	case spv::OpGroupNonUniformBitwiseAnd:
-	case spv::OpGroupNonUniformBitwiseOr:
-	case spv::OpGroupNonUniformBitwiseXor:
-	case spv::OpGroupNonUniformLogicalAnd:
-	case spv::OpGroupNonUniformLogicalOr:
-	case spv::OpGroupNonUniformLogicalXor:
-		return EmitGroupNonUniform(insn, state);
+		case spv::OpGroupNonUniformElect:
+		case spv::OpGroupNonUniformAll:
+		case spv::OpGroupNonUniformAny:
+		case spv::OpGroupNonUniformAllEqual:
+		case spv::OpGroupNonUniformBroadcast:
+		case spv::OpGroupNonUniformBroadcastFirst:
+		case spv::OpGroupNonUniformBallot:
+		case spv::OpGroupNonUniformInverseBallot:
+		case spv::OpGroupNonUniformBallotBitExtract:
+		case spv::OpGroupNonUniformBallotBitCount:
+		case spv::OpGroupNonUniformBallotFindLSB:
+		case spv::OpGroupNonUniformBallotFindMSB:
+		case spv::OpGroupNonUniformShuffle:
+		case spv::OpGroupNonUniformShuffleXor:
+		case spv::OpGroupNonUniformShuffleUp:
+		case spv::OpGroupNonUniformShuffleDown:
+		case spv::OpGroupNonUniformIAdd:
+		case spv::OpGroupNonUniformFAdd:
+		case spv::OpGroupNonUniformIMul:
+		case spv::OpGroupNonUniformFMul:
+		case spv::OpGroupNonUniformSMin:
+		case spv::OpGroupNonUniformUMin:
+		case spv::OpGroupNonUniformFMin:
+		case spv::OpGroupNonUniformSMax:
+		case spv::OpGroupNonUniformUMax:
+		case spv::OpGroupNonUniformFMax:
+		case spv::OpGroupNonUniformBitwiseAnd:
+		case spv::OpGroupNonUniformBitwiseOr:
+		case spv::OpGroupNonUniformBitwiseXor:
+		case spv::OpGroupNonUniformLogicalAnd:
+		case spv::OpGroupNonUniformLogicalOr:
+		case spv::OpGroupNonUniformLogicalXor:
+			return EmitGroupNonUniform(insn, state);
 
-	case spv::OpArrayLength:
-		return EmitArrayLength(insn, state);
+		case spv::OpArrayLength:
+			return EmitArrayLength(insn, state);
 
-	default:
-		UNREACHABLE("%s", OpcodeName(opcode).c_str());
-		break;
+		default:
+			UNREACHABLE("%s", OpcodeName(opcode).c_str());
+			break;
 	}
 
 	return EmitResult::Continue;
@@ -1948,8 +1953,8 @@
 	for(auto i = 0u; i < insn.wordCount() - 3; i++)
 	{
 		Object::ID srcObjectId = insn.word(3u + i);
-		auto & srcObject = getObject(srcObjectId);
-		auto & srcObjectTy = getType(srcObject.type);
+		auto &srcObject = getObject(srcObjectId);
+		auto &srcObjectTy = getType(srcObject.type);
 		GenericValue srcObjectAccess(this, state, srcObjectId);
 
 		for(auto j = 0u; j < srcObjectTy.sizeInComponents; j++)
@@ -2092,7 +2097,7 @@
 	for(auto i = 0u; i < type.sizeInComponents; i++)
 	{
 		auto sel = cond.Int(condIsScalar ? 0 : i);
-		dst.move(i, (sel & lhs.Int(i)) | (~sel & rhs.Int(i)));   // TODO: IfThenElse()
+		dst.move(i, (sel & lhs.Int(i)) | (~sel & rhs.Int(i)));  // TODO: IfThenElse()
 	}
 
 	return EmitResult::Continue;
@@ -2160,41 +2165,41 @@
 			UInt v;
 			switch(insn.opcode())
 			{
-			case spv::OpAtomicIAdd:
-			case spv::OpAtomicIIncrement:
-				v = AddAtomic(Pointer<UInt>(&ptr.base[offset]), laneValue, memoryOrder);
-				break;
-			case spv::OpAtomicISub:
-			case spv::OpAtomicIDecrement:
-				v = SubAtomic(Pointer<UInt>(&ptr.base[offset]), laneValue, memoryOrder);
-				break;
-			case spv::OpAtomicAnd:
-				v = AndAtomic(Pointer<UInt>(&ptr.base[offset]), laneValue, memoryOrder);
-				break;
-			case spv::OpAtomicOr:
-				v = OrAtomic(Pointer<UInt>(&ptr.base[offset]), laneValue, memoryOrder);
-				break;
-			case spv::OpAtomicXor:
-				v = XorAtomic(Pointer<UInt>(&ptr.base[offset]), laneValue, memoryOrder);
-				break;
-			case spv::OpAtomicSMin:
-				v = As<UInt>(MinAtomic(Pointer<Int>(&ptr.base[offset]), As<Int>(laneValue), memoryOrder));
-				break;
-			case spv::OpAtomicSMax:
-				v = As<UInt>(MaxAtomic(Pointer<Int>(&ptr.base[offset]), As<Int>(laneValue), memoryOrder));
-				break;
-			case spv::OpAtomicUMin:
-				v = MinAtomic(Pointer<UInt>(&ptr.base[offset]), laneValue, memoryOrder);
-				break;
-			case spv::OpAtomicUMax:
-				v = MaxAtomic(Pointer<UInt>(&ptr.base[offset]), laneValue, memoryOrder);
-				break;
-			case spv::OpAtomicExchange:
-				v = ExchangeAtomic(Pointer<UInt>(&ptr.base[offset]), laneValue, memoryOrder);
-				break;
-			default:
-				UNREACHABLE("%s", OpcodeName(insn.opcode()).c_str());
-				break;
+				case spv::OpAtomicIAdd:
+				case spv::OpAtomicIIncrement:
+					v = AddAtomic(Pointer<UInt>(&ptr.base[offset]), laneValue, memoryOrder);
+					break;
+				case spv::OpAtomicISub:
+				case spv::OpAtomicIDecrement:
+					v = SubAtomic(Pointer<UInt>(&ptr.base[offset]), laneValue, memoryOrder);
+					break;
+				case spv::OpAtomicAnd:
+					v = AndAtomic(Pointer<UInt>(&ptr.base[offset]), laneValue, memoryOrder);
+					break;
+				case spv::OpAtomicOr:
+					v = OrAtomic(Pointer<UInt>(&ptr.base[offset]), laneValue, memoryOrder);
+					break;
+				case spv::OpAtomicXor:
+					v = XorAtomic(Pointer<UInt>(&ptr.base[offset]), laneValue, memoryOrder);
+					break;
+				case spv::OpAtomicSMin:
+					v = As<UInt>(MinAtomic(Pointer<Int>(&ptr.base[offset]), As<Int>(laneValue), memoryOrder));
+					break;
+				case spv::OpAtomicSMax:
+					v = As<UInt>(MaxAtomic(Pointer<Int>(&ptr.base[offset]), As<Int>(laneValue), memoryOrder));
+					break;
+				case spv::OpAtomicUMin:
+					v = MinAtomic(Pointer<UInt>(&ptr.base[offset]), laneValue, memoryOrder);
+					break;
+				case spv::OpAtomicUMax:
+					v = MaxAtomic(Pointer<UInt>(&ptr.base[offset]), laneValue, memoryOrder);
+					break;
+				case spv::OpAtomicExchange:
+					v = ExchangeAtomic(Pointer<UInt>(&ptr.base[offset]), laneValue, memoryOrder);
+					break;
+				default:
+					UNREACHABLE("%s", OpcodeName(insn.opcode()).c_str());
+					break;
 			}
 			x = Insert(x, v, j);
 		}
@@ -2298,25 +2303,25 @@
 	{
 		switch(insn.opcode())
 		{
-		case spv::OpVariable:
-		{
-			Object::ID resultId = insn.word(2);
-			auto &object = getObject(resultId);
-			auto &objectTy = getType(object.type);
-			if(object.kind == Object::Kind::InterfaceVariable && objectTy.storageClass == spv::StorageClassOutput)
+			case spv::OpVariable:
 			{
-				auto &dst = routine->getVariable(resultId);
-				int offset = 0;
-				VisitInterface(resultId,
-							   [&](Decorations const &d, AttribType type) {
-								   auto scalarSlot = d.Location << 2 | d.Component;
-								   routine->outputs[scalarSlot] = dst[offset++];
-							   });
+				Object::ID resultId = insn.word(2);
+				auto &object = getObject(resultId);
+				auto &objectTy = getType(object.type);
+				if(object.kind == Object::Kind::InterfaceVariable && objectTy.storageClass == spv::StorageClassOutput)
+				{
+					auto &dst = routine->getVariable(resultId);
+					int offset = 0;
+					VisitInterface(resultId,
+					               [&](Decorations const &d, AttribType type) {
+						               auto scalarSlot = d.Location << 2 | d.Component;
+						               routine->outputs[scalarSlot] = dst[offset++];
+					               });
+				}
+				break;
 			}
-			break;
-		}
-		default:
-			break;
+			default:
+				break;
 		}
 	}
 
@@ -2331,47 +2336,46 @@
 {
 	switch(model)
 	{
-	case spv::ExecutionModelVertex:                 return VK_SHADER_STAGE_VERTEX_BIT;
-	// case spv::ExecutionModelTessellationControl:    return VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT;
-	// case spv::ExecutionModelTessellationEvaluation: return VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT;
-	// case spv::ExecutionModelGeometry:               return VK_SHADER_STAGE_GEOMETRY_BIT;
-	case spv::ExecutionModelFragment:               return VK_SHADER_STAGE_FRAGMENT_BIT;
-	case spv::ExecutionModelGLCompute:              return VK_SHADER_STAGE_COMPUTE_BIT;
-	// case spv::ExecutionModelKernel:                 return VkShaderStageFlagBits(0); // Not supported by vulkan.
-	// case spv::ExecutionModelTaskNV:                 return VK_SHADER_STAGE_TASK_BIT_NV;
-	// case spv::ExecutionModelMeshNV:                 return VK_SHADER_STAGE_MESH_BIT_NV;
-	// case spv::ExecutionModelRayGenerationNV:        return VK_SHADER_STAGE_RAYGEN_BIT_NV;
-	// case spv::ExecutionModelIntersectionNV:         return VK_SHADER_STAGE_INTERSECTION_BIT_NV;
-	// case spv::ExecutionModelAnyHitNV:               return VK_SHADER_STAGE_ANY_HIT_BIT_NV;
-	// case spv::ExecutionModelClosestHitNV:           return VK_SHADER_STAGE_CLOSEST_HIT_BIT_NV;
-	// case spv::ExecutionModelMissNV:                 return VK_SHADER_STAGE_MISS_BIT_NV;
-	// case spv::ExecutionModelCallableNV:             return VK_SHADER_STAGE_CALLABLE_BIT_NV;
-	default:
-		UNSUPPORTED("ExecutionModel: %d", int(model));
-		return VkShaderStageFlagBits(0);
+		case spv::ExecutionModelVertex: return VK_SHADER_STAGE_VERTEX_BIT;
+		// case spv::ExecutionModelTessellationControl:    return VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT;
+		// case spv::ExecutionModelTessellationEvaluation: return VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT;
+		// case spv::ExecutionModelGeometry:               return VK_SHADER_STAGE_GEOMETRY_BIT;
+		case spv::ExecutionModelFragment: return VK_SHADER_STAGE_FRAGMENT_BIT;
+		case spv::ExecutionModelGLCompute: return VK_SHADER_STAGE_COMPUTE_BIT;
+		// case spv::ExecutionModelKernel:                 return VkShaderStageFlagBits(0); // Not supported by vulkan.
+		// case spv::ExecutionModelTaskNV:                 return VK_SHADER_STAGE_TASK_BIT_NV;
+		// case spv::ExecutionModelMeshNV:                 return VK_SHADER_STAGE_MESH_BIT_NV;
+		// case spv::ExecutionModelRayGenerationNV:        return VK_SHADER_STAGE_RAYGEN_BIT_NV;
+		// case spv::ExecutionModelIntersectionNV:         return VK_SHADER_STAGE_INTERSECTION_BIT_NV;
+		// case spv::ExecutionModelAnyHitNV:               return VK_SHADER_STAGE_ANY_HIT_BIT_NV;
+		// case spv::ExecutionModelClosestHitNV:           return VK_SHADER_STAGE_CLOSEST_HIT_BIT_NV;
+		// case spv::ExecutionModelMissNV:                 return VK_SHADER_STAGE_MISS_BIT_NV;
+		// case spv::ExecutionModelCallableNV:             return VK_SHADER_STAGE_CALLABLE_BIT_NV;
+		default:
+			UNSUPPORTED("ExecutionModel: %d", int(model));
+			return VkShaderStageFlagBits(0);
 	}
 }
 
-SpirvShader::GenericValue::GenericValue(SpirvShader const *shader, EmitState const *state, SpirvShader::Object::ID objId) :
-		obj(shader->getObject(objId)),
-		intermediate(obj.kind == SpirvShader::Object::Kind::Intermediate ? &state->getIntermediate(objId) : nullptr),
-		type(obj.type) {}
+SpirvShader::GenericValue::GenericValue(SpirvShader const *shader, EmitState const *state, SpirvShader::Object::ID objId)
+    : obj(shader->getObject(objId))
+    , intermediate(obj.kind == SpirvShader::Object::Kind::Intermediate ? &state->getIntermediate(objId) : nullptr)
+    , type(obj.type)
+{}
 
-SpirvRoutine::SpirvRoutine(vk::PipelineLayout const *pipelineLayout) :
-	pipelineLayout(pipelineLayout)
+SpirvRoutine::SpirvRoutine(vk::PipelineLayout const *pipelineLayout)
+    : pipelineLayout(pipelineLayout)
 {
 }
 
 void SpirvRoutine::setImmutableInputBuiltins(SpirvShader const *shader)
 {
-	setInputBuiltin(shader, spv::BuiltInSubgroupLocalInvocationId, [&](const SpirvShader::BuiltinMapping& builtin, Array<SIMD::Float>& value)
-	{
+	setInputBuiltin(shader, spv::BuiltInSubgroupLocalInvocationId, [&](const SpirvShader::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 SpirvShader::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));
@@ -2379,8 +2383,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 SpirvShader::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));
@@ -2388,8 +2391,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 SpirvShader::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));
@@ -2397,8 +2399,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 SpirvShader::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));
@@ -2406,8 +2407,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 SpirvShader::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));
@@ -2415,8 +2415,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 SpirvShader::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));