diff --git a/src/reader/spirv/function.cc b/src/reader/spirv/function.cc index 26c47a1130..16217bb073 100644 --- a/src/reader/spirv/function.cc +++ b/src/reader/spirv/function.cc @@ -3444,6 +3444,17 @@ TypedExpression FunctionEmitter::MaybeEmitCombinatorialValue( TypedExpression FunctionEmitter::EmitGlslStd450ExtInst( const spvtools::opt::Instruction& inst) { const auto ext_opcode = inst.GetSingleWordInOperand(1); + + auto* result_type = parser_impl_.ConvertType(inst.type_id()); + + if ((ext_opcode == GLSLstd450Normalize) && result_type->IsScalar()) { + // WGSL does not have scalar form of the normalize builtin. + // The answer would be 1 anyway, so return that directly. + return {ty_.F32(), + create( + Source{}, create(Source{}, 1.0f))}; + } + const auto name = GetGlslStd450FuncName(ext_opcode); if (name.empty()) { Fail() << "unhandled GLSL.std.450 instruction " << ext_opcode; @@ -3462,9 +3473,8 @@ TypedExpression FunctionEmitter::EmitGlslStd450ExtInst( } operands.emplace_back(operand.expr); } - auto* ast_type = parser_impl_.ConvertType(inst.type_id()); auto* call = create(Source{}, func, std::move(operands)); - TypedExpression call_expr{ast_type, call}; + TypedExpression call_expr{result_type, call}; return parser_impl_.RectifyForcedResultType(call_expr, inst, first_operand_type); } diff --git a/src/reader/spirv/function_glsl_std_450_test.cc b/src/reader/spirv/function_glsl_std_450_test.cc index 3a44c0daa0..bb5756c099 100644 --- a/src/reader/spirv/function_glsl_std_450_test.cc +++ b/src/reader/spirv/function_glsl_std_450_test.cc @@ -681,7 +681,6 @@ INSTANTIATE_TEST_SUITE_P(Samples, {"InverseSqrt", "inverseSqrt"}, {"Log", "log"}, {"Log2", "log2"}, - {"Normalize", "normalize"}, {"Round", "round"}, {"RoundEven", "round"}, {"Sin", "sin"}, @@ -1080,6 +1079,121 @@ INSTANTIATE_TEST_SUITE_P(Samples, SpvParserTest_GlslStd450_Uinting_UintingUintingUinting, ::testing::Values(GlslStd450Case{"UClamp", "clamp"})); +// Test Normalize. WGSL does not have a scalar form of the normalize builtin. +// So we have to test it separately, as it does not fit the patterns tested +// above. + +TEST_F(SpvParserTest, Normalize_Scalar) { + // Scalar normalize always results in 1.0 + const auto assembly = Preamble() + R"( + %1 = OpExtInst %float %glsl Normalize %f1 + OpReturn + OpFunctionEnd + )"; + auto p = parser(test::Assemble(assembly)); + ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly; + 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 + __f32 + { + ScalarConstructor[not set]{1.000000} + } + })")) + << body; +} + +TEST_F(SpvParserTest, Normalize_Vector2) { + // Scalar normalize always results in 1.0 + const auto assembly = Preamble() + R"( + %1 = OpExtInst %v2float %glsl Normalize %v2f1 + OpReturn + OpFunctionEnd + )"; + auto p = parser(test::Assemble(assembly)); + ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly; + 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 + __vec_2__f32 + { + Call[not set]{ + Identifier[not set]{normalize} + ( + Identifier[not set]{v2f1} + ) + } + } + })")) + << body; +} + +TEST_F(SpvParserTest, Normalize_Vector3) { + // Scalar normalize always results in 1.0 + const auto assembly = Preamble() + R"( + %1 = OpExtInst %v3float %glsl Normalize %v3f1 + OpReturn + OpFunctionEnd + )"; + auto p = parser(test::Assemble(assembly)); + ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly; + 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 + __vec_3__f32 + { + Call[not set]{ + Identifier[not set]{normalize} + ( + Identifier[not set]{v3f1} + ) + } + } + })")) + << body; +} + +TEST_F(SpvParserTest, Normalize_Vector4) { + // Scalar normalize always results in 1.0 + const auto assembly = Preamble() + R"( + %1 = OpExtInst %v4float %glsl Normalize %v4f1 + OpReturn + OpFunctionEnd + )"; + auto p = parser(test::Assemble(assembly)); + ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly; + 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 + __vec_4__f32 + { + Call[not set]{ + Identifier[not set]{normalize} + ( + Identifier[not set]{v4f1} + ) + } + } + })")) + << body; +} + // Check that we convert signedness of operands and result type. // This is needed for each of the integer-based extended instructions.