diff --git a/src/tint/writer/spirv/builder_builtin_test.cc b/src/tint/writer/spirv/builder_builtin_test.cc index 4ed48268b8..829ee0dd8b 100644 --- a/src/tint/writer/spirv/builder_builtin_test.cc +++ b/src/tint/writer/spirv/builder_builtin_test.cc @@ -38,6 +38,102 @@ inline std::ostream& operator<<(std::ostream& out, BuiltinData data) { return out; } +// This tests that we do not push OpTypeSampledImage and float_0 type twice. +TEST_F(BuiltinBuilderTest, Call_TextureSampleCompare_Twice) { + auto* s = ty.sampler(ast::SamplerKind::kComparisonSampler); + auto* t = ty.depth_texture(ast::TextureDimension::k2d); + + auto* tex = GlobalVar("texture", t, + ast::AttributeList{ + create(0u), + create(0u), + }); + + auto* sampler = GlobalVar("sampler", s, + ast::AttributeList{ + create(1u), + create(0u), + }); + + auto* expr1 = Call("textureSampleCompare", "texture", "sampler", vec2(1_f, 2_f), 2_f); + auto* expr2 = Call("textureSampleCompare", "texture", "sampler", vec2(1_f, 2_f), 2_f); + + Func("f1", {}, ty.void_(), {CallStmt(expr1)}, {}); + Func("f2", {}, ty.void_(), {CallStmt(expr2)}, {}); + + spirv::Builder& b = Build(); + + b.push_function(Function{}); + + ASSERT_TRUE(b.GenerateGlobalVariable(tex)) << b.error(); + ASSERT_TRUE(b.GenerateGlobalVariable(sampler)) << b.error(); + + EXPECT_EQ(b.GenerateExpression(expr1), 8u) << b.error(); + EXPECT_EQ(b.GenerateExpression(expr2), 17u) << b.error(); + + EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeFloat 32 +%3 = OpTypeImage %4 2D 0 0 0 1 Unknown +%2 = OpTypePointer UniformConstant %3 +%1 = OpVariable %2 UniformConstant +%7 = OpTypeSampler +%6 = OpTypePointer UniformConstant %7 +%5 = OpVariable %6 UniformConstant +%11 = OpTypeSampledImage %3 +%13 = OpTypeVector %4 2 +%14 = OpConstant %4 1 +%15 = OpConstant %4 2 +%16 = OpConstantComposite %13 %14 %15 +)"); + + EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), + R"(%9 = OpLoad %7 %5 +%10 = OpLoad %3 %1 +%12 = OpSampledImage %11 %10 %9 +%8 = OpImageSampleDrefImplicitLod %4 %12 %16 %15 +%18 = OpLoad %7 %5 +%19 = OpLoad %3 %1 +%20 = OpSampledImage %11 %19 %18 +%17 = OpImageSampleDrefImplicitLod %4 %20 %16 %15 +)"); +} + +TEST_F(BuiltinBuilderTest, Call_GLSLMethod_WithLoad_f32) { + auto* var = GlobalVar("ident", ty.f32(), ast::StorageClass::kPrivate); + auto* expr = Call("round", "ident"); + auto* func = Func("a_func", {}, ty.void_(), + { + Assign(Phony(), expr), + }); + + spirv::Builder& b = Build(); + + ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error(); + ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); + + auto got = DumpBuilder(b); + auto expect = + R"(%10 = OpExtInstImport "GLSL.std.450" +OpName %1 "ident" +OpName %7 "a_func" +%3 = OpTypeFloat 32 +%2 = OpTypePointer Private %3 +%4 = OpConstantNull %3 +%1 = OpVariable %2 Private %4 +%6 = OpTypeVoid +%5 = OpTypeFunction %6 +%7 = OpFunction %6 None %5 +%8 = OpLabel +%11 = OpLoad %3 %1 +%9 = OpExtInst %3 %10 RoundEven %11 +OpReturn +OpFunctionEnd +)"; + EXPECT_EQ(got, expect); +} + +// Tests for Logical builtins +namespace logical_builtin_tests { + using BuiltinBoolTest = BuiltinBuilderTestWithParam; TEST_P(BuiltinBoolTest, Call_Bool_Scalar) { auto param = GetParam(); @@ -99,315 +195,6 @@ INSTANTIATE_TEST_SUITE_P(BuiltinBuilderTest, BuiltinBoolTest, testing::Values(BuiltinData{"any", "OpAny"}, BuiltinData{"all", "OpAll"})); -using BuiltinIntTest = BuiltinBuilderTestWithParam; -TEST_P(BuiltinIntTest, Call_SInt_Scalar) { - auto param = GetParam(); - auto* var = GlobalVar("v", ty.i32(), ast::StorageClass::kPrivate); - auto* expr = Call(param.name, "v"); - auto* func = Func("a_func", {}, ty.void_(), - { - Assign(Phony(), expr), - }); - - spirv::Builder& b = Build(); - - ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error(); - ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); - - EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeInt 32 1 -%2 = OpTypePointer Private %3 -%4 = OpConstantNull %3 -%1 = OpVariable %2 Private %4 -%6 = OpTypeVoid -%5 = OpTypeFunction %6 -)"); - - auto expected = utils::ReplaceAll(R"(%10 = OpLoad %3 %1 -%9 = ${op} %3 %10 -OpReturn -)", - "${op}", param.op); - EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), expected); -} - -TEST_P(BuiltinIntTest, Call_SInt_Vector) { - auto param = GetParam(); - auto* var = GlobalVar("v", ty.vec3(), ast::StorageClass::kPrivate); - auto* expr = Call(param.name, "v"); - auto* func = Func("a_func", {}, ty.void_(), - { - Assign(Phony(), expr), - }); - - spirv::Builder& b = Build(); - - ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error(); - ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); - - EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeInt 32 1 -%3 = OpTypeVector %4 3 -%2 = OpTypePointer Private %3 -%5 = OpConstantNull %3 -%1 = OpVariable %2 Private %5 -%7 = OpTypeVoid -%6 = OpTypeFunction %7 -)"); - - auto expected = utils::ReplaceAll(R"(%11 = OpLoad %3 %1 -%10 = ${op} %3 %11 -OpReturn -)", - "${op}", param.op); - EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), expected); -} - -TEST_P(BuiltinIntTest, Call_UInt_Scalar) { - auto param = GetParam(); - auto* var = GlobalVar("v", ty.u32(), ast::StorageClass::kPrivate); - auto* expr = Call(param.name, "v"); - auto* func = Func("a_func", {}, ty.void_(), - { - Assign(Phony(), expr), - }); - - spirv::Builder& b = Build(); - - ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error(); - ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); - - EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeInt 32 0 -%2 = OpTypePointer Private %3 -%4 = OpConstantNull %3 -%1 = OpVariable %2 Private %4 -%6 = OpTypeVoid -%5 = OpTypeFunction %6 -)"); - - auto expected = utils::ReplaceAll(R"(%10 = OpLoad %3 %1 -%9 = ${op} %3 %10 -OpReturn -)", - "${op}", param.op); - EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), expected); -} - -TEST_P(BuiltinIntTest, Call_UInt_Vector) { - auto param = GetParam(); - auto* var = GlobalVar("v", ty.vec3(), ast::StorageClass::kPrivate); - auto* expr = Call(param.name, "v"); - auto* func = Func("a_func", {}, ty.void_(), - { - Assign(Phony(), expr), - }); - - spirv::Builder& b = Build(); - - ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error(); - ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); - - EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeInt 32 0 -%3 = OpTypeVector %4 3 -%2 = OpTypePointer Private %3 -%5 = OpConstantNull %3 -%1 = OpVariable %2 Private %5 -%7 = OpTypeVoid -%6 = OpTypeFunction %7 -)"); - - auto expected = utils::ReplaceAll(R"(%11 = OpLoad %3 %1 -%10 = ${op} %3 %11 -OpReturn -)", - "${op}", param.op); - EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), expected); -} -INSTANTIATE_TEST_SUITE_P(BuiltinBuilderTest, - BuiltinIntTest, - testing::Values(BuiltinData{"countOneBits", "OpBitCount"}, - BuiltinData{"reverseBits", "OpBitReverse"})); - -TEST_F(BuiltinBuilderTest, Call_Dot_F32) { - auto* var = GlobalVar("v", ty.vec3(), ast::StorageClass::kPrivate); - auto* expr = Call("dot", "v", "v"); - auto* func = Func("a_func", {}, ty.void_(), - { - Assign(Phony(), expr), - }); - - spirv::Builder& b = Build(); - - ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error(); - ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); - - EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeFloat 32 -%3 = OpTypeVector %4 3 -%2 = OpTypePointer Private %3 -%5 = OpConstantNull %3 -%1 = OpVariable %2 Private %5 -%7 = OpTypeVoid -%6 = OpTypeFunction %7 -)"); - EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), - R"(%11 = OpLoad %3 %1 -%12 = OpLoad %3 %1 -%10 = OpDot %4 %11 %12 -OpReturn -)"); -} - -TEST_F(BuiltinBuilderTest, Call_Dot_U32) { - auto* var = GlobalVar("v", ty.vec3(), ast::StorageClass::kPrivate); - auto* expr = Call("dot", "v", "v"); - auto* func = Func("a_func", {}, ty.void_(), - { - Assign(Phony(), expr), - }); - - spirv::Builder& b = Build(); - - ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error(); - ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); - - EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeInt 32 0 -%3 = OpTypeVector %4 3 -%2 = OpTypePointer Private %3 -%5 = OpConstantNull %3 -%1 = OpVariable %2 Private %5 -%7 = OpTypeVoid -%6 = OpTypeFunction %7 -)"); - EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), - R"(%11 = OpLoad %3 %1 -%12 = OpLoad %3 %1 -%13 = OpCompositeExtract %4 %11 0 -%14 = OpCompositeExtract %4 %12 0 -%15 = OpIMul %4 %13 %14 -%16 = OpCompositeExtract %4 %11 1 -%17 = OpCompositeExtract %4 %12 1 -%18 = OpIMul %4 %16 %17 -%19 = OpIAdd %4 %15 %18 -%20 = OpCompositeExtract %4 %11 2 -%21 = OpCompositeExtract %4 %12 2 -%22 = OpIMul %4 %20 %21 -%10 = OpIAdd %4 %19 %22 -OpReturn -)"); -} - -TEST_F(BuiltinBuilderTest, Call_Dot_I32) { - auto* var = GlobalVar("v", ty.vec3(), ast::StorageClass::kPrivate); - auto* expr = Call("dot", "v", "v"); - auto* func = Func("a_func", {}, ty.void_(), - { - Assign(Phony(), expr), - }); - - spirv::Builder& b = Build(); - - ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error(); - ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); - - EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeInt 32 1 -%3 = OpTypeVector %4 3 -%2 = OpTypePointer Private %3 -%5 = OpConstantNull %3 -%1 = OpVariable %2 Private %5 -%7 = OpTypeVoid -%6 = OpTypeFunction %7 -)"); - EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), - R"(%11 = OpLoad %3 %1 -%12 = OpLoad %3 %1 -%13 = OpCompositeExtract %4 %11 0 -%14 = OpCompositeExtract %4 %12 0 -%15 = OpIMul %4 %13 %14 -%16 = OpCompositeExtract %4 %11 1 -%17 = OpCompositeExtract %4 %12 1 -%18 = OpIMul %4 %16 %17 -%19 = OpIAdd %4 %15 %18 -%20 = OpCompositeExtract %4 %11 2 -%21 = OpCompositeExtract %4 %12 2 -%22 = OpIMul %4 %20 %21 -%10 = OpIAdd %4 %19 %22 -OpReturn -)"); -} - -using BuiltinDeriveTest = BuiltinBuilderTestWithParam; -TEST_P(BuiltinDeriveTest, Call_Derivative_Scalar) { - auto param = GetParam(); - auto* var = GlobalVar("v", ty.f32(), ast::StorageClass::kPrivate); - auto* expr = Call(param.name, "v"); - auto* func = - Func("func", {}, ty.void_(), {CallStmt(expr)}, {Stage(ast::PipelineStage::kFragment)}); - - spirv::Builder& b = Build(); - - ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error(); - ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); - - EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 32 -%2 = OpTypePointer Private %3 -%4 = OpConstantNull %3 -%1 = OpVariable %2 Private %4 -%6 = OpTypeVoid -%5 = OpTypeFunction %6 -)"); - - auto expected = utils::ReplaceAll(R"(%10 = OpLoad %3 %1 -%9 = ${op} %3 %10 -OpReturn -)", - "${op}", param.op); - EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), expected); -} - -TEST_P(BuiltinDeriveTest, Call_Derivative_Vector) { - auto param = GetParam(); - auto* var = GlobalVar("v", ty.vec3(), ast::StorageClass::kPrivate); - auto* expr = Call(param.name, "v"); - auto* func = - Func("func", {}, ty.void_(), {CallStmt(expr)}, {Stage(ast::PipelineStage::kFragment)}); - - spirv::Builder& b = Build(); - - ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error(); - ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); - - if (param.name != "dpdx" && param.name != "dpdy" && param.name != "fwidth") { - EXPECT_EQ(DumpInstructions(b.capabilities()), - R"(OpCapability DerivativeControl -)"); - } - - EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeFloat 32 -%3 = OpTypeVector %4 3 -%2 = OpTypePointer Private %3 -%5 = OpConstantNull %3 -%1 = OpVariable %2 Private %5 -%7 = OpTypeVoid -%6 = OpTypeFunction %7 -)"); - - auto expected = utils::ReplaceAll(R"(%11 = OpLoad %3 %1 -%10 = ${op} %3 %11 -OpReturn -)", - "${op}", param.op); - EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), expected); -} -INSTANTIATE_TEST_SUITE_P(BuiltinBuilderTest, - BuiltinDeriveTest, - testing::Values(BuiltinData{"dpdx", "OpDPdx"}, - BuiltinData{"dpdxFine", "OpDPdxFine"}, - BuiltinData{"dpdxCoarse", "OpDPdxCoarse"}, - BuiltinData{"dpdy", "OpDPdy"}, - BuiltinData{"dpdyFine", "OpDPdyFine"}, - BuiltinData{"dpdyCoarse", "OpDPdyCoarse"}, - BuiltinData{"fwidth", "OpFwidth"}, - BuiltinData{"fwidthFine", "OpFwidthFine"}, - BuiltinData{"fwidthCoarse", "OpFwidthCoarse"})); - TEST_F(BuiltinBuilderTest, Call_Select) { auto* v3 = GlobalVar("v3", ty.vec3(), ast::StorageClass::kPrivate); @@ -446,1190 +233,10 @@ OpReturn )"); } -// This tests that we do not push OpTypeSampledImage and float_0 type twice. -TEST_F(BuiltinBuilderTest, Call_TextureSampleCompare_Twice) { - auto* s = ty.sampler(ast::SamplerKind::kComparisonSampler); - auto* t = ty.depth_texture(ast::TextureDimension::k2d); +} // namespace logical_builtin_tests - auto* tex = GlobalVar("texture", t, - ast::AttributeList{ - create(0u), - create(0u), - }); - - auto* sampler = GlobalVar("sampler", s, - ast::AttributeList{ - create(1u), - create(0u), - }); - - auto* expr1 = Call("textureSampleCompare", "texture", "sampler", vec2(1_f, 2_f), 2_f); - auto* expr2 = Call("textureSampleCompare", "texture", "sampler", vec2(1_f, 2_f), 2_f); - - Func("f1", {}, ty.void_(), {CallStmt(expr1)}, {}); - Func("f2", {}, ty.void_(), {CallStmt(expr2)}, {}); - - spirv::Builder& b = Build(); - - b.push_function(Function{}); - - ASSERT_TRUE(b.GenerateGlobalVariable(tex)) << b.error(); - ASSERT_TRUE(b.GenerateGlobalVariable(sampler)) << b.error(); - - EXPECT_EQ(b.GenerateExpression(expr1), 8u) << b.error(); - EXPECT_EQ(b.GenerateExpression(expr2), 17u) << b.error(); - - EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeFloat 32 -%3 = OpTypeImage %4 2D 0 0 0 1 Unknown -%2 = OpTypePointer UniformConstant %3 -%1 = OpVariable %2 UniformConstant -%7 = OpTypeSampler -%6 = OpTypePointer UniformConstant %7 -%5 = OpVariable %6 UniformConstant -%11 = OpTypeSampledImage %3 -%13 = OpTypeVector %4 2 -%14 = OpConstant %4 1 -%15 = OpConstant %4 2 -%16 = OpConstantComposite %13 %14 %15 -)"); - - EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), - R"(%9 = OpLoad %7 %5 -%10 = OpLoad %3 %1 -%12 = OpSampledImage %11 %10 %9 -%8 = OpImageSampleDrefImplicitLod %4 %12 %16 %15 -%18 = OpLoad %7 %5 -%19 = OpLoad %3 %1 -%20 = OpSampledImage %11 %19 %18 -%17 = OpImageSampleDrefImplicitLod %4 %20 %16 %15 -)"); -} - -TEST_F(BuiltinBuilderTest, Call_GLSLMethod_WithLoad) { - auto* var = GlobalVar("ident", ty.f32(), ast::StorageClass::kPrivate); - auto* expr = Call("round", "ident"); - auto* func = Func("a_func", {}, ty.void_(), - { - Assign(Phony(), expr), - }); - - spirv::Builder& b = Build(); - - ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error(); - ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); - - auto got = DumpBuilder(b); - auto expect = - R"(%10 = OpExtInstImport "GLSL.std.450" -OpName %1 "ident" -OpName %7 "a_func" -%3 = OpTypeFloat 32 -%2 = OpTypePointer Private %3 -%4 = OpConstantNull %3 -%1 = OpVariable %2 Private %4 -%6 = OpTypeVoid -%5 = OpTypeFunction %6 -%7 = OpFunction %6 None %5 -%8 = OpLabel -%11 = OpLoad %3 %1 -%9 = OpExtInst %3 %10 RoundEven %11 -OpReturn -OpFunctionEnd -)"; - EXPECT_EQ(got, expect); -} - -using Builtin_Builtin_SingleParam_Float_Test = BuiltinBuilderTestWithParam; -TEST_P(Builtin_Builtin_SingleParam_Float_Test, Call_Scalar) { - auto param = GetParam(); - auto* expr = Call(param.name, 1_f); - auto* func = Func("a_func", {}, ty.void_(), - { - Assign(Phony(), expr), - }); - - spirv::Builder& b = Build(); - - ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); - - auto got = DumpBuilder(b); - auto expect = R"(%7 = OpExtInstImport "GLSL.std.450" -OpName %3 "a_func" -%2 = OpTypeVoid -%1 = OpTypeFunction %2 -%6 = OpTypeFloat 32 -%8 = OpConstant %6 1 -%3 = OpFunction %2 None %1 -%4 = OpLabel -%5 = OpExtInst %6 %7 )" + - param.op + - R"( %8 -OpReturn -OpFunctionEnd -)"; - EXPECT_EQ(got, expect); -} - -TEST_P(Builtin_Builtin_SingleParam_Float_Test, Call_Vector) { - auto param = GetParam(); - auto* expr = Call(param.name, vec2(1_f, 1_f)); - auto* func = Func("a_func", {}, ty.void_(), - { - Assign(Phony(), expr), - }); - - spirv::Builder& b = Build(); - - ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); - - auto got = DumpBuilder(b); - auto expect = R"(%8 = OpExtInstImport "GLSL.std.450" -OpName %3 "a_func" -%2 = OpTypeVoid -%1 = OpTypeFunction %2 -%7 = OpTypeFloat 32 -%6 = OpTypeVector %7 2 -%9 = OpConstant %7 1 -%10 = OpConstantComposite %6 %9 %9 -%3 = OpFunction %2 None %1 -%4 = OpLabel -%5 = OpExtInst %6 %8 )" + - param.op + - R"( %10 -OpReturn -OpFunctionEnd -)"; - EXPECT_EQ(got, expect); -} -INSTANTIATE_TEST_SUITE_P(BuiltinBuilderTest, - Builtin_Builtin_SingleParam_Float_Test, - testing::Values(BuiltinData{"abs", "FAbs"}, - BuiltinData{"acos", "Acos"}, - BuiltinData{"asin", "Asin"}, - BuiltinData{"atan", "Atan"}, - BuiltinData{"ceil", "Ceil"}, - BuiltinData{"cos", "Cos"}, - BuiltinData{"cosh", "Cosh"}, - BuiltinData{"degrees", "Degrees"}, - BuiltinData{"exp", "Exp"}, - BuiltinData{"exp2", "Exp2"}, - BuiltinData{"floor", "Floor"}, - BuiltinData{"fract", "Fract"}, - BuiltinData{"inverseSqrt", "InverseSqrt"}, - BuiltinData{"log", "Log"}, - BuiltinData{"log2", "Log2"}, - BuiltinData{"radians", "Radians"}, - BuiltinData{"round", "RoundEven"}, - BuiltinData{"sign", "FSign"}, - BuiltinData{"sin", "Sin"}, - BuiltinData{"sinh", "Sinh"}, - BuiltinData{"sqrt", "Sqrt"}, - BuiltinData{"tan", "Tan"}, - BuiltinData{"tanh", "Tanh"}, - BuiltinData{"trunc", "Trunc"})); - -TEST_F(BuiltinBuilderTest, Call_Length_Scalar) { - auto* expr = Call("length", 1_f); - auto* func = Func("a_func", {}, ty.void_(), - { - Assign(Phony(), expr), - }); - - spirv::Builder& b = Build(); - - ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); - - auto got = DumpBuilder(b); - auto expect = R"(%7 = OpExtInstImport "GLSL.std.450" -OpName %3 "a_func" -%2 = OpTypeVoid -%1 = OpTypeFunction %2 -%6 = OpTypeFloat 32 -%8 = OpConstant %6 1 -%3 = OpFunction %2 None %1 -%4 = OpLabel -%5 = OpExtInst %6 %7 Length %8 -OpReturn -OpFunctionEnd -)"; - EXPECT_EQ(got, expect); -} - -TEST_F(BuiltinBuilderTest, Call_Length_Vector) { - auto* expr = Call("length", vec2(1_f, 1_f)); - auto* func = Func("a_func", {}, ty.void_(), - { - Assign(Phony(), expr), - }); - - spirv::Builder& b = Build(); - - ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); - - auto got = DumpBuilder(b); - auto expect = R"(%7 = OpExtInstImport "GLSL.std.450" -OpName %3 "a_func" -%2 = OpTypeVoid -%1 = OpTypeFunction %2 -%6 = OpTypeFloat 32 -%8 = OpTypeVector %6 2 -%9 = OpConstant %6 1 -%10 = OpConstantComposite %8 %9 %9 -%3 = OpFunction %2 None %1 -%4 = OpLabel -%5 = OpExtInst %6 %7 Length %10 -OpReturn -OpFunctionEnd -)"; - EXPECT_EQ(got, expect); -} - -TEST_F(BuiltinBuilderTest, Call_Normalize) { - auto* expr = Call("normalize", vec2(1_f, 1_f)); - auto* func = Func("a_func", {}, ty.void_(), - { - Assign(Phony(), expr), - }); - - spirv::Builder& b = Build(); - - ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); - - auto got = DumpBuilder(b); - auto expect = R"(%8 = OpExtInstImport "GLSL.std.450" -OpName %3 "a_func" -%2 = OpTypeVoid -%1 = OpTypeFunction %2 -%7 = OpTypeFloat 32 -%6 = OpTypeVector %7 2 -%9 = OpConstant %7 1 -%10 = OpConstantComposite %6 %9 %9 -%3 = OpFunction %2 None %1 -%4 = OpLabel -%5 = OpExtInst %6 %8 Normalize %10 -OpReturn -OpFunctionEnd -)"; - EXPECT_EQ(got, expect); -} - -using Builtin_Builtin_DualParam_Float_Test = BuiltinBuilderTestWithParam; -TEST_P(Builtin_Builtin_DualParam_Float_Test, Call_Scalar) { - auto param = GetParam(); - auto* scalar = Var("scalar", nullptr, Expr(1_f)); - auto* expr = Call(param.name, scalar, scalar); - auto* func = Func("a_func", {}, ty.void_(), - { - Decl(scalar), - Assign(Phony(), expr), - }); - - spirv::Builder& b = Build(); - - ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); - - auto got = DumpBuilder(b); - auto expect = R"(%11 = OpExtInstImport "GLSL.std.450" -OpName %3 "a_func" -OpName %7 "scalar" -%2 = OpTypeVoid -%1 = OpTypeFunction %2 -%5 = OpTypeFloat 32 -%6 = OpConstant %5 1 -%8 = OpTypePointer Function %5 -%9 = OpConstantNull %5 -%3 = OpFunction %2 None %1 -%4 = OpLabel -%7 = OpVariable %8 Function %9 -OpStore %7 %6 -%12 = OpLoad %5 %7 -%13 = OpLoad %5 %7 -%10 = OpExtInst %5 %11 )" + - param.op + - R"( %12 %13 -OpReturn -OpFunctionEnd -)"; - EXPECT_EQ(got, expect); -} - -TEST_P(Builtin_Builtin_DualParam_Float_Test, Call_Vector) { - auto param = GetParam(); - auto* vec = Var("vec", nullptr, vec2(1_f, 1_f)); - auto* expr = Call(param.name, vec, vec); - auto* func = Func("a_func", {}, ty.void_(), - { - Decl(vec), - Assign(Phony(), expr), - }); - - spirv::Builder& b = Build(); - - ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); - - auto got = DumpBuilder(b); - auto expect = R"(%13 = OpExtInstImport "GLSL.std.450" -OpName %3 "a_func" -OpName %9 "vec" -%2 = OpTypeVoid -%1 = OpTypeFunction %2 -%6 = OpTypeFloat 32 -%5 = OpTypeVector %6 2 -%7 = OpConstant %6 1 -%8 = OpConstantComposite %5 %7 %7 -%10 = OpTypePointer Function %5 -%11 = OpConstantNull %5 -%3 = OpFunction %2 None %1 -%4 = OpLabel -%9 = OpVariable %10 Function %11 -OpStore %9 %8 -%14 = OpLoad %5 %9 -%15 = OpLoad %5 %9 -%12 = OpExtInst %5 %13 )" + - param.op + - R"( %14 %15 -OpReturn -OpFunctionEnd -)"; - EXPECT_EQ(got, expect); -} -INSTANTIATE_TEST_SUITE_P(BuiltinBuilderTest, - Builtin_Builtin_DualParam_Float_Test, - testing::Values(BuiltinData{"atan2", "Atan2"}, - BuiltinData{"max", "NMax"}, - BuiltinData{"min", "NMin"}, - BuiltinData{"pow", "Pow"}, - BuiltinData{"step", "Step"})); - -TEST_F(BuiltinBuilderTest, Call_Reflect_Vector) { - auto* expr = Call("reflect", vec2(1_f, 1_f), vec2(1_f, 1_f)); - auto* func = Func("a_func", {}, ty.void_(), - { - Assign(Phony(), expr), - }); - - spirv::Builder& b = Build(); - - ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); - - auto got = DumpBuilder(b); - auto expect = R"(%8 = OpExtInstImport "GLSL.std.450" -OpName %3 "a_func" -%2 = OpTypeVoid -%1 = OpTypeFunction %2 -%7 = OpTypeFloat 32 -%6 = OpTypeVector %7 2 -%9 = OpConstant %7 1 -%10 = OpConstantComposite %6 %9 %9 -%3 = OpFunction %2 None %1 -%4 = OpLabel -%5 = OpExtInst %6 %8 Reflect %10 %10 -OpReturn -OpFunctionEnd -)"; - EXPECT_EQ(got, expect); -} - -TEST_F(BuiltinBuilderTest, Call_Distance_Scalar) { - auto* expr = Call("distance", 1_f, 1_f); - auto* func = Func("a_func", {}, ty.void_(), - { - Assign(Phony(), expr), - }); - - spirv::Builder& b = Build(); - - ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); - - auto got = DumpBuilder(b); - auto expect = R"(%7 = OpExtInstImport "GLSL.std.450" -OpName %3 "a_func" -%2 = OpTypeVoid -%1 = OpTypeFunction %2 -%6 = OpTypeFloat 32 -%8 = OpConstant %6 1 -%3 = OpFunction %2 None %1 -%4 = OpLabel -%5 = OpExtInst %6 %7 Distance %8 %8 -OpReturn -OpFunctionEnd -)"; - EXPECT_EQ(got, expect); -} - -TEST_F(BuiltinBuilderTest, Call_Distance_Vector) { - auto* expr = Call("distance", vec2(1_f, 1_f), vec2(1_f, 1_f)); - auto* func = Func("a_func", {}, ty.void_(), - { - Assign(Phony(), expr), - }); - - spirv::Builder& b = Build(); - - ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); - - auto got = DumpBuilder(b); - auto expect = R"(%7 = OpExtInstImport "GLSL.std.450" -OpName %3 "a_func" -%2 = OpTypeVoid -%1 = OpTypeFunction %2 -%6 = OpTypeFloat 32 -%8 = OpTypeVector %6 2 -%9 = OpConstant %6 1 -%10 = OpConstantComposite %8 %9 %9 -%3 = OpFunction %2 None %1 -%4 = OpLabel -%5 = OpExtInst %6 %7 Distance %10 %10 -OpReturn -OpFunctionEnd -)"; - EXPECT_EQ(got, expect); -} - -TEST_F(BuiltinBuilderTest, Call_Cross) { - auto* expr = Call("cross", vec3(1_f, 1_f, 1_f), vec3(1_f, 1_f, 1_f)); - auto* func = Func("a_func", {}, ty.void_(), - { - Assign(Phony(), expr), - }); - - spirv::Builder& b = Build(); - - ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); - - auto got = DumpBuilder(b); - auto expect = R"(%8 = OpExtInstImport "GLSL.std.450" -OpName %3 "a_func" -%2 = OpTypeVoid -%1 = OpTypeFunction %2 -%7 = OpTypeFloat 32 -%6 = OpTypeVector %7 3 -%9 = OpConstant %7 1 -%10 = OpConstantComposite %6 %9 %9 %9 -%3 = OpFunction %2 None %1 -%4 = OpLabel -%5 = OpExtInst %6 %8 Cross %10 %10 -OpReturn -OpFunctionEnd -)"; - EXPECT_EQ(got, expect); -} - -using Builtin_Builtin_ThreeParam_Float_Test = BuiltinBuilderTestWithParam; -TEST_P(Builtin_Builtin_ThreeParam_Float_Test, Call_Scalar) { - auto param = GetParam(); - auto* decl = Decl(Var("a", nullptr, Expr(1_f))); - auto* expr = Call(param.name, Expr("a"), 1_f, 1_f); - auto* func = Func("a_func", {}, ty.void_(), - { - decl, - Assign(Phony(), expr), - }); - - spirv::Builder& b = Build(); - - ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); - - auto got = DumpBuilder(b); - auto expect = R"(%11 = OpExtInstImport "GLSL.std.450" -OpName %3 "a_func" -OpName %7 "a" -%2 = OpTypeVoid -%1 = OpTypeFunction %2 -%5 = OpTypeFloat 32 -%6 = OpConstant %5 1 -%8 = OpTypePointer Function %5 -%9 = OpConstantNull %5 -%3 = OpFunction %2 None %1 -%4 = OpLabel -%7 = OpVariable %8 Function %9 -OpStore %7 %6 -%12 = OpLoad %5 %7 -%10 = OpExtInst %5 %11 )" + - param.op + - R"( %12 %6 %6 -OpReturn -OpFunctionEnd -)"; - EXPECT_EQ(got, expect); -} - -TEST_P(Builtin_Builtin_ThreeParam_Float_Test, Call_Vector) { - auto param = GetParam(); - auto* decl = Decl(Var("a", nullptr, vec2(1_f, 1_f))); - auto* expr = Call(param.name, Expr("a"), vec2(1_f, 1_f), vec2(1_f, 1_f)); - auto* func = Func("a_func", {}, ty.void_(), - { - decl, - Assign(Phony(), expr), - }); - - spirv::Builder& b = Build(); - - ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); - - auto got = DumpBuilder(b); - auto expect = R"(%13 = OpExtInstImport "GLSL.std.450" -OpName %3 "a_func" -OpName %9 "a" -%2 = OpTypeVoid -%1 = OpTypeFunction %2 -%6 = OpTypeFloat 32 -%5 = OpTypeVector %6 2 -%7 = OpConstant %6 1 -%8 = OpConstantComposite %5 %7 %7 -%10 = OpTypePointer Function %5 -%11 = OpConstantNull %5 -%3 = OpFunction %2 None %1 -%4 = OpLabel -%9 = OpVariable %10 Function %11 -OpStore %9 %8 -%14 = OpLoad %5 %9 -%12 = OpExtInst %5 %13 )" + - param.op + - R"( %14 %8 %8 -OpReturn -OpFunctionEnd -)"; - EXPECT_EQ(got, expect); -} -INSTANTIATE_TEST_SUITE_P(BuiltinBuilderTest, - Builtin_Builtin_ThreeParam_Float_Test, - testing::Values(BuiltinData{"clamp", "NClamp"}, - BuiltinData{"fma", "Fma"}, - BuiltinData{"mix", "FMix"}, - - BuiltinData{"smoothstep", "SmoothStep"})); - -TEST_F(BuiltinBuilderTest, Call_FaceForward_Vector) { - auto* expr = Call("faceForward", vec2(1_f, 1_f), vec2(1_f, 1_f), vec2(1_f, 1_f)); - auto* func = Func("a_func", {}, ty.void_(), - { - Assign(Phony(), expr), - }); - - spirv::Builder& b = Build(); - - ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); - - auto got = DumpBuilder(b); - auto expect = R"(%8 = OpExtInstImport "GLSL.std.450" -OpName %3 "a_func" -%2 = OpTypeVoid -%1 = OpTypeFunction %2 -%7 = OpTypeFloat 32 -%6 = OpTypeVector %7 2 -%9 = OpConstant %7 1 -%10 = OpConstantComposite %6 %9 %9 -%3 = OpFunction %2 None %1 -%4 = OpLabel -%5 = OpExtInst %6 %8 FaceForward %10 %10 %10 -OpReturn -OpFunctionEnd -)"; - EXPECT_EQ(got, expect); -} - -using Builtin_Builtin_SingleParam_Sint_Test = BuiltinBuilderTestWithParam; -TEST_P(Builtin_Builtin_SingleParam_Sint_Test, Call_Scalar) { - auto param = GetParam(); - auto* expr = Call(param.name, 1_i); - auto* func = Func("a_func", {}, ty.void_(), - { - Assign(Phony(), expr), - }); - - spirv::Builder& b = Build(); - - ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); - - auto got = DumpBuilder(b); - auto expect = R"(%7 = OpExtInstImport "GLSL.std.450" -OpName %3 "a_func" -%2 = OpTypeVoid -%1 = OpTypeFunction %2 -%6 = OpTypeInt 32 1 -%8 = OpConstant %6 1 -%3 = OpFunction %2 None %1 -%4 = OpLabel -%5 = OpExtInst %6 %7 )" + - param.op + - R"( %8 -OpReturn -OpFunctionEnd -)"; - EXPECT_EQ(got, expect); -} - -TEST_P(Builtin_Builtin_SingleParam_Sint_Test, Call_Vector) { - auto param = GetParam(); - auto* expr = Call(param.name, vec2(1_i, 1_i)); - auto* func = Func("a_func", {}, ty.void_(), - { - Assign(Phony(), expr), - }); - - spirv::Builder& b = Build(); - - ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); - - auto got = DumpBuilder(b); - auto expect = R"(%8 = OpExtInstImport "GLSL.std.450" -OpName %3 "a_func" -%2 = OpTypeVoid -%1 = OpTypeFunction %2 -%7 = OpTypeInt 32 1 -%6 = OpTypeVector %7 2 -%9 = OpConstant %7 1 -%10 = OpConstantComposite %6 %9 %9 -%3 = OpFunction %2 None %1 -%4 = OpLabel -%5 = OpExtInst %6 %8 )" + - param.op + - R"( %10 -OpReturn -OpFunctionEnd -)"; - EXPECT_EQ(got, expect); -} -INSTANTIATE_TEST_SUITE_P(BuiltinBuilderTest, - Builtin_Builtin_SingleParam_Sint_Test, - testing::Values(BuiltinData{"abs", "SAbs"})); - -// Calling abs() on an unsigned integer scalar / vector is a no-op. -using Builtin_Builtin_Abs_Uint_Test = BuiltinBuilderTest; -TEST_F(Builtin_Builtin_Abs_Uint_Test, Call_Scalar) { - auto* expr = Call("abs", Expr(1_u)); - auto* func = Func("a_func", {}, ty.void_(), - { - Assign(Phony(), expr), - }); - - spirv::Builder& b = Build(); - - ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); - - auto got = DumpBuilder(b); - auto expect = R"(OpName %3 "a_func" -%2 = OpTypeVoid -%1 = OpTypeFunction %2 -%6 = OpTypeInt 32 0 -%7 = OpConstant %6 1 -%3 = OpFunction %2 None %1 -%4 = OpLabel -OpReturn -OpFunctionEnd -)"; - EXPECT_EQ(got, expect); -} - -TEST_F(Builtin_Builtin_Abs_Uint_Test, Call_Vector) { - auto* expr = Call("abs", vec2(1_u, 1_u)); - auto* func = Func("a_func", {}, ty.void_(), - { - Assign(Phony(), expr), - }); - - spirv::Builder& b = Build(); - - ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); - - auto got = DumpBuilder(b); - auto expect = R"(OpName %3 "a_func" -%2 = OpTypeVoid -%1 = OpTypeFunction %2 -%7 = OpTypeInt 32 0 -%6 = OpTypeVector %7 2 -%8 = OpConstant %7 1 -%9 = OpConstantComposite %6 %8 %8 -%3 = OpFunction %2 None %1 -%4 = OpLabel -OpReturn -OpFunctionEnd -)"; - EXPECT_EQ(got, expect); -} - -using Builtin_Builtin_DualParam_SInt_Test = BuiltinBuilderTestWithParam; -TEST_P(Builtin_Builtin_DualParam_SInt_Test, Call_Scalar) { - auto param = GetParam(); - auto* scalar = Var("scalar", nullptr, Expr(1_i)); - auto* expr = Call(param.name, scalar, scalar); - auto* func = Func("a_func", {}, ty.void_(), - { - Decl(scalar), - Assign(Phony(), expr), - }); - - spirv::Builder& b = Build(); - - ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); - - auto got = DumpBuilder(b); - auto expect = R"(%11 = OpExtInstImport "GLSL.std.450" -OpName %3 "a_func" -OpName %7 "scalar" -%2 = OpTypeVoid -%1 = OpTypeFunction %2 -%5 = OpTypeInt 32 1 -%6 = OpConstant %5 1 -%8 = OpTypePointer Function %5 -%9 = OpConstantNull %5 -%3 = OpFunction %2 None %1 -%4 = OpLabel -%7 = OpVariable %8 Function %9 -OpStore %7 %6 -%12 = OpLoad %5 %7 -%13 = OpLoad %5 %7 -%10 = OpExtInst %5 %11 )" + - param.op + - R"( %12 %13 -OpReturn -OpFunctionEnd -)"; - EXPECT_EQ(got, expect); -} - -TEST_P(Builtin_Builtin_DualParam_SInt_Test, Call_Vector) { - auto param = GetParam(); - auto* vec = Var("vec", nullptr, vec2(1_i, 1_i)); - auto* expr = Call(param.name, vec, vec); - auto* func = Func("a_func", {}, ty.void_(), - { - Decl(vec), - Assign(Phony(), expr), - }); - - spirv::Builder& b = Build(); - - ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); - - auto got = DumpBuilder(b); - auto expect = R"(%13 = OpExtInstImport "GLSL.std.450" -OpName %3 "a_func" -OpName %9 "vec" -%2 = OpTypeVoid -%1 = OpTypeFunction %2 -%6 = OpTypeInt 32 1 -%5 = OpTypeVector %6 2 -%7 = OpConstant %6 1 -%8 = OpConstantComposite %5 %7 %7 -%10 = OpTypePointer Function %5 -%11 = OpConstantNull %5 -%3 = OpFunction %2 None %1 -%4 = OpLabel -%9 = OpVariable %10 Function %11 -OpStore %9 %8 -%14 = OpLoad %5 %9 -%15 = OpLoad %5 %9 -%12 = OpExtInst %5 %13 )" + - param.op + - R"( %14 %15 -OpReturn -OpFunctionEnd -)"; - EXPECT_EQ(got, expect); -} -INSTANTIATE_TEST_SUITE_P(BuiltinBuilderTest, - Builtin_Builtin_DualParam_SInt_Test, - testing::Values(BuiltinData{"max", "SMax"}, BuiltinData{"min", "SMin"})); - -using Builtin_Builtin_DualParam_UInt_Test = BuiltinBuilderTestWithParam; -TEST_P(Builtin_Builtin_DualParam_UInt_Test, Call_Scalar) { - auto param = GetParam(); - auto* scalar = Var("scalar", nullptr, Expr(1_u)); - auto* expr = Call(param.name, scalar, scalar); - auto* func = Func("a_func", {}, ty.void_(), - { - Decl(scalar), - Assign(Phony(), expr), - }); - - spirv::Builder& b = Build(); - - ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); - - auto got = DumpBuilder(b); - auto expect = R"(%11 = OpExtInstImport "GLSL.std.450" -OpName %3 "a_func" -OpName %7 "scalar" -%2 = OpTypeVoid -%1 = OpTypeFunction %2 -%5 = OpTypeInt 32 0 -%6 = OpConstant %5 1 -%8 = OpTypePointer Function %5 -%9 = OpConstantNull %5 -%3 = OpFunction %2 None %1 -%4 = OpLabel -%7 = OpVariable %8 Function %9 -OpStore %7 %6 -%12 = OpLoad %5 %7 -%13 = OpLoad %5 %7 -%10 = OpExtInst %5 %11 )" + - param.op + - R"( %12 %13 -OpReturn -OpFunctionEnd -)"; - EXPECT_EQ(got, expect); -} - -TEST_P(Builtin_Builtin_DualParam_UInt_Test, Call_Vector) { - auto param = GetParam(); - auto* vec = Var("vec", nullptr, vec2(1_u, 1_u)); - auto* expr = Call(param.name, vec, vec); - auto* func = Func("a_func", {}, ty.void_(), - { - Decl(vec), - Assign(Phony(), expr), - }); - - spirv::Builder& b = Build(); - - ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); - - auto got = DumpBuilder(b); - auto expect = R"(%13 = OpExtInstImport "GLSL.std.450" -OpName %3 "a_func" -OpName %9 "vec" -%2 = OpTypeVoid -%1 = OpTypeFunction %2 -%6 = OpTypeInt 32 0 -%5 = OpTypeVector %6 2 -%7 = OpConstant %6 1 -%8 = OpConstantComposite %5 %7 %7 -%10 = OpTypePointer Function %5 -%11 = OpConstantNull %5 -%3 = OpFunction %2 None %1 -%4 = OpLabel -%9 = OpVariable %10 Function %11 -OpStore %9 %8 -%14 = OpLoad %5 %9 -%15 = OpLoad %5 %9 -%12 = OpExtInst %5 %13 )" + - param.op + - R"( %14 %15 -OpReturn -OpFunctionEnd -)"; - EXPECT_EQ(got, expect); -} -INSTANTIATE_TEST_SUITE_P(BuiltinBuilderTest, - Builtin_Builtin_DualParam_UInt_Test, - testing::Values(BuiltinData{"max", "UMax"}, BuiltinData{"min", "UMin"})); - -using Builtin_Builtin_ThreeParam_Sint_Test = BuiltinBuilderTestWithParam; -TEST_P(Builtin_Builtin_ThreeParam_Sint_Test, Call_Scalar) { - auto param = GetParam(); - auto* decl = Decl(Var("a", nullptr, Expr(1_i))); - auto* expr = Call(param.name, Expr("a"), 1_i, 1_i); - auto* func = Func("a_func", {}, ty.void_(), - { - decl, - Assign(Phony(), expr), - }); - - spirv::Builder& b = Build(); - - ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); - - auto got = DumpBuilder(b); - auto expect = R"(%11 = OpExtInstImport "GLSL.std.450" -OpName %3 "a_func" -OpName %7 "a" -%2 = OpTypeVoid -%1 = OpTypeFunction %2 -%5 = OpTypeInt 32 1 -%6 = OpConstant %5 1 -%8 = OpTypePointer Function %5 -%9 = OpConstantNull %5 -%3 = OpFunction %2 None %1 -%4 = OpLabel -%7 = OpVariable %8 Function %9 -OpStore %7 %6 -%12 = OpLoad %5 %7 -%10 = OpExtInst %5 %11 )" + - param.op + - R"( %12 %6 %6 -OpReturn -OpFunctionEnd -)"; - EXPECT_EQ(got, expect); -} - -TEST_P(Builtin_Builtin_ThreeParam_Sint_Test, Call_Vector) { - auto param = GetParam(); - auto* decl = Decl(Var("a", nullptr, vec2(1_i, 1_i))); - auto* expr = Call(param.name, Expr("a"), vec2(1_i, 1_i), vec2(1_i, 1_i)); - auto* func = Func("a_func", {}, ty.void_(), - { - decl, - Assign(Phony(), expr), - }); - - spirv::Builder& b = Build(); - - ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); - - auto got = DumpBuilder(b); - auto expect = R"(%13 = OpExtInstImport "GLSL.std.450" -OpName %3 "a_func" -OpName %9 "a" -%2 = OpTypeVoid -%1 = OpTypeFunction %2 -%6 = OpTypeInt 32 1 -%5 = OpTypeVector %6 2 -%7 = OpConstant %6 1 -%8 = OpConstantComposite %5 %7 %7 -%10 = OpTypePointer Function %5 -%11 = OpConstantNull %5 -%3 = OpFunction %2 None %1 -%4 = OpLabel -%9 = OpVariable %10 Function %11 -OpStore %9 %8 -%14 = OpLoad %5 %9 -%12 = OpExtInst %5 %13 )" + - param.op + - R"( %14 %8 %8 -OpReturn -OpFunctionEnd -)"; - EXPECT_EQ(got, expect); -} -INSTANTIATE_TEST_SUITE_P(BuiltinBuilderTest, - Builtin_Builtin_ThreeParam_Sint_Test, - testing::Values(BuiltinData{"clamp", "SClamp"})); - -using Builtin_Builtin_ThreeParam_Uint_Test = BuiltinBuilderTestWithParam; -TEST_P(Builtin_Builtin_ThreeParam_Uint_Test, Call_Scalar) { - auto param = GetParam(); - auto* decl = Decl(Var("a", nullptr, Expr(1_u))); - auto* expr = Call(param.name, Expr("a"), 1_u, 1_u); - auto* func = Func("a_func", {}, ty.void_(), - { - decl, - Assign(Phony(), expr), - }); - - spirv::Builder& b = Build(); - - ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); - - auto got = DumpBuilder(b); - auto expect = R"(%11 = OpExtInstImport "GLSL.std.450" -OpName %3 "a_func" -OpName %7 "a" -%2 = OpTypeVoid -%1 = OpTypeFunction %2 -%5 = OpTypeInt 32 0 -%6 = OpConstant %5 1 -%8 = OpTypePointer Function %5 -%9 = OpConstantNull %5 -%3 = OpFunction %2 None %1 -%4 = OpLabel -%7 = OpVariable %8 Function %9 -OpStore %7 %6 -%12 = OpLoad %5 %7 -%10 = OpExtInst %5 %11 )" + - param.op + - R"( %12 %6 %6 -OpReturn -OpFunctionEnd -)"; - EXPECT_EQ(got, expect); -} - -TEST_P(Builtin_Builtin_ThreeParam_Uint_Test, Call_Vector) { - auto param = GetParam(); - auto* decl = Decl(Var("a", nullptr, vec2(1_u, 1_u))); - auto* expr = Call(param.name, Expr("a"), vec2(1_u, 1_u), vec2(1_u, 1_u)); - auto* func = Func("a_func", {}, ty.void_(), - { - decl, - Assign(Phony(), expr), - }); - - spirv::Builder& b = Build(); - - ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); - - auto got = DumpBuilder(b); - auto expect = R"(%13 = OpExtInstImport "GLSL.std.450" -OpName %3 "a_func" -OpName %9 "a" -%2 = OpTypeVoid -%1 = OpTypeFunction %2 -%6 = OpTypeInt 32 0 -%5 = OpTypeVector %6 2 -%7 = OpConstant %6 1 -%8 = OpConstantComposite %5 %7 %7 -%10 = OpTypePointer Function %5 -%11 = OpConstantNull %5 -%3 = OpFunction %2 None %1 -%4 = OpLabel -%9 = OpVariable %10 Function %11 -OpStore %9 %8 -%14 = OpLoad %5 %9 -%12 = OpExtInst %5 %13 )" + - param.op + - R"( %14 %8 %8 -OpReturn -OpFunctionEnd -)"; - EXPECT_EQ(got, expect); -} -INSTANTIATE_TEST_SUITE_P(BuiltinBuilderTest, - Builtin_Builtin_ThreeParam_Uint_Test, - testing::Values(BuiltinData{"clamp", "UClamp"})); - -TEST_F(BuiltinBuilderTest, Call_Modf) { - auto* expr = Call("modf", vec2(1_f, 2_f)); - Func("a_func", {}, ty.void_(), {CallStmt(expr)}, {Stage(ast::PipelineStage::kFragment)}); - - spirv::Builder& b = Build(); - - ASSERT_TRUE(b.Build()) << b.error(); - auto got = DumpBuilder(b); - auto* expect = R"(OpCapability Shader -%9 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %3 "a_func" -OpExecutionMode %3 OriginUpperLeft -OpName %3 "a_func" -OpName %6 "__modf_result_vec2" -OpMemberName %6 0 "fract" -OpMemberName %6 1 "whole" -OpMemberDecorate %6 0 Offset 0 -OpMemberDecorate %6 1 Offset 8 -%2 = OpTypeVoid -%1 = OpTypeFunction %2 -%8 = OpTypeFloat 32 -%7 = OpTypeVector %8 2 -%6 = OpTypeStruct %7 %7 -%10 = OpConstant %8 1 -%11 = OpConstant %8 2 -%12 = OpConstantComposite %7 %10 %11 -%3 = OpFunction %2 None %1 -%4 = OpLabel -%5 = OpExtInst %6 %9 ModfStruct %12 -OpReturn -OpFunctionEnd -)"; - EXPECT_EQ(expect, got); - - Validate(b); -} - -TEST_F(BuiltinBuilderTest, Call_Frexp) { - auto* expr = Call("frexp", vec2(1_f, 2_f)); - Func("a_func", {}, ty.void_(), {CallStmt(expr)}, {Stage(ast::PipelineStage::kFragment)}); - - spirv::Builder& b = Build(); - - ASSERT_TRUE(b.Build()) << b.error(); - auto got = DumpBuilder(b); - auto* expect = R"(OpCapability Shader -%11 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %3 "a_func" -OpExecutionMode %3 OriginUpperLeft -OpName %3 "a_func" -OpName %6 "__frexp_result_vec2" -OpMemberName %6 0 "sig" -OpMemberName %6 1 "exp" -OpMemberDecorate %6 0 Offset 0 -OpMemberDecorate %6 1 Offset 8 -%2 = OpTypeVoid -%1 = OpTypeFunction %2 -%8 = OpTypeFloat 32 -%7 = OpTypeVector %8 2 -%10 = OpTypeInt 32 1 -%9 = OpTypeVector %10 2 -%6 = OpTypeStruct %7 %9 -%12 = OpConstant %8 1 -%13 = OpConstant %8 2 -%14 = OpConstantComposite %7 %12 %13 -%3 = OpFunction %2 None %1 -%4 = OpLabel -%5 = OpExtInst %6 %11 FrexpStruct %14 -OpReturn -OpFunctionEnd -)"; - EXPECT_EQ(expect, got); - - Validate(b); -} - -TEST_F(BuiltinBuilderTest, Call_Determinant) { - auto* var = GlobalVar("var", ty.mat3x3(), ast::StorageClass::kPrivate); - auto* expr = Call("determinant", "var"); - auto* func = Func("a_func", {}, ty.void_(), - { - Assign(Phony(), expr), - }); - - spirv::Builder& b = Build(); - - ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error(); - ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); - - auto got = DumpBuilder(b); - auto expect = R"(%12 = OpExtInstImport "GLSL.std.450" -OpName %1 "var" -OpName %9 "a_func" -%5 = OpTypeFloat 32 -%4 = OpTypeVector %5 3 -%3 = OpTypeMatrix %4 3 -%2 = OpTypePointer Private %3 -%6 = OpConstantNull %3 -%1 = OpVariable %2 Private %6 -%8 = OpTypeVoid -%7 = OpTypeFunction %8 -%9 = OpFunction %8 None %7 -%10 = OpLabel -%13 = OpLoad %3 %1 -%11 = OpExtInst %5 %12 Determinant %13 -OpReturn -OpFunctionEnd -)"; - EXPECT_EQ(got, expect); -} - -TEST_F(BuiltinBuilderTest, Call_Transpose) { - auto* var = GlobalVar("var", ty.mat2x3(), ast::StorageClass::kPrivate); - auto* expr = Call("transpose", "var"); - auto* func = Func("a_func", {}, ty.void_(), - { - Assign(Phony(), expr), - }); - - spirv::Builder& b = Build(); - - ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error(); - ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); - - auto got = DumpBuilder(b); - auto expect = R"(OpName %1 "var" -OpName %9 "a_func" -%5 = OpTypeFloat 32 -%4 = OpTypeVector %5 3 -%3 = OpTypeMatrix %4 2 -%2 = OpTypePointer Private %3 -%6 = OpConstantNull %3 -%1 = OpVariable %2 Private %6 -%8 = OpTypeVoid -%7 = OpTypeFunction %8 -%13 = OpTypeVector %5 2 -%12 = OpTypeMatrix %13 3 -%9 = OpFunction %8 None %7 -%10 = OpLabel -%14 = OpLoad %3 %1 -%11 = OpTranspose %12 %14 -OpReturn -OpFunctionEnd -)"; - EXPECT_EQ(got, expect); -} +// Tests for Array builtins +namespace array_builtin_tests { TEST_F(BuiltinBuilderTest, Call_ArrayLength) { auto* s = Structure("my_struct", {Member("a", ty.array(4))}); @@ -1833,635 +440,1316 @@ OpReturn Validate(b); } -TEST_F(BuiltinBuilderTest, Call_AtomicLoad) { - // struct S { - // u : atomic; - // i : atomic; - // } - // - // @binding(1) @group(2) var b : S; - // - // fn a_func() { - // let u : u32 = atomicLoad(&b.u); - // let i : i32 = atomicLoad(&b.i); - // } - auto* s = Structure("S", { - Member("u", ty.atomic()), - Member("i", ty.atomic()), - }); - GlobalVar("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite, - ast::AttributeList{ - create(1u), - create(2u), - }); +} // namespace array_builtin_tests - Func("a_func", {}, ty.void_(), - ast::StatementList{ - Decl(Let("u", ty.u32(), Call("atomicLoad", AddressOf(MemberAccessor("b", "u"))))), - Decl(Let("i", ty.i32(), Call("atomicLoad", AddressOf(MemberAccessor("b", "i"))))), - }, - ast::AttributeList{Stage(ast::PipelineStage::kFragment)}); +// Tests for Numeric builtins with float parameter +namespace float_builtin_tests { - spirv::Builder& b = SanitizeAndBuild(); - - ASSERT_TRUE(b.Build()) << b.error(); - - ASSERT_EQ(b.functions().size(), 1_u); - - auto* expected_types = R"(%4 = OpTypeInt 32 0 -%5 = OpTypeInt 32 1 -%3 = OpTypeStruct %4 %5 -%2 = OpTypePointer StorageBuffer %3 -%1 = OpVariable %2 StorageBuffer -%7 = OpTypeVoid -%6 = OpTypeFunction %7 -%11 = OpConstant %4 1 -%12 = OpConstant %4 0 -%14 = OpTypePointer StorageBuffer %4 -%18 = OpTypePointer StorageBuffer %5 -)"; - auto got_types = DumpInstructions(b.types()); - EXPECT_EQ(expected_types, got_types); - - auto* expected_instructions = R"(%15 = OpAccessChain %14 %1 %12 -%10 = OpAtomicLoad %4 %15 %11 %12 -%19 = OpAccessChain %18 %1 %11 -%16 = OpAtomicLoad %5 %19 %11 %12 -OpReturn -)"; - auto got_instructions = DumpInstructions(b.functions()[0].instructions()); - EXPECT_EQ(expected_instructions, got_instructions); - - Validate(b); -} - -TEST_F(BuiltinBuilderTest, Call_AtomicStore) { - // struct S { - // u : atomic; - // i : atomic; - // } - // - // @binding(1) @group(2) var b : S; - // - // fn a_func() { - // var u = 1_u; - // var i = 2; - // atomicStore(&b.u, u); - // atomicStore(&b.i, i); - // } - auto* s = Structure("S", { - Member("u", ty.atomic()), - Member("i", ty.atomic()), - }); - GlobalVar("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite, - ast::AttributeList{ - create(1u), - create(2u), - }); - - Func("a_func", {}, ty.void_(), - ast::StatementList{ - Decl(Var("u", nullptr, Expr(1_u))), - Decl(Var("i", nullptr, Expr(2_i))), - CallStmt(Call("atomicStore", AddressOf(MemberAccessor("b", "u")), "u")), - CallStmt(Call("atomicStore", AddressOf(MemberAccessor("b", "i")), "i")), - }, - ast::AttributeList{Stage(ast::PipelineStage::kFragment)}); - - spirv::Builder& b = SanitizeAndBuild(); - - ASSERT_TRUE(b.Build()) << b.error(); - - ASSERT_EQ(b.functions().size(), 1_u); - - auto* expected_types = R"(%4 = OpTypeInt 32 0 -%5 = OpTypeInt 32 1 -%3 = OpTypeStruct %4 %5 -%2 = OpTypePointer StorageBuffer %3 -%1 = OpVariable %2 StorageBuffer -%7 = OpTypeVoid -%6 = OpTypeFunction %7 -%10 = OpConstant %4 1 -%12 = OpTypePointer Function %4 -%13 = OpConstantNull %4 -%14 = OpConstant %5 2 -%16 = OpTypePointer Function %5 -%17 = OpConstantNull %5 -%19 = OpConstant %4 0 -%21 = OpTypePointer StorageBuffer %4 -%26 = OpTypePointer StorageBuffer %5 -)"; - auto got_types = DumpInstructions(b.types()); - EXPECT_EQ(expected_types, got_types); - - auto* expected_instructions = R"(OpStore %11 %10 -OpStore %15 %14 -%22 = OpAccessChain %21 %1 %19 -%23 = OpLoad %4 %11 -OpAtomicStore %22 %10 %19 %23 -%27 = OpAccessChain %26 %1 %10 -%28 = OpLoad %5 %15 -OpAtomicStore %27 %10 %19 %28 -OpReturn -)"; - auto got_instructions = DumpInstructions(b.functions()[0].instructions()); - EXPECT_EQ(expected_instructions, got_instructions); - - Validate(b); -} - -using Builtin_Builtin_AtomicRMW_i32 = BuiltinBuilderTestWithParam; -TEST_P(Builtin_Builtin_AtomicRMW_i32, Test) { - // struct S { - // v : atomic; - // } - // - // @binding(1) @group(2) var b : S; - // - // fn a_func() { - // var v = 10; - // let x : i32 = atomicOP(&b.v, v); - // } - auto* s = Structure("S", { - Member("v", ty.atomic()), - }); - GlobalVar("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite, - ast::AttributeList{ - create(1u), - create(2u), - }); - - Func("a_func", {}, ty.void_(), - ast::StatementList{ - Decl(Var("v", nullptr, Expr(10_i))), - Decl(Let("x", ty.i32(), - Call(GetParam().name, AddressOf(MemberAccessor("b", "v")), "v"))), - }, - ast::AttributeList{Stage(ast::PipelineStage::kFragment)}); - - spirv::Builder& b = SanitizeAndBuild(); - - ASSERT_TRUE(b.Build()) << b.error(); - - ASSERT_EQ(b.functions().size(), 1_u); - - std::string expected_types = R"(%4 = OpTypeInt 32 1 -%3 = OpTypeStruct %4 -%2 = OpTypePointer StorageBuffer %3 -%1 = OpVariable %2 StorageBuffer -%6 = OpTypeVoid -%5 = OpTypeFunction %6 -%9 = OpConstant %4 10 -%11 = OpTypePointer Function %4 -%12 = OpConstantNull %4 -%14 = OpTypeInt 32 0 -%15 = OpConstant %14 1 -%16 = OpConstant %14 0 -%18 = OpTypePointer StorageBuffer %4 -)"; - auto got_types = DumpInstructions(b.types()); - EXPECT_EQ(expected_types, got_types); - - std::string expected_instructions = R"(OpStore %10 %9 -%19 = OpAccessChain %18 %1 %16 -%20 = OpLoad %4 %10 -)"; - expected_instructions += "%13 = " + GetParam().op + " %4 %19 %15 %16 %20\n"; - expected_instructions += "OpReturn\n"; - - auto got_instructions = DumpInstructions(b.functions()[0].instructions()); - EXPECT_EQ(expected_instructions, got_instructions); - - Validate(b); -} -INSTANTIATE_TEST_SUITE_P(BuiltinBuilderTest, - Builtin_Builtin_AtomicRMW_i32, - testing::Values(BuiltinData{"atomicAdd", "OpAtomicIAdd"}, - BuiltinData{"atomicMax", "OpAtomicSMax"}, - BuiltinData{"atomicMin", "OpAtomicSMin"}, - BuiltinData{"atomicAnd", "OpAtomicAnd"}, - BuiltinData{"atomicOr", "OpAtomicOr"}, - BuiltinData{"atomicXor", "OpAtomicXor"})); - -using Builtin_Builtin_AtomicRMW_u32 = BuiltinBuilderTestWithParam; -TEST_P(Builtin_Builtin_AtomicRMW_u32, Test) { - // struct S { - // v : atomic; - // } - // - // @binding(1) @group(2) var b : S; - // - // fn a_func() { - // var v = 10u; - // let x : u32 = atomicOP(&b.v, v); - // } - auto* s = Structure("S", { - Member("v", ty.atomic()), - }); - GlobalVar("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite, - ast::AttributeList{ - create(1u), - create(2u), - }); - - Func("a_func", {}, ty.void_(), - ast::StatementList{ - Decl(Var("v", nullptr, Expr(10_u))), - Decl(Let("x", ty.u32(), - Call(GetParam().name, AddressOf(MemberAccessor("b", "v")), "v"))), - }, - ast::AttributeList{Stage(ast::PipelineStage::kFragment)}); - - spirv::Builder& b = SanitizeAndBuild(); - - ASSERT_TRUE(b.Build()) << b.error(); - - ASSERT_EQ(b.functions().size(), 1_u); - - std::string expected_types = R"(%4 = OpTypeInt 32 0 -%3 = OpTypeStruct %4 -%2 = OpTypePointer StorageBuffer %3 -%1 = OpVariable %2 StorageBuffer -%6 = OpTypeVoid -%5 = OpTypeFunction %6 -%9 = OpConstant %4 10 -%11 = OpTypePointer Function %4 -%12 = OpConstantNull %4 -%14 = OpConstant %4 1 -%15 = OpConstant %4 0 -%17 = OpTypePointer StorageBuffer %4 -)"; - auto got_types = DumpInstructions(b.types()); - EXPECT_EQ(expected_types, got_types); - - std::string expected_instructions = R"(OpStore %10 %9 -%18 = OpAccessChain %17 %1 %15 -%19 = OpLoad %4 %10 -)"; - expected_instructions += "%13 = " + GetParam().op + " %4 %18 %14 %15 %19\n"; - expected_instructions += "OpReturn\n"; - - auto got_instructions = DumpInstructions(b.functions()[0].instructions()); - EXPECT_EQ(expected_instructions, got_instructions); - - Validate(b); -} -INSTANTIATE_TEST_SUITE_P(BuiltinBuilderTest, - Builtin_Builtin_AtomicRMW_u32, - testing::Values(BuiltinData{"atomicAdd", "OpAtomicIAdd"}, - BuiltinData{"atomicMax", "OpAtomicUMax"}, - BuiltinData{"atomicMin", "OpAtomicUMin"}, - BuiltinData{"atomicAnd", "OpAtomicAnd"}, - BuiltinData{"atomicOr", "OpAtomicOr"}, - BuiltinData{"atomicXor", "OpAtomicXor"})); - -TEST_F(BuiltinBuilderTest, Call_AtomicExchange) { - // struct S { - // u : atomic; - // i : atomic; - // } - // - // @binding(1) @group(2) var b : S; - // - // fn a_func() { - // var u = 10u; - // var i = 10i; - // let r : u32 = atomicExchange(&b.u, u); - // let s : i32 = atomicExchange(&b.i, i); - // } - auto* s = Structure("S", { - Member("u", ty.atomic()), - Member("i", ty.atomic()), - }); - GlobalVar("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite, - ast::AttributeList{ - create(1u), - create(2u), - }); - - Func("a_func", {}, ty.void_(), - ast::StatementList{ - Decl(Var("u", nullptr, Expr(10_u))), - Decl(Var("i", nullptr, Expr(10_i))), - Decl(Let("r", ty.u32(), - Call("atomicExchange", AddressOf(MemberAccessor("b", "u")), "u"))), - Decl(Let("s", ty.i32(), - Call("atomicExchange", AddressOf(MemberAccessor("b", "i")), "i"))), - }, - ast::AttributeList{Stage(ast::PipelineStage::kFragment)}); - - spirv::Builder& b = SanitizeAndBuild(); - - ASSERT_TRUE(b.Build()) << b.error(); - - ASSERT_EQ(b.functions().size(), 1_u); - - auto* expected_types = R"(%4 = OpTypeInt 32 0 -%5 = OpTypeInt 32 1 -%3 = OpTypeStruct %4 %5 -%2 = OpTypePointer StorageBuffer %3 -%1 = OpVariable %2 StorageBuffer -%7 = OpTypeVoid -%6 = OpTypeFunction %7 -%10 = OpConstant %4 10 -%12 = OpTypePointer Function %4 -%13 = OpConstantNull %4 -%14 = OpConstant %5 10 -%16 = OpTypePointer Function %5 -%17 = OpConstantNull %5 -%19 = OpConstant %4 1 -%20 = OpConstant %4 0 -%22 = OpTypePointer StorageBuffer %4 -%27 = OpTypePointer StorageBuffer %5 -)"; - auto got_types = DumpInstructions(b.types()); - EXPECT_EQ(expected_types, got_types); - - auto* expected_instructions = R"(OpStore %11 %10 -OpStore %15 %14 -%23 = OpAccessChain %22 %1 %20 -%24 = OpLoad %4 %11 -%18 = OpAtomicExchange %4 %23 %19 %20 %24 -%28 = OpAccessChain %27 %1 %19 -%29 = OpLoad %5 %15 -%25 = OpAtomicExchange %5 %28 %19 %20 %29 -OpReturn -)"; - auto got_instructions = DumpInstructions(b.functions()[0].instructions()); - EXPECT_EQ(expected_instructions, got_instructions); - - Validate(b); -} - -TEST_F(BuiltinBuilderTest, Call_AtomicCompareExchangeWeak) { - // struct S { - // u : atomic, - // i : atomic, - // } - // - // @binding(1) @group(2) var b : S; - // - // fn a_func() { - // let u = atomicCompareExchangeWeak(&b.u, 10u, 20u); - // let i = atomicCompareExchangeWeak(&b.i, 10, 10); - // } - auto* s = Structure("S", { - Member("u", ty.atomic()), - Member("i", ty.atomic()), - }); - GlobalVar("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite, - ast::AttributeList{ - create(1u), - create(2u), - }); - - Func("a_func", {}, ty.void_(), - ast::StatementList{ - Decl(Let("u", nullptr, - Call("atomicCompareExchangeWeak", AddressOf(MemberAccessor("b", "u")), 10_u, - 20_u))), - Decl(Let("i", nullptr, - Call("atomicCompareExchangeWeak", AddressOf(MemberAccessor("b", "i")), 10_i, - 20_i))), - }, - ast::AttributeList{Stage(ast::PipelineStage::kFragment)}); - - spirv::Builder& b = SanitizeAndBuild(); - - ASSERT_TRUE(b.Build()) << b.error(); - - ASSERT_EQ(b.functions().size(), 1_u); - - auto* expected_types = R"(%4 = OpTypeInt 32 0 -%5 = OpTypeInt 32 1 -%3 = OpTypeStruct %4 %5 -%2 = OpTypePointer StorageBuffer %3 -%1 = OpVariable %2 StorageBuffer -%7 = OpTypeVoid -%6 = OpTypeFunction %7 -%12 = OpTypeBool -%11 = OpTypeStruct %4 %12 -%13 = OpConstant %4 1 -%14 = OpConstant %4 0 -%16 = OpTypePointer StorageBuffer %4 -%18 = OpConstant %4 20 -%19 = OpConstant %4 10 -%23 = OpTypeStruct %5 %12 -%25 = OpTypePointer StorageBuffer %5 -%27 = OpConstant %5 20 -%28 = OpConstant %5 10 -)"; - auto got_types = DumpInstructions(b.types()); - EXPECT_EQ(expected_types, got_types); - - auto* expected_instructions = R"(%17 = OpAccessChain %16 %1 %14 -%20 = OpAtomicCompareExchange %4 %17 %13 %14 %14 %18 %19 -%21 = OpIEqual %12 %20 %18 -%10 = OpCompositeConstruct %11 %20 %21 -%26 = OpAccessChain %25 %1 %13 -%29 = OpAtomicCompareExchange %5 %26 %13 %14 %14 %27 %28 -%30 = OpIEqual %12 %29 %27 -%22 = OpCompositeConstruct %23 %29 %30 -OpReturn -)"; - auto got_instructions = DumpInstructions(b.functions()[0].instructions()); - EXPECT_EQ(expected_instructions, got_instructions); - - Validate(b); -} - -using Builtin_Builtin_DataPacking_Test = BuiltinBuilderTestWithParam; -TEST_P(Builtin_Builtin_DataPacking_Test, Binary) { +using Builtin_Builder_SingleParam_Float_Test = BuiltinBuilderTestWithParam; +TEST_P(Builtin_Builder_SingleParam_Float_Test, Call_Scalar_f32) { auto param = GetParam(); - - bool pack4 = param.name == "pack4x8snorm" || param.name == "pack4x8unorm"; - auto* call = pack4 ? Call(param.name, vec4(1_f, 1_f, 1_f, 1_f)) - : Call(param.name, vec2(1_f, 1_f)); - auto* func = Func("a_func", {}, ty.void_(), {CallStmt(call)}); + // Use a variable to prevent the function being evaluated as constant. + auto* scalar = Var("a", nullptr, Expr(1_f)); + auto* expr = Call(param.name, scalar); + auto* func = Func("a_func", {}, ty.void_(), + { + Decl(scalar), + Assign(Phony(), expr), + }); spirv::Builder& b = Build(); ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); - if (pack4) { - auto got = DumpBuilder(b); - auto expect = R"(%7 = OpExtInstImport "GLSL.std.450" + auto got = DumpBuilder(b); + auto expect = R"(%11 = OpExtInstImport "GLSL.std.450" OpName %3 "a_func" +OpName %7 "a" %2 = OpTypeVoid %1 = OpTypeFunction %2 -%6 = OpTypeInt 32 0 -%9 = OpTypeFloat 32 -%8 = OpTypeVector %9 4 -%10 = OpConstant %9 1 -%11 = OpConstantComposite %8 %10 %10 %10 %10 +%5 = OpTypeFloat 32 +%6 = OpConstant %5 1 +%8 = OpTypePointer Function %5 +%9 = OpConstantNull %5 %3 = OpFunction %2 None %1 %4 = OpLabel -%5 = OpExtInst %6 %7 )" + - param.op + - R"( %11 +%7 = OpVariable %8 Function %9 +OpStore %7 %6 +%12 = OpLoad %5 %7 +%10 = OpExtInst %5 %11 )" + + param.op + + R"( %12 OpReturn OpFunctionEnd )"; - EXPECT_EQ(got, expect); - } else { - auto got = DumpBuilder(b); - auto expect = R"(%7 = OpExtInstImport "GLSL.std.450" -OpName %3 "a_func" -%2 = OpTypeVoid -%1 = OpTypeFunction %2 -%6 = OpTypeInt 32 0 -%9 = OpTypeFloat 32 -%8 = OpTypeVector %9 2 -%10 = OpConstant %9 1 -%11 = OpConstantComposite %8 %10 %10 -%3 = OpFunction %2 None %1 -%4 = OpLabel -%5 = OpExtInst %6 %7 )" + - param.op + - R"( %11 -OpReturn -OpFunctionEnd -)"; - EXPECT_EQ(got, expect); - } + EXPECT_EQ(got, expect); } -INSTANTIATE_TEST_SUITE_P(BuiltinBuilderTest, - Builtin_Builtin_DataPacking_Test, - testing::Values(BuiltinData{"pack4x8snorm", "PackSnorm4x8"}, - BuiltinData{"pack4x8unorm", "PackUnorm4x8"}, - BuiltinData{"pack2x16snorm", "PackSnorm2x16"}, - BuiltinData{"pack2x16unorm", "PackUnorm2x16"}, - BuiltinData{"pack2x16float", "PackHalf2x16"})); - -using Builtin_Builtin_DataUnpacking_Test = BuiltinBuilderTestWithParam; -TEST_P(Builtin_Builtin_DataUnpacking_Test, Binary) { +TEST_P(Builtin_Builder_SingleParam_Float_Test, Call_Vector_f32) { auto param = GetParam(); - bool pack4 = param.name == "unpack4x8snorm" || param.name == "unpack4x8unorm"; - auto* func = Func("a_func", {}, ty.void_(), {CallStmt(Call(param.name, 1_u))}); + // Use a variable to prevent the function being evaluated as constant. + auto* vec = Var("a", nullptr, vec2(1_f, 1_f)); + auto* expr = Call(param.name, vec); + auto* func = Func("a_func", {}, ty.void_(), + { + Decl(vec), + Assign(Phony(), expr), + }); spirv::Builder& b = Build(); ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); - if (pack4) { - auto got = DumpBuilder(b); - auto expect = R"(%8 = OpExtInstImport "GLSL.std.450" + auto got = DumpBuilder(b); + auto expect = R"(%13 = OpExtInstImport "GLSL.std.450" OpName %3 "a_func" +OpName %9 "a" %2 = OpTypeVoid %1 = OpTypeFunction %2 -%7 = OpTypeFloat 32 -%6 = OpTypeVector %7 4 -%9 = OpTypeInt 32 0 -%10 = OpConstant %9 1 +%6 = OpTypeFloat 32 +%5 = OpTypeVector %6 2 +%7 = OpConstant %6 1 +%8 = OpConstantComposite %5 %7 %7 +%10 = OpTypePointer Function %5 +%11 = OpConstantNull %5 %3 = OpFunction %2 None %1 %4 = OpLabel -%5 = OpExtInst %6 %8 )" + - param.op + - R"( %10 +%9 = OpVariable %10 Function %11 +OpStore %9 %8 +%14 = OpLoad %5 %9 +%12 = OpExtInst %5 %13 )" + + param.op + + R"( %14 OpReturn OpFunctionEnd )"; - EXPECT_EQ(got, expect); - } else { - auto got = DumpBuilder(b); - auto expect = R"(%8 = OpExtInstImport "GLSL.std.450" -OpName %3 "a_func" -%2 = OpTypeVoid -%1 = OpTypeFunction %2 -%7 = OpTypeFloat 32 -%6 = OpTypeVector %7 2 -%9 = OpTypeInt 32 0 -%10 = OpConstant %9 1 -%3 = OpFunction %2 None %1 -%4 = OpLabel -%5 = OpExtInst %6 %8 )" + - param.op + - R"( %10 -OpReturn -OpFunctionEnd -)"; - EXPECT_EQ(got, expect); - } + EXPECT_EQ(got, expect); } INSTANTIATE_TEST_SUITE_P(BuiltinBuilderTest, - Builtin_Builtin_DataUnpacking_Test, - testing::Values(BuiltinData{"unpack4x8snorm", "UnpackSnorm4x8"}, - BuiltinData{"unpack4x8unorm", "UnpackUnorm4x8"}, - BuiltinData{"unpack2x16snorm", "UnpackSnorm2x16"}, - BuiltinData{"unpack2x16unorm", "UnpackUnorm2x16"}, - BuiltinData{"unpack2x16float", "UnpackHalf2x16"})); + Builtin_Builder_SingleParam_Float_Test, + testing::Values(BuiltinData{"abs", "FAbs"}, + BuiltinData{"acos", "Acos"}, + BuiltinData{"asin", "Asin"}, + BuiltinData{"atan", "Atan"}, + BuiltinData{"ceil", "Ceil"}, + BuiltinData{"cos", "Cos"}, + BuiltinData{"cosh", "Cosh"}, + BuiltinData{"degrees", "Degrees"}, + BuiltinData{"exp", "Exp"}, + BuiltinData{"exp2", "Exp2"}, + BuiltinData{"floor", "Floor"}, + BuiltinData{"fract", "Fract"}, + BuiltinData{"inverseSqrt", "InverseSqrt"}, + BuiltinData{"log", "Log"}, + BuiltinData{"log2", "Log2"}, + BuiltinData{"radians", "Radians"}, + BuiltinData{"round", "RoundEven"}, + BuiltinData{"sign", "FSign"}, + BuiltinData{"sin", "Sin"}, + BuiltinData{"sinh", "Sinh"}, + BuiltinData{"sqrt", "Sqrt"}, + BuiltinData{"tan", "Tan"}, + BuiltinData{"tanh", "Tanh"}, + BuiltinData{"trunc", "Trunc"})); -TEST_F(BuiltinBuilderTest, Call_WorkgroupBarrier) { - Func("f", {}, ty.void_(), - ast::StatementList{ - CallStmt(Call("workgroupBarrier")), - }, - ast::AttributeList{ - Stage(ast::PipelineStage::kCompute), - WorkgroupSize(1_i), - }); +TEST_F(BuiltinBuilderTest, Call_Length_Scalar_f32) { + auto* scalar = Var("a", nullptr, Expr(1_f)); + auto* expr = Call("length", scalar); + auto* func = Func("a_func", {}, ty.void_(), + { + Decl(scalar), + Assign(Phony(), expr), + }); + + spirv::Builder& b = Build(); + + ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); + + auto got = DumpBuilder(b); + auto expect = R"(%11 = OpExtInstImport "GLSL.std.450" +OpName %3 "a_func" +OpName %7 "a" +%2 = OpTypeVoid +%1 = OpTypeFunction %2 +%5 = OpTypeFloat 32 +%6 = OpConstant %5 1 +%8 = OpTypePointer Function %5 +%9 = OpConstantNull %5 +%3 = OpFunction %2 None %1 +%4 = OpLabel +%7 = OpVariable %8 Function %9 +OpStore %7 %6 +%12 = OpLoad %5 %7 +%10 = OpExtInst %5 %11 Length %12 +OpReturn +OpFunctionEnd +)"; + EXPECT_EQ(got, expect); +} + +TEST_F(BuiltinBuilderTest, Call_Length_Vector_f32) { + auto* vec = Var("a", nullptr, vec2(1_f, 1_f)); + auto* expr = Call("length", vec); + auto* func = Func("a_func", {}, ty.void_(), + { + Decl(vec), + Assign(Phony(), expr), + }); + + spirv::Builder& b = Build(); + + ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); + + auto got = DumpBuilder(b); + auto expect = R"(%13 = OpExtInstImport "GLSL.std.450" +OpName %3 "a_func" +OpName %9 "a" +%2 = OpTypeVoid +%1 = OpTypeFunction %2 +%6 = OpTypeFloat 32 +%5 = OpTypeVector %6 2 +%7 = OpConstant %6 1 +%8 = OpConstantComposite %5 %7 %7 +%10 = OpTypePointer Function %5 +%11 = OpConstantNull %5 +%3 = OpFunction %2 None %1 +%4 = OpLabel +%9 = OpVariable %10 Function %11 +OpStore %9 %8 +%14 = OpLoad %5 %9 +%12 = OpExtInst %6 %13 Length %14 +OpReturn +OpFunctionEnd +)"; + EXPECT_EQ(got, expect); +} + +TEST_F(BuiltinBuilderTest, Call_Normalize_f32) { + auto* vec = Var("a", nullptr, vec2(1_f, 1_f)); + auto* expr = Call("normalize", vec); + auto* func = Func("a_func", {}, ty.void_(), + { + Decl(vec), + Assign(Phony(), expr), + }); + + spirv::Builder& b = Build(); + + ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); + + auto got = DumpBuilder(b); + auto expect = R"(%13 = OpExtInstImport "GLSL.std.450" +OpName %3 "a_func" +OpName %9 "a" +%2 = OpTypeVoid +%1 = OpTypeFunction %2 +%6 = OpTypeFloat 32 +%5 = OpTypeVector %6 2 +%7 = OpConstant %6 1 +%8 = OpConstantComposite %5 %7 %7 +%10 = OpTypePointer Function %5 +%11 = OpConstantNull %5 +%3 = OpFunction %2 None %1 +%4 = OpLabel +%9 = OpVariable %10 Function %11 +OpStore %9 %8 +%14 = OpLoad %5 %9 +%12 = OpExtInst %5 %13 Normalize %14 +OpReturn +OpFunctionEnd +)"; + EXPECT_EQ(got, expect); +} + +using Builtin_Builder_DualParam_Float_Test = BuiltinBuilderTestWithParam; +TEST_P(Builtin_Builder_DualParam_Float_Test, Call_Scalar_f32) { + auto param = GetParam(); + auto* scalar = Var("scalar", nullptr, Expr(1_f)); + auto* expr = Call(param.name, scalar, scalar); + auto* func = Func("a_func", {}, ty.void_(), + { + Decl(scalar), + Assign(Phony(), expr), + }); + + spirv::Builder& b = Build(); + + ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); + + auto got = DumpBuilder(b); + auto expect = R"(%11 = OpExtInstImport "GLSL.std.450" +OpName %3 "a_func" +OpName %7 "scalar" +%2 = OpTypeVoid +%1 = OpTypeFunction %2 +%5 = OpTypeFloat 32 +%6 = OpConstant %5 1 +%8 = OpTypePointer Function %5 +%9 = OpConstantNull %5 +%3 = OpFunction %2 None %1 +%4 = OpLabel +%7 = OpVariable %8 Function %9 +OpStore %7 %6 +%12 = OpLoad %5 %7 +%13 = OpLoad %5 %7 +%10 = OpExtInst %5 %11 )" + + param.op + + R"( %12 %13 +OpReturn +OpFunctionEnd +)"; + EXPECT_EQ(got, expect); +} + +TEST_P(Builtin_Builder_DualParam_Float_Test, Call_Vector_f32) { + auto param = GetParam(); + auto* vec = Var("vec", nullptr, vec2(1_f, 1_f)); + auto* expr = Call(param.name, vec, vec); + auto* func = Func("a_func", {}, ty.void_(), + { + Decl(vec), + Assign(Phony(), expr), + }); + + spirv::Builder& b = Build(); + + ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); + + auto got = DumpBuilder(b); + auto expect = R"(%13 = OpExtInstImport "GLSL.std.450" +OpName %3 "a_func" +OpName %9 "vec" +%2 = OpTypeVoid +%1 = OpTypeFunction %2 +%6 = OpTypeFloat 32 +%5 = OpTypeVector %6 2 +%7 = OpConstant %6 1 +%8 = OpConstantComposite %5 %7 %7 +%10 = OpTypePointer Function %5 +%11 = OpConstantNull %5 +%3 = OpFunction %2 None %1 +%4 = OpLabel +%9 = OpVariable %10 Function %11 +OpStore %9 %8 +%14 = OpLoad %5 %9 +%15 = OpLoad %5 %9 +%12 = OpExtInst %5 %13 )" + + param.op + + R"( %14 %15 +OpReturn +OpFunctionEnd +)"; + EXPECT_EQ(got, expect); +} + +INSTANTIATE_TEST_SUITE_P(BuiltinBuilderTest, + Builtin_Builder_DualParam_Float_Test, + testing::Values(BuiltinData{"atan2", "Atan2"}, + BuiltinData{"max", "NMax"}, + BuiltinData{"min", "NMin"}, + BuiltinData{"pow", "Pow"}, + BuiltinData{"step", "Step"})); + +TEST_F(BuiltinBuilderTest, Call_Reflect_Vector_f32) { + auto* vec = Var("vec", nullptr, vec2(1_f, 1_f)); + auto* expr = Call("reflect", vec, vec); + auto* func = Func("a_func", {}, ty.void_(), + { + Decl(vec), + Assign(Phony(), expr), + }); + + spirv::Builder& b = Build(); + + ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); + + auto got = DumpBuilder(b); + auto expect = R"(%13 = OpExtInstImport "GLSL.std.450" +OpName %3 "a_func" +OpName %9 "vec" +%2 = OpTypeVoid +%1 = OpTypeFunction %2 +%6 = OpTypeFloat 32 +%5 = OpTypeVector %6 2 +%7 = OpConstant %6 1 +%8 = OpConstantComposite %5 %7 %7 +%10 = OpTypePointer Function %5 +%11 = OpConstantNull %5 +%3 = OpFunction %2 None %1 +%4 = OpLabel +%9 = OpVariable %10 Function %11 +OpStore %9 %8 +%14 = OpLoad %5 %9 +%15 = OpLoad %5 %9 +%12 = OpExtInst %5 %13 Reflect %14 %15 +OpReturn +OpFunctionEnd +)"; + EXPECT_EQ(got, expect); +} + +TEST_F(BuiltinBuilderTest, Call_Distance_Scalar_f32) { + auto* scalar = Var("scalar", nullptr, Expr(1_f)); + auto* expr = Call("distance", scalar, scalar); + auto* func = Func("a_func", {}, ty.void_(), + { + Decl(scalar), + Assign(Phony(), expr), + }); + + spirv::Builder& b = Build(); + + ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); + + auto got = DumpBuilder(b); + auto expect = R"(%11 = OpExtInstImport "GLSL.std.450" +OpName %3 "a_func" +OpName %7 "scalar" +%2 = OpTypeVoid +%1 = OpTypeFunction %2 +%5 = OpTypeFloat 32 +%6 = OpConstant %5 1 +%8 = OpTypePointer Function %5 +%9 = OpConstantNull %5 +%3 = OpFunction %2 None %1 +%4 = OpLabel +%7 = OpVariable %8 Function %9 +OpStore %7 %6 +%12 = OpLoad %5 %7 +%13 = OpLoad %5 %7 +%10 = OpExtInst %5 %11 Distance %12 %13 +OpReturn +OpFunctionEnd +)"; + EXPECT_EQ(got, expect); +} + +TEST_F(BuiltinBuilderTest, Call_Distance_Vector_f32) { + auto* vec = Var("vec", nullptr, vec2(1_f, 1_f)); + auto* expr = Call("distance", vec, vec); + auto* func = Func("a_func", {}, ty.void_(), + { + Decl(vec), + Assign(Phony(), expr), + }); + + spirv::Builder& b = Build(); + + ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); + + auto got = DumpBuilder(b); + auto expect = R"(%13 = OpExtInstImport "GLSL.std.450" +OpName %3 "a_func" +OpName %9 "vec" +%2 = OpTypeVoid +%1 = OpTypeFunction %2 +%6 = OpTypeFloat 32 +%5 = OpTypeVector %6 2 +%7 = OpConstant %6 1 +%8 = OpConstantComposite %5 %7 %7 +%10 = OpTypePointer Function %5 +%11 = OpConstantNull %5 +%3 = OpFunction %2 None %1 +%4 = OpLabel +%9 = OpVariable %10 Function %11 +OpStore %9 %8 +%14 = OpLoad %5 %9 +%15 = OpLoad %5 %9 +%12 = OpExtInst %6 %13 Distance %14 %15 +OpReturn +OpFunctionEnd +)"; + EXPECT_EQ(got, expect); +} + +TEST_F(BuiltinBuilderTest, Call_Cross_f32) { + auto* vec = Var("vec", nullptr, vec3(1_f, 1_f, 1_f)); + auto* expr = Call("cross", vec, vec); + auto* func = Func("a_func", {}, ty.void_(), + { + Decl(vec), + Assign(Phony(), expr), + }); + + spirv::Builder& b = Build(); + + ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); + + auto got = DumpBuilder(b); + auto expect = R"(%13 = OpExtInstImport "GLSL.std.450" +OpName %3 "a_func" +OpName %9 "vec" +%2 = OpTypeVoid +%1 = OpTypeFunction %2 +%6 = OpTypeFloat 32 +%5 = OpTypeVector %6 3 +%7 = OpConstant %6 1 +%8 = OpConstantComposite %5 %7 %7 %7 +%10 = OpTypePointer Function %5 +%11 = OpConstantNull %5 +%3 = OpFunction %2 None %1 +%4 = OpLabel +%9 = OpVariable %10 Function %11 +OpStore %9 %8 +%14 = OpLoad %5 %9 +%15 = OpLoad %5 %9 +%12 = OpExtInst %5 %13 Cross %14 %15 +OpReturn +OpFunctionEnd +)"; + EXPECT_EQ(got, expect); +} + +using Builtin_Builder_ThreeParam_Float_Test = BuiltinBuilderTestWithParam; +TEST_P(Builtin_Builder_ThreeParam_Float_Test, Call_Scalar_f32) { + auto param = GetParam(); + auto* scalar = Var("scalar", nullptr, Expr(1_f)); + auto* expr = Call(param.name, scalar, scalar, scalar); + auto* func = Func("a_func", {}, ty.void_(), + { + Decl(scalar), + Assign(Phony(), expr), + }); + + spirv::Builder& b = Build(); + + ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); + + auto got = DumpBuilder(b); + auto expect = R"(%11 = OpExtInstImport "GLSL.std.450" +OpName %3 "a_func" +OpName %7 "scalar" +%2 = OpTypeVoid +%1 = OpTypeFunction %2 +%5 = OpTypeFloat 32 +%6 = OpConstant %5 1 +%8 = OpTypePointer Function %5 +%9 = OpConstantNull %5 +%3 = OpFunction %2 None %1 +%4 = OpLabel +%7 = OpVariable %8 Function %9 +OpStore %7 %6 +%12 = OpLoad %5 %7 +%13 = OpLoad %5 %7 +%14 = OpLoad %5 %7 +%10 = OpExtInst %5 %11 )" + + param.op + + R"( %12 %13 %14 +OpReturn +OpFunctionEnd +)"; + EXPECT_EQ(got, expect); +} + +TEST_P(Builtin_Builder_ThreeParam_Float_Test, Call_Vector_f32) { + auto param = GetParam(); + auto* vec = Var("vec", nullptr, vec2(1_f, 1_f)); + auto* expr = Call(param.name, vec, vec, vec); + auto* func = Func("a_func", {}, ty.void_(), + { + Decl(vec), + Assign(Phony(), expr), + }); + + spirv::Builder& b = Build(); + + ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); + + auto got = DumpBuilder(b); + auto expect = R"(%13 = OpExtInstImport "GLSL.std.450" +OpName %3 "a_func" +OpName %9 "vec" +%2 = OpTypeVoid +%1 = OpTypeFunction %2 +%6 = OpTypeFloat 32 +%5 = OpTypeVector %6 2 +%7 = OpConstant %6 1 +%8 = OpConstantComposite %5 %7 %7 +%10 = OpTypePointer Function %5 +%11 = OpConstantNull %5 +%3 = OpFunction %2 None %1 +%4 = OpLabel +%9 = OpVariable %10 Function %11 +OpStore %9 %8 +%14 = OpLoad %5 %9 +%15 = OpLoad %5 %9 +%16 = OpLoad %5 %9 +%12 = OpExtInst %5 %13 )" + + param.op + + R"( %14 %15 %16 +OpReturn +OpFunctionEnd +)"; + EXPECT_EQ(got, expect); +} + +INSTANTIATE_TEST_SUITE_P(BuiltinBuilderTest, + Builtin_Builder_ThreeParam_Float_Test, + testing::Values(BuiltinData{"clamp", "NClamp"}, + BuiltinData{"fma", "Fma"}, + BuiltinData{"mix", "FMix"}, + + BuiltinData{"smoothstep", "SmoothStep"})); + +TEST_F(BuiltinBuilderTest, Call_FaceForward_Vector_f32) { + auto* vec = Var("vec", nullptr, vec2(1_f, 1_f)); + auto* expr = Call("faceForward", vec, vec, vec); + auto* func = Func("a_func", {}, ty.void_(), + { + Decl(vec), + Assign(Phony(), expr), + }); + + spirv::Builder& b = Build(); + + ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); + + auto got = DumpBuilder(b); + auto expect = R"(%13 = OpExtInstImport "GLSL.std.450" +OpName %3 "a_func" +OpName %9 "vec" +%2 = OpTypeVoid +%1 = OpTypeFunction %2 +%6 = OpTypeFloat 32 +%5 = OpTypeVector %6 2 +%7 = OpConstant %6 1 +%8 = OpConstantComposite %5 %7 %7 +%10 = OpTypePointer Function %5 +%11 = OpConstantNull %5 +%3 = OpFunction %2 None %1 +%4 = OpLabel +%9 = OpVariable %10 Function %11 +OpStore %9 %8 +%14 = OpLoad %5 %9 +%15 = OpLoad %5 %9 +%16 = OpLoad %5 %9 +%12 = OpExtInst %5 %13 FaceForward %14 %15 %16 +OpReturn +OpFunctionEnd +)"; + EXPECT_EQ(got, expect); +} + +TEST_F(BuiltinBuilderTest, Call_Modf) { + auto* vec = Var("vec", nullptr, vec2(1_f, 2_f)); + auto* expr = Call("modf", vec); + Func("a_func", {}, ty.void_(), {Decl(vec), CallStmt(expr)}, + {Stage(ast::PipelineStage::kFragment)}); spirv::Builder& b = Build(); ASSERT_TRUE(b.Build()) << b.error(); - - ASSERT_EQ(b.functions().size(), 1_u); - - auto* expected_types = R"(%2 = OpTypeVoid + auto got = DumpBuilder(b); + auto* expect = R"(OpCapability Shader +%15 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %3 "a_func" +OpExecutionMode %3 OriginUpperLeft +OpName %3 "a_func" +OpName %10 "vec" +OpName %14 "__modf_result_vec2" +OpMemberName %14 0 "fract" +OpMemberName %14 1 "whole" +OpMemberDecorate %14 0 Offset 0 +OpMemberDecorate %14 1 Offset 8 +%2 = OpTypeVoid %1 = OpTypeFunction %2 -%6 = OpTypeInt 32 0 -%7 = OpConstant %6 2 -%8 = OpConstant %6 264 -)"; - auto got_types = DumpInstructions(b.types()); - EXPECT_EQ(expected_types, got_types); - - auto* expected_instructions = R"(OpControlBarrier %7 %7 %8 +%6 = OpTypeFloat 32 +%5 = OpTypeVector %6 2 +%7 = OpConstant %6 1 +%8 = OpConstant %6 2 +%9 = OpConstantComposite %5 %7 %8 +%11 = OpTypePointer Function %5 +%12 = OpConstantNull %5 +%14 = OpTypeStruct %5 %5 +%3 = OpFunction %2 None %1 +%4 = OpLabel +%10 = OpVariable %11 Function %12 +OpStore %10 %9 +%16 = OpLoad %5 %10 +%13 = OpExtInst %14 %15 ModfStruct %16 OpReturn +OpFunctionEnd )"; - auto got_instructions = DumpInstructions(b.functions()[0].instructions()); - EXPECT_EQ(expected_instructions, got_instructions); + EXPECT_EQ(expect, got); Validate(b); } -TEST_F(BuiltinBuilderTest, Call_StorageBarrier) { - Func("f", {}, ty.void_(), - ast::StatementList{ - CallStmt(Call("storageBarrier")), - }, - ast::AttributeList{ - Stage(ast::PipelineStage::kCompute), - WorkgroupSize(1_i), - }); +TEST_F(BuiltinBuilderTest, Call_Frexp) { + auto* vec = Var("vec", nullptr, vec2(1_f, 2_f)); + auto* expr = Call("frexp", vec); + Func("a_func", {}, ty.void_(), {Decl(vec), CallStmt(expr)}, + {Stage(ast::PipelineStage::kFragment)}); spirv::Builder& b = Build(); ASSERT_TRUE(b.Build()) << b.error(); - - ASSERT_EQ(b.functions().size(), 1_u); - - auto* expected_types = R"(%2 = OpTypeVoid + auto got = DumpBuilder(b); + auto* expect = R"(OpCapability Shader +%17 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %3 "a_func" +OpExecutionMode %3 OriginUpperLeft +OpName %3 "a_func" +OpName %10 "vec" +OpName %14 "__frexp_result_vec2" +OpMemberName %14 0 "sig" +OpMemberName %14 1 "exp" +OpMemberDecorate %14 0 Offset 0 +OpMemberDecorate %14 1 Offset 8 +%2 = OpTypeVoid %1 = OpTypeFunction %2 -%6 = OpTypeInt 32 0 -%7 = OpConstant %6 2 -%8 = OpConstant %6 72 -)"; - auto got_types = DumpInstructions(b.types()); - EXPECT_EQ(expected_types, got_types); - - auto* expected_instructions = R"(OpControlBarrier %7 %7 %8 +%6 = OpTypeFloat 32 +%5 = OpTypeVector %6 2 +%7 = OpConstant %6 1 +%8 = OpConstant %6 2 +%9 = OpConstantComposite %5 %7 %8 +%11 = OpTypePointer Function %5 +%12 = OpConstantNull %5 +%16 = OpTypeInt 32 1 +%15 = OpTypeVector %16 2 +%14 = OpTypeStruct %5 %15 +%3 = OpFunction %2 None %1 +%4 = OpLabel +%10 = OpVariable %11 Function %12 +OpStore %10 %9 +%18 = OpLoad %5 %10 +%13 = OpExtInst %14 %17 FrexpStruct %18 OpReturn +OpFunctionEnd )"; - auto got_instructions = DumpInstructions(b.functions()[0].instructions()); - EXPECT_EQ(expected_instructions, got_instructions); + EXPECT_EQ(expect, got); Validate(b); } +} // namespace float_builtin_tests + +// Tests for Numeric builtins with all integer parameter +namespace integer_builtin_tests { + +using BuiltinIntTest = BuiltinBuilderTestWithParam; +TEST_P(BuiltinIntTest, Call_SInt_Scalar) { + auto param = GetParam(); + auto* var = GlobalVar("v", ty.i32(), ast::StorageClass::kPrivate); + auto* expr = Call(param.name, "v"); + auto* func = Func("a_func", {}, ty.void_(), + { + Assign(Phony(), expr), + }); + + spirv::Builder& b = Build(); + + ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error(); + ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); + + EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeInt 32 1 +%2 = OpTypePointer Private %3 +%4 = OpConstantNull %3 +%1 = OpVariable %2 Private %4 +%6 = OpTypeVoid +%5 = OpTypeFunction %6 +)"); + + auto expected = utils::ReplaceAll(R"(%10 = OpLoad %3 %1 +%9 = ${op} %3 %10 +OpReturn +)", + "${op}", param.op); + EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), expected); +} + +TEST_P(BuiltinIntTest, Call_SInt_Vector) { + auto param = GetParam(); + auto* var = GlobalVar("v", ty.vec3(), ast::StorageClass::kPrivate); + auto* expr = Call(param.name, "v"); + auto* func = Func("a_func", {}, ty.void_(), + { + Assign(Phony(), expr), + }); + + spirv::Builder& b = Build(); + + ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error(); + ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); + + EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeInt 32 1 +%3 = OpTypeVector %4 3 +%2 = OpTypePointer Private %3 +%5 = OpConstantNull %3 +%1 = OpVariable %2 Private %5 +%7 = OpTypeVoid +%6 = OpTypeFunction %7 +)"); + + auto expected = utils::ReplaceAll(R"(%11 = OpLoad %3 %1 +%10 = ${op} %3 %11 +OpReturn +)", + "${op}", param.op); + EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), expected); +} + +TEST_P(BuiltinIntTest, Call_UInt_Scalar) { + auto param = GetParam(); + auto* var = GlobalVar("v", ty.u32(), ast::StorageClass::kPrivate); + auto* expr = Call(param.name, "v"); + auto* func = Func("a_func", {}, ty.void_(), + { + Assign(Phony(), expr), + }); + + spirv::Builder& b = Build(); + + ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error(); + ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); + + EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeInt 32 0 +%2 = OpTypePointer Private %3 +%4 = OpConstantNull %3 +%1 = OpVariable %2 Private %4 +%6 = OpTypeVoid +%5 = OpTypeFunction %6 +)"); + + auto expected = utils::ReplaceAll(R"(%10 = OpLoad %3 %1 +%9 = ${op} %3 %10 +OpReturn +)", + "${op}", param.op); + EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), expected); +} + +TEST_P(BuiltinIntTest, Call_UInt_Vector) { + auto param = GetParam(); + auto* var = GlobalVar("v", ty.vec3(), ast::StorageClass::kPrivate); + auto* expr = Call(param.name, "v"); + auto* func = Func("a_func", {}, ty.void_(), + { + Assign(Phony(), expr), + }); + + spirv::Builder& b = Build(); + + ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error(); + ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); + + EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeInt 32 0 +%3 = OpTypeVector %4 3 +%2 = OpTypePointer Private %3 +%5 = OpConstantNull %3 +%1 = OpVariable %2 Private %5 +%7 = OpTypeVoid +%6 = OpTypeFunction %7 +)"); + + auto expected = utils::ReplaceAll(R"(%11 = OpLoad %3 %1 +%10 = ${op} %3 %11 +OpReturn +)", + "${op}", param.op); + EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), expected); +} +INSTANTIATE_TEST_SUITE_P(BuiltinBuilderTest, + BuiltinIntTest, + testing::Values(BuiltinData{"countOneBits", "OpBitCount"}, + BuiltinData{"reverseBits", "OpBitReverse"})); + +using Builtin_Builder_SingleParam_Sint_Test = BuiltinBuilderTestWithParam; +TEST_P(Builtin_Builder_SingleParam_Sint_Test, Call_Scalar) { + auto param = GetParam(); + auto* scalar = Var("scalar", nullptr, Expr(1_i)); + auto* expr = Call(param.name, scalar); + auto* func = Func("a_func", {}, ty.void_(), + { + Decl(scalar), + Assign(Phony(), expr), + }); + + spirv::Builder& b = Build(); + + ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); + + auto got = DumpBuilder(b); + auto expect = R"(%11 = OpExtInstImport "GLSL.std.450" +OpName %3 "a_func" +OpName %7 "scalar" +%2 = OpTypeVoid +%1 = OpTypeFunction %2 +%5 = OpTypeInt 32 1 +%6 = OpConstant %5 1 +%8 = OpTypePointer Function %5 +%9 = OpConstantNull %5 +%3 = OpFunction %2 None %1 +%4 = OpLabel +%7 = OpVariable %8 Function %9 +OpStore %7 %6 +%12 = OpLoad %5 %7 +%10 = OpExtInst %5 %11 )" + + param.op + + R"( %12 +OpReturn +OpFunctionEnd +)"; + EXPECT_EQ(got, expect); +} + +TEST_P(Builtin_Builder_SingleParam_Sint_Test, Call_Vector) { + auto param = GetParam(); + auto* vec = Var("vec", nullptr, vec2(1_i, 1_i)); + auto* expr = Call(param.name, vec); + auto* func = Func("a_func", {}, ty.void_(), + { + Decl(vec), + Assign(Phony(), expr), + }); + + spirv::Builder& b = Build(); + + ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); + + auto got = DumpBuilder(b); + auto expect = R"(%13 = OpExtInstImport "GLSL.std.450" +OpName %3 "a_func" +OpName %9 "vec" +%2 = OpTypeVoid +%1 = OpTypeFunction %2 +%6 = OpTypeInt 32 1 +%5 = OpTypeVector %6 2 +%7 = OpConstant %6 1 +%8 = OpConstantComposite %5 %7 %7 +%10 = OpTypePointer Function %5 +%11 = OpConstantNull %5 +%3 = OpFunction %2 None %1 +%4 = OpLabel +%9 = OpVariable %10 Function %11 +OpStore %9 %8 +%14 = OpLoad %5 %9 +%12 = OpExtInst %5 %13 )" + + param.op + + R"( %14 +OpReturn +OpFunctionEnd +)"; + EXPECT_EQ(got, expect); +} +INSTANTIATE_TEST_SUITE_P(BuiltinBuilderTest, + Builtin_Builder_SingleParam_Sint_Test, + testing::Values(BuiltinData{"abs", "SAbs"})); + +// Calling abs() on an unsigned integer scalar / vector is a no-op. +using Builtin_Builder_Abs_Uint_Test = BuiltinBuilderTest; +TEST_F(Builtin_Builder_Abs_Uint_Test, Call_Scalar) { + auto* scalar = Var("scalar", nullptr, Expr(1_u)); + auto* expr = Call("abs", scalar); + auto* func = Func("a_func", {}, ty.void_(), + { + Decl(scalar), + Assign(Phony(), expr), + }); + + spirv::Builder& b = Build(); + + ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); + + auto got = DumpBuilder(b); + auto expect = R"(OpName %3 "a_func" +OpName %7 "scalar" +%2 = OpTypeVoid +%1 = OpTypeFunction %2 +%5 = OpTypeInt 32 0 +%6 = OpConstant %5 1 +%8 = OpTypePointer Function %5 +%9 = OpConstantNull %5 +%3 = OpFunction %2 None %1 +%4 = OpLabel +%7 = OpVariable %8 Function %9 +OpStore %7 %6 +%11 = OpLoad %5 %7 +OpReturn +OpFunctionEnd +)"; + EXPECT_EQ(got, expect); +} + +TEST_F(Builtin_Builder_Abs_Uint_Test, Call_Vector) { + auto* scalar = Var("scalar", nullptr, vec2(1_u, 1_u)); + auto* expr = Call("abs", scalar); + auto* func = Func("a_func", {}, ty.void_(), + { + Decl(scalar), + Assign(Phony(), expr), + }); + + spirv::Builder& b = Build(); + + ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); + + auto got = DumpBuilder(b); + auto expect = R"(OpName %3 "a_func" +OpName %9 "scalar" +%2 = OpTypeVoid +%1 = OpTypeFunction %2 +%6 = OpTypeInt 32 0 +%5 = OpTypeVector %6 2 +%7 = OpConstant %6 1 +%8 = OpConstantComposite %5 %7 %7 +%10 = OpTypePointer Function %5 +%11 = OpConstantNull %5 +%3 = OpFunction %2 None %1 +%4 = OpLabel +%9 = OpVariable %10 Function %11 +OpStore %9 %8 +%13 = OpLoad %5 %9 +OpReturn +OpFunctionEnd +)"; + EXPECT_EQ(got, expect); +} + +using Builtin_Builder_DualParam_SInt_Test = BuiltinBuilderTestWithParam; +TEST_P(Builtin_Builder_DualParam_SInt_Test, Call_Scalar) { + auto param = GetParam(); + auto* scalar = Var("scalar", nullptr, Expr(1_i)); + auto* expr = Call(param.name, scalar, scalar); + auto* func = Func("a_func", {}, ty.void_(), + { + Decl(scalar), + Assign(Phony(), expr), + }); + + spirv::Builder& b = Build(); + + ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); + + auto got = DumpBuilder(b); + auto expect = R"(%11 = OpExtInstImport "GLSL.std.450" +OpName %3 "a_func" +OpName %7 "scalar" +%2 = OpTypeVoid +%1 = OpTypeFunction %2 +%5 = OpTypeInt 32 1 +%6 = OpConstant %5 1 +%8 = OpTypePointer Function %5 +%9 = OpConstantNull %5 +%3 = OpFunction %2 None %1 +%4 = OpLabel +%7 = OpVariable %8 Function %9 +OpStore %7 %6 +%12 = OpLoad %5 %7 +%13 = OpLoad %5 %7 +%10 = OpExtInst %5 %11 )" + + param.op + + R"( %12 %13 +OpReturn +OpFunctionEnd +)"; + EXPECT_EQ(got, expect); +} + +TEST_P(Builtin_Builder_DualParam_SInt_Test, Call_Vector) { + auto param = GetParam(); + auto* vec = Var("vec", nullptr, vec2(1_i, 1_i)); + auto* expr = Call(param.name, vec, vec); + auto* func = Func("a_func", {}, ty.void_(), + { + Decl(vec), + Assign(Phony(), expr), + }); + + spirv::Builder& b = Build(); + + ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); + + auto got = DumpBuilder(b); + auto expect = R"(%13 = OpExtInstImport "GLSL.std.450" +OpName %3 "a_func" +OpName %9 "vec" +%2 = OpTypeVoid +%1 = OpTypeFunction %2 +%6 = OpTypeInt 32 1 +%5 = OpTypeVector %6 2 +%7 = OpConstant %6 1 +%8 = OpConstantComposite %5 %7 %7 +%10 = OpTypePointer Function %5 +%11 = OpConstantNull %5 +%3 = OpFunction %2 None %1 +%4 = OpLabel +%9 = OpVariable %10 Function %11 +OpStore %9 %8 +%14 = OpLoad %5 %9 +%15 = OpLoad %5 %9 +%12 = OpExtInst %5 %13 )" + + param.op + + R"( %14 %15 +OpReturn +OpFunctionEnd +)"; + EXPECT_EQ(got, expect); +} +INSTANTIATE_TEST_SUITE_P(BuiltinBuilderTest, + Builtin_Builder_DualParam_SInt_Test, + testing::Values(BuiltinData{"max", "SMax"}, BuiltinData{"min", "SMin"})); + +using Builtin_Builder_DualParam_UInt_Test = BuiltinBuilderTestWithParam; +TEST_P(Builtin_Builder_DualParam_UInt_Test, Call_Scalar) { + auto param = GetParam(); + auto* scalar = Var("scalar", nullptr, Expr(1_u)); + auto* expr = Call(param.name, scalar, scalar); + auto* func = Func("a_func", {}, ty.void_(), + { + Decl(scalar), + Assign(Phony(), expr), + }); + + spirv::Builder& b = Build(); + + ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); + + auto got = DumpBuilder(b); + auto expect = R"(%11 = OpExtInstImport "GLSL.std.450" +OpName %3 "a_func" +OpName %7 "scalar" +%2 = OpTypeVoid +%1 = OpTypeFunction %2 +%5 = OpTypeInt 32 0 +%6 = OpConstant %5 1 +%8 = OpTypePointer Function %5 +%9 = OpConstantNull %5 +%3 = OpFunction %2 None %1 +%4 = OpLabel +%7 = OpVariable %8 Function %9 +OpStore %7 %6 +%12 = OpLoad %5 %7 +%13 = OpLoad %5 %7 +%10 = OpExtInst %5 %11 )" + + param.op + + R"( %12 %13 +OpReturn +OpFunctionEnd +)"; + EXPECT_EQ(got, expect); +} + +TEST_P(Builtin_Builder_DualParam_UInt_Test, Call_Vector) { + auto param = GetParam(); + auto* vec = Var("vec", nullptr, vec2(1_u, 1_u)); + auto* expr = Call(param.name, vec, vec); + auto* func = Func("a_func", {}, ty.void_(), + { + Decl(vec), + Assign(Phony(), expr), + }); + + spirv::Builder& b = Build(); + + ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); + + auto got = DumpBuilder(b); + auto expect = R"(%13 = OpExtInstImport "GLSL.std.450" +OpName %3 "a_func" +OpName %9 "vec" +%2 = OpTypeVoid +%1 = OpTypeFunction %2 +%6 = OpTypeInt 32 0 +%5 = OpTypeVector %6 2 +%7 = OpConstant %6 1 +%8 = OpConstantComposite %5 %7 %7 +%10 = OpTypePointer Function %5 +%11 = OpConstantNull %5 +%3 = OpFunction %2 None %1 +%4 = OpLabel +%9 = OpVariable %10 Function %11 +OpStore %9 %8 +%14 = OpLoad %5 %9 +%15 = OpLoad %5 %9 +%12 = OpExtInst %5 %13 )" + + param.op + + R"( %14 %15 +OpReturn +OpFunctionEnd +)"; + EXPECT_EQ(got, expect); +} +INSTANTIATE_TEST_SUITE_P(BuiltinBuilderTest, + Builtin_Builder_DualParam_UInt_Test, + testing::Values(BuiltinData{"max", "UMax"}, BuiltinData{"min", "UMin"})); + +using Builtin_Builder_ThreeParam_Sint_Test = BuiltinBuilderTestWithParam; +TEST_P(Builtin_Builder_ThreeParam_Sint_Test, Call_Scalar) { + auto param = GetParam(); + auto* scalar = Var("scalar", nullptr, Expr(1_i)); + auto* expr = Call(param.name, scalar, scalar, scalar); + auto* func = Func("a_func", {}, ty.void_(), + { + Decl(scalar), + Assign(Phony(), expr), + }); + + spirv::Builder& b = Build(); + + ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); + + auto got = DumpBuilder(b); + auto expect = R"(%11 = OpExtInstImport "GLSL.std.450" +OpName %3 "a_func" +OpName %7 "scalar" +%2 = OpTypeVoid +%1 = OpTypeFunction %2 +%5 = OpTypeInt 32 1 +%6 = OpConstant %5 1 +%8 = OpTypePointer Function %5 +%9 = OpConstantNull %5 +%3 = OpFunction %2 None %1 +%4 = OpLabel +%7 = OpVariable %8 Function %9 +OpStore %7 %6 +%12 = OpLoad %5 %7 +%13 = OpLoad %5 %7 +%14 = OpLoad %5 %7 +%10 = OpExtInst %5 %11 )" + + param.op + + R"( %12 %13 %14 +OpReturn +OpFunctionEnd +)"; + EXPECT_EQ(got, expect); +} + +TEST_P(Builtin_Builder_ThreeParam_Sint_Test, Call_Vector) { + auto param = GetParam(); + auto* vec = Var("vec", nullptr, vec2(1_i, 1_i)); + auto* expr = Call(param.name, vec, vec, vec); + auto* func = Func("a_func", {}, ty.void_(), + { + Decl(vec), + Assign(Phony(), expr), + }); + + spirv::Builder& b = Build(); + + ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); + + auto got = DumpBuilder(b); + auto expect = R"(%13 = OpExtInstImport "GLSL.std.450" +OpName %3 "a_func" +OpName %9 "vec" +%2 = OpTypeVoid +%1 = OpTypeFunction %2 +%6 = OpTypeInt 32 1 +%5 = OpTypeVector %6 2 +%7 = OpConstant %6 1 +%8 = OpConstantComposite %5 %7 %7 +%10 = OpTypePointer Function %5 +%11 = OpConstantNull %5 +%3 = OpFunction %2 None %1 +%4 = OpLabel +%9 = OpVariable %10 Function %11 +OpStore %9 %8 +%14 = OpLoad %5 %9 +%15 = OpLoad %5 %9 +%16 = OpLoad %5 %9 +%12 = OpExtInst %5 %13 )" + + param.op + + R"( %14 %15 %16 +OpReturn +OpFunctionEnd +)"; + EXPECT_EQ(got, expect); +} +INSTANTIATE_TEST_SUITE_P(BuiltinBuilderTest, + Builtin_Builder_ThreeParam_Sint_Test, + testing::Values(BuiltinData{"clamp", "SClamp"})); + +using Builtin_Builder_ThreeParam_Uint_Test = BuiltinBuilderTestWithParam; +TEST_P(Builtin_Builder_ThreeParam_Uint_Test, Call_Scalar) { + auto param = GetParam(); + auto* scalar = Var("scalar", nullptr, Expr(1_u)); + auto* expr = Call(param.name, scalar, scalar, scalar); + auto* func = Func("a_func", {}, ty.void_(), + { + Decl(scalar), + Assign(Phony(), expr), + }); + + spirv::Builder& b = Build(); + + ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); + + auto got = DumpBuilder(b); + auto expect = R"(%11 = OpExtInstImport "GLSL.std.450" +OpName %3 "a_func" +OpName %7 "scalar" +%2 = OpTypeVoid +%1 = OpTypeFunction %2 +%5 = OpTypeInt 32 0 +%6 = OpConstant %5 1 +%8 = OpTypePointer Function %5 +%9 = OpConstantNull %5 +%3 = OpFunction %2 None %1 +%4 = OpLabel +%7 = OpVariable %8 Function %9 +OpStore %7 %6 +%12 = OpLoad %5 %7 +%13 = OpLoad %5 %7 +%14 = OpLoad %5 %7 +%10 = OpExtInst %5 %11 )" + + param.op + + R"( %12 %13 %14 +OpReturn +OpFunctionEnd +)"; + EXPECT_EQ(got, expect); +} + +TEST_P(Builtin_Builder_ThreeParam_Uint_Test, Call_Vector) { + auto param = GetParam(); + auto* vec = Var("vec", nullptr, vec2(1_u, 1_u)); + auto* expr = Call(param.name, vec, vec, vec); + auto* func = Func("a_func", {}, ty.void_(), + { + Decl(vec), + Assign(Phony(), expr), + }); + + spirv::Builder& b = Build(); + + ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); + + auto got = DumpBuilder(b); + auto expect = R"(%13 = OpExtInstImport "GLSL.std.450" +OpName %3 "a_func" +OpName %9 "vec" +%2 = OpTypeVoid +%1 = OpTypeFunction %2 +%6 = OpTypeInt 32 0 +%5 = OpTypeVector %6 2 +%7 = OpConstant %6 1 +%8 = OpConstantComposite %5 %7 %7 +%10 = OpTypePointer Function %5 +%11 = OpConstantNull %5 +%3 = OpFunction %2 None %1 +%4 = OpLabel +%9 = OpVariable %10 Function %11 +OpStore %9 %8 +%14 = OpLoad %5 %9 +%15 = OpLoad %5 %9 +%16 = OpLoad %5 %9 +%12 = OpExtInst %5 %13 )" + + param.op + + R"( %14 %15 %16 +OpReturn +OpFunctionEnd +)"; + EXPECT_EQ(got, expect); +} +INSTANTIATE_TEST_SUITE_P(BuiltinBuilderTest, + Builtin_Builder_ThreeParam_Uint_Test, + testing::Values(BuiltinData{"clamp", "UClamp"})); + TEST_F(BuiltinBuilderTest, Call_ExtractBits_i32) { auto* v = Var("v", ty.i32()); auto* offset = Var("offset", ty.u32()); @@ -2802,6 +2090,928 @@ OpFunctionEnd EXPECT_EQ(got, expect); } +} // namespace integer_builtin_tests + +// Tests for Numeric builtins with matrix parameter, i.e. "determinant" and "transpose" +namespace matrix_builtin_tests { + +TEST_F(BuiltinBuilderTest, Call_Determinant_f32) { + auto* var = GlobalVar("var", ty.mat3x3(), ast::StorageClass::kPrivate); + auto* expr = Call("determinant", "var"); + auto* func = Func("a_func", {}, ty.void_(), + { + Assign(Phony(), expr), + }); + + spirv::Builder& b = Build(); + + ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error(); + ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); + + auto got = DumpBuilder(b); + auto expect = R"(%12 = OpExtInstImport "GLSL.std.450" +OpName %1 "var" +OpName %9 "a_func" +%5 = OpTypeFloat 32 +%4 = OpTypeVector %5 3 +%3 = OpTypeMatrix %4 3 +%2 = OpTypePointer Private %3 +%6 = OpConstantNull %3 +%1 = OpVariable %2 Private %6 +%8 = OpTypeVoid +%7 = OpTypeFunction %8 +%9 = OpFunction %8 None %7 +%10 = OpLabel +%13 = OpLoad %3 %1 +%11 = OpExtInst %5 %12 Determinant %13 +OpReturn +OpFunctionEnd +)"; + EXPECT_EQ(got, expect); +} + +TEST_F(BuiltinBuilderTest, Call_Transpose_f32) { + auto* var = GlobalVar("var", ty.mat2x3(), ast::StorageClass::kPrivate); + auto* expr = Call("transpose", "var"); + auto* func = Func("a_func", {}, ty.void_(), + { + Assign(Phony(), expr), + }); + + spirv::Builder& b = Build(); + + ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error(); + ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); + + auto got = DumpBuilder(b); + auto expect = R"(OpName %1 "var" +OpName %9 "a_func" +%5 = OpTypeFloat 32 +%4 = OpTypeVector %5 3 +%3 = OpTypeMatrix %4 2 +%2 = OpTypePointer Private %3 +%6 = OpConstantNull %3 +%1 = OpVariable %2 Private %6 +%8 = OpTypeVoid +%7 = OpTypeFunction %8 +%13 = OpTypeVector %5 2 +%12 = OpTypeMatrix %13 3 +%9 = OpFunction %8 None %7 +%10 = OpLabel +%14 = OpLoad %3 %1 +%11 = OpTranspose %12 %14 +OpReturn +OpFunctionEnd +)"; + EXPECT_EQ(got, expect); +} + +} // namespace matrix_builtin_tests + +// Tests for Numeric builtins with float and integer vector parameter, i.e. "dot" +namespace vector_builtin_tests { + +TEST_F(BuiltinBuilderTest, Call_Dot_F32) { + auto* var = GlobalVar("v", ty.vec3(), ast::StorageClass::kPrivate); + auto* expr = Call("dot", "v", "v"); + auto* func = Func("a_func", {}, ty.void_(), + { + Assign(Phony(), expr), + }); + + spirv::Builder& b = Build(); + + ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error(); + ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); + + EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeFloat 32 +%3 = OpTypeVector %4 3 +%2 = OpTypePointer Private %3 +%5 = OpConstantNull %3 +%1 = OpVariable %2 Private %5 +%7 = OpTypeVoid +%6 = OpTypeFunction %7 +)"); + EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), + R"(%11 = OpLoad %3 %1 +%12 = OpLoad %3 %1 +%10 = OpDot %4 %11 %12 +OpReturn +)"); +} + +TEST_F(BuiltinBuilderTest, Call_Dot_U32) { + auto* var = GlobalVar("v", ty.vec3(), ast::StorageClass::kPrivate); + auto* expr = Call("dot", "v", "v"); + auto* func = Func("a_func", {}, ty.void_(), + { + Assign(Phony(), expr), + }); + + spirv::Builder& b = Build(); + + ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error(); + ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); + + EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeInt 32 0 +%3 = OpTypeVector %4 3 +%2 = OpTypePointer Private %3 +%5 = OpConstantNull %3 +%1 = OpVariable %2 Private %5 +%7 = OpTypeVoid +%6 = OpTypeFunction %7 +)"); + EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), + R"(%11 = OpLoad %3 %1 +%12 = OpLoad %3 %1 +%13 = OpCompositeExtract %4 %11 0 +%14 = OpCompositeExtract %4 %12 0 +%15 = OpIMul %4 %13 %14 +%16 = OpCompositeExtract %4 %11 1 +%17 = OpCompositeExtract %4 %12 1 +%18 = OpIMul %4 %16 %17 +%19 = OpIAdd %4 %15 %18 +%20 = OpCompositeExtract %4 %11 2 +%21 = OpCompositeExtract %4 %12 2 +%22 = OpIMul %4 %20 %21 +%10 = OpIAdd %4 %19 %22 +OpReturn +)"); +} + +TEST_F(BuiltinBuilderTest, Call_Dot_I32) { + auto* var = GlobalVar("v", ty.vec3(), ast::StorageClass::kPrivate); + auto* expr = Call("dot", "v", "v"); + auto* func = Func("a_func", {}, ty.void_(), + { + Assign(Phony(), expr), + }); + + spirv::Builder& b = Build(); + + ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error(); + ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); + + EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeInt 32 1 +%3 = OpTypeVector %4 3 +%2 = OpTypePointer Private %3 +%5 = OpConstantNull %3 +%1 = OpVariable %2 Private %5 +%7 = OpTypeVoid +%6 = OpTypeFunction %7 +)"); + EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), + R"(%11 = OpLoad %3 %1 +%12 = OpLoad %3 %1 +%13 = OpCompositeExtract %4 %11 0 +%14 = OpCompositeExtract %4 %12 0 +%15 = OpIMul %4 %13 %14 +%16 = OpCompositeExtract %4 %11 1 +%17 = OpCompositeExtract %4 %12 1 +%18 = OpIMul %4 %16 %17 +%19 = OpIAdd %4 %15 %18 +%20 = OpCompositeExtract %4 %11 2 +%21 = OpCompositeExtract %4 %12 2 +%22 = OpIMul %4 %20 %21 +%10 = OpIAdd %4 %19 %22 +OpReturn +)"); +} + +} // namespace vector_builtin_tests + +// Tests for Derivative builtins +namespace derivative_builtin_tests { + +using BuiltinDeriveTest = BuiltinBuilderTestWithParam; +TEST_P(BuiltinDeriveTest, Call_Derivative_Scalar) { + auto param = GetParam(); + auto* var = GlobalVar("v", ty.f32(), ast::StorageClass::kPrivate); + auto* expr = Call(param.name, "v"); + auto* func = + Func("func", {}, ty.void_(), {CallStmt(expr)}, {Stage(ast::PipelineStage::kFragment)}); + + spirv::Builder& b = Build(); + + ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error(); + ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); + + EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 32 +%2 = OpTypePointer Private %3 +%4 = OpConstantNull %3 +%1 = OpVariable %2 Private %4 +%6 = OpTypeVoid +%5 = OpTypeFunction %6 +)"); + + auto expected = utils::ReplaceAll(R"(%10 = OpLoad %3 %1 +%9 = ${op} %3 %10 +OpReturn +)", + "${op}", param.op); + EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), expected); +} + +TEST_P(BuiltinDeriveTest, Call_Derivative_Vector) { + auto param = GetParam(); + auto* var = GlobalVar("v", ty.vec3(), ast::StorageClass::kPrivate); + auto* expr = Call(param.name, "v"); + auto* func = + Func("func", {}, ty.void_(), {CallStmt(expr)}, {Stage(ast::PipelineStage::kFragment)}); + + spirv::Builder& b = Build(); + + ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error(); + ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); + + if (param.name != "dpdx" && param.name != "dpdy" && param.name != "fwidth") { + EXPECT_EQ(DumpInstructions(b.capabilities()), + R"(OpCapability DerivativeControl +)"); + } + + EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeFloat 32 +%3 = OpTypeVector %4 3 +%2 = OpTypePointer Private %3 +%5 = OpConstantNull %3 +%1 = OpVariable %2 Private %5 +%7 = OpTypeVoid +%6 = OpTypeFunction %7 +)"); + + auto expected = utils::ReplaceAll(R"(%11 = OpLoad %3 %1 +%10 = ${op} %3 %11 +OpReturn +)", + "${op}", param.op); + EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), expected); +} +INSTANTIATE_TEST_SUITE_P(BuiltinBuilderTest, + BuiltinDeriveTest, + testing::Values(BuiltinData{"dpdx", "OpDPdx"}, + BuiltinData{"dpdxFine", "OpDPdxFine"}, + BuiltinData{"dpdxCoarse", "OpDPdxCoarse"}, + BuiltinData{"dpdy", "OpDPdy"}, + BuiltinData{"dpdyFine", "OpDPdyFine"}, + BuiltinData{"dpdyCoarse", "OpDPdyCoarse"}, + BuiltinData{"fwidth", "OpFwidth"}, + BuiltinData{"fwidthFine", "OpFwidthFine"}, + BuiltinData{"fwidthCoarse", "OpFwidthCoarse"})); + +} // namespace derivative_builtin_tests + +// Tests for Atomic builtins +namespace atomic_builtin_tests { + +TEST_F(BuiltinBuilderTest, Call_AtomicLoad) { + // struct S { + // u : atomic; + // i : atomic; + // } + // + // @binding(1) @group(2) var b : S; + // + // fn a_func() { + // let u : u32 = atomicLoad(&b.u); + // let i : i32 = atomicLoad(&b.i); + // } + auto* s = Structure("S", { + Member("u", ty.atomic()), + Member("i", ty.atomic()), + }); + GlobalVar("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite, + ast::AttributeList{ + create(1u), + create(2u), + }); + + Func("a_func", {}, ty.void_(), + ast::StatementList{ + Decl(Let("u", ty.u32(), Call("atomicLoad", AddressOf(MemberAccessor("b", "u"))))), + Decl(Let("i", ty.i32(), Call("atomicLoad", AddressOf(MemberAccessor("b", "i"))))), + }, + ast::AttributeList{Stage(ast::PipelineStage::kFragment)}); + + spirv::Builder& b = SanitizeAndBuild(); + + ASSERT_TRUE(b.Build()) << b.error(); + + ASSERT_EQ(b.functions().size(), 1_u); + + auto* expected_types = R"(%4 = OpTypeInt 32 0 +%5 = OpTypeInt 32 1 +%3 = OpTypeStruct %4 %5 +%2 = OpTypePointer StorageBuffer %3 +%1 = OpVariable %2 StorageBuffer +%7 = OpTypeVoid +%6 = OpTypeFunction %7 +%11 = OpConstant %4 1 +%12 = OpConstant %4 0 +%14 = OpTypePointer StorageBuffer %4 +%18 = OpTypePointer StorageBuffer %5 +)"; + auto got_types = DumpInstructions(b.types()); + EXPECT_EQ(expected_types, got_types); + + auto* expected_instructions = R"(%15 = OpAccessChain %14 %1 %12 +%10 = OpAtomicLoad %4 %15 %11 %12 +%19 = OpAccessChain %18 %1 %11 +%16 = OpAtomicLoad %5 %19 %11 %12 +OpReturn +)"; + auto got_instructions = DumpInstructions(b.functions()[0].instructions()); + EXPECT_EQ(expected_instructions, got_instructions); + + Validate(b); +} + +TEST_F(BuiltinBuilderTest, Call_AtomicStore) { + // struct S { + // u : atomic; + // i : atomic; + // } + // + // @binding(1) @group(2) var b : S; + // + // fn a_func() { + // var u = 1_u; + // var i = 2; + // atomicStore(&b.u, u); + // atomicStore(&b.i, i); + // } + auto* s = Structure("S", { + Member("u", ty.atomic()), + Member("i", ty.atomic()), + }); + GlobalVar("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite, + ast::AttributeList{ + create(1u), + create(2u), + }); + + Func("a_func", {}, ty.void_(), + ast::StatementList{ + Decl(Var("u", nullptr, Expr(1_u))), + Decl(Var("i", nullptr, Expr(2_i))), + CallStmt(Call("atomicStore", AddressOf(MemberAccessor("b", "u")), "u")), + CallStmt(Call("atomicStore", AddressOf(MemberAccessor("b", "i")), "i")), + }, + ast::AttributeList{Stage(ast::PipelineStage::kFragment)}); + + spirv::Builder& b = SanitizeAndBuild(); + + ASSERT_TRUE(b.Build()) << b.error(); + + ASSERT_EQ(b.functions().size(), 1_u); + + auto* expected_types = R"(%4 = OpTypeInt 32 0 +%5 = OpTypeInt 32 1 +%3 = OpTypeStruct %4 %5 +%2 = OpTypePointer StorageBuffer %3 +%1 = OpVariable %2 StorageBuffer +%7 = OpTypeVoid +%6 = OpTypeFunction %7 +%10 = OpConstant %4 1 +%12 = OpTypePointer Function %4 +%13 = OpConstantNull %4 +%14 = OpConstant %5 2 +%16 = OpTypePointer Function %5 +%17 = OpConstantNull %5 +%19 = OpConstant %4 0 +%21 = OpTypePointer StorageBuffer %4 +%26 = OpTypePointer StorageBuffer %5 +)"; + auto got_types = DumpInstructions(b.types()); + EXPECT_EQ(expected_types, got_types); + + auto* expected_instructions = R"(OpStore %11 %10 +OpStore %15 %14 +%22 = OpAccessChain %21 %1 %19 +%23 = OpLoad %4 %11 +OpAtomicStore %22 %10 %19 %23 +%27 = OpAccessChain %26 %1 %10 +%28 = OpLoad %5 %15 +OpAtomicStore %27 %10 %19 %28 +OpReturn +)"; + auto got_instructions = DumpInstructions(b.functions()[0].instructions()); + EXPECT_EQ(expected_instructions, got_instructions); + + Validate(b); +} + +using Builtin_Builder_AtomicRMW_i32 = BuiltinBuilderTestWithParam; +TEST_P(Builtin_Builder_AtomicRMW_i32, Test) { + // struct S { + // v : atomic; + // } + // + // @binding(1) @group(2) var b : S; + // + // fn a_func() { + // var v = 10; + // let x : i32 = atomicOP(&b.v, v); + // } + auto* s = Structure("S", { + Member("v", ty.atomic()), + }); + GlobalVar("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite, + ast::AttributeList{ + create(1u), + create(2u), + }); + + Func("a_func", {}, ty.void_(), + ast::StatementList{ + Decl(Var("v", nullptr, Expr(10_i))), + Decl(Let("x", ty.i32(), + Call(GetParam().name, AddressOf(MemberAccessor("b", "v")), "v"))), + }, + ast::AttributeList{Stage(ast::PipelineStage::kFragment)}); + + spirv::Builder& b = SanitizeAndBuild(); + + ASSERT_TRUE(b.Build()) << b.error(); + + ASSERT_EQ(b.functions().size(), 1_u); + + std::string expected_types = R"(%4 = OpTypeInt 32 1 +%3 = OpTypeStruct %4 +%2 = OpTypePointer StorageBuffer %3 +%1 = OpVariable %2 StorageBuffer +%6 = OpTypeVoid +%5 = OpTypeFunction %6 +%9 = OpConstant %4 10 +%11 = OpTypePointer Function %4 +%12 = OpConstantNull %4 +%14 = OpTypeInt 32 0 +%15 = OpConstant %14 1 +%16 = OpConstant %14 0 +%18 = OpTypePointer StorageBuffer %4 +)"; + auto got_types = DumpInstructions(b.types()); + EXPECT_EQ(expected_types, got_types); + + std::string expected_instructions = R"(OpStore %10 %9 +%19 = OpAccessChain %18 %1 %16 +%20 = OpLoad %4 %10 +)"; + expected_instructions += "%13 = " + GetParam().op + " %4 %19 %15 %16 %20\n"; + expected_instructions += "OpReturn\n"; + + auto got_instructions = DumpInstructions(b.functions()[0].instructions()); + EXPECT_EQ(expected_instructions, got_instructions); + + Validate(b); +} +INSTANTIATE_TEST_SUITE_P(BuiltinBuilderTest, + Builtin_Builder_AtomicRMW_i32, + testing::Values(BuiltinData{"atomicAdd", "OpAtomicIAdd"}, + BuiltinData{"atomicMax", "OpAtomicSMax"}, + BuiltinData{"atomicMin", "OpAtomicSMin"}, + BuiltinData{"atomicAnd", "OpAtomicAnd"}, + BuiltinData{"atomicOr", "OpAtomicOr"}, + BuiltinData{"atomicXor", "OpAtomicXor"})); + +using Builtin_Builder_AtomicRMW_u32 = BuiltinBuilderTestWithParam; +TEST_P(Builtin_Builder_AtomicRMW_u32, Test) { + // struct S { + // v : atomic; + // } + // + // @binding(1) @group(2) var b : S; + // + // fn a_func() { + // var v = 10u; + // let x : u32 = atomicOP(&b.v, v); + // } + auto* s = Structure("S", { + Member("v", ty.atomic()), + }); + GlobalVar("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite, + ast::AttributeList{ + create(1u), + create(2u), + }); + + Func("a_func", {}, ty.void_(), + ast::StatementList{ + Decl(Var("v", nullptr, Expr(10_u))), + Decl(Let("x", ty.u32(), + Call(GetParam().name, AddressOf(MemberAccessor("b", "v")), "v"))), + }, + ast::AttributeList{Stage(ast::PipelineStage::kFragment)}); + + spirv::Builder& b = SanitizeAndBuild(); + + ASSERT_TRUE(b.Build()) << b.error(); + + ASSERT_EQ(b.functions().size(), 1_u); + + std::string expected_types = R"(%4 = OpTypeInt 32 0 +%3 = OpTypeStruct %4 +%2 = OpTypePointer StorageBuffer %3 +%1 = OpVariable %2 StorageBuffer +%6 = OpTypeVoid +%5 = OpTypeFunction %6 +%9 = OpConstant %4 10 +%11 = OpTypePointer Function %4 +%12 = OpConstantNull %4 +%14 = OpConstant %4 1 +%15 = OpConstant %4 0 +%17 = OpTypePointer StorageBuffer %4 +)"; + auto got_types = DumpInstructions(b.types()); + EXPECT_EQ(expected_types, got_types); + + std::string expected_instructions = R"(OpStore %10 %9 +%18 = OpAccessChain %17 %1 %15 +%19 = OpLoad %4 %10 +)"; + expected_instructions += "%13 = " + GetParam().op + " %4 %18 %14 %15 %19\n"; + expected_instructions += "OpReturn\n"; + + auto got_instructions = DumpInstructions(b.functions()[0].instructions()); + EXPECT_EQ(expected_instructions, got_instructions); + + Validate(b); +} +INSTANTIATE_TEST_SUITE_P(BuiltinBuilderTest, + Builtin_Builder_AtomicRMW_u32, + testing::Values(BuiltinData{"atomicAdd", "OpAtomicIAdd"}, + BuiltinData{"atomicMax", "OpAtomicUMax"}, + BuiltinData{"atomicMin", "OpAtomicUMin"}, + BuiltinData{"atomicAnd", "OpAtomicAnd"}, + BuiltinData{"atomicOr", "OpAtomicOr"}, + BuiltinData{"atomicXor", "OpAtomicXor"})); + +TEST_F(BuiltinBuilderTest, Call_AtomicExchange) { + // struct S { + // u : atomic; + // i : atomic; + // } + // + // @binding(1) @group(2) var b : S; + // + // fn a_func() { + // var u = 10u; + // var i = 10i; + // let r : u32 = atomicExchange(&b.u, u); + // let s : i32 = atomicExchange(&b.i, i); + // } + auto* s = Structure("S", { + Member("u", ty.atomic()), + Member("i", ty.atomic()), + }); + GlobalVar("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite, + ast::AttributeList{ + create(1u), + create(2u), + }); + + Func("a_func", {}, ty.void_(), + ast::StatementList{ + Decl(Var("u", nullptr, Expr(10_u))), + Decl(Var("i", nullptr, Expr(10_i))), + Decl(Let("r", ty.u32(), + Call("atomicExchange", AddressOf(MemberAccessor("b", "u")), "u"))), + Decl(Let("s", ty.i32(), + Call("atomicExchange", AddressOf(MemberAccessor("b", "i")), "i"))), + }, + ast::AttributeList{Stage(ast::PipelineStage::kFragment)}); + + spirv::Builder& b = SanitizeAndBuild(); + + ASSERT_TRUE(b.Build()) << b.error(); + + ASSERT_EQ(b.functions().size(), 1_u); + + auto* expected_types = R"(%4 = OpTypeInt 32 0 +%5 = OpTypeInt 32 1 +%3 = OpTypeStruct %4 %5 +%2 = OpTypePointer StorageBuffer %3 +%1 = OpVariable %2 StorageBuffer +%7 = OpTypeVoid +%6 = OpTypeFunction %7 +%10 = OpConstant %4 10 +%12 = OpTypePointer Function %4 +%13 = OpConstantNull %4 +%14 = OpConstant %5 10 +%16 = OpTypePointer Function %5 +%17 = OpConstantNull %5 +%19 = OpConstant %4 1 +%20 = OpConstant %4 0 +%22 = OpTypePointer StorageBuffer %4 +%27 = OpTypePointer StorageBuffer %5 +)"; + auto got_types = DumpInstructions(b.types()); + EXPECT_EQ(expected_types, got_types); + + auto* expected_instructions = R"(OpStore %11 %10 +OpStore %15 %14 +%23 = OpAccessChain %22 %1 %20 +%24 = OpLoad %4 %11 +%18 = OpAtomicExchange %4 %23 %19 %20 %24 +%28 = OpAccessChain %27 %1 %19 +%29 = OpLoad %5 %15 +%25 = OpAtomicExchange %5 %28 %19 %20 %29 +OpReturn +)"; + auto got_instructions = DumpInstructions(b.functions()[0].instructions()); + EXPECT_EQ(expected_instructions, got_instructions); + + Validate(b); +} + +TEST_F(BuiltinBuilderTest, Call_AtomicCompareExchangeWeak) { + // struct S { + // u : atomic, + // i : atomic, + // } + // + // @binding(1) @group(2) var b : S; + // + // fn a_func() { + // let u = atomicCompareExchangeWeak(&b.u, 10u, 20u); + // let i = atomicCompareExchangeWeak(&b.i, 10, 10); + // } + auto* s = Structure("S", { + Member("u", ty.atomic()), + Member("i", ty.atomic()), + }); + GlobalVar("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite, + ast::AttributeList{ + create(1u), + create(2u), + }); + + Func("a_func", {}, ty.void_(), + ast::StatementList{ + Decl(Let("u", nullptr, + Call("atomicCompareExchangeWeak", AddressOf(MemberAccessor("b", "u")), 10_u, + 20_u))), + Decl(Let("i", nullptr, + Call("atomicCompareExchangeWeak", AddressOf(MemberAccessor("b", "i")), 10_i, + 20_i))), + }, + ast::AttributeList{Stage(ast::PipelineStage::kFragment)}); + + spirv::Builder& b = SanitizeAndBuild(); + + ASSERT_TRUE(b.Build()) << b.error(); + + ASSERT_EQ(b.functions().size(), 1_u); + + auto* expected_types = R"(%4 = OpTypeInt 32 0 +%5 = OpTypeInt 32 1 +%3 = OpTypeStruct %4 %5 +%2 = OpTypePointer StorageBuffer %3 +%1 = OpVariable %2 StorageBuffer +%7 = OpTypeVoid +%6 = OpTypeFunction %7 +%12 = OpTypeBool +%11 = OpTypeStruct %4 %12 +%13 = OpConstant %4 1 +%14 = OpConstant %4 0 +%16 = OpTypePointer StorageBuffer %4 +%18 = OpConstant %4 20 +%19 = OpConstant %4 10 +%23 = OpTypeStruct %5 %12 +%25 = OpTypePointer StorageBuffer %5 +%27 = OpConstant %5 20 +%28 = OpConstant %5 10 +)"; + auto got_types = DumpInstructions(b.types()); + EXPECT_EQ(expected_types, got_types); + + auto* expected_instructions = R"(%17 = OpAccessChain %16 %1 %14 +%20 = OpAtomicCompareExchange %4 %17 %13 %14 %14 %18 %19 +%21 = OpIEqual %12 %20 %18 +%10 = OpCompositeConstruct %11 %20 %21 +%26 = OpAccessChain %25 %1 %13 +%29 = OpAtomicCompareExchange %5 %26 %13 %14 %14 %27 %28 +%30 = OpIEqual %12 %29 %27 +%22 = OpCompositeConstruct %23 %29 %30 +OpReturn +)"; + auto got_instructions = DumpInstructions(b.functions()[0].instructions()); + EXPECT_EQ(expected_instructions, got_instructions); + + Validate(b); +} + +} // namespace atomic_builtin_tests + +// Tests for Data Packing builtins +namespace data_packing_builtin_tests { + +using Builtin_Builder_DataPacking_Test = BuiltinBuilderTestWithParam; +TEST_P(Builtin_Builder_DataPacking_Test, Binary) { + auto param = GetParam(); + + bool pack4 = param.name == "pack4x8snorm" || param.name == "pack4x8unorm"; + auto* call = pack4 ? Call(param.name, vec4(1_f, 1_f, 1_f, 1_f)) + : Call(param.name, vec2(1_f, 1_f)); + auto* func = Func("a_func", {}, ty.void_(), {CallStmt(call)}); + + spirv::Builder& b = Build(); + + ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); + + if (pack4) { + auto got = DumpBuilder(b); + auto expect = R"(%7 = OpExtInstImport "GLSL.std.450" +OpName %3 "a_func" +%2 = OpTypeVoid +%1 = OpTypeFunction %2 +%6 = OpTypeInt 32 0 +%9 = OpTypeFloat 32 +%8 = OpTypeVector %9 4 +%10 = OpConstant %9 1 +%11 = OpConstantComposite %8 %10 %10 %10 %10 +%3 = OpFunction %2 None %1 +%4 = OpLabel +%5 = OpExtInst %6 %7 )" + + param.op + + R"( %11 +OpReturn +OpFunctionEnd +)"; + EXPECT_EQ(got, expect); + } else { + auto got = DumpBuilder(b); + auto expect = R"(%7 = OpExtInstImport "GLSL.std.450" +OpName %3 "a_func" +%2 = OpTypeVoid +%1 = OpTypeFunction %2 +%6 = OpTypeInt 32 0 +%9 = OpTypeFloat 32 +%8 = OpTypeVector %9 2 +%10 = OpConstant %9 1 +%11 = OpConstantComposite %8 %10 %10 +%3 = OpFunction %2 None %1 +%4 = OpLabel +%5 = OpExtInst %6 %7 )" + + param.op + + R"( %11 +OpReturn +OpFunctionEnd +)"; + EXPECT_EQ(got, expect); + } +} + +INSTANTIATE_TEST_SUITE_P(BuiltinBuilderTest, + Builtin_Builder_DataPacking_Test, + testing::Values(BuiltinData{"pack4x8snorm", "PackSnorm4x8"}, + BuiltinData{"pack4x8unorm", "PackUnorm4x8"}, + BuiltinData{"pack2x16snorm", "PackSnorm2x16"}, + BuiltinData{"pack2x16unorm", "PackUnorm2x16"}, + BuiltinData{"pack2x16float", "PackHalf2x16"})); + +} // namespace data_packing_builtin_tests + +// Tests for Data Unpacking builtins +namespace data_unpacking_builtin_tests { + +using Builtin_Builder_DataUnpacking_Test = BuiltinBuilderTestWithParam; +TEST_P(Builtin_Builder_DataUnpacking_Test, Binary) { + auto param = GetParam(); + + bool pack4 = param.name == "unpack4x8snorm" || param.name == "unpack4x8unorm"; + auto* func = Func("a_func", {}, ty.void_(), {CallStmt(Call(param.name, 1_u))}); + + spirv::Builder& b = Build(); + + ASSERT_TRUE(b.GenerateFunction(func)) << b.error(); + + if (pack4) { + auto got = DumpBuilder(b); + auto expect = R"(%8 = OpExtInstImport "GLSL.std.450" +OpName %3 "a_func" +%2 = OpTypeVoid +%1 = OpTypeFunction %2 +%7 = OpTypeFloat 32 +%6 = OpTypeVector %7 4 +%9 = OpTypeInt 32 0 +%10 = OpConstant %9 1 +%3 = OpFunction %2 None %1 +%4 = OpLabel +%5 = OpExtInst %6 %8 )" + + param.op + + R"( %10 +OpReturn +OpFunctionEnd +)"; + EXPECT_EQ(got, expect); + } else { + auto got = DumpBuilder(b); + auto expect = R"(%8 = OpExtInstImport "GLSL.std.450" +OpName %3 "a_func" +%2 = OpTypeVoid +%1 = OpTypeFunction %2 +%7 = OpTypeFloat 32 +%6 = OpTypeVector %7 2 +%9 = OpTypeInt 32 0 +%10 = OpConstant %9 1 +%3 = OpFunction %2 None %1 +%4 = OpLabel +%5 = OpExtInst %6 %8 )" + + param.op + + R"( %10 +OpReturn +OpFunctionEnd +)"; + EXPECT_EQ(got, expect); + } +} + +INSTANTIATE_TEST_SUITE_P(BuiltinBuilderTest, + Builtin_Builder_DataUnpacking_Test, + testing::Values(BuiltinData{"unpack4x8snorm", "UnpackSnorm4x8"}, + BuiltinData{"unpack4x8unorm", "UnpackUnorm4x8"}, + BuiltinData{"unpack2x16snorm", "UnpackSnorm2x16"}, + BuiltinData{"unpack2x16unorm", "UnpackUnorm2x16"}, + BuiltinData{"unpack2x16float", "UnpackHalf2x16"})); + +} // namespace data_unpacking_builtin_tests + +// Tests for Synchronization builtins +namespace synchronization_builtin_tests { + +TEST_F(BuiltinBuilderTest, Call_WorkgroupBarrier) { + Func("f", {}, ty.void_(), + ast::StatementList{ + CallStmt(Call("workgroupBarrier")), + }, + ast::AttributeList{ + Stage(ast::PipelineStage::kCompute), + WorkgroupSize(1_i), + }); + + spirv::Builder& b = Build(); + + ASSERT_TRUE(b.Build()) << b.error(); + + ASSERT_EQ(b.functions().size(), 1_u); + + auto* expected_types = R"(%2 = OpTypeVoid +%1 = OpTypeFunction %2 +%6 = OpTypeInt 32 0 +%7 = OpConstant %6 2 +%8 = OpConstant %6 264 +)"; + auto got_types = DumpInstructions(b.types()); + EXPECT_EQ(expected_types, got_types); + + auto* expected_instructions = R"(OpControlBarrier %7 %7 %8 +OpReturn +)"; + auto got_instructions = DumpInstructions(b.functions()[0].instructions()); + EXPECT_EQ(expected_instructions, got_instructions); + + Validate(b); +} + +TEST_F(BuiltinBuilderTest, Call_StorageBarrier) { + Func("f", {}, ty.void_(), + ast::StatementList{ + CallStmt(Call("storageBarrier")), + }, + ast::AttributeList{ + Stage(ast::PipelineStage::kCompute), + WorkgroupSize(1_i), + }); + + spirv::Builder& b = Build(); + + ASSERT_TRUE(b.Build()) << b.error(); + + ASSERT_EQ(b.functions().size(), 1_u); + + auto* expected_types = R"(%2 = OpTypeVoid +%1 = OpTypeFunction %2 +%6 = OpTypeInt 32 0 +%7 = OpConstant %6 2 +%8 = OpConstant %6 72 +)"; + auto got_types = DumpInstructions(b.types()); + EXPECT_EQ(expected_types, got_types); + + auto* expected_instructions = R"(OpControlBarrier %7 %7 %8 +OpReturn +)"; + auto got_instructions = DumpInstructions(b.functions()[0].instructions()); + EXPECT_EQ(expected_instructions, got_instructions); + + Validate(b); +} + +} // namespace synchronization_builtin_tests + +// Tests for DP4A builtins, tint:1497 +namespace DP4A_builtin_tests { + TEST_F(BuiltinBuilderTest, Call_Dot4I8Packed) { auto* ext = create(Source{Source::Range{Source::Location{10, 2}, Source::Location{10, 5}}}, @@ -2881,5 +3091,7 @@ OpFunctionEnd EXPECT_EQ(got, expect); } +} // namespace DP4A_builtin_tests + } // namespace } // namespace tint::writer::spirv