transform: Avoid symbol collision in Canonicalize IO
Correctly rename fields when combining two or more input structures together into a single input structure.
Bug: chromium:1251009
Change-Id: I0c7ab5ed3116b97035e100d1ef96e772e819f640
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/64545
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: James Price <jrprice@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
diff --git a/src/transform/canonicalize_entry_point_io.cc b/src/transform/canonicalize_entry_point_io.cc
index 91568cb..4814e12 100644
--- a/src/transform/canonicalize_entry_point_io.cc
+++ b/src/transform/canonicalize_entry_point_io.cc
@@ -112,6 +112,8 @@
std::vector<OutputValue> wrapper_output_values;
/// The body of the wrapper function.
ast::StatementList wrapper_body;
+ /// Input names used by the entrypoint
+ std::unordered_set<std::string> input_names;
/// Constructor
/// @param context the clone context
@@ -171,29 +173,35 @@
ctx.dst->ID(), ast::DisabledValidation::kIgnoreStorageClass));
// Create the global variable and use its value for the shader input.
- auto var = ctx.dst->Symbols().New(name);
- ast::Expression* value = ctx.dst->Expr(var);
+ auto symbol = ctx.dst->Symbols().New(name);
+ ast::Expression* value = ctx.dst->Expr(symbol);
if (HasSampleMask(attributes)) {
// Vulkan requires the type of a SampleMask builtin to be an array.
// Declare it as array<u32, 1> and then load the first element.
type = ctx.dst->ty.array(type, 1);
value = ctx.dst->IndexAccessor(value, 0);
}
- ctx.dst->Global(var, type, ast::StorageClass::kInput,
+ ctx.dst->Global(symbol, type, ast::StorageClass::kInput,
std::move(attributes));
return value;
} else if (cfg.shader_style == ShaderStyle::kMsl &&
ast::HasDecoration<ast::BuiltinDecoration>(attributes)) {
// If this input is a builtin and we are targeting MSL, then add it to the
// parameter list and pass it directly to the inner function.
+ Symbol symbol = input_names.emplace(name).second
+ ? ctx.dst->Symbols().Register(name)
+ : ctx.dst->Symbols().New(name);
wrapper_ep_parameters.push_back(
- ctx.dst->Param(name, type, std::move(attributes)));
- return ctx.dst->Expr(name);
+ ctx.dst->Param(symbol, type, std::move(attributes)));
+ return ctx.dst->Expr(symbol);
} else {
// Otherwise, move it to the new structure member list.
+ Symbol symbol = input_names.emplace(name).second
+ ? ctx.dst->Symbols().Register(name)
+ : ctx.dst->Symbols().New(name);
wrapper_struct_param_members.push_back(
- ctx.dst->Member(name, type, std::move(attributes)));
- return ctx.dst->MemberAccessor(InputStructSymbol(), name);
+ ctx.dst->Member(symbol, type, std::move(attributes)));
+ return ctx.dst->MemberAccessor(InputStructSymbol(), symbol);
}
}
diff --git a/src/transform/canonicalize_entry_point_io_test.cc b/src/transform/canonicalize_entry_point_io_test.cc
index 1dc93b1..084d221 100644
--- a/src/transform/canonicalize_entry_point_io_test.cc
+++ b/src/transform/canonicalize_entry_point_io_test.cc
@@ -2062,18 +2062,31 @@
var<private> vertex_point_size_1 : f32;
var<private> vertex_point_size_2 : f32;
+struct VertIn1 {
+ [[location(0)]] collide : f32;
+};
+
+struct VertIn2 {
+ [[location(1)]] collide : f32;
+};
+
struct VertOut {
[[location(0)]] vertex_point_size : f32;
[[builtin(position)]] vertex_point_size_1 : vec4<f32>;
};
[[stage(vertex)]]
-fn vert_main() -> VertOut {
+fn vert_main(collide : VertIn1, collide_1 : VertIn2) -> VertOut {
+ let x = collide.collide + collide_1.collide;
return VertOut();
}
)";
auto* expect = R"(
+[[location(0), internal(disable_validation__ignore_storage_class)]] var<in> collide_2 : f32;
+
+[[location(1), internal(disable_validation__ignore_storage_class)]] var<in> collide_3 : f32;
+
[[location(0), internal(disable_validation__ignore_storage_class)]] var<out> vertex_point_size_3 : f32;
[[builtin(position), internal(disable_validation__ignore_storage_class)]] var<out> vertex_point_size_1_1 : vec4<f32>;
@@ -2086,18 +2099,27 @@
var<private> vertex_point_size_2 : f32;
+struct VertIn1 {
+ collide : f32;
+};
+
+struct VertIn2 {
+ collide : f32;
+};
+
struct VertOut {
vertex_point_size : f32;
vertex_point_size_1 : vec4<f32>;
};
-fn vert_main_inner() -> VertOut {
+fn vert_main_inner(collide : VertIn1, collide_1 : VertIn2) -> VertOut {
+ let x = (collide.collide + collide_1.collide);
return VertOut();
}
[[stage(vertex)]]
fn vert_main() {
- let inner_result = vert_main_inner();
+ let inner_result = vert_main_inner(VertIn1(collide_2), VertIn2(collide_3));
vertex_point_size_3 = inner_result.vertex_point_size;
vertex_point_size_1_1 = inner_result.vertex_point_size_1;
vertex_point_size_4 = 1.0;
@@ -2114,24 +2136,48 @@
TEST_F(CanonicalizeEntryPointIOTest, EmitVertexPointSize_AvoidNameClash_Msl) {
auto* src = R"(
+struct VertIn1 {
+ [[location(0)]] collide : f32;
+};
+
+struct VertIn2 {
+ [[location(1)]] collide : f32;
+};
+
struct VertOut {
[[location(0)]] vertex_point_size : vec4<f32>;
[[builtin(position)]] vertex_point_size_1 : vec4<f32>;
};
[[stage(vertex)]]
-fn vert_main() -> VertOut {
+fn vert_main(collide : VertIn1, collide_1 : VertIn2) -> VertOut {
+ let x = collide.collide + collide_1.collide;
return VertOut();
}
)";
auto* expect = R"(
+struct VertIn1 {
+ collide : f32;
+};
+
+struct VertIn2 {
+ collide : f32;
+};
+
struct VertOut {
vertex_point_size : vec4<f32>;
vertex_point_size_1 : vec4<f32>;
};
-struct tint_symbol {
+struct tint_symbol_1 {
+ [[location(0)]]
+ collide : f32;
+ [[location(1)]]
+ collide_2 : f32;
+};
+
+struct tint_symbol_2 {
[[location(0)]]
vertex_point_size : vec4<f32>;
[[builtin(position)]]
@@ -2140,14 +2186,15 @@
vertex_point_size_2 : f32;
};
-fn vert_main_inner() -> VertOut {
+fn vert_main_inner(collide : VertIn1, collide_1 : VertIn2) -> VertOut {
+ let x = (collide.collide + collide_1.collide);
return VertOut();
}
[[stage(vertex)]]
-fn vert_main() -> tint_symbol {
- let inner_result = vert_main_inner();
- var wrapper_result : tint_symbol;
+fn vert_main(tint_symbol : tint_symbol_1) -> tint_symbol_2 {
+ let inner_result = vert_main_inner(VertIn1(tint_symbol.collide), VertIn2(tint_symbol.collide_2));
+ var wrapper_result : tint_symbol_2;
wrapper_result.vertex_point_size = inner_result.vertex_point_size;
wrapper_result.vertex_point_size_1 = inner_result.vertex_point_size_1;
wrapper_result.vertex_point_size_2 = 1.0;
@@ -2163,6 +2210,82 @@
EXPECT_EQ(expect, str(got));
}
+TEST_F(CanonicalizeEntryPointIOTest, EmitVertexPointSize_AvoidNameClash_Hlsl) {
+ auto* src = R"(
+struct VertIn1 {
+ [[location(0)]] collide : f32;
+};
+
+struct VertIn2 {
+ [[location(1)]] collide : f32;
+};
+
+struct VertOut {
+ [[location(0)]] vertex_point_size : vec4<f32>;
+ [[builtin(position)]] vertex_point_size_1 : vec4<f32>;
+};
+
+[[stage(vertex)]]
+fn vert_main(collide : VertIn1, collide_1 : VertIn2) -> VertOut {
+ let x = collide.collide + collide_1.collide;
+ return VertOut();
+}
+)";
+
+ auto* expect = R"(
+struct VertIn1 {
+ collide : f32;
+};
+
+struct VertIn2 {
+ collide : f32;
+};
+
+struct VertOut {
+ vertex_point_size : vec4<f32>;
+ vertex_point_size_1 : vec4<f32>;
+};
+
+struct tint_symbol_1 {
+ [[location(0)]]
+ collide : f32;
+ [[location(1)]]
+ collide_2 : f32;
+};
+
+struct tint_symbol_2 {
+ [[location(0)]]
+ vertex_point_size : vec4<f32>;
+ [[builtin(position)]]
+ vertex_point_size_1 : vec4<f32>;
+ [[builtin(pointsize)]]
+ vertex_point_size_2 : f32;
+};
+
+fn vert_main_inner(collide : VertIn1, collide_1 : VertIn2) -> VertOut {
+ let x = (collide.collide + collide_1.collide);
+ return VertOut();
+}
+
+[[stage(vertex)]]
+fn vert_main(tint_symbol : tint_symbol_1) -> tint_symbol_2 {
+ let inner_result = vert_main_inner(VertIn1(tint_symbol.collide), VertIn2(tint_symbol.collide_2));
+ var wrapper_result : tint_symbol_2;
+ wrapper_result.vertex_point_size = inner_result.vertex_point_size;
+ wrapper_result.vertex_point_size_1 = inner_result.vertex_point_size_1;
+ wrapper_result.vertex_point_size_2 = 1.0;
+ return wrapper_result;
+}
+)";
+
+ DataMap data;
+ data.Add<CanonicalizeEntryPointIO::Config>(
+ CanonicalizeEntryPointIO::ShaderStyle::kHlsl, 0xFFFFFFFF, true);
+ auto got = Run<CanonicalizeEntryPointIO>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+}
+
TEST_F(CanonicalizeEntryPointIOTest, SpirvSampleMaskBuiltins) {
auto* src = R"(
[[stage(fragment)]]
diff --git a/test/bug/chromium/1251009.wgsl b/test/bug/chromium/1251009.wgsl
new file mode 100644
index 0000000..4abe77e
--- /dev/null
+++ b/test/bug/chromium/1251009.wgsl
@@ -0,0 +1,19 @@
+struct VertexInputs0 {
+ [[builtin(vertex_index)]] vertex_index : u32;
+ [[location(0)]] loc0 : i32;
+};
+struct VertexInputs1 {
+ [[location(2)]] loc1 : u32;
+ [[location(3)]] loc3 : vec4<f32>;
+};
+
+[[stage(vertex)]]
+fn main(
+ inputs0 : VertexInputs0,
+ [[location(1)]] loc1 : u32,
+ [[builtin(instance_index)]] instance_index : u32,
+ inputs1 : VertexInputs1,
+) -> [[builtin(position)]] vec4<f32> {
+ let foo : u32 = inputs0.vertex_index + instance_index;
+ return vec4<f32>();
+}
diff --git a/test/bug/chromium/1251009.wgsl.expected.hlsl b/test/bug/chromium/1251009.wgsl.expected.hlsl
new file mode 100644
index 0000000..d0c8230
--- /dev/null
+++ b/test/bug/chromium/1251009.wgsl.expected.hlsl
@@ -0,0 +1,33 @@
+struct VertexInputs0 {
+ uint vertex_index;
+ int loc0;
+};
+struct VertexInputs1 {
+ uint loc1;
+ float4 loc3;
+};
+struct tint_symbol_1 {
+ int loc0 : TEXCOORD0;
+ uint loc1 : TEXCOORD1;
+ uint loc1_1 : TEXCOORD2;
+ float4 loc3 : TEXCOORD3;
+ uint vertex_index : SV_VertexID;
+ uint instance_index : SV_InstanceID;
+};
+struct tint_symbol_2 {
+ float4 value : SV_Position;
+};
+
+float4 main_inner(VertexInputs0 inputs0, uint loc1, uint instance_index, VertexInputs1 inputs1) {
+ const uint foo = (inputs0.vertex_index + instance_index);
+ return float4(0.0f, 0.0f, 0.0f, 0.0f);
+}
+
+tint_symbol_2 main(tint_symbol_1 tint_symbol) {
+ const VertexInputs0 tint_symbol_3 = {tint_symbol.vertex_index, tint_symbol.loc0};
+ const VertexInputs1 tint_symbol_4 = {tint_symbol.loc1_1, tint_symbol.loc3};
+ const float4 inner_result = main_inner(tint_symbol_3, tint_symbol.loc1, tint_symbol.instance_index, tint_symbol_4);
+ tint_symbol_2 wrapper_result = (tint_symbol_2)0;
+ wrapper_result.value = inner_result;
+ return wrapper_result;
+}
diff --git a/test/bug/chromium/1251009.wgsl.expected.msl b/test/bug/chromium/1251009.wgsl.expected.msl
new file mode 100644
index 0000000..b958532
--- /dev/null
+++ b/test/bug/chromium/1251009.wgsl.expected.msl
@@ -0,0 +1,35 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct VertexInputs0 {
+ uint vertex_index;
+ int loc0;
+};
+struct VertexInputs1 {
+ uint loc1;
+ float4 loc3;
+};
+struct tint_symbol_2 {
+ int loc0 [[attribute(0)]];
+ uint loc1 [[attribute(1)]];
+ uint loc1_1 [[attribute(2)]];
+ float4 loc3 [[attribute(3)]];
+};
+struct tint_symbol_3 {
+ float4 value [[position]];
+};
+
+float4 tint_symbol_inner(VertexInputs0 inputs0, uint loc1, uint instance_index, VertexInputs1 inputs1) {
+ uint const foo = (inputs0.vertex_index + instance_index);
+ return float4();
+}
+
+vertex tint_symbol_3 tint_symbol(uint vertex_index [[vertex_id]], uint instance_index [[instance_id]], tint_symbol_2 tint_symbol_1 [[stage_in]]) {
+ VertexInputs0 const tint_symbol_4 = {.vertex_index=vertex_index, .loc0=tint_symbol_1.loc0};
+ VertexInputs1 const tint_symbol_5 = {.loc1=tint_symbol_1.loc1_1, .loc3=tint_symbol_1.loc3};
+ float4 const inner_result = tint_symbol_inner(tint_symbol_4, tint_symbol_1.loc1, instance_index, tint_symbol_5);
+ tint_symbol_3 wrapper_result = {};
+ wrapper_result.value = inner_result;
+ return wrapper_result;
+}
+
diff --git a/test/bug/chromium/1251009.wgsl.expected.spvasm b/test/bug/chromium/1251009.wgsl.expected.spvasm
new file mode 100644
index 0000000..0a2969f
--- /dev/null
+++ b/test/bug/chromium/1251009.wgsl.expected.spvasm
@@ -0,0 +1,90 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 45
+; Schema: 0
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Vertex %main "main" %vertex_index_1 %loc0_1 %loc1_1 %instance_index_1 %loc1_2 %loc3_1 %value %vertex_point_size
+ OpName %vertex_index_1 "vertex_index_1"
+ OpName %loc0_1 "loc0_1"
+ OpName %loc1_1 "loc1_1"
+ OpName %instance_index_1 "instance_index_1"
+ OpName %loc1_2 "loc1_2"
+ OpName %loc3_1 "loc3_1"
+ OpName %value "value"
+ OpName %vertex_point_size "vertex_point_size"
+ OpName %VertexInputs0 "VertexInputs0"
+ OpMemberName %VertexInputs0 0 "vertex_index"
+ OpMemberName %VertexInputs0 1 "loc0"
+ OpName %VertexInputs1 "VertexInputs1"
+ OpMemberName %VertexInputs1 0 "loc1"
+ OpMemberName %VertexInputs1 1 "loc3"
+ OpName %main_inner "main_inner"
+ OpName %inputs0 "inputs0"
+ OpName %loc1 "loc1"
+ OpName %instance_index "instance_index"
+ OpName %inputs1 "inputs1"
+ OpName %main "main"
+ OpDecorate %vertex_index_1 BuiltIn VertexIndex
+ OpDecorate %loc0_1 Location 0
+ OpDecorate %loc1_1 Location 1
+ OpDecorate %instance_index_1 BuiltIn InstanceIndex
+ OpDecorate %loc1_2 Location 2
+ OpDecorate %loc3_1 Location 3
+ OpDecorate %value BuiltIn Position
+ OpDecorate %vertex_point_size BuiltIn PointSize
+ OpMemberDecorate %VertexInputs0 0 Offset 0
+ OpMemberDecorate %VertexInputs0 1 Offset 4
+ OpMemberDecorate %VertexInputs1 0 Offset 0
+ OpMemberDecorate %VertexInputs1 1 Offset 16
+ %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%vertex_index_1 = OpVariable %_ptr_Input_uint Input
+ %int = OpTypeInt 32 1
+%_ptr_Input_int = OpTypePointer Input %int
+ %loc0_1 = OpVariable %_ptr_Input_int Input
+ %loc1_1 = OpVariable %_ptr_Input_uint Input
+%instance_index_1 = OpVariable %_ptr_Input_uint Input
+ %loc1_2 = OpVariable %_ptr_Input_uint Input
+ %float = OpTypeFloat 32
+ %v4float = OpTypeVector %float 4
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+ %loc3_1 = OpVariable %_ptr_Input_v4float Input
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+ %16 = OpConstantNull %v4float
+ %value = OpVariable %_ptr_Output_v4float Output %16
+%_ptr_Output_float = OpTypePointer Output %float
+ %19 = OpConstantNull %float
+%vertex_point_size = OpVariable %_ptr_Output_float Output %19
+%VertexInputs0 = OpTypeStruct %uint %int
+%VertexInputs1 = OpTypeStruct %uint %v4float
+ %20 = OpTypeFunction %v4float %VertexInputs0 %uint %uint %VertexInputs1
+ %void = OpTypeVoid
+ %31 = OpTypeFunction %void
+ %float_1 = OpConstant %float 1
+ %main_inner = OpFunction %v4float None %20
+ %inputs0 = OpFunctionParameter %VertexInputs0
+ %loc1 = OpFunctionParameter %uint
+%instance_index = OpFunctionParameter %uint
+ %inputs1 = OpFunctionParameter %VertexInputs1
+ %28 = OpLabel
+ %29 = OpCompositeExtract %uint %inputs0 0
+ %30 = OpIAdd %uint %29 %instance_index
+ OpReturnValue %16
+ OpFunctionEnd
+ %main = OpFunction %void None %31
+ %34 = OpLabel
+ %36 = OpLoad %uint %vertex_index_1
+ %37 = OpLoad %int %loc0_1
+ %38 = OpCompositeConstruct %VertexInputs0 %36 %37
+ %39 = OpLoad %uint %loc1_1
+ %40 = OpLoad %uint %instance_index_1
+ %41 = OpLoad %uint %loc1_2
+ %42 = OpLoad %v4float %loc3_1
+ %43 = OpCompositeConstruct %VertexInputs1 %41 %42
+ %35 = OpFunctionCall %v4float %main_inner %38 %39 %40 %43
+ OpStore %value %35
+ OpStore %vertex_point_size %float_1
+ OpReturn
+ OpFunctionEnd
diff --git a/test/bug/chromium/1251009.wgsl.expected.wgsl b/test/bug/chromium/1251009.wgsl.expected.wgsl
new file mode 100644
index 0000000..5de6e50
--- /dev/null
+++ b/test/bug/chromium/1251009.wgsl.expected.wgsl
@@ -0,0 +1,19 @@
+struct VertexInputs0 {
+ [[builtin(vertex_index)]]
+ vertex_index : u32;
+ [[location(0)]]
+ loc0 : i32;
+};
+
+struct VertexInputs1 {
+ [[location(2)]]
+ loc1 : u32;
+ [[location(3)]]
+ loc3 : vec4<f32>;
+};
+
+[[stage(vertex)]]
+fn main(inputs0 : VertexInputs0, [[location(1)]] loc1 : u32, [[builtin(instance_index)]] instance_index : u32, inputs1 : VertexInputs1) -> [[builtin(position)]] vec4<f32> {
+ let foo : u32 = (inputs0.vertex_index + instance_index);
+ return vec4<f32>();
+}