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