spirv-reader: ldexp second argument must be signed

Fixed: tint:1079
Change-Id: I628b81d95201474c6d1c584eafacd125448de30b
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/60240
Commit-Queue: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
diff --git a/src/reader/spirv/function.cc b/src/reader/spirv/function.cc
index 727ad83..b4aeaec 100644
--- a/src/reader/spirv/function.cc
+++ b/src/reader/spirv/function.cc
@@ -3986,6 +3986,20 @@
     const spvtools::opt::Instruction& inst) {
   const auto ext_opcode = inst.GetSingleWordInOperand(1);
 
+  if (ext_opcode == GLSLstd450Ldexp) {
+    // WGSL requires the second argument to be signed.
+    // Use a type constructor to convert it, which is the same as a bitcast.
+    // If the value would go from very large positive to negative, then the
+    // original result would have been infinity.  And since WGSL
+    // implementations may assume that infinities are not present, then we
+    // don't have to worry about that case.
+    auto e1 = MakeOperand(inst, 2);
+    auto e2 = ToSignedIfUnsigned(MakeOperand(inst, 3));
+
+    return {e1.type, builder_.Call(Source{}, "ldexp",
+                                   ast::ExpressionList{e1.expr, e2.expr})};
+  }
+
   auto* result_type = parser_impl_.ConvertType(inst.type_id());
 
   if (result_type->IsScalar()) {
diff --git a/src/reader/spirv/function_glsl_std_450_test.cc b/src/reader/spirv/function_glsl_std_450_test.cc
index 05ad700..2159ef0 100644
--- a/src/reader/spirv/function_glsl_std_450_test.cc
+++ b/src/reader/spirv/function_glsl_std_450_test.cc
@@ -158,8 +158,6 @@
     SpvParserTestBase<::testing::TestWithParam<GlslStd450Case>>;
 using SpvParserTest_GlslStd450_Floating_FloatingInting =
     SpvParserTestBase<::testing::TestWithParam<GlslStd450Case>>;
-using SpvParserTest_GlslStd450_Floating_FloatingUinting =
-    SpvParserTestBase<::testing::TestWithParam<GlslStd450Case>>;
 using SpvParserTest_GlslStd450_Float3_Float3Float3 =
     SpvParserTestBase<::testing::TestWithParam<GlslStd450Case>>;
 
@@ -503,73 +501,6 @@
       << body;
 }
 
-TEST_P(SpvParserTest_GlslStd450_Floating_FloatingUinting, Scalar) {
-  const auto assembly = Preamble() + R"(
-     %1 = OpExtInst %float %glsl )" +
-                        GetParam().opcode + R"( %f1 %u1
-     OpReturn
-     OpFunctionEnd
-  )";
-  auto p = parser(test::Assemble(assembly));
-  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
-  auto fe = p->function_emitter(100);
-  EXPECT_TRUE(fe.EmitBody()) << p->error();
-  const auto body = ToString(p->builder(), fe.ast_body());
-  EXPECT_THAT(body, HasSubstr(R"(
-  VariableConst{
-    x_1
-    none
-    undefined
-    __f32
-    {
-      Call[not set]{
-        Identifier[not set]{)" +
-                              GetParam().wgsl_func +
-                              R"(}
-        (
-          Identifier[not set]{f1}
-          Identifier[not set]{u1}
-        )
-      }
-    }
-  })"))
-      << body;
-}
-
-TEST_P(SpvParserTest_GlslStd450_Floating_FloatingUinting, Vector) {
-  const auto assembly = Preamble() + R"(
-     %1 = OpExtInst %v2float %glsl )" +
-                        GetParam().opcode +
-                        R"( %v2f1 %v2u1
-     OpReturn
-     OpFunctionEnd
-  )";
-  auto p = parser(test::Assemble(assembly));
-  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
-  auto fe = p->function_emitter(100);
-  EXPECT_TRUE(fe.EmitBody()) << p->error();
-  const auto body = ToString(p->builder(), fe.ast_body());
-  EXPECT_THAT(body, HasSubstr(R"(
-  VariableConst{
-    x_1
-    none
-    undefined
-    __vec_2__f32
-    {
-      Call[not set]{
-        Identifier[not set]{)" +
-                              GetParam().wgsl_func +
-                              R"(}
-        (
-          Identifier[not set]{v2f1}
-          Identifier[not set]{v2u1}
-        )
-      }
-    }
-  })"))
-      << body;
-}
-
 TEST_P(SpvParserTest_GlslStd450_Floating_FloatingInting, Scalar) {
   const auto assembly = Preamble() + R"(
      %1 = OpExtInst %float %glsl )" +
