Implement some common forms of OpLoad

Bug: b/124388146
Change-Id: I79b4c3bc59631332f4748394a166612ad5d975de
Reviewed-on: https://swiftshader-review.googlesource.com/c/24600
Reviewed-by: Ben Clayton <bclayton@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
Tested-by: Chris Forbes <chrisforbes@google.com>
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
diff --git a/src/Pipeline/SpirvShader.cpp b/src/Pipeline/SpirvShader.cpp
index 86c1a20..3b2fac8 100644
--- a/src/Pipeline/SpirvShader.cpp
+++ b/src/Pipeline/SpirvShader.cpp
@@ -166,6 +166,7 @@
 				// OpVariable's "size" is the size of the allocation required (the size of the pointee)
 				object.sizeInComponents = pointeeType.sizeInComponents;
 				object.isBuiltInBlock = type.isBuiltInBlock;
+				object.pointerBase = insn.word(2);	// base is itself
 
 				// Register builtins
 
@@ -233,7 +234,7 @@
 					// interior ptr has two parts:
 					// - logical base ptr, common across all lanes and known at compile time
 					// - per-lane offset
-					object.pointerBase = insn.word(3);
+					object.pointerBase = getObject(insn.word(3)).pointerBase;
 				}
 				break;
 			}
@@ -597,13 +598,12 @@
 				if (object.kind != Object::Kind::InterfaceVariable && object.sizeInComponents > 0)
 				{
 					// any variable not in a location-oriented interface
-					routine->lvalues.emplace(insn.word(2), std::unique_ptr<Array<Float4>>(
-							new Array<Float4>(object.sizeInComponents)));
+					routine->createLvalue(insn.word(2), object.sizeInComponents);
 				}
 				break;
 			}
 			default:
-				printf("emitEarly: ignoring opcode %d\n", insn.opcode());
+				// Nothing else produces interface variables, so can all be safely ignored.
 				break;
 			}
 		}
@@ -611,12 +611,55 @@
 
 	void SpirvShader::emit(SpirvRoutine *routine) const
 	{
-		(void) routine;
-
 		for (auto insn : *this)
 		{
 			switch (insn.opcode())
 			{
+			case spv::OpLoad:
+			{
+				auto &object = getObject(insn.word(2));
+				auto &type = getType(insn.word(1));
+				auto &pointer = getObject(insn.word(3));
+				routine->createLvalue(insn.word(2), type.sizeInComponents);		// TODO: this should be an ssavalue!
+				auto &pointerBase = getObject(pointer.pointerBase);
+
+				if (pointerBase.kind == Object::Kind::InterfaceVariable)
+				{
+					UNIMPLEMENTED("Location-based load not yet implemented");
+				}
+
+				if (pointerBase.storageClass == spv::StorageClassImage ||
+					pointerBase.storageClass == spv::StorageClassUniform ||
+					pointerBase.storageClass == spv::StorageClassUniformConstant)
+				{
+					UNIMPLEMENTED("Descriptor-backed load not yet implemented");
+				}
+
+				SpirvRoutine::Value& ptrBase = *(routine->lvalues)[pointer.pointerBase];
+				auto & dst = *(routine->lvalues)[insn.word(2)];
+
+				if (pointer.kind == Object::Kind::Value)
+				{
+					auto offsets = As<Int4>(*(routine->lvalues)[insn.word(3)]);
+					for (auto i = 0u; i < object.sizeInComponents; i++)
+					{
+						// i wish i had a Float,Float,Float,Float constructor here..
+						Float4 v;
+						for (int j = 0; j < 4; j++)
+							v = Insert(v, Extract(ptrBase[Int(i) + Extract(offsets, j)], j), j);
+						dst[i] = v;
+					}
+				}
+				else
+				{
+					// no divergent offsets to worry about
+					for (auto i = 0u; i < object.sizeInComponents; i++)
+					{
+						dst[i] = ptrBase[i];
+					}
+				}
+				break;
+			}
 			default:
 				printf("emit: ignoring opcode %d\n", insn.opcode());
 				break;
diff --git a/src/Pipeline/SpirvShader.hpp b/src/Pipeline/SpirvShader.hpp
index 1419004..ec8f308 100644
--- a/src/Pipeline/SpirvShader.hpp
+++ b/src/Pipeline/SpirvShader.hpp
@@ -33,9 +33,15 @@
 	class SpirvRoutine
 	{
 	public:
-		std::unordered_map<uint32_t, std::unique_ptr<Array<Float4>>> lvalues;
-		std::unique_ptr<Array<Float4>> inputs = std::unique_ptr<Array<Float4>>(new Array<Float4>(MAX_INTERFACE_COMPONENTS));
-		std::unique_ptr<Array<Float4>> outputs = std::unique_ptr<Array<Float4>>(new Array<Float4>(MAX_INTERFACE_COMPONENTS));
+		using Value = Array<Float4>;
+		std::unordered_map<uint32_t, std::unique_ptr<Value>> lvalues;
+		std::unique_ptr<Value> inputs = std::unique_ptr<Value>(new Value(MAX_INTERFACE_COMPONENTS));
+		std::unique_ptr<Value> outputs = std::unique_ptr<Value>(new Value(MAX_INTERFACE_COMPONENTS));
+
+		void createLvalue(uint32_t id, uint32_t size)
+		{
+			lvalues.emplace(id, std::unique_ptr<Value>(new Value(size)));
+		}
 	};
 
 	class SpirvShader