spirv-reader: support OpTranspose

Change-Id: If338b22b703257e863e511579cfd3abcaa0bdfe7
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/48761
Auto-Submit: David Neto <dneto@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Alan Baker <alanbaker@google.com>
Reviewed-by: Alan Baker <alanbaker@google.com>
diff --git a/src/reader/spirv/function.cc b/src/reader/spirv/function.cc
index 888f7cc..a6270c1 100644
--- a/src/reader/spirv/function.cc
+++ b/src/reader/spirv/function.cc
@@ -175,6 +175,8 @@
       return "isNan";
     case SpvOpIsInf:
       return "isInf";
+    case SpvOpTranspose:
+      return "transpose";
     default:
       break;
   }
diff --git a/src/reader/spirv/function_arithmetic_test.cc b/src/reader/spirv/function_arithmetic_test.cc
index 9a05b46..869729a 100644
--- a/src/reader/spirv/function_arithmetic_test.cc
+++ b/src/reader/spirv/function_arithmetic_test.cc
@@ -57,11 +57,15 @@
   %v2float_50_60 = OpConstantComposite %v2float %float_50 %float_60
   %v2float_60_50 = OpConstantComposite %v2float %float_60 %float_50
   %v3float_50_60_70 = OpConstantComposite %v2float %float_50 %float_60 %float_70
+  %v3float_60_70_50 = OpConstantComposite %v2float %float_60 %float_70 %float_50
 
   %m2v2float = OpTypeMatrix %v2float 2
+  %m2v3float = OpTypeMatrix %v3float 2
+  %m3v2float = OpTypeMatrix %v2float 3
   %m2v2float_a = OpConstantComposite %m2v2float %v2float_50_60 %v2float_60_50
   %m2v2float_b = OpConstantComposite %m2v2float %v2float_60_50 %v2float_50_60
-  %m2v3float = OpTypeMatrix %v3float 2
+  %m3v2float_a = OpConstantComposite %m3v2float %v2float_50_60 %v2float_60_50 %v2float_50_60
+  %m2v3float_a = OpConstantComposite %m2v3float %v3float_50_60_70 %v3float_60_70_50
 )";
 }
 
@@ -1551,6 +1555,108 @@
             ArgAndTypeData{"v2float", "v2float_50_60", "__vec_2__f32"},
             ArgAndTypeData{"v3float", "v3float_50_60_70", "__vec_3__f32"})));
 
+TEST_F(SpvUnaryArithTest, Transpose_2x2) {
+  const auto assembly = CommonTypes() + R"(
+     %100 = OpFunction %void None %voidfn
+     %entry = OpLabel
+     %1 = OpCopyObject %m2v2float %m2v2float_a
+     %2 = OpTranspose %m2v2float %1
+     OpReturn
+     OpFunctionEnd
+  )";
+  auto p = parser(test::Assemble(assembly));
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
+  FunctionEmitter fe(p.get(), *spirv_function(p.get(), 100));
+  EXPECT_TRUE(fe.EmitBody()) << p->error();
+  const auto* expected = R"(
+VariableDeclStatement{
+  VariableConst{
+    x_2
+    none
+    __mat_2_2__f32
+    {
+      Call[not set]{
+        Identifier[not set]{transpose}
+        (
+          Identifier[not set]{x_1}
+        )
+      }
+    }
+  }
+})";
+  const auto got = ToString(p->builder(), fe.ast_body());
+  EXPECT_THAT(got, HasSubstr(expected)) << got;
+}
+
+TEST_F(SpvUnaryArithTest, Transpose_2x3) {
+  const auto assembly = CommonTypes() + R"(
+     %100 = OpFunction %void None %voidfn
+     %entry = OpLabel
+     %1 = OpCopyObject %m2v3float %m2v3float_a
+     %2 = OpTranspose %m3v2float %1
+     OpReturn
+     OpFunctionEnd
+  )";
+  auto p = parser(test::Assemble(assembly));
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
+  FunctionEmitter fe(p.get(), *spirv_function(p.get(), 100));
+  EXPECT_TRUE(fe.EmitBody()) << p->error();
+  // Note, in the AST dump mat_2_3 means 2 rows and 3 columns.
+  // So the column vectors have 2 elements.
+  // That is,   %m3v2float is __mat_2_3__f32.
+  const auto* expected = R"(
+VariableDeclStatement{
+  VariableConst{
+    x_2
+    none
+    __mat_2_3__f32
+    {
+      Call[not set]{
+        Identifier[not set]{transpose}
+        (
+          Identifier[not set]{x_1}
+        )
+      }
+    }
+  }
+})";
+  const auto got = ToString(p->builder(), fe.ast_body());
+  EXPECT_THAT(got, HasSubstr(expected)) << got;
+}
+
+TEST_F(SpvUnaryArithTest, Transpose_3x2) {
+  const auto assembly = CommonTypes() + R"(
+     %100 = OpFunction %void None %voidfn
+     %entry = OpLabel
+     %1 = OpCopyObject %m3v2float %m3v2float_a
+     %2 = OpTranspose %m2v3float %1
+     OpReturn
+     OpFunctionEnd
+  )";
+  auto p = parser(test::Assemble(assembly));
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
+  FunctionEmitter fe(p.get(), *spirv_function(p.get(), 100));
+  EXPECT_TRUE(fe.EmitBody()) << p->error();
+  const auto* expected = R"(
+VariableDeclStatement{
+  VariableConst{
+    x_2
+    none
+    __mat_3_2__f32
+    {
+      Call[not set]{
+        Identifier[not set]{transpose}
+        (
+          Identifier[not set]{x_1}
+        )
+      }
+    }
+  }
+})";
+  const auto got = ToString(p->builder(), fe.ast_body());
+  EXPECT_THAT(got, HasSubstr(expected)) << got;
+}
+
 // TODO(dneto): OpSRem. Missing from WGSL
 // https://github.com/gpuweb/gpuweb/issues/702