Import Tint changes from Dawn
Changes:
- 6ab02659de463f93a0e723845455bf06d00bb683 tint/spirv-reader: add support for GLSLstd450MatrixInverse by Antonio Maiorano <amaiorano@google.com>
GitOrigin-RevId: 6ab02659de463f93a0e723845455bf06d00bb683
Change-Id: Ib06eb42479d4d863fb9d6e8f546d65f0edca4eeb
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/102520
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
diff --git a/src/tint/reader/spirv/function.cc b/src/tint/reader/spirv/function.cc
index 773ba46..b16d08d 100644
--- a/src/tint/reader/spirv/function.cc
+++ b/src/tint/reader/spirv/function.cc
@@ -415,8 +415,6 @@
case GLSLstd450Acosh:
case GLSLstd450Atanh:
- case GLSLstd450MatrixInverse:
-
case GLSLstd450Modf:
case GLSLstd450ModfStruct:
case GLSLstd450IMix:
@@ -4043,6 +4041,11 @@
default:
break;
}
+ } else {
+ switch (ext_opcode) {
+ case GLSLstd450MatrixInverse:
+ return EmitGlslStd450MatrixInverse(inst);
+ }
}
const auto name = GetGlslStd450FuncName(ext_opcode);
@@ -4067,6 +4070,196 @@
return parser_impl_.RectifyForcedResultType(call_expr, inst, first_operand_type);
}
+TypedExpression FunctionEmitter::EmitGlslStd450MatrixInverse(
+ const spvtools::opt::Instruction& inst) {
+ auto mat = MakeOperand(inst, 2);
+ auto* mat_ty = mat.type->As<Matrix>();
+ TINT_ASSERT(Reader, mat_ty);
+ TINT_ASSERT(Reader, mat_ty->columns == mat_ty->rows);
+ auto& pb = builder_;
+
+ auto idx = [&](size_t row, size_t col) {
+ return pb.IndexAccessor(pb.IndexAccessor(mat.expr, u32(row)), u32(col));
+ };
+
+ // Compute and save determinant to a let
+ auto* det = pb.Div(1.0_f, pb.Call(Source{}, "determinant", mat.expr));
+ auto s = pb.Symbols().New("s");
+ AddStatement(pb.Decl(pb.Let(s, det)));
+
+ // Returns (a * b) - (c * d)
+ auto sub_mul2 = [&](auto* a, auto* b, auto* c, auto* d) {
+ return pb.Sub(pb.Mul(a, b), pb.Mul(c, d));
+ };
+
+ // Returns (a * b) - (c * d) + (e * f)
+ auto sub_add_mul3 = [&](auto* a, auto* b, auto* c, auto* d, auto* e, auto* f) {
+ return pb.Add(pb.Sub(pb.Mul(a, b), pb.Mul(c, d)), pb.Mul(e, f));
+ };
+
+ // Returns (a * b) + (c * d) - (e * f)
+ auto add_sub_mul3 = [&](auto* a, auto* b, auto* c, auto* d, auto* e, auto* f) {
+ return pb.Sub(pb.Add(pb.Mul(a, b), pb.Mul(c, d)), pb.Mul(e, f));
+ };
+
+ // Returns -a
+ auto neg = [&](auto&& a) { return pb.Negation(a); };
+
+ switch (mat_ty->columns) {
+ case 2: {
+ // a, b
+ // c, d
+ auto* a = idx(0, 0);
+ auto* b = idx(0, 1);
+ auto* c = idx(1, 0);
+ auto* d = idx(1, 1);
+
+ // s * d, -s * b, -s * c, s * a
+ auto* r = pb.mat2x2<f32>( //
+ pb.vec2<f32>(pb.Mul(s, d), pb.Mul(neg(s), b)),
+ pb.vec2<f32>(pb.Mul(neg(s), c), pb.Mul(s, a)));
+ return {mat.type, r};
+ }
+
+ case 3: {
+ // a, b, c,
+ // d, e, f,
+ // g, h, i
+ auto* a = idx(0, 0);
+ auto* b = idx(0, 1);
+ auto* c = idx(0, 2);
+ auto* d = idx(1, 0);
+ auto* e = idx(1, 1);
+ auto* f = idx(1, 2);
+ auto* g = idx(2, 0);
+ auto* h = idx(2, 1);
+ auto* i = idx(2, 2);
+
+ auto r = pb.Mul(s, //
+ pb.mat3x3<f32>( //
+ pb.vec3<f32>(
+ // e * i - f * h
+ sub_mul2(e, i, f, h),
+ // c * h - b * i
+ sub_mul2(c, h, b, i),
+ // b * f - c * e
+ sub_mul2(b, f, c, e)),
+ pb.vec3<f32>(
+ // f * g - d * i
+ sub_mul2(f, g, d, i),
+ // a * i - c * g
+ sub_mul2(a, i, c, g),
+ // c * d - a * f
+ sub_mul2(c, d, a, f)),
+ pb.vec3<f32>(
+ // d * h - e * g
+ sub_mul2(d, h, e, g),
+ // b * g - a * h
+ sub_mul2(b, g, a, h),
+ // a * e - b * d
+ sub_mul2(a, e, b, d))));
+ return {mat.type, r};
+ }
+
+ case 4: {
+ // a, b, c, d,
+ // e, f, g, h,
+ // i, j, k, l,
+ // m, n, o, p
+ auto* a = idx(0, 0);
+ auto* b = idx(0, 1);
+ auto* c = idx(0, 2);
+ auto* d = idx(0, 3);
+ auto* e = idx(1, 0);
+ auto* f = idx(1, 1);
+ auto* g = idx(1, 2);
+ auto* h = idx(1, 3);
+ auto* i = idx(2, 0);
+ auto* j = idx(2, 1);
+ auto* k = idx(2, 2);
+ auto* l = idx(2, 3);
+ auto* m = idx(3, 0);
+ auto* n = idx(3, 1);
+ auto* o = idx(3, 2);
+ auto* p = idx(3, 3);
+
+ // kplo = k * p - l * o, jpln = j * p - l * n, jokn = j * o - k * n;
+ auto* kplo = sub_mul2(k, p, l, o);
+ auto* jpln = sub_mul2(j, p, l, n);
+ auto* jokn = sub_mul2(j, o, k, n);
+
+ // gpho = g * p - h * o, fphn = f * p - h * n, fogn = f * o - g * n;
+ auto* gpho = sub_mul2(g, p, h, o);
+ auto* fphn = sub_mul2(f, p, h, n);
+ auto* fogn = sub_mul2(f, o, g, n);
+
+ // glhk = g * l - h * k, flhj = f * l - h * j, fkgj = f * k - g * j;
+ auto* glhk = sub_mul2(g, l, h, k);
+ auto* flhj = sub_mul2(f, l, h, j);
+ auto* fkgj = sub_mul2(f, k, g, j);
+
+ // iplm = i * p - l * m, iokm = i * o - k * m, ephm = e * p - h * m;
+ auto* iplm = sub_mul2(i, p, l, m);
+ auto* iokm = sub_mul2(i, o, k, m);
+ auto* ephm = sub_mul2(e, p, h, m);
+
+ // eogm = e * o - g * m, elhi = e * l - h * i, ekgi = e * k - g * i;
+ auto* eogm = sub_mul2(e, o, g, m);
+ auto* elhi = sub_mul2(e, l, h, i);
+ auto* ekgi = sub_mul2(e, k, g, i);
+
+ // injm = i * n - j * m, enfm = e * n - f * m, ejfi = e * j - f * i;
+ auto* injm = sub_mul2(i, n, j, m);
+ auto* enfm = sub_mul2(e, n, f, m);
+ auto* ejfi = sub_mul2(e, j, f, i);
+
+ auto r = pb.Mul(s, //
+ pb.mat4x4<f32>( //
+ pb.vec4<f32>(
+ // f * kplo - g * jpln + h * jokn
+ sub_add_mul3(f, kplo, g, jpln, h, jokn),
+ // -b * kplo + c * jpln - d * jokn
+ add_sub_mul3(neg(b), kplo, c, jpln, d, jokn),
+ // b * gpho - c * fphn + d * fogn
+ sub_add_mul3(b, gpho, c, fphn, d, fogn),
+ // -b * glhk + c * flhj - d * fkgj
+ add_sub_mul3(neg(b), glhk, c, flhj, d, fkgj)),
+ pb.vec4<f32>(
+ // -e * kplo + g * iplm - h * iokm
+ add_sub_mul3(neg(e), kplo, g, iplm, h, iokm),
+ // a * kplo - c * iplm + d * iokm
+ sub_add_mul3(a, kplo, c, iplm, d, iokm),
+ // -a * gpho + c * ephm - d * eogm
+ add_sub_mul3(neg(a), gpho, c, ephm, d, eogm),
+ // a * glhk - c * elhi + d * ekgi
+ sub_add_mul3(a, glhk, c, elhi, d, ekgi)),
+ pb.vec4<f32>(
+ // e * jpln - f * iplm + h * injm
+ sub_add_mul3(e, jpln, f, iplm, h, injm),
+ // -a * jpln + b * iplm - d * injm
+ add_sub_mul3(neg(a), jpln, b, iplm, d, injm),
+ // a * fphn - b * ephm + d * enfm
+ sub_add_mul3(a, fphn, b, ephm, d, enfm),
+ // -a * flhj + b * elhi - d * ejfi
+ add_sub_mul3(neg(a), flhj, b, elhi, d, ejfi)),
+ pb.vec4<f32>(
+ // -e * jokn + f * iokm - g * injm
+ add_sub_mul3(neg(e), jokn, f, iokm, g, injm),
+ // a * jokn - b * iokm + c * injm
+ sub_add_mul3(a, jokn, b, iokm, c, injm),
+ // -a * fogn + b * eogm - c * enfm
+ add_sub_mul3(neg(a), fogn, b, eogm, c, enfm),
+ // a * fkgj - b * ekgi + c * ejfi
+ sub_add_mul3(a, fkgj, b, ekgi, c, ejfi))));
+ return {mat.type, r};
+ }
+ }
+
+ const auto ext_opcode = inst.GetSingleWordInOperand(1);
+ Fail() << "invalid matrix size for " << GetGlslStd450FuncName(ext_opcode);
+ return {};
+}
+
ast::IdentifierExpression* FunctionEmitter::Swizzle(uint32_t i) {
if (i >= kMaxVectorLen) {
Fail() << "vector component index is larger than " << kMaxVectorLen - 1 << ": " << i;
diff --git a/src/tint/reader/spirv/function.h b/src/tint/reader/spirv/function.h
index 7a2a86b..d779c93 100644
--- a/src/tint/reader/spirv/function.h
+++ b/src/tint/reader/spirv/function.h
@@ -804,6 +804,11 @@
/// @returns an AST expression for the instruction, or nullptr.
TypedExpression EmitGlslStd450ExtInst(const spvtools::opt::Instruction& inst);
+ /// Creates an expression for the GLSL.std.450 matrix `inverse` extended instruction.
+ /// @param inst a SPIR-V OpExtInst instruction from GLSL.std.450
+ /// @returns an AST expression for the instruction, or nullptr.
+ TypedExpression EmitGlslStd450MatrixInverse(const spvtools::opt::Instruction& inst);
+
/// Creates an expression for OpCompositeExtract
/// @param inst an OpCompositeExtract instruction.
/// @returns an AST expression for the instruction, or nullptr.
diff --git a/src/tint/reader/spirv/function_glsl_std_450_test.cc b/src/tint/reader/spirv/function_glsl_std_450_test.cc
index f97f3e9..c5131e9 100644
--- a/src/tint/reader/spirv/function_glsl_std_450_test.cc
+++ b/src/tint/reader/spirv/function_glsl_std_450_test.cc
@@ -98,8 +98,8 @@
%v4float_50_50_50_50 = OpConstantComposite %v4float %float_50 %float_50 %float_50 %float_50
%mat2v2float_50_60 = OpConstantComposite %mat2v2float %v2float_50_60 %v2float_50_60
- %mat3v3float_50_60_70 = OpConstantComposite %mat2v2float %v3float_50_60_70 %v3float_50_60_70 %v3float_50_60_70
- %mat4v4float_50_50_50_50 = OpConstantComposite %mat2v2float %v4float_50_50_50_50 %v4float_50_50_50_50 %v4float_50_50_50_50 %v4float_50_50_50_50
+ %mat3v3float_50_60_70 = OpConstantComposite %mat3v3float %v3float_50_60_70 %v3float_50_60_70 %v3float_50_60_70
+ %mat4v4float_50_50_50_50 = OpConstantComposite %mat4v4float %v4float_50_50_50_50 %v4float_50_50_50_50 %v4float_50_50_50_50 %v4float_50_50_50_50
%100 = OpFunction %void None %voidfn
%entry = OpLabel
@@ -1158,5 +1158,159 @@
GlslStd450_Determinant,
::testing::Values("m2x2f1", "m3x3f1", "m4x4f1"));
+TEST_F(SpvParserTest, GlslStd450_MatrixInverse_mat2x2) {
+ const auto assembly = Preamble() + R"(
+ %1 = OpExtInst %mat2v2float %glsl MatrixInverse %m2x2f1
+ OpReturn
+ OpFunctionEnd
+ )";
+ auto p = parser(test::Assemble(assembly));
+ ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
+ auto fe = p->function_emitter(100);
+ EXPECT_TRUE(fe.EmitBody()) << p->error();
+ auto ast_body = fe.ast_body();
+ const auto body = test::ToString(p->program(), ast_body);
+
+ std::string expected =
+ "let s = (1.0f / determinant(m2x2f1));\n"
+ "let x_1 : mat2x2<f32> = mat2x2<f32>(vec2<f32>((s * m2x2f1[1u][1u]), (-(s) * "
+ "m2x2f1[0u][1u])), vec2<f32>((-(s) * m2x2f1[1u][0u]), (s * m2x2f1[0u][0u])));";
+
+ EXPECT_THAT(body, HasSubstr(expected)) << body;
+}
+
+TEST_F(SpvParserTest, GlslStd450_MatrixInverse_mat3x3) {
+ const auto assembly = Preamble() + R"(
+ %1 = OpExtInst %mat3v3float %glsl MatrixInverse %m3x3f1
+ OpReturn
+ OpFunctionEnd
+ )";
+ auto p = parser(test::Assemble(assembly));
+ ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
+ auto fe = p->function_emitter(100);
+ EXPECT_TRUE(fe.EmitBody()) << p->error();
+ auto ast_body = fe.ast_body();
+ const auto body = test::ToString(p->program(), ast_body);
+
+ std::string expected =
+ "let s = (1.0f / determinant(m3x3f1));\n"
+ "let x_1 : mat3x3<f32> = (s * mat3x3<f32>(vec3<f32>(((m3x3f1[1u][1u] * m3x3f1[2u][2u]) - "
+ "(m3x3f1[1u][2u] * m3x3f1[2u][1u])), ((m3x3f1[0u][2u] * m3x3f1[2u][1u]) - (m3x3f1[0u][1u] "
+ "* m3x3f1[2u][2u])), ((m3x3f1[0u][1u] * m3x3f1[1u][2u]) - (m3x3f1[0u][2u] * "
+ "m3x3f1[1u][1u]))), vec3<f32>(((m3x3f1[1u][2u] * m3x3f1[2u][0u]) - (m3x3f1[1u][0u] * "
+ "m3x3f1[2u][2u])), ((m3x3f1[0u][0u] * m3x3f1[2u][2u]) - (m3x3f1[0u][2u] * "
+ "m3x3f1[2u][0u])), ((m3x3f1[0u][2u] * m3x3f1[1u][0u]) - (m3x3f1[0u][0u] * "
+ "m3x3f1[1u][2u]))), vec3<f32>(((m3x3f1[1u][0u] * m3x3f1[2u][1u]) - (m3x3f1[1u][1u] * "
+ "m3x3f1[2u][0u])), ((m3x3f1[0u][1u] * m3x3f1[2u][0u]) - (m3x3f1[0u][0u] * "
+ "m3x3f1[2u][1u])), ((m3x3f1[0u][0u] * m3x3f1[1u][1u]) - (m3x3f1[0u][1u] * "
+ "m3x3f1[1u][0u])))));";
+
+ EXPECT_THAT(body, HasSubstr(expected)) << body;
+}
+
+TEST_F(SpvParserTest, GlslStd450_MatrixInverse_mat4x4) {
+ const auto assembly = Preamble() + R"(
+ %1 = OpExtInst %mat4v4float %glsl MatrixInverse %m4x4f1
+ OpReturn
+ OpFunctionEnd
+ )";
+ auto p = parser(test::Assemble(assembly));
+ ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
+ auto fe = p->function_emitter(100);
+ EXPECT_TRUE(fe.EmitBody()) << p->error();
+ auto ast_body = fe.ast_body();
+ const auto body = test::ToString(p->program(), ast_body);
+
+ std::string expected =
+ "let s = (1.0f / determinant(m4x4f1));\n"
+ "let x_1 : mat4x4<f32> = (s * mat4x4<f32>(vec4<f32>((((m4x4f1[1u][1u] * ((m4x4f1[2u][2u] * "
+ "m4x4f1[3u][3u]) - (m4x4f1[2u][3u] * m4x4f1[3u][2u]))) - (m4x4f1[1u][2u] * "
+ "((m4x4f1[2u][1u] * m4x4f1[3u][3u]) - (m4x4f1[2u][3u] * m4x4f1[3u][1u])))) + "
+ "(m4x4f1[1u][3u] * ((m4x4f1[2u][1u] * m4x4f1[3u][2u]) - (m4x4f1[2u][2u] * "
+ "m4x4f1[3u][1u])))), (((-(m4x4f1[0u][1u]) * ((m4x4f1[2u][2u] * m4x4f1[3u][3u]) - "
+ "(m4x4f1[2u][3u] * m4x4f1[3u][2u]))) + (m4x4f1[0u][2u] * ((m4x4f1[2u][1u] * "
+ "m4x4f1[3u][3u]) - (m4x4f1[2u][3u] * m4x4f1[3u][1u])))) - (m4x4f1[0u][3u] * "
+ "((m4x4f1[2u][1u] * m4x4f1[3u][2u]) - (m4x4f1[2u][2u] * m4x4f1[3u][1u])))), "
+ "(((m4x4f1[0u][1u] * ((m4x4f1[1u][2u] * m4x4f1[3u][3u]) - (m4x4f1[1u][3u] * "
+ "m4x4f1[3u][2u]))) - (m4x4f1[0u][2u] * ((m4x4f1[1u][1u] * m4x4f1[3u][3u]) - "
+ "(m4x4f1[1u][3u] * m4x4f1[3u][1u])))) + (m4x4f1[0u][3u] * ((m4x4f1[1u][1u] * "
+ "m4x4f1[3u][2u]) - (m4x4f1[1u][2u] * m4x4f1[3u][1u])))), (((-(m4x4f1[0u][1u]) * "
+ "((m4x4f1[1u][2u] * m4x4f1[2u][3u]) - (m4x4f1[1u][3u] * m4x4f1[2u][2u]))) + "
+ "(m4x4f1[0u][2u] * ((m4x4f1[1u][1u] * m4x4f1[2u][3u]) - (m4x4f1[1u][3u] * "
+ "m4x4f1[2u][1u])))) - (m4x4f1[0u][3u] * ((m4x4f1[1u][1u] * m4x4f1[2u][2u]) - "
+ "(m4x4f1[1u][2u] * m4x4f1[2u][1u]))))), vec4<f32>((((-(m4x4f1[1u][0u]) * ((m4x4f1[2u][2u] "
+ "* m4x4f1[3u][3u]) - (m4x4f1[2u][3u] * m4x4f1[3u][2u]))) + (m4x4f1[1u][2u] * "
+ "((m4x4f1[2u][0u] * m4x4f1[3u][3u]) - (m4x4f1[2u][3u] * m4x4f1[3u][0u])))) - "
+ "(m4x4f1[1u][3u] * ((m4x4f1[2u][0u] * m4x4f1[3u][2u]) - (m4x4f1[2u][2u] * "
+ "m4x4f1[3u][0u])))), (((m4x4f1[0u][0u] * ((m4x4f1[2u][2u] * m4x4f1[3u][3u]) - "
+ "(m4x4f1[2u][3u] * m4x4f1[3u][2u]))) - (m4x4f1[0u][2u] * ((m4x4f1[2u][0u] * "
+ "m4x4f1[3u][3u]) - (m4x4f1[2u][3u] * m4x4f1[3u][0u])))) + (m4x4f1[0u][3u] * "
+ "((m4x4f1[2u][0u] * m4x4f1[3u][2u]) - (m4x4f1[2u][2u] * m4x4f1[3u][0u])))), "
+ "(((-(m4x4f1[0u][0u]) * ((m4x4f1[1u][2u] * m4x4f1[3u][3u]) - (m4x4f1[1u][3u] * "
+ "m4x4f1[3u][2u]))) + (m4x4f1[0u][2u] * ((m4x4f1[1u][0u] * m4x4f1[3u][3u]) - "
+ "(m4x4f1[1u][3u] * m4x4f1[3u][0u])))) - (m4x4f1[0u][3u] * ((m4x4f1[1u][0u] * "
+ "m4x4f1[3u][2u]) - (m4x4f1[1u][2u] * m4x4f1[3u][0u])))), (((m4x4f1[0u][0u] * "
+ "((m4x4f1[1u][2u] * m4x4f1[2u][3u]) - (m4x4f1[1u][3u] * m4x4f1[2u][2u]))) - "
+ "(m4x4f1[0u][2u] * ((m4x4f1[1u][0u] * m4x4f1[2u][3u]) - (m4x4f1[1u][3u] * "
+ "m4x4f1[2u][0u])))) + (m4x4f1[0u][3u] * ((m4x4f1[1u][0u] * m4x4f1[2u][2u]) - "
+ "(m4x4f1[1u][2u] * m4x4f1[2u][0u]))))), vec4<f32>((((m4x4f1[1u][0u] * ((m4x4f1[2u][1u] * "
+ "m4x4f1[3u][3u]) - (m4x4f1[2u][3u] * m4x4f1[3u][1u]))) - (m4x4f1[1u][1u] * "
+ "((m4x4f1[2u][0u] * m4x4f1[3u][3u]) - (m4x4f1[2u][3u] * m4x4f1[3u][0u])))) + "
+ "(m4x4f1[1u][3u] * ((m4x4f1[2u][0u] * m4x4f1[3u][1u]) - (m4x4f1[2u][1u] * "
+ "m4x4f1[3u][0u])))), (((-(m4x4f1[0u][0u]) * ((m4x4f1[2u][1u] * m4x4f1[3u][3u]) - "
+ "(m4x4f1[2u][3u] * m4x4f1[3u][1u]))) + (m4x4f1[0u][1u] * ((m4x4f1[2u][0u] * "
+ "m4x4f1[3u][3u]) - (m4x4f1[2u][3u] * m4x4f1[3u][0u])))) - (m4x4f1[0u][3u] * "
+ "((m4x4f1[2u][0u] * m4x4f1[3u][1u]) - (m4x4f1[2u][1u] * m4x4f1[3u][0u])))), "
+ "(((m4x4f1[0u][0u] * ((m4x4f1[1u][1u] * m4x4f1[3u][3u]) - (m4x4f1[1u][3u] * "
+ "m4x4f1[3u][1u]))) - (m4x4f1[0u][1u] * ((m4x4f1[1u][0u] * m4x4f1[3u][3u]) - "
+ "(m4x4f1[1u][3u] * m4x4f1[3u][0u])))) + (m4x4f1[0u][3u] * ((m4x4f1[1u][0u] * "
+ "m4x4f1[3u][1u]) - (m4x4f1[1u][1u] * m4x4f1[3u][0u])))), (((-(m4x4f1[0u][0u]) * "
+ "((m4x4f1[1u][1u] * m4x4f1[2u][3u]) - (m4x4f1[1u][3u] * m4x4f1[2u][1u]))) + "
+ "(m4x4f1[0u][1u] * ((m4x4f1[1u][0u] * m4x4f1[2u][3u]) - (m4x4f1[1u][3u] * "
+ "m4x4f1[2u][0u])))) - (m4x4f1[0u][3u] * ((m4x4f1[1u][0u] * m4x4f1[2u][1u]) - "
+ "(m4x4f1[1u][1u] * m4x4f1[2u][0u]))))), vec4<f32>((((-(m4x4f1[1u][0u]) * ((m4x4f1[2u][1u] "
+ "* m4x4f1[3u][2u]) - (m4x4f1[2u][2u] * m4x4f1[3u][1u]))) + (m4x4f1[1u][1u] * "
+ "((m4x4f1[2u][0u] * m4x4f1[3u][2u]) - (m4x4f1[2u][2u] * m4x4f1[3u][0u])))) - "
+ "(m4x4f1[1u][2u] * ((m4x4f1[2u][0u] * m4x4f1[3u][1u]) - (m4x4f1[2u][1u] * "
+ "m4x4f1[3u][0u])))), (((m4x4f1[0u][0u] * ((m4x4f1[2u][1u] * m4x4f1[3u][2u]) - "
+ "(m4x4f1[2u][2u] * m4x4f1[3u][1u]))) - (m4x4f1[0u][1u] * ((m4x4f1[2u][0u] * "
+ "m4x4f1[3u][2u]) - (m4x4f1[2u][2u] * m4x4f1[3u][0u])))) + (m4x4f1[0u][2u] * "
+ "((m4x4f1[2u][0u] * m4x4f1[3u][1u]) - (m4x4f1[2u][1u] * m4x4f1[3u][0u])))), "
+ "(((-(m4x4f1[0u][0u]) * ((m4x4f1[1u][1u] * m4x4f1[3u][2u]) - (m4x4f1[1u][2u] * "
+ "m4x4f1[3u][1u]))) + (m4x4f1[0u][1u] * ((m4x4f1[1u][0u] * m4x4f1[3u][2u]) - "
+ "(m4x4f1[1u][2u] * m4x4f1[3u][0u])))) - (m4x4f1[0u][2u] * ((m4x4f1[1u][0u] * "
+ "m4x4f1[3u][1u]) - (m4x4f1[1u][1u] * m4x4f1[3u][0u])))), (((m4x4f1[0u][0u] * "
+ "((m4x4f1[1u][1u] * m4x4f1[2u][2u]) - (m4x4f1[1u][2u] * m4x4f1[2u][1u]))) - "
+ "(m4x4f1[0u][1u] * ((m4x4f1[1u][0u] * m4x4f1[2u][2u]) - (m4x4f1[1u][2u] * "
+ "m4x4f1[2u][0u])))) + (m4x4f1[0u][2u] * ((m4x4f1[1u][0u] * m4x4f1[2u][1u]) - "
+ "(m4x4f1[1u][1u] * m4x4f1[2u][0u])))))));";
+
+ EXPECT_THAT(body, HasSubstr(expected)) << body;
+}
+
+TEST_F(SpvParserTest, GlslStd450_MatrixInverse_MultipleInScope) {
+ const auto assembly = Preamble() + R"(
+ %1 = OpExtInst %mat2v2float %glsl MatrixInverse %m2x2f1
+ %2 = OpExtInst %mat2v2float %glsl MatrixInverse %m2x2f1
+ OpReturn
+ OpFunctionEnd
+ )";
+ auto p = parser(test::Assemble(assembly));
+ ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
+ auto fe = p->function_emitter(100);
+ EXPECT_TRUE(fe.EmitBody()) << p->error();
+ auto ast_body = fe.ast_body();
+ const auto body = test::ToString(p->program(), ast_body);
+
+ std::string expected =
+ "let s = (1.0f / determinant(m2x2f1));\n"
+ "let x_1 : mat2x2<f32> = mat2x2<f32>(vec2<f32>((s * m2x2f1[1u][1u]), (-(s) * "
+ "m2x2f1[0u][1u])), vec2<f32>((-(s) * m2x2f1[1u][0u]), (s * m2x2f1[0u][0u])));\n"
+ "let s_1 = (1.0f / determinant(m2x2f1));\n"
+ "let x_2 : mat2x2<f32> = mat2x2<f32>(vec2<f32>((s_1 * m2x2f1[1u][1u]), (-(s_1) * "
+ "m2x2f1[0u][1u])), vec2<f32>((-(s_1) * m2x2f1[1u][0u]), (s_1 * m2x2f1[0u][0u])));";
+
+ EXPECT_THAT(body, HasSubstr(expected)) << body;
+}
} // namespace
} // namespace tint::reader::spirv