Implement some common forms of OpAccessChain

Bug: b/124388146
Change-Id: I948ad5607c1e41be920006aaa4b7d18d210af4a3
Reviewed-on: https://swiftshader-review.googlesource.com/c/24790
Tested-by: Chris Forbes <chrisforbes@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
diff --git a/src/Pipeline/SpirvShader.cpp b/src/Pipeline/SpirvShader.cpp
index cdd3a63..aa48093 100644
--- a/src/Pipeline/SpirvShader.cpp
+++ b/src/Pipeline/SpirvShader.cpp
@@ -487,6 +487,56 @@
 		VisitInterfaceInner<F>(def.word(1), d, f);
 	}
 
+	Int4 SpirvShader::WalkAccessChain(uint32_t id, uint32_t numIndexes, uint32_t const *indexIds, SpirvRoutine *routine) const
+	{
+		// TODO: think about decorations, to make this work on location based interfaces
+		// TODO: think about explicit layout (UBO/SSBO) storage classes
+		// TODO: avoid doing per-lane work in some cases if we can?
+
+		Int4 res = Int4(0);
+		auto & baseObject = getObject(id);
+		auto typeId = baseObject.definition.word(1);
+
+		if (baseObject.kind == Object::Kind::Value)
+			res += As<Int4>((*routine->lvalues[id])[0]);
+
+		for (auto i = 0u; i < numIndexes; i++)
+		{
+			auto & type = getType(typeId);
+			switch (type.definition.opcode())
+			{
+			case spv::OpTypeStruct:
+			{
+				int memberIndex = GetConstantInt(indexIds[i]);
+				int offsetIntoStruct = 0;
+				for (auto j = 0; j < memberIndex; j++) {
+					offsetIntoStruct += getType(type.definition.word(2 + memberIndex)).sizeInComponents;
+				}
+				res += Int4(offsetIntoStruct);
+				break;
+			}
+
+			case spv::OpTypeVector:
+			case spv::OpTypeMatrix:
+			case spv::OpTypeArray:
+			{
+				auto stride = getType(type.definition.word(2)).sizeInComponents;
+				auto & obj = getObject(indexIds[i]);
+				if (obj.kind == Object::Kind::Constant)
+					res += Int4(stride * GetConstantInt(indexIds[i]));
+				else
+					res += Int4(stride) * As<Int4>((*(routine->lvalues)[indexIds[i]])[0]);
+				break;
+			}
+
+			default:
+				UNIMPLEMENTED("Unexpected type in WalkAccessChain");
+			}
+		}
+
+		return res;
+	}
+
 	void SpirvShader::Decorations::Apply(spv::Decoration decoration, uint32_t arg)
 	{
 		switch (decoration)
@@ -660,6 +710,32 @@
 				}
 				break;
 			}
+			case spv::OpAccessChain:
+			{
+				auto &object = getObject(insn.word(2));
+				auto &type = getType(insn.word(1));
+				auto &base = getObject(insn.word(3));
+				routine->createLvalue(insn.word(2), type.sizeInComponents);		// TODO: this should be an ssavalue!
+				auto &pointerBase = getObject(object.pointerBase);
+				assert(type.sizeInComponents == 1);
+				assert(base.pointerBase == object.pointerBase);
+
+				if (pointerBase.kind == Object::Kind::InterfaceVariable)
+				{
+					UNIMPLEMENTED("Location-based OpAccessChain not yet implemented");
+				}
+
+				if (pointerBase.storageClass == spv::StorageClassImage ||
+					pointerBase.storageClass == spv::StorageClassUniform ||
+					pointerBase.storageClass == spv::StorageClassUniformConstant)
+				{
+					UNIMPLEMENTED("Descriptor-backed OpAccessChain not yet implemented");
+				}
+
+				auto & dst = *(routine->lvalues)[insn.word(2)];
+				dst[0] = As<Float4>(WalkAccessChain(insn.word(3), insn.wordCount() - 4, insn.wordPointer(4), routine));
+				break;
+			}
 			case spv::OpStore:
 			{
 				auto &object = getObject(insn.word(2));
diff --git a/src/Pipeline/SpirvShader.hpp b/src/Pipeline/SpirvShader.hpp
index ec8f308..86dfaed 100644
--- a/src/Pipeline/SpirvShader.hpp
+++ b/src/Pipeline/SpirvShader.hpp
@@ -72,6 +72,12 @@
 				return iter[n];
 			}
 
+			uint32_t const * wordPointer(uint32_t n) const
+			{
+				ASSERT(n < wordCount());
+				return &iter[n];
+			}
+
 			bool operator!=(InsnIterator const &other) const
 			{
 				return iter != other.iter;
@@ -275,6 +281,8 @@
 		uint32_t GetConstantInt(uint32_t id) const;
 
 		void ProcessInterfaceVariable(Object &object);
+
+		Int4 WalkAccessChain(uint32_t id, uint32_t numIndexes, uint32_t const *indexIds, SpirvRoutine *routine) const;
 	};
 }