[tint] Polyfill subgroupShuffle param range (clamped) This now includes SubgroupShuffleDown, SubgroupShuffleUp and SubgroupShuffleXor variants. Bug: 435246627 Change-Id: I7238e4f628bf8508168e0c5d01a89d752aa48ee8 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/259014 Commit-Queue: Peter McNeeley <petermcneeley@google.com> Reviewed-by: James Price <jrprice@google.com>
diff --git a/src/tint/lang/spirv/writer/raise/builtin_polyfill.cc b/src/tint/lang/spirv/writer/raise/builtin_polyfill.cc index 3adeb01..3f989f5 100644 --- a/src/tint/lang/spirv/writer/raise/builtin_polyfill.cc +++ b/src/tint/lang/spirv/writer/raise/builtin_polyfill.cc
@@ -216,6 +216,9 @@ case core::BuiltinFn::kSelect: case core::BuiltinFn::kSubgroupBroadcast: case core::BuiltinFn::kSubgroupShuffle: + case core::BuiltinFn::kSubgroupShuffleDown: + case core::BuiltinFn::kSubgroupShuffleUp: + case core::BuiltinFn::kSubgroupShuffleXor: case core::BuiltinFn::kTextureDimensions: case core::BuiltinFn::kTextureGather: case core::BuiltinFn::kTextureGatherCompare: @@ -284,6 +287,9 @@ SubgroupBroadcast(builtin); break; case core::BuiltinFn::kSubgroupShuffle: + case core::BuiltinFn::kSubgroupShuffleDown: + case core::BuiltinFn::kSubgroupShuffleUp: + case core::BuiltinFn::kSubgroupShuffleXor: SubgroupShuffle(builtin, config.subgroup_shuffle_clamped); break; case core::BuiltinFn::kTextureDimensions: @@ -1095,20 +1101,22 @@ builtin->Destroy(); } - /// Handle a SubgroupShuffle() builtin. + /// Handles SubgroupShuffle(), SubgroupShuffleDown(), SubgroupShuffleUp(), SubgroupShuffleXor() + /// builtins. /// @param builtin the builtin call instruction void SubgroupShuffle(core::ir::CoreBuiltinCall* builtin, bool clamp_subgroup_shuffle) { TINT_ASSERT(builtin->Args().Length() == 2); - auto* id = builtin->Args()[1]; - - // Id must be an unsigned integer scalar, so bitcast if necessary. - if (id->Type()->IsSignedIntegerScalar()) { - auto* cast = b.Bitcast(ty.u32(), id); + // The second argument is either 'id' , 'delta', or 'mask'. + // All must be bound by [0, 128) + auto* arg2 = builtin->Args()[1]; + // arg2 must be an unsigned integer scalar, so bitcast if necessary. + if (arg2->Type()->IsSignedIntegerScalar()) { + auto* cast = b.Bitcast(ty.u32(), arg2); cast->InsertBefore(builtin); builtin->SetArg(1, cast->Result()); } - /// Polyfill a `subgroupShuffle()` builtin call with one that has clamped the 'id' param + /// Polyfill a `subgroupShuffleX` builtin call with one that has clamped the arg2 param if (clamp_subgroup_shuffle) { auto* shuffle_id = builtin->Args()[1]; auto* mask_max_subgroup_size =
diff --git a/src/tint/lang/spirv/writer/raise/builtin_polyfill_test.cc b/src/tint/lang/spirv/writer/raise/builtin_polyfill_test.cc index 77dd296..e7e0a67 100644 --- a/src/tint/lang/spirv/writer/raise/builtin_polyfill_test.cc +++ b/src/tint/lang/spirv/writer/raise/builtin_polyfill_test.cc
@@ -1286,6 +1286,308 @@ EXPECT_EQ(expect, str()); } +TEST_F(SpirvWriter_BuiltinPolyfillTest, SubgroupShuffleDown_Clamped) { + auto* val = b.FunctionParam("val", ty.i32()); + auto* delta = b.FunctionParam("delta", ty.u32()); + auto* func = b.Function("foo", ty.i32()); + func->SetParams({val, delta}); + + b.Append(func->Block(), [&] { + auto* result = b.Call(ty.i32(), core::BuiltinFn::kSubgroupShuffleDown, val, delta); + b.Return(func, result); + }); + + auto* src = R"( +%foo = func(%val:i32, %delta:u32):i32 { + $B1: { + %4:i32 = subgroupShuffleDown %val, %delta + ret %4 + } +} +)"; + EXPECT_EQ(src, str()); + + auto* expect = R"( +%foo = func(%val:i32, %delta:u32):i32 { + $B1: { + %4:u32 = and %delta, 127u + %5:i32 = subgroupShuffleDown %val, %4 + ret %5 + } +} +)"; + + PolyfillConfig config{.subgroup_shuffle_clamped = true}; + Run(BuiltinPolyfill, config); + + EXPECT_EQ(expect, str()); +} + +TEST_F(SpirvWriter_BuiltinPolyfillTest, SubgroupShuffleDown_SignedDelta_Clamped) { + auto* val = b.FunctionParam("val", ty.i32()); + auto* delta = b.FunctionParam("delta", ty.u32()); + auto* func = b.Function("foo", ty.i32()); + func->SetParams({val, delta}); + + b.Append(func->Block(), [&] { + auto* result = b.Call(ty.i32(), core::BuiltinFn::kSubgroupShuffleDown, val, delta); + b.Return(func, result); + }); + + auto* src = R"( +%foo = func(%val:i32, %delta:u32):i32 { + $B1: { + %4:i32 = subgroupShuffleDown %val, %delta + ret %4 + } +} +)"; + EXPECT_EQ(src, str()); + + auto* expect = R"( +%foo = func(%val:i32, %delta:u32):i32 { + $B1: { + %4:u32 = and %delta, 127u + %5:i32 = subgroupShuffleDown %val, %4 + ret %5 + } +} +)"; + + PolyfillConfig config{.subgroup_shuffle_clamped = true}; + Run(BuiltinPolyfill, config); + + EXPECT_EQ(expect, str()); +} + +TEST_F(SpirvWriter_BuiltinPolyfillTest, SubgroupShuffleDown_Unclamped) { + auto* val = b.FunctionParam("val", ty.i32()); + auto* delta = b.FunctionParam("delta", ty.u32()); + auto* func = b.Function("foo", ty.i32()); + func->SetParams({val, delta}); + + b.Append(func->Block(), [&] { + auto* result = b.Call(ty.i32(), core::BuiltinFn::kSubgroupShuffleDown, val, delta); + b.Return(func, result); + }); + + auto* src = R"( +%foo = func(%val:i32, %delta:u32):i32 { + $B1: { + %4:i32 = subgroupShuffleDown %val, %delta + ret %4 + } +} +)"; + EXPECT_EQ(src, str()); + + PolyfillConfig config{.subgroup_shuffle_clamped = false}; + Run(BuiltinPolyfill, config); + + EXPECT_EQ(src, str()); +} + +TEST_F(SpirvWriter_BuiltinPolyfillTest, SubgroupShuffleDown_SignedDelta_Unclamped) { + auto* val = b.FunctionParam("val", ty.i32()); + auto* delta = b.FunctionParam("delta", ty.u32()); + auto* func = b.Function("foo", ty.i32()); + func->SetParams({val, delta}); + + b.Append(func->Block(), [&] { + auto* result = b.Call(ty.i32(), core::BuiltinFn::kSubgroupShuffleDown, val, delta); + b.Return(func, result); + }); + + auto* src = R"( +%foo = func(%val:i32, %delta:u32):i32 { + $B1: { + %4:i32 = subgroupShuffleDown %val, %delta + ret %4 + } +} +)"; + EXPECT_EQ(src, str()); + + auto* expect = R"( +%foo = func(%val:i32, %delta:u32):i32 { + $B1: { + %4:i32 = subgroupShuffleDown %val, %delta + ret %4 + } +} +)"; + + PolyfillConfig config{.subgroup_shuffle_clamped = false}; + Run(BuiltinPolyfill, config); + + EXPECT_EQ(expect, str()); +} + +TEST_F(SpirvWriter_BuiltinPolyfillTest, SubgroupShuffleUp_Clamped) { + auto* val = b.FunctionParam("val", ty.i32()); + auto* delta = b.FunctionParam("delta", ty.u32()); + auto* func = b.Function("foo", ty.i32()); + func->SetParams({val, delta}); + + b.Append(func->Block(), [&] { + auto* result = b.Call(ty.i32(), core::BuiltinFn::kSubgroupShuffleUp, val, delta); + b.Return(func, result); + }); + + auto* src = R"( +%foo = func(%val:i32, %delta:u32):i32 { + $B1: { + %4:i32 = subgroupShuffleUp %val, %delta + ret %4 + } +} +)"; + EXPECT_EQ(src, str()); + + auto* expect = R"( +%foo = func(%val:i32, %delta:u32):i32 { + $B1: { + %4:u32 = and %delta, 127u + %5:i32 = subgroupShuffleUp %val, %4 + ret %5 + } +} +)"; + + PolyfillConfig config{.subgroup_shuffle_clamped = true}; + Run(BuiltinPolyfill, config); + + EXPECT_EQ(expect, str()); +} + +TEST_F(SpirvWriter_BuiltinPolyfillTest, SubgroupShuffleUp_SignedDelta_Clamped) { + auto* val = b.FunctionParam("val", ty.i32()); + auto* delta = b.FunctionParam("delta", ty.u32()); + auto* func = b.Function("foo", ty.i32()); + func->SetParams({val, delta}); + + b.Append(func->Block(), [&] { + auto* result = b.Call(ty.i32(), core::BuiltinFn::kSubgroupShuffleUp, val, delta); + b.Return(func, result); + }); + + auto* src = R"( +%foo = func(%val:i32, %delta:u32):i32 { + $B1: { + %4:i32 = subgroupShuffleUp %val, %delta + ret %4 + } +} +)"; + EXPECT_EQ(src, str()); + + auto* expect = R"( +%foo = func(%val:i32, %delta:u32):i32 { + $B1: { + %4:u32 = and %delta, 127u + %5:i32 = subgroupShuffleUp %val, %4 + ret %5 + } +} +)"; + + PolyfillConfig config{.subgroup_shuffle_clamped = true}; + Run(BuiltinPolyfill, config); + + EXPECT_EQ(expect, str()); +} + +TEST_F(SpirvWriter_BuiltinPolyfillTest, SubgroupShuffleUp_Unclamped) { + auto* val = b.FunctionParam("val", ty.i32()); + auto* delta = b.FunctionParam("delta", ty.u32()); + auto* func = b.Function("foo", ty.i32()); + func->SetParams({val, delta}); + + b.Append(func->Block(), [&] { + auto* result = b.Call(ty.i32(), core::BuiltinFn::kSubgroupShuffleUp, val, delta); + b.Return(func, result); + }); + + auto* src = R"( +%foo = func(%val:i32, %delta:u32):i32 { + $B1: { + %4:i32 = subgroupShuffleUp %val, %delta + ret %4 + } +} +)"; + EXPECT_EQ(src, str()); + + PolyfillConfig config{.subgroup_shuffle_clamped = false}; + Run(BuiltinPolyfill, config); + + EXPECT_EQ(src, str()); +} + +TEST_F(SpirvWriter_BuiltinPolyfillTest, SubgroupShuffleXor_Clamped) { + auto* val = b.FunctionParam("val", ty.i32()); + auto* mask = b.FunctionParam("mask", ty.u32()); + auto* func = b.Function("foo", ty.i32()); + func->SetParams({val, mask}); + + b.Append(func->Block(), [&] { + auto* result = b.Call(ty.i32(), core::BuiltinFn::kSubgroupShuffleXor, val, mask); + b.Return(func, result); + }); + + auto* src = R"( +%foo = func(%val:i32, %mask:u32):i32 { + $B1: { + %4:i32 = subgroupShuffleXor %val, %mask + ret %4 + } +} +)"; + EXPECT_EQ(src, str()); + + auto* expect = R"( +%foo = func(%val:i32, %mask:u32):i32 { + $B1: { + %4:u32 = and %mask, 127u + %5:i32 = subgroupShuffleXor %val, %4 + ret %5 + } +} +)"; + + PolyfillConfig config{.subgroup_shuffle_clamped = true}; + Run(BuiltinPolyfill, config); + + EXPECT_EQ(expect, str()); +} + +TEST_F(SpirvWriter_BuiltinPolyfillTest, SubgroupShuffleXor_Unclamped) { + auto* val = b.FunctionParam("val", ty.i32()); + auto* mask = b.FunctionParam("mask", ty.u32()); + auto* func = b.Function("foo", ty.i32()); + func->SetParams({val, mask}); + + b.Append(func->Block(), [&] { + auto* result = b.Call(ty.i32(), core::BuiltinFn::kSubgroupShuffleXor, val, mask); + b.Return(func, result); + }); + + auto* src = R"( +%foo = func(%val:i32, %mask:u32):i32 { + $B1: { + %4:i32 = subgroupShuffleXor %val, %mask + ret %4 + } +} +)"; + EXPECT_EQ(src, str()); + + PolyfillConfig config{.subgroup_shuffle_clamped = false}; + Run(BuiltinPolyfill, config); + + EXPECT_EQ(src, str()); +} + TEST_F(SpirvWriter_BuiltinPolyfillTest, TextureLoad_2D) { capabilities = core::ir::Capability::kAllowNonCoreTypes;