Obtain type ID from instruction

The defining instruction already contains the type ID.

Bug: b/129000021
Change-Id: I0b29fda73964f5f4a73181a61a0f30cd1c47f404
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/43692
Presubmit-Ready: Nicolas Capens <nicolascapens@google.com>
Kokoro-Result: kokoro <noreply+kokoro@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
Tested-by: Nicolas Capens <nicolascapens@google.com>
diff --git a/src/Pipeline/SpirvShader.cpp b/src/Pipeline/SpirvShader.cpp
index 0e47618..14b40da 100644
--- a/src/Pipeline/SpirvShader.cpp
+++ b/src/Pipeline/SpirvShader.cpp
@@ -239,7 +239,6 @@
 				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);
@@ -308,7 +307,7 @@
 				// 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);
+				auto &objectTy = getType(object);
 				for(auto i = 0u; i < objectTy.sizeInComponents; i++)
 				{
 					object.constantValue[i] = 0;
@@ -323,7 +322,7 @@
 				for(auto i = 0u; i < insn.wordCount() - 3; i++)
 				{
 					auto &constituent = getObject(insn.word(i + 3));
-					auto &constituentTy = getType(constituent.type);
+					auto &constituentTy = getType(constituent);
 					for(auto j = 0u; j < constituentTy.sizeInComponents; j++)
 					{
 						object.constantValue[offset++] = constituent.constantValue[j];
@@ -343,7 +342,7 @@
 					// 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);
+					ASSERT(getType(object).sizeInComponents == 3);
 					modes.WorkgroupSizeX = object.constantValue[0];
 					modes.WorkgroupSizeY = object.constantValue[1];
 					modes.WorkgroupSizeZ = object.constantValue[2];
@@ -794,16 +793,16 @@
 	Object::ID resultId = insn.word(2);
 	auto &object = defs[resultId];
 	auto &objectTy = getType(typeId);
-	object.type = typeId;
 	object.kind = Object::Kind::Constant;
 	object.definition = insn;
 	object.constantValue = std::unique_ptr<uint32_t[]>(new uint32_t[objectTy.sizeInComponents]);
+
 	return object;
 }
 
 void SpirvShader::ProcessInterfaceVariable(Object &object)
 {
-	auto &objectTy = getType(object.type);
+	auto &objectTy = getType(object);
 	ASSERT(objectTy.storageClass == spv::StorageClassInput || objectTy.storageClass == spv::StorageClassOutput);
 
 	ASSERT(objectTy.opcode() == spv::OpTypePointer);
@@ -1038,8 +1037,8 @@
 {
 	ApplyDecorationsForId(d, baseId);
 	auto &baseObject = getObject(baseId);
-	ApplyDecorationsForId(d, baseObject.type);
-	auto typeId = getType(baseObject.type).element;
+	ApplyDecorationsForId(d, baseObject.typeId());
+	auto typeId = getType(baseObject).element;
 
 	for(auto i = 0u; i < numIndexes; i++)
 	{
@@ -1080,9 +1079,9 @@
 	// Produce a offset into external memory in sizeof(float) units
 
 	auto &baseObject = getObject(baseId);
-	Type::ID typeId = getType(baseObject.type).element;
+	Type::ID typeId = getType(baseObject).element;
 	Decorations d = {};
-	ApplyDecorationsForId(&d, baseObject.type);
+	ApplyDecorationsForId(&d, baseObject.typeId());
 
 	uint32_t arrayIndex = 0;
 	if(baseObject.kind == Object::Kind::DescriptorSet)
@@ -1183,7 +1182,7 @@
 	// TODO: avoid doing per-lane work in some cases if we can?
 	auto routine = state->routine;
 	auto &baseObject = getObject(baseId);
-	Type::ID typeId = getType(baseObject.type).element;
+	Type::ID typeId = getType(baseObject).element;
 
 	auto ptr = state->getPointer(baseId);
 
@@ -1214,7 +1213,7 @@
 			case spv::OpTypeRuntimeArray:
 			{
 				// TODO: b/127950082: Check bounds.
-				if(getType(baseObject.type).storageClass == spv::StorageClassUniformConstant)
+				if(getType(baseObject).storageClass == spv::StorageClassUniformConstant)
 				{
 					// indexing into an array of descriptors.
 					auto &obj = getObject(indexIds[i]);
@@ -1453,7 +1452,6 @@
 	Type::ID typeId = insn.word(1);
 	Object::ID resultId = insn.word(2);
 	auto &object = defs[resultId];
-	object.type = typeId;
 
 	switch(getType(typeId).opcode())
 	{
@@ -2008,7 +2006,7 @@
 	{
 		Object::ID srcObjectId = insn.word(3u + i);
 		auto &srcObject = getObject(srcObjectId);
-		auto &srcObjectTy = getType(srcObject.type);
+		auto &srcObjectTy = getType(srcObject);
 		Operand srcObjectAccess(this, state, srcObjectId);
 
 		for(auto j = 0u; j < srcObjectTy.sizeInComponents; j++)
@@ -2026,7 +2024,7 @@
 	auto &type = getType(resultTypeId);
 	auto &dst = state->createIntermediate(insn.word(2), type.sizeInComponents);
 	auto &newPartObject = getObject(insn.word(3));
-	auto &newPartObjectTy = getType(newPartObject.type);
+	auto &newPartObjectTy = getType(newPartObject);
 	auto firstNewComponent = WalkLiteralAccessChain(resultTypeId, insn.wordCount() - 5, insn.wordPointer(5));
 
 	Operand srcObjectAccess(this, state, insn.word(4));
@@ -2075,7 +2073,7 @@
 
 	// Note: number of components in result type, first half type, and second
 	// half type are all independent.
-	auto &firstHalfType = getType(getObject(insn.word(3)).type);
+	auto &firstHalfType = getType(getObject(insn.word(3)));
 
 	Operand firstHalfAccess(this, state, insn.word(3));
 	Operand secondHalfAccess(this, state, insn.word(4));
@@ -2106,7 +2104,7 @@
 {
 	auto &type = getType(insn.word(1));
 	auto &dst = state->createIntermediate(insn.word(2), type.sizeInComponents);
-	auto &srcType = getType(getObject(insn.word(3)).type);
+	auto &srcType = getType(getObject(insn.word(3)));
 
 	Operand src(this, state, insn.word(3));
 	Operand index(this, state, insn.word(4));
@@ -2144,7 +2142,7 @@
 	auto &type = getType(insn.word(1));
 	auto &dst = state->createIntermediate(insn.word(2), type.sizeInComponents);
 	auto cond = Operand(this, state, insn.word(3));
-	auto condIsScalar = (getType(cond.type).sizeInComponents == 1);
+	auto condIsScalar = (getType(cond).sizeInComponents == 1);
 	auto lhs = Operand(this, state, insn.word(4));
 	auto rhs = Operand(this, state, insn.word(5));
 
@@ -2162,7 +2160,7 @@
 	auto &type = getType(insn.word(1));
 	ASSERT(type.sizeInComponents == 1);
 	auto &dst = state->createIntermediate(insn.word(2), type.sizeInComponents);
-	auto &srcType = getType(getObject(insn.word(3)).type);
+	auto &srcType = getType(getObject(insn.word(3)));
 	auto src = Operand(this, state, insn.word(3));
 
 	SIMD::UInt result = src.UInt(0);
@@ -2181,7 +2179,7 @@
 	auto &type = getType(insn.word(1));
 	ASSERT(type.sizeInComponents == 1);
 	auto &dst = state->createIntermediate(insn.word(2), type.sizeInComponents);
-	auto &srcType = getType(getObject(insn.word(3)).type);
+	auto &srcType = getType(getObject(insn.word(3)));
 	auto src = Operand(this, state, insn.word(3));
 
 	SIMD::UInt result = src.UInt(0);
@@ -2321,7 +2319,7 @@
 	ASSERT(resultType.sizeInComponents == 1);
 	ASSERT(resultType.definition.opcode() == spv::OpTypeInt);
 
-	auto &structPtrTy = getType(getObject(structPtrId).type);
+	auto &structPtrTy = getType(getObject(structPtrId));
 	auto &structTy = getType(structPtrTy.element);
 	auto arrayId = Type::ID(structTy.definition.word(2 + arrayFieldIdx));
 
@@ -2364,7 +2362,7 @@
 {
 	auto &scopeObj = getObject(id);
 	ASSERT(scopeObj.kind == Object::Kind::Constant);
-	ASSERT(getType(scopeObj.type).sizeInComponents == 1);
+	ASSERT(getType(scopeObj).sizeInComponents == 1);
 	return scopeObj.constantValue[0];
 }
 
@@ -2378,7 +2376,7 @@
 			{
 				Object::ID resultId = insn.word(2);
 				auto &object = getObject(resultId);
-				auto &objectTy = getType(object.type);
+				auto &objectTy = getType(object);
 				if(object.kind == Object::Kind::InterfaceVariable && objectTy.storageClass == spv::StorageClassOutput)
 				{
 					auto &dst = routine->getVariable(resultId);
@@ -2431,7 +2429,6 @@
 SpirvShader::Operand::Operand(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)
diff --git a/src/Pipeline/SpirvShader.hpp b/src/Pipeline/SpirvShader.hpp
index 96b67ef..1410ef1 100644
--- a/src/Pipeline/SpirvShader.hpp
+++ b/src/Pipeline/SpirvShader.hpp
@@ -276,7 +276,6 @@
 		Object::ID id() const { return definition.resultId(); }
 
 		InsnIterator definition;
-		Type::ID type;  // TODO(b/129000021): Eliminate. Use typeId() instead.
 		std::unique_ptr<uint32_t[]> constantValue = nullptr;
 
 		enum class Kind
@@ -1033,8 +1032,6 @@
 			return SIMD::UInt(constantValue[i]);
 		}
 
-		SpirvShader::Type::ID const type;  // TODO(b/129000021): Eliminate. Use typeId() instead.
-
 		Type::ID typeId() const
 		{
 			return obj.typeId();
diff --git a/src/Pipeline/SpirvShaderArithmetic.cpp b/src/Pipeline/SpirvShaderArithmetic.cpp
index a4c14f9..7eff4d4 100644
--- a/src/Pipeline/SpirvShaderArithmetic.cpp
+++ b/src/Pipeline/SpirvShaderArithmetic.cpp
@@ -41,7 +41,7 @@
 	auto &dst = state->createIntermediate(insn.word(2), type.sizeInComponents);
 	auto lhs = Operand(this, state, insn.word(3));
 	auto rhs = Operand(this, state, insn.word(4));
-	auto rhsType = getType(rhs.type);
+	auto rhsType = getType(rhs);
 
 	for(auto i = 0u; i < type.sizeInComponents; i++)
 	{
@@ -62,7 +62,7 @@
 	auto &dst = state->createIntermediate(insn.word(2), type.sizeInComponents);
 	auto lhs = Operand(this, state, insn.word(3));
 	auto rhs = Operand(this, state, insn.word(4));
-	auto lhsType = getType(lhs.type);
+	auto lhsType = getType(lhs);
 
 	for(auto i = 0u; i < type.sizeInComponents; i++)
 	{
@@ -86,7 +86,7 @@
 
 	auto numColumns = type.definition.word(3);
 	auto numRows = getType(type.definition.word(2)).definition.word(3);
-	auto numAdds = getType(getObject(insn.word(3)).type).definition.word(3);
+	auto numAdds = getType(getObject(insn.word(3))).definition.word(3);
 
 	for(auto row = 0u; row < numRows; row++)
 	{
@@ -110,8 +110,8 @@
 	auto &dst = state->createIntermediate(insn.word(2), type.sizeInComponents);
 	auto lhs = Operand(this, state, insn.word(3));
 	auto rhs = Operand(this, state, insn.word(4));
-	auto &lhsType = getType(lhs.type);
-	auto &rhsType = getType(rhs.type);
+	auto &lhsType = getType(lhs);
+	auto &rhsType = getType(rhs);
 
 	ASSERT(type.definition.opcode() == spv::OpTypeMatrix);
 	ASSERT(lhsType.definition.opcode() == spv::OpTypeVector);
@@ -319,7 +319,7 @@
 {
 	auto &type = getType(insn.word(1));
 	auto &dst = state->createIntermediate(insn.word(2), type.sizeInComponents);
-	auto &lhsType = getType(getObject(insn.word(3)).type);
+	auto &lhsType = getType(getObject(insn.word(3)));
 	auto lhs = Operand(this, state, insn.word(3));
 	auto rhs = Operand(this, state, insn.word(4));
 
@@ -523,7 +523,7 @@
 	auto &type = getType(insn.word(1));
 	ASSERT(type.sizeInComponents == 1);
 	auto &dst = state->createIntermediate(insn.word(2), type.sizeInComponents);
-	auto &lhsType = getType(getObject(insn.word(3)).type);
+	auto &lhsType = getType(getObject(insn.word(3)));
 	auto lhs = Operand(this, state, insn.word(3));
 	auto rhs = Operand(this, state, insn.word(4));
 
diff --git a/src/Pipeline/SpirvShaderControlFlow.cpp b/src/Pipeline/SpirvShaderControlFlow.cpp
index 95b1e05..bcfed7f 100644
--- a/src/Pipeline/SpirvShaderControlFlow.cpp
+++ b/src/Pipeline/SpirvShaderControlFlow.cpp
@@ -496,7 +496,7 @@
 	auto falseBlockId = Block::ID(block.branchInstruction.word(3));
 
 	auto cond = Operand(this, state, condId);
-	ASSERT_MSG(getType(cond.type).sizeInComponents == 1, "Condition must be a Boolean type scalar");
+	ASSERT_MSG(getType(cond).sizeInComponents == 1, "Condition must be a Boolean type scalar");
 
 	// TODO: Optimize for case where all lanes take same path.
 
@@ -515,7 +515,7 @@
 	auto selId = Object::ID(block.branchInstruction.word(1));
 
 	auto sel = Operand(this, state, selId);
-	ASSERT_MSG(getType(sel.type).sizeInComponents == 1, "Selector must be a scalar");
+	ASSERT_MSG(getType(sel).sizeInComponents == 1, "Selector must be a scalar");
 
 	auto numCases = (block.branchInstruction.wordCount() - 3) / 2;
 
diff --git a/src/Pipeline/SpirvShaderDebugger.cpp b/src/Pipeline/SpirvShaderDebugger.cpp
index 0af1966..0a555d9 100644
--- a/src/Pipeline/SpirvShaderDebugger.cpp
+++ b/src/Pipeline/SpirvShaderDebugger.cpp
@@ -1165,7 +1165,7 @@
 				case Object::Kind::Pointer:
 				{
 					auto ptr = shader->GetPointerToData(id, 0, state) + sizeof(uint32_t) * wordOffset;
-					auto &ptrTy = shader->getType(obj.type);
+					auto &ptrTy = shader->getType(obj);
 					if(IsStorageInterleavedByLane(ptrTy.storageClass))
 					{
 						ptr = InterleaveByLane(ptr);
@@ -1288,7 +1288,7 @@
 
 	// No debug type information. Derive from SPIR-V.
 	Operand val(shader, state, id);
-	switch(shader->getType(val.type).opcode())
+	switch(shader->getType(val).opcode())
 	{
 		case spv::OpTypeInt:
 		{
@@ -1302,7 +1302,7 @@
 		break;
 		case spv::OpTypeVector:
 		{
-			auto count = shader->getType(val.type).definition.word(3);
+			auto count = shader->getType(val).definition.word(3);
 			switch(count)
 			{
 				case 1:
@@ -1331,7 +1331,7 @@
 		break;
 		case spv::OpTypePointer:
 		{
-			auto objectTy = shader->getType(shader->getObject(id).type);
+			auto objectTy = shader->getType(shader->getObject(id));
 			bool interleavedByLane = IsStorageInterleavedByLane(objectTy.storageClass);
 			auto ptr = state->getPointer(id);
 			auto ptrGroup = group.group<Key>(key);
diff --git a/src/Pipeline/SpirvShaderGLSLstd450.cpp b/src/Pipeline/SpirvShaderGLSLstd450.cpp
index a6be75f..75d2847 100644
--- a/src/Pipeline/SpirvShaderGLSLstd450.cpp
+++ b/src/Pipeline/SpirvShaderGLSLstd450.cpp
@@ -317,7 +317,7 @@
 		case GLSLstd450Length:
 		{
 			auto x = Operand(this, state, insn.word(5));
-			SIMD::Float d = Dot(getType(getObject(insn.word(5)).type).sizeInComponents, x, x);
+			SIMD::Float d = Dot(getType(getObject(insn.word(5))).sizeInComponents, x, x);
 
 			dst.move(0, Sqrt(d));
 			break;
@@ -325,7 +325,7 @@
 		case GLSLstd450Normalize:
 		{
 			auto x = Operand(this, state, insn.word(5));
-			SIMD::Float d = Dot(getType(getObject(insn.word(5)).type).sizeInComponents, x, x);
+			SIMD::Float d = Dot(getType(getObject(insn.word(5))).sizeInComponents, x, x);
 			SIMD::Float invLength = SIMD::Float(1.0f) / Sqrt(d);
 
 			for(auto i = 0u; i < type.sizeInComponents; i++)
@@ -338,7 +338,7 @@
 		{
 			auto p0 = Operand(this, state, insn.word(5));
 			auto p1 = Operand(this, state, insn.word(6));
-			auto p0Type = getType(p0.type);
+			auto p0Type = getType(p0);
 
 			// sqrt(dot(p0-p1, p0-p1))
 			SIMD::Float d = (p0.Float(0) - p1.Float(0)) * (p0.Float(0) - p1.Float(0));
@@ -355,7 +355,7 @@
 		{
 			auto val = Operand(this, state, insn.word(5));
 			auto ptrId = Object::ID(insn.word(6));
-			auto ptrTy = getType(getObject(ptrId).type);
+			auto ptrTy = getType(getObject(ptrId));
 			auto ptr = GetPointerToData(ptrId, 0, state);
 			bool interleavedByLane = IsStorageInterleavedByLane(ptrTy.storageClass);
 			// TODO: GLSL modf() takes an output parameter and thus the pointer is assumed
@@ -378,7 +378,7 @@
 		case GLSLstd450ModfStruct:
 		{
 			auto val = Operand(this, state, insn.word(5));
-			auto valTy = getType(val.type);
+			auto valTy = getType(val);
 
 			for(auto i = 0u; i < valTy.sizeInComponents; i++)
 			{
@@ -501,7 +501,7 @@
 		{
 			auto val = Operand(this, state, insn.word(5));
 			auto ptrId = Object::ID(insn.word(6));
-			auto ptrTy = getType(getObject(ptrId).type);
+			auto ptrTy = getType(getObject(ptrId));
 			auto ptr = GetPointerToData(ptrId, 0, state);
 			bool interleavedByLane = IsStorageInterleavedByLane(ptrTy.storageClass);
 			// TODO: GLSL frexp() takes an output parameter and thus the pointer is assumed
@@ -527,7 +527,7 @@
 		case GLSLstd450FrexpStruct:
 		{
 			auto val = Operand(this, state, insn.word(5));
-			auto numComponents = getType(val.type).sizeInComponents;
+			auto numComponents = getType(val).sizeInComponents;
 			for(auto i = 0u; i < numComponents; i++)
 			{
 				auto significandAndExponent = Frexp(val.Float(i));
@@ -785,7 +785,7 @@
 		case GLSLstd450Determinant:
 		{
 			auto mat = Operand(this, state, insn.word(5));
-			auto numComponents = getType(mat.type).sizeInComponents;
+			auto numComponents = getType(mat).sizeInComponents;
 			switch(numComponents)
 			{
 				case 4:  // 2x2
@@ -814,7 +814,7 @@
 		case GLSLstd450MatrixInverse:
 		{
 			auto mat = Operand(this, state, insn.word(5));
-			auto numComponents = getType(mat.type).sizeInComponents;
+			auto numComponents = getType(mat).sizeInComponents;
 			switch(numComponents)
 			{
 				case 4:  // 2x2
diff --git a/src/Pipeline/SpirvShaderGroup.cpp b/src/Pipeline/SpirvShaderGroup.cpp
index ddbc7ca..680ea6c 100644
--- a/src/Pipeline/SpirvShaderGroup.cpp
+++ b/src/Pipeline/SpirvShaderGroup.cpp
@@ -182,7 +182,7 @@
 		{
 			auto valueId = Object::ID(insn.word(4));
 			ASSERT(type.sizeInComponents == 1);
-			ASSERT(getType(getObject(valueId).type).sizeInComponents == 4);
+			ASSERT(getType(getObject(valueId)).sizeInComponents == 4);
 			Operand value(this, state, valueId);
 			auto bit = (value.Int(0) >> SIMD::Int(0, 1, 2, 3)) & SIMD::Int(1);
 			dst.move(0, -bit);
@@ -194,8 +194,8 @@
 			auto valueId = Object::ID(insn.word(4));
 			auto indexId = Object::ID(insn.word(5));
 			ASSERT(type.sizeInComponents == 1);
-			ASSERT(getType(getObject(valueId).type).sizeInComponents == 4);
-			ASSERT(getType(getObject(indexId).type).sizeInComponents == 1);
+			ASSERT(getType(getObject(valueId)).sizeInComponents == 4);
+			ASSERT(getType(getObject(indexId)).sizeInComponents == 1);
 			Operand value(this, state, valueId);
 			Operand index(this, state, indexId);
 			auto vecIdx = index.Int(0) / SIMD::Int(32);
@@ -213,7 +213,7 @@
 			auto operation = spv::GroupOperation(insn.word(4));
 			auto valueId = Object::ID(insn.word(5));
 			ASSERT(type.sizeInComponents == 1);
-			ASSERT(getType(getObject(valueId).type).sizeInComponents == 4);
+			ASSERT(getType(getObject(valueId)).sizeInComponents == 4);
 			Operand value(this, state, valueId);
 			switch(operation)
 			{
@@ -236,7 +236,7 @@
 		{
 			auto valueId = Object::ID(insn.word(4));
 			ASSERT(type.sizeInComponents == 1);
-			ASSERT(getType(getObject(valueId).type).sizeInComponents == 4);
+			ASSERT(getType(getObject(valueId)).sizeInComponents == 4);
 			Operand value(this, state, valueId);
 			dst.move(0, Cttz(value.UInt(0) & SIMD::UInt(15), true));
 			break;
@@ -246,7 +246,7 @@
 		{
 			auto valueId = Object::ID(insn.word(4));
 			ASSERT(type.sizeInComponents == 1);
-			ASSERT(getType(getObject(valueId).type).sizeInComponents == 4);
+			ASSERT(getType(getObject(valueId)).sizeInComponents == 4);
 			Operand value(this, state, valueId);
 			dst.move(0, SIMD::UInt(31) - Ctlz(value.UInt(0) & SIMD::UInt(15), false));
 			break;
diff --git a/src/Pipeline/SpirvShaderImage.cpp b/src/Pipeline/SpirvShaderImage.cpp
index eb20f48..399e8d1 100644
--- a/src/Pipeline/SpirvShaderImage.cpp
+++ b/src/Pipeline/SpirvShaderImage.cpp
@@ -121,7 +121,7 @@
 	auto samplerDescriptor = (sampledImage.opcode() == spv::OpSampledImage) ? state->getPointer(sampledImage.definition.word(4)).base : imageDescriptor;
 
 	auto coordinate = Operand(this, state, coordinateId);
-	auto &coordinateType = getType(coordinate.type);
+	auto &coordinateType = getType(coordinate);
 
 	Pointer<Byte> sampler = samplerDescriptor + OFFSET(vk::SampledImageDescriptor, sampler);  // vk::Sampler*
 	Pointer<Byte> texture = imageDescriptor + OFFSET(vk::SampledImageDescriptor, texture);    // sw::Texture*
@@ -129,7 +129,7 @@
 	// Above we assumed that if the SampledImage operand is not the result of an OpSampledImage,
 	// it must be a combined image sampler loaded straight from the descriptor set. For OpImageFetch
 	// it's just an Image operand, so there's no sampler descriptor data.
-	if(getType(sampledImage.type).opcode() != spv::OpTypeSampledImage)
+	if(getType(sampledImage).opcode() != spv::OpTypeSampledImage)
 	{
 		sampler = Pointer<Byte>(nullptr);
 	}
@@ -248,8 +248,8 @@
 	{
 		auto dxValue = Operand(this, state, gradDxId);
 		auto dyValue = Operand(this, state, gradDyId);
-		auto &dxyType = getType(dxValue.type);
-		ASSERT(dxyType.sizeInComponents == getType(dyValue.type).sizeInComponents);
+		auto &dxyType = getType(dxValue);
+		ASSERT(dxyType.sizeInComponents == getType(dyValue).sizeInComponents);
 
 		instruction.grad = dxyType.sizeInComponents;
 
@@ -275,7 +275,7 @@
 	if(constOffset)
 	{
 		auto offsetValue = Operand(this, state, offsetId);
-		auto &offsetType = getType(offsetValue.type);
+		auto &offsetType = getType(offsetValue);
 
 		instruction.offset = offsetType.sizeInComponents;
 
@@ -346,7 +346,7 @@
 {
 	auto routine = state->routine;
 	auto &image = getObject(imageId);
-	auto &imageType = getType(image.type);
+	auto &imageType = getType(image);
 
 	ASSERT(imageType.definition.opcode() == spv::OpTypeImage);
 	bool isArrayed = imageType.definition.word(5) != 0;
@@ -387,7 +387,7 @@
 	if(lodId != 0)
 	{
 		auto lodVal = Operand(this, state, lodId);
-		ASSERT(getType(lodVal.type).sizeInComponents == 1);
+		ASSERT(getType(lodVal).sizeInComponents == 1);
 		auto lod = lodVal.Int(0);
 		auto one = SIMD::Int(1);
 		for(uint32_t i = 0; i < dimensions; i++)
@@ -446,7 +446,7 @@
 	ASSERT(resultTy.sizeInComponents == 1);
 	auto resultId = Object::ID(insn.word(2));
 	auto imageId = Object::ID(insn.word(3));
-	auto imageTy = getType(getObject(imageId).type);
+	auto imageTy = getType(getObject(imageId));
 	ASSERT(imageTy.definition.opcode() == spv::OpTypeImage);
 	ASSERT(imageTy.definition.word(3) == spv::Dim2D);
 	ASSERT(imageTy.definition.word(6 /* MS */) == 1);
@@ -482,12 +482,12 @@
 	auto routine = state->routine;
 	bool isArrayed = imageType.definition.word(5) != 0;
 	auto dim = static_cast<spv::Dim>(imageType.definition.word(3));
-	int dims = getType(coordinate.type).sizeInComponents - (isArrayed ? 1 : 0);
+	int dims = getType(coordinate).sizeInComponents - (isArrayed ? 1 : 0);
 
 	SIMD::Int u = coordinate.Int(0);
 	SIMD::Int v = SIMD::Int(0);
 
-	if(getType(coordinate.type).sizeInComponents > 1)
+	if(getType(coordinate).sizeInComponents > 1)
 	{
 		v = coordinate.Int(1);
 	}
@@ -544,7 +544,7 @@
 	auto &resultType = getType(Type::ID(insn.word(1)));
 	auto imageId = Object::ID(insn.word(3));
 	auto &image = getObject(imageId);
-	auto &imageType = getType(image.type);
+	auto &imageType = getType(image);
 	Object::ID resultId = insn.word(2);
 
 	Object::ID sampleId = 0;
@@ -848,7 +848,7 @@
 {
 	auto imageId = Object::ID(insn.word(1));
 	auto &image = getObject(imageId);
-	auto &imageType = getType(image.type);
+	auto &imageType = getType(image);
 
 	ASSERT(imageType.definition.opcode() == spv::OpTypeImage);
 
@@ -1000,7 +1000,7 @@
 	auto &image = getObject(imageId);
 	// Note: OpImageTexelPointer is unusual in that the image is passed by pointer.
 	// Look through to get the actual image type.
-	auto &imageType = getType(getType(image.type).element);
+	auto &imageType = getType(getType(image).element);
 	Object::ID resultId = insn.word(2);
 
 	ASSERT(imageType.opcode() == spv::OpTypeImage);
diff --git a/src/Pipeline/SpirvShaderMemory.cpp b/src/Pipeline/SpirvShaderMemory.cpp
index 7d1fc93..58b05fe 100644
--- a/src/Pipeline/SpirvShaderMemory.cpp
+++ b/src/Pipeline/SpirvShaderMemory.cpp
@@ -28,14 +28,14 @@
 	Object::ID resultId = insn.word(2);
 	Object::ID pointerId = insn.word(3);
 	auto &result = getObject(resultId);
-	auto &resultTy = getType(result.type);
+	auto &resultTy = getType(result);
 	auto &pointer = getObject(pointerId);
-	auto &pointerTy = getType(pointer.type);
+	auto &pointerTy = getType(pointer);
 	std::memory_order memoryOrder = std::memory_order_relaxed;
 
-	ASSERT(getType(pointer.type).element == result.type);
-	ASSERT(Type::ID(insn.word(1)) == result.type);
-	ASSERT(!atomic || getType(getType(pointer.type).element).opcode() == spv::OpTypeInt);  // Vulkan 1.1: "Atomic instructions must declare a scalar 32-bit integer type, for the value pointed to by Pointer."
+	ASSERT(getType(pointer).element == result.typeId());
+	ASSERT(Type::ID(insn.word(1)) == result.typeId());
+	ASSERT(!atomic || getType(getType(pointer).element).opcode() == spv::OpTypeInt);  // Vulkan 1.1: "Atomic instructions must declare a scalar 32-bit integer type, for the value pointed to by Pointer."
 
 	if(pointerTy.storageClass == spv::StorageClassUniformConstant)
 	{
@@ -73,7 +73,7 @@
 	Object::ID objectId = insn.word(atomic ? 4 : 2);
 	auto &object = getObject(objectId);
 	auto &pointer = getObject(pointerId);
-	auto &pointerTy = getType(pointer.type);
+	auto &pointerTy = getType(pointer);
 	auto &elementTy = getType(pointerTy.element);
 	std::memory_order memoryOrder = std::memory_order_relaxed;
 
@@ -125,7 +125,7 @@
 	auto routine = state->routine;
 	Object::ID resultId = insn.word(2);
 	auto &object = getObject(resultId);
-	auto &objectTy = getType(object.type);
+	auto &objectTy = getType(object);
 
 	switch(objectTy.storageClass)
 	{
@@ -257,8 +257,8 @@
 {
 	Object::ID dstPtrId = insn.word(1);
 	Object::ID srcPtrId = insn.word(2);
-	auto &dstPtrTy = getType(getObject(dstPtrId).type);
-	auto &srcPtrTy = getType(getObject(srcPtrId).type);
+	auto &dstPtrTy = getType(getObject(dstPtrId));
+	auto &srcPtrTy = getType(getObject(srcPtrId));
 	ASSERT(dstPtrTy.element == srcPtrTy.element);
 
 	bool dstInterleavedByLane = IsStorageInterleavedByLane(dstPtrTy.storageClass);
@@ -362,10 +362,11 @@
 	}
 }
 
-void SpirvShader::VisitMemoryObject(sw::SpirvShader::Object::ID id, const MemoryVisitor &f) const
+void SpirvShader::VisitMemoryObject(Object::ID id, const MemoryVisitor &f) const
 {
-	auto typeId = getObject(id).type;
+	auto typeId = getObject(id).typeId();
 	auto const &type = getType(typeId);
+
 	if(IsExplicitLayout(type.storageClass))
 	{
 		Decorations d{};
diff --git a/src/Pipeline/SpirvShaderSpec.cpp b/src/Pipeline/SpirvShaderSpec.cpp
index 3bfd153..27abddd 100644
--- a/src/Pipeline/SpirvShaderSpec.cpp
+++ b/src/Pipeline/SpirvShaderSpec.cpp
@@ -69,11 +69,11 @@
 		{
 			auto &result = CreateConstant(insn);
 			auto const &cond = getObject(insn.word(4));
-			auto condIsScalar = (getType(cond.type).sizeInComponents == 1);
+			auto condIsScalar = (getType(cond).sizeInComponents == 1);
 			auto const &left = getObject(insn.word(5));
 			auto const &right = getObject(insn.word(6));
 
-			for(auto i = 0u; i < getType(result.type).sizeInComponents; i++)
+			for(auto i = 0u; i < getType(result).sizeInComponents; i++)
 			{
 				auto sel = cond.constantValue[condIsScalar ? 0 : i];
 				result.constantValue[i] = sel ? left.constantValue[i] : right.constantValue[i];
@@ -85,9 +85,9 @@
 		{
 			auto &result = CreateConstant(insn);
 			auto const &compositeObject = getObject(insn.word(4));
-			auto firstComponent = WalkLiteralAccessChain(compositeObject.type, insn.wordCount() - 5, insn.wordPointer(5));
+			auto firstComponent = WalkLiteralAccessChain(compositeObject.typeId(), insn.wordCount() - 5, insn.wordPointer(5));
 
-			for(auto i = 0u; i < getType(result.type).sizeInComponents; i++)
+			for(auto i = 0u; i < getType(result).sizeInComponents; i++)
 			{
 				result.constantValue[i] = compositeObject.constantValue[firstComponent + i];
 			}
@@ -99,7 +99,7 @@
 			auto &result = CreateConstant(insn);
 			auto const &newPart = getObject(insn.word(4));
 			auto const &oldObject = getObject(insn.word(5));
-			auto firstNewComponent = WalkLiteralAccessChain(result.type, insn.wordCount() - 6, insn.wordPointer(6));
+			auto firstNewComponent = WalkLiteralAccessChain(result.typeId(), insn.wordCount() - 6, insn.wordPointer(6));
 
 			// old components before
 			for(auto i = 0u; i < firstNewComponent; i++)
@@ -107,12 +107,12 @@
 				result.constantValue[i] = oldObject.constantValue[i];
 			}
 			// new part
-			for(auto i = 0u; i < getType(newPart.type).sizeInComponents; i++)
+			for(auto i = 0u; i < getType(newPart).sizeInComponents; i++)
 			{
 				result.constantValue[firstNewComponent + i] = newPart.constantValue[i];
 			}
 			// old components after
-			for(auto i = firstNewComponent + getType(newPart.type).sizeInComponents; i < getType(result.type).sizeInComponents; i++)
+			for(auto i = firstNewComponent + getType(newPart).sizeInComponents; i < getType(result).sizeInComponents; i++)
 			{
 				result.constantValue[i] = oldObject.constantValue[i];
 			}
@@ -125,7 +125,7 @@
 			auto const &firstHalf = getObject(insn.word(4));
 			auto const &secondHalf = getObject(insn.word(5));
 
-			for(auto i = 0u; i < getType(result.type).sizeInComponents; i++)
+			for(auto i = 0u; i < getType(result).sizeInComponents; i++)
 			{
 				auto selector = insn.word(6 + i);
 				if(selector == static_cast<uint32_t>(-1))
@@ -133,13 +133,13 @@
 					// Undefined value, we'll use zero
 					result.constantValue[i] = 0;
 				}
-				else if(selector < getType(firstHalf.type).sizeInComponents)
+				else if(selector < getType(firstHalf).sizeInComponents)
 				{
 					result.constantValue[i] = firstHalf.constantValue[selector];
 				}
 				else
 				{
-					result.constantValue[i] = secondHalf.constantValue[selector - getType(firstHalf.type).sizeInComponents];
+					result.constantValue[i] = secondHalf.constantValue[selector - getType(firstHalf).sizeInComponents];
 				}
 			}
 			break;
@@ -159,7 +159,7 @@
 
 	auto opcode = static_cast<spv::Op>(insn.word(3));
 	auto const &lhs = getObject(insn.word(4));
-	auto size = getType(lhs.type).sizeInComponents;
+	auto size = getType(lhs).sizeInComponents;
 
 	for(auto i = 0u; i < size; i++)
 	{
@@ -210,7 +210,7 @@
 	auto opcode = static_cast<spv::Op>(insn.word(3));
 	auto const &lhs = getObject(insn.word(4));
 	auto const &rhs = getObject(insn.word(5));
-	auto size = getType(lhs.type).sizeInComponents;
+	auto size = getType(lhs).sizeInComponents;
 
 	for(auto i = 0u; i < size; i++)
 	{