Add support for OpMatrixTimesMatrix

Bug: b/126873455
Test: dEQP-VK.glsl.matrix.*
Change-Id: I3070690017263b3bf766a329ef7729206f285e45
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/28228
Tested-by: Chris Forbes <chrisforbes@google.com>
Presubmit-Ready: Chris Forbes <chrisforbes@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
diff --git a/src/Pipeline/SpirvShader.cpp b/src/Pipeline/SpirvShader.cpp
index 9383517..c54c4cb 100644
--- a/src/Pipeline/SpirvShader.cpp
+++ b/src/Pipeline/SpirvShader.cpp
@@ -361,6 +361,7 @@
 			case spv::OpMatrixTimesScalar:
 			case spv::OpMatrixTimesVector:
 			case spv::OpVectorTimesMatrix:
+			case spv::OpMatrixTimesMatrix:
 			case spv::OpVectorExtractDynamic:
 			case spv::OpVectorInsertDynamic:
 			case spv::OpNot: // Unary ops
@@ -1480,6 +1481,9 @@
 		case spv::OpVectorTimesMatrix:
 			return EmitVectorTimesMatrix(insn, state);
 
+		case spv::OpMatrixTimesMatrix:
+			return EmitMatrixTimesMatrix(insn, state);
+
 		case spv::OpNot:
 		case spv::OpSNegate:
 		case spv::OpFNegate:
@@ -2105,6 +2109,34 @@
 		return EmitResult::Continue;
 	}
 
+	SpirvShader::EmitResult SpirvShader::EmitMatrixTimesMatrix(InsnIterator insn, EmitState *state) const
+	{
+		auto routine = state->routine;
+		auto &type = getType(insn.word(1));
+		auto &dst = routine->createIntermediate(insn.word(2), type.sizeInComponents);
+		auto lhs = GenericValue(this, routine, insn.word(3));
+		auto rhs = GenericValue(this, routine, insn.word(4));
+
+		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);
+
+		for (auto row = 0u; row < numRows; row++)
+		{
+			for (auto col = 0u; col < numColumns; col++)
+			{
+				SIMD::Float v = SIMD::Float(0);
+				for (auto i = 0u; i < numAdds; i++)
+				{
+					v += lhs.Float(i * numRows + row) * rhs.Float(col * numAdds + i);
+				}
+				dst.move(numRows * col + row, v);
+			}
+		}
+
+		return EmitResult::Continue;
+	}
+
 	SpirvShader::EmitResult SpirvShader::EmitUnaryOp(InsnIterator insn, EmitState *state) const
 	{
 		auto routine = state->routine;
diff --git a/src/Pipeline/SpirvShader.hpp b/src/Pipeline/SpirvShader.hpp
index 3410dc1..498c547 100644
--- a/src/Pipeline/SpirvShader.hpp
+++ b/src/Pipeline/SpirvShader.hpp
@@ -598,6 +598,7 @@
 		EmitResult EmitVectorTimesScalar(InsnIterator insn, EmitState *state) const;
 		EmitResult EmitMatrixTimesVector(InsnIterator insn, EmitState *state) const;
 		EmitResult EmitVectorTimesMatrix(InsnIterator insn, EmitState *state) const;
+		EmitResult EmitMatrixTimesMatrix(InsnIterator insn, EmitState *state) const;
 		EmitResult EmitVectorExtractDynamic(InsnIterator insn, EmitState *state) const;
 		EmitResult EmitVectorInsertDynamic(InsnIterator insn, EmitState *state) const;
 		EmitResult EmitUnaryOp(InsnIterator insn, EmitState *state) const;