@@ -721,12 +652,9 @@
                          }));
 
 INSTANTIATE_TEST_SUITE_P(Samples,
-                         SpvParserTest_GlslStd450_Floating_FloatingUinting,
-                         ::testing::Values(GlslStd450Case{"Ldexp", "ldexp"}));
-
-INSTANTIATE_TEST_SUITE_P(Samples,
                          SpvParserTest_GlslStd450_Floating_FloatingInting,
                          ::testing::Values(GlslStd450Case{"Ldexp", "ldexp"}));
+// For ldexp with unsigned second argument, see below.
 
 INSTANTIATE_TEST_SUITE_P(Samples,
                          SpvParserTest_GlslStd450_Float3_Float3Float3,
@@ -2187,6 +2115,77 @@
   EXPECT_THAT(body, HasSubstr(expected)) << body;
 }
 
+// For ldexp with signed second argument, see above.
+TEST_F(SpvParserTest, GlslStd450_Ldexp_Scalar_Float_Uint) {
+  const auto assembly = Preamble() + R"(
+     %1 = OpExtInst %float %glsl Ldexp %f1 %u1
+     OpReturn
+     OpFunctionEnd
+  )";
+  auto p = parser(test::Assemble(assembly));
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
+  auto fe = p->function_emitter(100);
+  EXPECT_TRUE(fe.EmitBody()) << p->error();
+  const auto body = ToString(p->builder(), fe.ast_body());
+  const auto* expected = R"(VariableDeclStatement{
+  VariableConst{
+    x_1
+    none
+    undefined
+    __f32
+    {
+      Call[not set]{
+        Identifier[not set]{ldexp}
+        (
+          Identifier[not set]{f1}
+          TypeConstructor[not set]{
+            __i32
+            Identifier[not set]{u1}
+          }
+        )
+      }
+    }
+  }
+})";
+
+  EXPECT_THAT(body, HasSubstr(expected)) << body;
+}
+
+TEST_F(SpvParserTest, GlslStd450_Ldexp_Vector_Floatvec_Uintvec) {
+  const auto assembly = Preamble() + R"(
+     %1 = OpExtInst %v2float %glsl Ldexp %v2f1 %v2u1
+     OpReturn
+     OpFunctionEnd
+  )";
+  auto p = parser(test::Assemble(assembly));
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
+  auto fe = p->function_emitter(100);
+  EXPECT_TRUE(fe.EmitBody()) << p->error();
+  const auto body = ToString(p->builder(), fe.ast_body());
+  const auto* expected = R"(VariableDeclStatement{
+  VariableConst{
+    x_1
+    none
+    undefined
+    __vec_2__f32
+    {
+      Call[not set]{
+        Identifier[not set]{ldexp}
+        (
+          Identifier[not set]{v2f1}
+          TypeConstructor[not set]{
+            __vec_2__i32
+            Identifier[not set]{v2u1}
+          }
+        )
+      }
+    }
+  }
+})";
+
+  EXPECT_THAT(body, HasSubstr(expected)) << body;
+}
+
 }  // namespace
 }  // namespace spirv
 }  // namespace reader