|  | // Copyright 2021 The Tint Authors. | 
|  | // | 
|  | // Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | // you may not use this file except in compliance with the License. | 
|  | // You may obtain a copy of the License at | 
|  | // | 
|  | //     http://www.apache.org/licenses/LICENSE-2.0 | 
|  | // | 
|  | // Unless required by applicable law or agreed to in writing, software | 
|  | // distributed under the License is distributed on an "AS IS" BASIS, | 
|  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | // See the License for the specific language governing permissions and | 
|  | // limitations under the License. | 
|  |  | 
|  | #include "src/transform/canonicalize_entry_point_io.h" | 
|  |  | 
|  | #include "src/transform/test_helper.h" | 
|  |  | 
|  | namespace tint { | 
|  | namespace transform { | 
|  | namespace { | 
|  |  | 
|  | using CanonicalizeEntryPointIOTest = TransformTest; | 
|  |  | 
|  | TEST_F(CanonicalizeEntryPointIOTest, Error_MissingTransformData) { | 
|  | auto* src = ""; | 
|  |  | 
|  | auto* expect = | 
|  | "error: missing transform data for " | 
|  | "tint::transform::CanonicalizeEntryPointIO"; | 
|  |  | 
|  | auto got = Run<CanonicalizeEntryPointIO>(src); | 
|  |  | 
|  | EXPECT_EQ(expect, str(got)); | 
|  | } | 
|  |  | 
|  | TEST_F(CanonicalizeEntryPointIOTest, Parameters_BuiltinsAsParameters) { | 
|  | auto* src = R"( | 
|  | [[stage(fragment)]] | 
|  | fn frag_main([[location(1)]] loc1 : f32, | 
|  | [[location(2)]] loc2 : vec4<u32>, | 
|  | [[builtin(position)]] coord : vec4<f32>) { | 
|  | var col : f32 = (coord.x * loc1); | 
|  | } | 
|  | )"; | 
|  |  | 
|  | auto* expect = R"( | 
|  | struct tint_symbol_1 { | 
|  | [[location(1)]] | 
|  | loc1 : f32; | 
|  | [[location(2)]] | 
|  | loc2 : vec4<u32>; | 
|  | }; | 
|  |  | 
|  | [[stage(fragment)]] | 
|  | fn frag_main([[builtin(position)]] coord : vec4<f32>, tint_symbol : tint_symbol_1) { | 
|  | let loc1 : f32 = tint_symbol.loc1; | 
|  | let loc2 : vec4<u32> = tint_symbol.loc2; | 
|  | var col : f32 = (coord.x * loc1); | 
|  | } | 
|  | )"; | 
|  |  | 
|  | DataMap data; | 
|  | data.Add<CanonicalizeEntryPointIO::Config>( | 
|  | CanonicalizeEntryPointIO::BuiltinStyle::kParameter); | 
|  | auto got = Run<CanonicalizeEntryPointIO>(src, data); | 
|  |  | 
|  | EXPECT_EQ(expect, str(got)); | 
|  | } | 
|  |  | 
|  | TEST_F(CanonicalizeEntryPointIOTest, Parameters_BuiltinsAsStructMembers) { | 
|  | auto* src = R"( | 
|  | [[stage(fragment)]] | 
|  | fn frag_main([[location(1)]] loc1 : f32, | 
|  | [[location(2)]] loc2 : vec4<u32>, | 
|  | [[builtin(position)]] coord : vec4<f32>) { | 
|  | var col : f32 = (coord.x * loc1); | 
|  | } | 
|  | )"; | 
|  |  | 
|  | auto* expect = R"( | 
|  | struct tint_symbol_1 { | 
|  | [[location(1)]] | 
|  | loc1 : f32; | 
|  | [[location(2)]] | 
|  | loc2 : vec4<u32>; | 
|  | [[builtin(position)]] | 
|  | coord : vec4<f32>; | 
|  | }; | 
|  |  | 
|  | [[stage(fragment)]] | 
|  | fn frag_main(tint_symbol : tint_symbol_1) { | 
|  | let loc1 : f32 = tint_symbol.loc1; | 
|  | let loc2 : vec4<u32> = tint_symbol.loc2; | 
|  | let coord : vec4<f32> = tint_symbol.coord; | 
|  | var col : f32 = (coord.x * loc1); | 
|  | } | 
|  | )"; | 
|  |  | 
|  | DataMap data; | 
|  | data.Add<CanonicalizeEntryPointIO::Config>( | 
|  | CanonicalizeEntryPointIO::BuiltinStyle::kStructMember); | 
|  | auto got = Run<CanonicalizeEntryPointIO>(src, data); | 
|  |  | 
|  | EXPECT_EQ(expect, str(got)); | 
|  | } | 
|  |  | 
|  | TEST_F(CanonicalizeEntryPointIOTest, Parameter_TypeAlias) { | 
|  | auto* src = R"( | 
|  | type myf32 = f32; | 
|  |  | 
|  | [[stage(fragment)]] | 
|  | fn frag_main([[location(1)]] loc1 : myf32) { | 
|  | var x : myf32 = loc1; | 
|  | } | 
|  | )"; | 
|  |  | 
|  | auto* expect = R"( | 
|  | type myf32 = f32; | 
|  |  | 
|  | struct tint_symbol_1 { | 
|  | [[location(1)]] | 
|  | loc1 : myf32; | 
|  | }; | 
|  |  | 
|  | [[stage(fragment)]] | 
|  | fn frag_main(tint_symbol : tint_symbol_1) { | 
|  | let loc1 : myf32 = tint_symbol.loc1; | 
|  | var x : myf32 = loc1; | 
|  | } | 
|  | )"; | 
|  |  | 
|  | DataMap data; | 
|  | data.Add<CanonicalizeEntryPointIO::Config>( | 
|  | CanonicalizeEntryPointIO::BuiltinStyle::kParameter); | 
|  | auto got = Run<CanonicalizeEntryPointIO>(src, data); | 
|  |  | 
|  | EXPECT_EQ(expect, str(got)); | 
|  | } | 
|  |  | 
|  | TEST_F(CanonicalizeEntryPointIOTest, | 
|  | Parameters_EmptyBody_BuiltinsAsParameters) { | 
|  | auto* src = R"( | 
|  | [[stage(fragment)]] | 
|  | fn frag_main([[location(1)]] loc1 : f32, | 
|  | [[location(2)]] loc2 : vec4<u32>, | 
|  | [[builtin(position)]] coord : vec4<f32>) { | 
|  | } | 
|  | )"; | 
|  |  | 
|  | auto* expect = R"( | 
|  | struct tint_symbol_1 { | 
|  | [[location(1)]] | 
|  | loc1 : f32; | 
|  | [[location(2)]] | 
|  | loc2 : vec4<u32>; | 
|  | }; | 
|  |  | 
|  | [[stage(fragment)]] | 
|  | fn frag_main([[builtin(position)]] coord : vec4<f32>, tint_symbol : tint_symbol_1) { | 
|  | let loc1 : f32 = tint_symbol.loc1; | 
|  | let loc2 : vec4<u32> = tint_symbol.loc2; | 
|  | } | 
|  | )"; | 
|  |  | 
|  | DataMap data; | 
|  | data.Add<CanonicalizeEntryPointIO::Config>( | 
|  | CanonicalizeEntryPointIO::BuiltinStyle::kParameter); | 
|  | auto got = Run<CanonicalizeEntryPointIO>(src, data); | 
|  |  | 
|  | EXPECT_EQ(expect, str(got)); | 
|  | } | 
|  |  | 
|  | TEST_F(CanonicalizeEntryPointIOTest, | 
|  | Parameters_EmptyBody_BuiltinsAsStructMembers) { | 
|  | auto* src = R"( | 
|  | [[stage(fragment)]] | 
|  | fn frag_main([[location(1)]] loc1 : f32, | 
|  | [[location(2)]] loc2 : vec4<u32>, | 
|  | [[builtin(position)]] coord : vec4<f32>) { | 
|  | } | 
|  | )"; | 
|  |  | 
|  | auto* expect = R"( | 
|  | struct tint_symbol_1 { | 
|  | [[location(1)]] | 
|  | loc1 : f32; | 
|  | [[location(2)]] | 
|  | loc2 : vec4<u32>; | 
|  | [[builtin(position)]] | 
|  | coord : vec4<f32>; | 
|  | }; | 
|  |  | 
|  | [[stage(fragment)]] | 
|  | fn frag_main(tint_symbol : tint_symbol_1) { | 
|  | let loc1 : f32 = tint_symbol.loc1; | 
|  | let loc2 : vec4<u32> = tint_symbol.loc2; | 
|  | let coord : vec4<f32> = tint_symbol.coord; | 
|  | } | 
|  | )"; | 
|  |  | 
|  | DataMap data; | 
|  | data.Add<CanonicalizeEntryPointIO::Config>( | 
|  | CanonicalizeEntryPointIO::BuiltinStyle::kStructMember); | 
|  | auto got = Run<CanonicalizeEntryPointIO>(src, data); | 
|  |  | 
|  | EXPECT_EQ(expect, str(got)); | 
|  | } | 
|  |  | 
|  | TEST_F(CanonicalizeEntryPointIOTest, StructParameters_BuiltinsAsParameters) { | 
|  | auto* src = R"( | 
|  | struct FragBuiltins { | 
|  | [[builtin(position)]] coord : vec4<f32>; | 
|  | }; | 
|  | struct FragLocations { | 
|  | [[location(1)]] loc1 : f32; | 
|  | [[location(2)]] loc2 : vec4<u32>; | 
|  | }; | 
|  |  | 
|  | [[stage(fragment)]] | 
|  | fn frag_main([[location(0)]] loc0 : f32, | 
|  | locations : FragLocations, | 
|  | builtins : FragBuiltins) { | 
|  | var col : f32 = ((builtins.coord.x * locations.loc1) + loc0); | 
|  | } | 
|  | )"; | 
|  |  | 
|  | auto* expect = R"( | 
|  | struct FragBuiltins { | 
|  | coord : vec4<f32>; | 
|  | }; | 
|  |  | 
|  | struct FragLocations { | 
|  | loc1 : f32; | 
|  | loc2 : vec4<u32>; | 
|  | }; | 
|  |  | 
|  | struct tint_symbol_2 { | 
|  | [[location(0)]] | 
|  | loc0 : f32; | 
|  | [[location(1)]] | 
|  | loc1 : f32; | 
|  | [[location(2)]] | 
|  | loc2 : vec4<u32>; | 
|  | }; | 
|  |  | 
|  | [[stage(fragment)]] | 
|  | fn frag_main([[builtin(position)]] tint_symbol_1 : vec4<f32>, tint_symbol : tint_symbol_2) { | 
|  | let loc0 : f32 = tint_symbol.loc0; | 
|  | let locations : FragLocations = FragLocations(tint_symbol.loc1, tint_symbol.loc2); | 
|  | let builtins : FragBuiltins = FragBuiltins(tint_symbol_1); | 
|  | var col : f32 = ((builtins.coord.x * locations.loc1) + loc0); | 
|  | } | 
|  | )"; | 
|  |  | 
|  | DataMap data; | 
|  | data.Add<CanonicalizeEntryPointIO::Config>( | 
|  | CanonicalizeEntryPointIO::BuiltinStyle::kParameter); | 
|  | auto got = Run<CanonicalizeEntryPointIO>(src, data); | 
|  |  | 
|  | EXPECT_EQ(expect, str(got)); | 
|  | } | 
|  |  | 
|  | TEST_F(CanonicalizeEntryPointIOTest, StructParameters_BuiltinsAsStructMembers) { | 
|  | auto* src = R"( | 
|  | struct FragBuiltins { | 
|  | [[builtin(position)]] coord : vec4<f32>; | 
|  | }; | 
|  | struct FragLocations { | 
|  | [[location(1)]] loc1 : f32; | 
|  | [[location(2)]] loc2 : vec4<u32>; | 
|  | }; | 
|  |  | 
|  | [[stage(fragment)]] | 
|  | fn frag_main([[location(0)]] loc0 : f32, | 
|  | locations : FragLocations, | 
|  | builtins : FragBuiltins) { | 
|  | var col : f32 = ((builtins.coord.x * locations.loc1) + loc0); | 
|  | } | 
|  | )"; | 
|  |  | 
|  | auto* expect = R"( | 
|  | struct FragBuiltins { | 
|  | coord : vec4<f32>; | 
|  | }; | 
|  |  | 
|  | struct FragLocations { | 
|  | loc1 : f32; | 
|  | loc2 : vec4<u32>; | 
|  | }; | 
|  |  | 
|  | struct tint_symbol_1 { | 
|  | [[location(0)]] | 
|  | loc0 : f32; | 
|  | [[location(1)]] | 
|  | loc1 : f32; | 
|  | [[location(2)]] | 
|  | loc2 : vec4<u32>; | 
|  | [[builtin(position)]] | 
|  | coord : vec4<f32>; | 
|  | }; | 
|  |  | 
|  | [[stage(fragment)]] | 
|  | fn frag_main(tint_symbol : tint_symbol_1) { | 
|  | let loc0 : f32 = tint_symbol.loc0; | 
|  | let locations : FragLocations = FragLocations(tint_symbol.loc1, tint_symbol.loc2); | 
|  | let builtins : FragBuiltins = FragBuiltins(tint_symbol.coord); | 
|  | var col : f32 = ((builtins.coord.x * locations.loc1) + loc0); | 
|  | } | 
|  | )"; | 
|  |  | 
|  | DataMap data; | 
|  | data.Add<CanonicalizeEntryPointIO::Config>( | 
|  | CanonicalizeEntryPointIO::BuiltinStyle::kStructMember); | 
|  | auto got = Run<CanonicalizeEntryPointIO>(src, data); | 
|  |  | 
|  | EXPECT_EQ(expect, str(got)); | 
|  | } | 
|  |  | 
|  | TEST_F(CanonicalizeEntryPointIOTest, Return_Scalar) { | 
|  | auto* src = R"( | 
|  | [[stage(fragment)]] | 
|  | fn frag_main() -> [[builtin(frag_depth)]] f32 { | 
|  | return 1.0; | 
|  | } | 
|  | )"; | 
|  |  | 
|  | auto* expect = R"( | 
|  | struct tint_symbol { | 
|  | [[builtin(frag_depth)]] | 
|  | value : f32; | 
|  | }; | 
|  |  | 
|  | [[stage(fragment)]] | 
|  | fn frag_main() -> tint_symbol { | 
|  | return tint_symbol(1.0); | 
|  | } | 
|  | )"; | 
|  |  | 
|  | DataMap data; | 
|  | data.Add<CanonicalizeEntryPointIO::Config>( | 
|  | CanonicalizeEntryPointIO::BuiltinStyle::kParameter); | 
|  | auto got = Run<CanonicalizeEntryPointIO>(src, data); | 
|  |  | 
|  | EXPECT_EQ(expect, str(got)); | 
|  | } | 
|  |  | 
|  | TEST_F(CanonicalizeEntryPointIOTest, Return_Struct) { | 
|  | auto* src = R"( | 
|  | struct FragOutput { | 
|  | [[location(0)]] color : vec4<f32>; | 
|  | [[builtin(frag_depth)]] depth : f32; | 
|  | [[builtin(sample_mask)]] mask : u32; | 
|  | }; | 
|  |  | 
|  | [[stage(fragment)]] | 
|  | fn frag_main() -> FragOutput { | 
|  | var output : FragOutput; | 
|  | output.depth = 1.0; | 
|  | output.mask = 7u; | 
|  | output.color = vec4<f32>(0.5, 0.5, 0.5, 1.0); | 
|  | return output; | 
|  | } | 
|  | )"; | 
|  |  | 
|  | auto* expect = R"( | 
|  | struct FragOutput { | 
|  | color : vec4<f32>; | 
|  | depth : f32; | 
|  | mask : u32; | 
|  | }; | 
|  |  | 
|  | struct tint_symbol { | 
|  | [[location(0)]] | 
|  | color : vec4<f32>; | 
|  | [[builtin(frag_depth)]] | 
|  | depth : f32; | 
|  | [[builtin(sample_mask)]] | 
|  | mask : u32; | 
|  | }; | 
|  |  | 
|  | [[stage(fragment)]] | 
|  | fn frag_main() -> tint_symbol { | 
|  | var output : FragOutput; | 
|  | output.depth = 1.0; | 
|  | output.mask = 7u; | 
|  | output.color = vec4<f32>(0.5, 0.5, 0.5, 1.0); | 
|  | return tint_symbol(output.color, output.depth, output.mask); | 
|  | } | 
|  | )"; | 
|  |  | 
|  | DataMap data; | 
|  | data.Add<CanonicalizeEntryPointIO::Config>( | 
|  | CanonicalizeEntryPointIO::BuiltinStyle::kParameter); | 
|  | auto got = Run<CanonicalizeEntryPointIO>(src, data); | 
|  |  | 
|  | EXPECT_EQ(expect, str(got)); | 
|  | } | 
|  |  | 
|  | TEST_F(CanonicalizeEntryPointIOTest, StructParameters_SharedDeviceFunction) { | 
|  | auto* src = R"( | 
|  | struct FragmentInput { | 
|  | [[location(0)]] value : f32; | 
|  | [[location(1)]] mul : f32; | 
|  | }; | 
|  |  | 
|  | fn foo(x : FragmentInput) -> f32 { | 
|  | return x.value * x.mul; | 
|  | } | 
|  |  | 
|  | [[stage(fragment)]] | 
|  | fn frag_main1(inputs : FragmentInput) { | 
|  | var x : f32 = foo(inputs); | 
|  | } | 
|  |  | 
|  | [[stage(fragment)]] | 
|  | fn frag_main2(inputs : FragmentInput) { | 
|  | var x : f32 = foo(inputs); | 
|  | } | 
|  | )"; | 
|  |  | 
|  | auto* expect = R"( | 
|  | struct FragmentInput { | 
|  | value : f32; | 
|  | mul : f32; | 
|  | }; | 
|  |  | 
|  | fn foo(x : FragmentInput) -> f32 { | 
|  | return (x.value * x.mul); | 
|  | } | 
|  |  | 
|  | struct tint_symbol_1 { | 
|  | [[location(0)]] | 
|  | value : f32; | 
|  | [[location(1)]] | 
|  | mul : f32; | 
|  | }; | 
|  |  | 
|  | [[stage(fragment)]] | 
|  | fn frag_main1(tint_symbol : tint_symbol_1) { | 
|  | let inputs : FragmentInput = FragmentInput(tint_symbol.value, tint_symbol.mul); | 
|  | var x : f32 = foo(inputs); | 
|  | } | 
|  |  | 
|  | struct tint_symbol_3 { | 
|  | [[location(0)]] | 
|  | value : f32; | 
|  | [[location(1)]] | 
|  | mul : f32; | 
|  | }; | 
|  |  | 
|  | [[stage(fragment)]] | 
|  | fn frag_main2(tint_symbol_2 : tint_symbol_3) { | 
|  | let inputs : FragmentInput = FragmentInput(tint_symbol_2.value, tint_symbol_2.mul); | 
|  | var x : f32 = foo(inputs); | 
|  | } | 
|  | )"; | 
|  |  | 
|  | DataMap data; | 
|  | data.Add<CanonicalizeEntryPointIO::Config>( | 
|  | CanonicalizeEntryPointIO::BuiltinStyle::kParameter); | 
|  | auto got = Run<CanonicalizeEntryPointIO>(src, data); | 
|  |  | 
|  | EXPECT_EQ(expect, str(got)); | 
|  | } | 
|  |  | 
|  | TEST_F(CanonicalizeEntryPointIOTest, Struct_ModuleScopeVariable) { | 
|  | auto* src = R"( | 
|  | struct FragmentInput { | 
|  | [[location(0)]] col1 : f32; | 
|  | [[location(1)]] col2 : f32; | 
|  | }; | 
|  |  | 
|  | var<private> global_inputs : FragmentInput; | 
|  |  | 
|  | fn foo() -> f32 { | 
|  | return global_inputs.col1 * 0.5; | 
|  | } | 
|  |  | 
|  | fn bar() -> f32 { | 
|  | return global_inputs.col2 * 2.0; | 
|  | } | 
|  |  | 
|  | [[stage(fragment)]] | 
|  | fn frag_main1(inputs : FragmentInput) { | 
|  | global_inputs = inputs; | 
|  | var r : f32 = foo(); | 
|  | var g : f32 = bar(); | 
|  | } | 
|  | )"; | 
|  |  | 
|  | auto* expect = R"( | 
|  | struct FragmentInput { | 
|  | col1 : f32; | 
|  | col2 : f32; | 
|  | }; | 
|  |  | 
|  | var<private> global_inputs : FragmentInput; | 
|  |  | 
|  | fn foo() -> f32 { | 
|  | return (global_inputs.col1 * 0.5); | 
|  | } | 
|  |  | 
|  | fn bar() -> f32 { | 
|  | return (global_inputs.col2 * 2.0); | 
|  | } | 
|  |  | 
|  | struct tint_symbol_1 { | 
|  | [[location(0)]] | 
|  | col1 : f32; | 
|  | [[location(1)]] | 
|  | col2 : f32; | 
|  | }; | 
|  |  | 
|  | [[stage(fragment)]] | 
|  | fn frag_main1(tint_symbol : tint_symbol_1) { | 
|  | let inputs : FragmentInput = FragmentInput(tint_symbol.col1, tint_symbol.col2); | 
|  | global_inputs = inputs; | 
|  | var r : f32 = foo(); | 
|  | var g : f32 = bar(); | 
|  | } | 
|  | )"; | 
|  |  | 
|  | DataMap data; | 
|  | data.Add<CanonicalizeEntryPointIO::Config>( | 
|  | CanonicalizeEntryPointIO::BuiltinStyle::kParameter); | 
|  | auto got = Run<CanonicalizeEntryPointIO>(src, data); | 
|  |  | 
|  | EXPECT_EQ(expect, str(got)); | 
|  | } | 
|  |  | 
|  | TEST_F(CanonicalizeEntryPointIOTest, Struct_TypeAliases) { | 
|  | auto* src = R"( | 
|  | type myf32 = f32; | 
|  |  | 
|  | struct FragmentInput { | 
|  | [[location(0)]] col1 : myf32; | 
|  | [[location(1)]] col2 : myf32; | 
|  | }; | 
|  |  | 
|  | struct FragmentOutput { | 
|  | [[location(0)]] col1 : myf32; | 
|  | [[location(1)]] col2 : myf32; | 
|  | }; | 
|  |  | 
|  | type MyFragmentInput = FragmentInput; | 
|  |  | 
|  | type MyFragmentOutput = FragmentOutput; | 
|  |  | 
|  | fn foo(x : MyFragmentInput) -> myf32 { | 
|  | return x.col1; | 
|  | } | 
|  |  | 
|  | [[stage(fragment)]] | 
|  | fn frag_main(inputs : MyFragmentInput) -> MyFragmentOutput { | 
|  | var x : myf32 = foo(inputs); | 
|  | return MyFragmentOutput(x, inputs.col2); | 
|  | } | 
|  | )"; | 
|  |  | 
|  | auto* expect = R"( | 
|  | type myf32 = f32; | 
|  |  | 
|  | struct FragmentInput { | 
|  | col1 : myf32; | 
|  | col2 : myf32; | 
|  | }; | 
|  |  | 
|  | struct FragmentOutput { | 
|  | col1 : myf32; | 
|  | col2 : myf32; | 
|  | }; | 
|  |  | 
|  | type MyFragmentInput = FragmentInput; | 
|  |  | 
|  | type MyFragmentOutput = FragmentOutput; | 
|  |  | 
|  | fn foo(x : MyFragmentInput) -> myf32 { | 
|  | return x.col1; | 
|  | } | 
|  |  | 
|  | struct tint_symbol_1 { | 
|  | [[location(0)]] | 
|  | col1 : myf32; | 
|  | [[location(1)]] | 
|  | col2 : myf32; | 
|  | }; | 
|  |  | 
|  | struct tint_symbol_2 { | 
|  | [[location(0)]] | 
|  | col1 : myf32; | 
|  | [[location(1)]] | 
|  | col2 : myf32; | 
|  | }; | 
|  |  | 
|  | [[stage(fragment)]] | 
|  | fn frag_main(tint_symbol : tint_symbol_1) -> tint_symbol_2 { | 
|  | let inputs : MyFragmentInput = MyFragmentInput(tint_symbol.col1, tint_symbol.col2); | 
|  | var x : myf32 = foo(inputs); | 
|  | let tint_symbol_3 : FragmentOutput = MyFragmentOutput(x, inputs.col2); | 
|  | return tint_symbol_2(tint_symbol_3.col1, tint_symbol_3.col2); | 
|  | } | 
|  | )"; | 
|  |  | 
|  | DataMap data; | 
|  | data.Add<CanonicalizeEntryPointIO::Config>( | 
|  | CanonicalizeEntryPointIO::BuiltinStyle::kParameter); | 
|  | auto got = Run<CanonicalizeEntryPointIO>(src, data); | 
|  |  | 
|  | EXPECT_EQ(expect, str(got)); | 
|  | } | 
|  |  | 
|  | TEST_F(CanonicalizeEntryPointIOTest, InterpolateAttributes) { | 
|  | auto* src = R"( | 
|  | struct VertexOut { | 
|  | [[builtin(position)]] pos : vec4<f32>; | 
|  | [[location(1), interpolate(flat)]] loc1: f32; | 
|  | [[location(2), interpolate(linear, sample)]] loc2 : f32; | 
|  | [[location(3), interpolate(perspective, centroid)]] loc3 : f32; | 
|  | }; | 
|  |  | 
|  | struct FragmentIn { | 
|  | [[location(1), interpolate(flat)]] loc1: f32; | 
|  | [[location(2), interpolate(linear, sample)]] loc2 : f32; | 
|  | }; | 
|  |  | 
|  | [[stage(vertex)]] | 
|  | fn vert_main() -> VertexOut { | 
|  | return VertexOut(); | 
|  | } | 
|  |  | 
|  | [[stage(fragment)]] | 
|  | fn frag_main(inputs : FragmentIn, | 
|  | [[location(3), interpolate(perspective, centroid)]] loc3 : f32) { | 
|  | let x = inputs.loc1 + inputs.loc2 + loc3; | 
|  | } | 
|  | )"; | 
|  |  | 
|  | auto* expect = R"( | 
|  | struct VertexOut { | 
|  | pos : vec4<f32>; | 
|  | loc1 : f32; | 
|  | loc2 : f32; | 
|  | loc3 : f32; | 
|  | }; | 
|  |  | 
|  | struct FragmentIn { | 
|  | loc1 : f32; | 
|  | loc2 : f32; | 
|  | }; | 
|  |  | 
|  | struct tint_symbol { | 
|  | [[location(1), interpolate(flat)]] | 
|  | loc1 : f32; | 
|  | [[location(2), interpolate(linear, sample)]] | 
|  | loc2 : f32; | 
|  | [[location(3), interpolate(perspective, centroid)]] | 
|  | loc3 : f32; | 
|  | [[builtin(position)]] | 
|  | pos : vec4<f32>; | 
|  | }; | 
|  |  | 
|  | [[stage(vertex)]] | 
|  | fn vert_main() -> tint_symbol { | 
|  | let tint_symbol_1 : VertexOut = VertexOut(); | 
|  | return tint_symbol(tint_symbol_1.loc1, tint_symbol_1.loc2, tint_symbol_1.loc3, tint_symbol_1.pos); | 
|  | } | 
|  |  | 
|  | struct tint_symbol_3 { | 
|  | [[location(1), interpolate(flat)]] | 
|  | loc1 : f32; | 
|  | [[location(2), interpolate(linear, sample)]] | 
|  | loc2 : f32; | 
|  | [[location(3), interpolate(perspective, centroid)]] | 
|  | loc3 : f32; | 
|  | }; | 
|  |  | 
|  | [[stage(fragment)]] | 
|  | fn frag_main(tint_symbol_2 : tint_symbol_3) { | 
|  | let inputs : FragmentIn = FragmentIn(tint_symbol_2.loc1, tint_symbol_2.loc2); | 
|  | let loc3 : f32 = tint_symbol_2.loc3; | 
|  | let x = ((inputs.loc1 + inputs.loc2) + loc3); | 
|  | } | 
|  | )"; | 
|  |  | 
|  | DataMap data; | 
|  | data.Add<CanonicalizeEntryPointIO::Config>( | 
|  | CanonicalizeEntryPointIO::BuiltinStyle::kStructMember); | 
|  | auto got = Run<CanonicalizeEntryPointIO>(src, data); | 
|  |  | 
|  | EXPECT_EQ(expect, str(got)); | 
|  | } | 
|  |  | 
|  | TEST_F(CanonicalizeEntryPointIOTest, InvariantAttributes) { | 
|  | auto* src = R"( | 
|  | struct VertexOut { | 
|  | [[builtin(position), invariant]] pos : vec4<f32>; | 
|  | }; | 
|  |  | 
|  | [[stage(vertex)]] | 
|  | fn main1() -> VertexOut { | 
|  | return VertexOut(); | 
|  | } | 
|  |  | 
|  | [[stage(vertex)]] | 
|  | fn main2() -> [[builtin(position), invariant]] vec4<f32> { | 
|  | return vec4<f32>(); | 
|  | } | 
|  | )"; | 
|  |  | 
|  | auto* expect = R"( | 
|  | struct VertexOut { | 
|  | pos : vec4<f32>; | 
|  | }; | 
|  |  | 
|  | struct tint_symbol { | 
|  | [[builtin(position), invariant]] | 
|  | pos : vec4<f32>; | 
|  | }; | 
|  |  | 
|  | [[stage(vertex)]] | 
|  | fn main1() -> tint_symbol { | 
|  | let tint_symbol_1 : VertexOut = VertexOut(); | 
|  | return tint_symbol(tint_symbol_1.pos); | 
|  | } | 
|  |  | 
|  | struct tint_symbol_2 { | 
|  | [[builtin(position), invariant]] | 
|  | value : vec4<f32>; | 
|  | }; | 
|  |  | 
|  | [[stage(vertex)]] | 
|  | fn main2() -> tint_symbol_2 { | 
|  | return tint_symbol_2(vec4<f32>()); | 
|  | } | 
|  | )"; | 
|  |  | 
|  | DataMap data; | 
|  | data.Add<CanonicalizeEntryPointIO::Config>( | 
|  | CanonicalizeEntryPointIO::BuiltinStyle::kStructMember); | 
|  | auto got = Run<CanonicalizeEntryPointIO>(src, data); | 
|  |  | 
|  | EXPECT_EQ(expect, str(got)); | 
|  | } | 
|  |  | 
|  | TEST_F(CanonicalizeEntryPointIOTest, Struct_LayoutDecorations) { | 
|  | auto* src = R"( | 
|  | [[block]] | 
|  | struct FragmentInput { | 
|  | [[size(16), location(1)]] value : f32; | 
|  | [[builtin(position)]] [[align(32)]] coord : vec4<f32>; | 
|  | [[location(0), interpolate(linear, sample)]] [[align(128)]] loc0 : f32; | 
|  | }; | 
|  |  | 
|  | struct FragmentOutput { | 
|  | [[size(16), location(1), interpolate(flat)]] value : f32; | 
|  | }; | 
|  |  | 
|  | [[stage(fragment)]] | 
|  | fn frag_main(inputs : FragmentInput) -> FragmentOutput { | 
|  | return FragmentOutput(inputs.coord.x * inputs.value + inputs.loc0); | 
|  | } | 
|  | )"; | 
|  |  | 
|  | auto* expect = R"( | 
|  | [[block]] | 
|  | struct FragmentInput { | 
|  | [[size(16)]] | 
|  | value : f32; | 
|  | [[align(32)]] | 
|  | coord : vec4<f32>; | 
|  | [[align(128)]] | 
|  | loc0 : f32; | 
|  | }; | 
|  |  | 
|  | struct FragmentOutput { | 
|  | [[size(16)]] | 
|  | value : f32; | 
|  | }; | 
|  |  | 
|  | struct tint_symbol_1 { | 
|  | [[location(0), interpolate(linear, sample)]] | 
|  | loc0 : f32; | 
|  | [[location(1)]] | 
|  | value : f32; | 
|  | [[builtin(position)]] | 
|  | coord : vec4<f32>; | 
|  | }; | 
|  |  | 
|  | struct tint_symbol_2 { | 
|  | [[location(1), interpolate(flat)]] | 
|  | value : f32; | 
|  | }; | 
|  |  | 
|  | [[stage(fragment)]] | 
|  | fn frag_main(tint_symbol : tint_symbol_1) -> tint_symbol_2 { | 
|  | let inputs : FragmentInput = FragmentInput(tint_symbol.value, tint_symbol.coord, tint_symbol.loc0); | 
|  | let tint_symbol_3 : FragmentOutput = FragmentOutput(((inputs.coord.x * inputs.value) + inputs.loc0)); | 
|  | return tint_symbol_2(tint_symbol_3.value); | 
|  | } | 
|  | )"; | 
|  |  | 
|  | DataMap data; | 
|  | data.Add<CanonicalizeEntryPointIO::Config>( | 
|  | CanonicalizeEntryPointIO::BuiltinStyle::kStructMember); | 
|  | auto got = Run<CanonicalizeEntryPointIO>(src, data); | 
|  |  | 
|  | EXPECT_EQ(expect, str(got)); | 
|  | } | 
|  |  | 
|  | TEST_F(CanonicalizeEntryPointIOTest, SortedMembers) { | 
|  | auto* src = R"( | 
|  | struct VertexOutput { | 
|  | [[location(1)]] b : u32; | 
|  | [[builtin(position)]] pos : vec4<f32>; | 
|  | [[location(3)]] d : u32; | 
|  | [[location(0)]] a : f32; | 
|  | [[location(2)]] c : i32; | 
|  | }; | 
|  |  | 
|  | struct FragmentInputExtra { | 
|  | [[location(3)]] d : u32; | 
|  | [[builtin(position)]] pos : vec4<f32>; | 
|  | [[location(0)]] a : f32; | 
|  | }; | 
|  |  | 
|  | [[stage(vertex)]] | 
|  | fn vert_main() -> VertexOutput { | 
|  | return VertexOutput(); | 
|  | } | 
|  |  | 
|  | [[stage(fragment)]] | 
|  | fn frag_main([[builtin(front_facing)]] ff : bool, | 
|  | [[location(2)]] c : i32, | 
|  | inputs : FragmentInputExtra, | 
|  | [[location(1)]] b : u32) { | 
|  | } | 
|  | )"; | 
|  |  | 
|  | auto* expect = R"( | 
|  | struct VertexOutput { | 
|  | b : u32; | 
|  | pos : vec4<f32>; | 
|  | d : u32; | 
|  | a : f32; | 
|  | c : i32; | 
|  | }; | 
|  |  | 
|  | struct FragmentInputExtra { | 
|  | d : u32; | 
|  | pos : vec4<f32>; | 
|  | a : f32; | 
|  | }; | 
|  |  | 
|  | struct tint_symbol { | 
|  | [[location(0)]] | 
|  | a : f32; | 
|  | [[location(1)]] | 
|  | b : u32; | 
|  | [[location(2)]] | 
|  | c : i32; | 
|  | [[location(3)]] | 
|  | d : u32; | 
|  | [[builtin(position)]] | 
|  | pos : vec4<f32>; | 
|  | }; | 
|  |  | 
|  | [[stage(vertex)]] | 
|  | fn vert_main() -> tint_symbol { | 
|  | let tint_symbol_1 : VertexOutput = VertexOutput(); | 
|  | return tint_symbol(tint_symbol_1.a, tint_symbol_1.b, tint_symbol_1.c, tint_symbol_1.d, tint_symbol_1.pos); | 
|  | } | 
|  |  | 
|  | struct tint_symbol_3 { | 
|  | [[location(0)]] | 
|  | a : f32; | 
|  | [[location(1)]] | 
|  | b : u32; | 
|  | [[location(2)]] | 
|  | c : i32; | 
|  | [[location(3)]] | 
|  | d : u32; | 
|  | [[builtin(position)]] | 
|  | pos : vec4<f32>; | 
|  | [[builtin(front_facing)]] | 
|  | ff : bool; | 
|  | }; | 
|  |  | 
|  | [[stage(fragment)]] | 
|  | fn frag_main(tint_symbol_2 : tint_symbol_3) { | 
|  | let ff : bool = tint_symbol_2.ff; | 
|  | let c : i32 = tint_symbol_2.c; | 
|  | let inputs : FragmentInputExtra = FragmentInputExtra(tint_symbol_2.d, tint_symbol_2.pos, tint_symbol_2.a); | 
|  | let b : u32 = tint_symbol_2.b; | 
|  | } | 
|  | )"; | 
|  |  | 
|  | DataMap data; | 
|  | data.Add<CanonicalizeEntryPointIO::Config>( | 
|  | CanonicalizeEntryPointIO::BuiltinStyle::kStructMember); | 
|  | auto got = Run<CanonicalizeEntryPointIO>(src, data); | 
|  |  | 
|  | EXPECT_EQ(expect, str(got)); | 
|  | } | 
|  |  | 
|  | TEST_F(CanonicalizeEntryPointIOTest, DontRenameSymbols) { | 
|  | auto* src = R"( | 
|  | [[stage(fragment)]] | 
|  | fn tint_symbol_1([[location(0)]] col : f32) { | 
|  | } | 
|  | )"; | 
|  |  | 
|  | auto* expect = R"( | 
|  | struct tint_symbol_2 { | 
|  | [[location(0)]] | 
|  | col : f32; | 
|  | }; | 
|  |  | 
|  | [[stage(fragment)]] | 
|  | fn tint_symbol_1(tint_symbol : tint_symbol_2) { | 
|  | let col : f32 = tint_symbol.col; | 
|  | } | 
|  | )"; | 
|  |  | 
|  | DataMap data; | 
|  | data.Add<CanonicalizeEntryPointIO::Config>( | 
|  | CanonicalizeEntryPointIO::BuiltinStyle::kParameter); | 
|  | auto got = Run<CanonicalizeEntryPointIO>(src, data); | 
|  |  | 
|  | EXPECT_EQ(expect, str(got)); | 
|  | } | 
|  |  | 
|  | TEST_F(CanonicalizeEntryPointIOTest, FixedSampleMask_VoidNoReturn) { | 
|  | auto* src = R"( | 
|  | [[stage(fragment)]] | 
|  | fn frag_main() { | 
|  | } | 
|  | )"; | 
|  |  | 
|  | auto* expect = R"( | 
|  | struct tint_symbol_1 { | 
|  | [[builtin(sample_mask)]] | 
|  | tint_symbol : u32; | 
|  | }; | 
|  |  | 
|  | [[stage(fragment)]] | 
|  | fn frag_main() -> tint_symbol_1 { | 
|  | return tint_symbol_1(3u); | 
|  | } | 
|  | )"; | 
|  |  | 
|  | DataMap data; | 
|  | data.Add<CanonicalizeEntryPointIO::Config>( | 
|  | CanonicalizeEntryPointIO::BuiltinStyle::kParameter, 0x03u); | 
|  | auto got = Run<CanonicalizeEntryPointIO>(src, data); | 
|  |  | 
|  | EXPECT_EQ(expect, str(got)); | 
|  | } | 
|  |  | 
|  | TEST_F(CanonicalizeEntryPointIOTest, FixedSampleMask_VoidWithReturn) { | 
|  | auto* src = R"( | 
|  | [[stage(fragment)]] | 
|  | fn frag_main() { | 
|  | return; | 
|  | } | 
|  | )"; | 
|  |  | 
|  | auto* expect = R"( | 
|  | struct tint_symbol_1 { | 
|  | [[builtin(sample_mask)]] | 
|  | tint_symbol : u32; | 
|  | }; | 
|  |  | 
|  | [[stage(fragment)]] | 
|  | fn frag_main() -> tint_symbol_1 { | 
|  | return tint_symbol_1(3u); | 
|  | } | 
|  | )"; | 
|  |  | 
|  | DataMap data; | 
|  | data.Add<CanonicalizeEntryPointIO::Config>( | 
|  | CanonicalizeEntryPointIO::BuiltinStyle::kParameter, 0x03u); | 
|  | auto got = Run<CanonicalizeEntryPointIO>(src, data); | 
|  |  | 
|  | EXPECT_EQ(expect, str(got)); | 
|  | } | 
|  |  | 
|  | TEST_F(CanonicalizeEntryPointIOTest, FixedSampleMask_WithAuthoredMask) { | 
|  | auto* src = R"( | 
|  | [[stage(fragment)]] | 
|  | fn frag_main() -> [[builtin(sample_mask)]] u32 { | 
|  | return 7u; | 
|  | } | 
|  | )"; | 
|  |  | 
|  | auto* expect = R"( | 
|  | struct tint_symbol { | 
|  | [[builtin(sample_mask)]] | 
|  | value : u32; | 
|  | }; | 
|  |  | 
|  | [[stage(fragment)]] | 
|  | fn frag_main() -> tint_symbol { | 
|  | return tint_symbol((7u & 3u)); | 
|  | } | 
|  | )"; | 
|  |  | 
|  | DataMap data; | 
|  | data.Add<CanonicalizeEntryPointIO::Config>( | 
|  | CanonicalizeEntryPointIO::BuiltinStyle::kParameter, 0x03u); | 
|  | auto got = Run<CanonicalizeEntryPointIO>(src, data); | 
|  |  | 
|  | EXPECT_EQ(expect, str(got)); | 
|  | } | 
|  |  | 
|  | TEST_F(CanonicalizeEntryPointIOTest, FixedSampleMask_WithoutAuthoredMask) { | 
|  | auto* src = R"( | 
|  | [[stage(fragment)]] | 
|  | fn frag_main() -> [[location(0)]] f32 { | 
|  | return 1.0; | 
|  | } | 
|  | )"; | 
|  |  | 
|  | auto* expect = R"( | 
|  | struct tint_symbol_1 { | 
|  | [[location(0)]] | 
|  | value : f32; | 
|  | [[builtin(sample_mask)]] | 
|  | tint_symbol : u32; | 
|  | }; | 
|  |  | 
|  | [[stage(fragment)]] | 
|  | fn frag_main() -> tint_symbol_1 { | 
|  | return tint_symbol_1(1.0, 3u); | 
|  | } | 
|  | )"; | 
|  |  | 
|  | DataMap data; | 
|  | data.Add<CanonicalizeEntryPointIO::Config>( | 
|  | CanonicalizeEntryPointIO::BuiltinStyle::kParameter, 0x03u); | 
|  | auto got = Run<CanonicalizeEntryPointIO>(src, data); | 
|  |  | 
|  | EXPECT_EQ(expect, str(got)); | 
|  | } | 
|  |  | 
|  | TEST_F(CanonicalizeEntryPointIOTest, FixedSampleMask_StructWithAuthoredMask) { | 
|  | auto* src = R"( | 
|  | struct Output { | 
|  | [[builtin(frag_depth)]] depth : f32; | 
|  | [[builtin(sample_mask)]] mask : u32; | 
|  | [[location(0)]] value : f32; | 
|  | }; | 
|  |  | 
|  | [[stage(fragment)]] | 
|  | fn frag_main() -> Output { | 
|  | return Output(0.5, 7u, 1.0); | 
|  | } | 
|  | )"; | 
|  |  | 
|  | auto* expect = R"( | 
|  | struct Output { | 
|  | depth : f32; | 
|  | mask : u32; | 
|  | value : f32; | 
|  | }; | 
|  |  | 
|  | struct tint_symbol { | 
|  | [[location(0)]] | 
|  | value : f32; | 
|  | [[builtin(frag_depth)]] | 
|  | depth : f32; | 
|  | [[builtin(sample_mask)]] | 
|  | mask : u32; | 
|  | }; | 
|  |  | 
|  | [[stage(fragment)]] | 
|  | fn frag_main() -> tint_symbol { | 
|  | let tint_symbol_1 : Output = Output(0.5, 7u, 1.0); | 
|  | return tint_symbol(tint_symbol_1.value, tint_symbol_1.depth, (tint_symbol_1.mask & 3u)); | 
|  | } | 
|  | )"; | 
|  |  | 
|  | DataMap data; | 
|  | data.Add<CanonicalizeEntryPointIO::Config>( | 
|  | CanonicalizeEntryPointIO::BuiltinStyle::kParameter, 0x03u); | 
|  | auto got = Run<CanonicalizeEntryPointIO>(src, data); | 
|  |  | 
|  | EXPECT_EQ(expect, str(got)); | 
|  | } | 
|  |  | 
|  | TEST_F(CanonicalizeEntryPointIOTest, | 
|  | FixedSampleMask_StructWithoutAuthoredMask) { | 
|  | auto* src = R"( | 
|  | struct Output { | 
|  | [[builtin(frag_depth)]] depth : f32; | 
|  | [[location(0)]] value : f32; | 
|  | }; | 
|  |  | 
|  | [[stage(fragment)]] | 
|  | fn frag_main() -> Output { | 
|  | return Output(0.5, 1.0); | 
|  | } | 
|  | )"; | 
|  |  | 
|  | auto* expect = R"( | 
|  | struct Output { | 
|  | depth : f32; | 
|  | value : f32; | 
|  | }; | 
|  |  | 
|  | struct tint_symbol_1 { | 
|  | [[location(0)]] | 
|  | value : f32; | 
|  | [[builtin(frag_depth)]] | 
|  | depth : f32; | 
|  | [[builtin(sample_mask)]] | 
|  | tint_symbol : u32; | 
|  | }; | 
|  |  | 
|  | [[stage(fragment)]] | 
|  | fn frag_main() -> tint_symbol_1 { | 
|  | let tint_symbol_2 : Output = Output(0.5, 1.0); | 
|  | return tint_symbol_1(tint_symbol_2.value, tint_symbol_2.depth, 3u); | 
|  | } | 
|  | )"; | 
|  |  | 
|  | DataMap data; | 
|  | data.Add<CanonicalizeEntryPointIO::Config>( | 
|  | CanonicalizeEntryPointIO::BuiltinStyle::kParameter, 0x03u); | 
|  | auto got = Run<CanonicalizeEntryPointIO>(src, data); | 
|  |  | 
|  | EXPECT_EQ(expect, str(got)); | 
|  | } | 
|  |  | 
|  | TEST_F(CanonicalizeEntryPointIOTest, FixedSampleMask_MultipleShaders) { | 
|  | auto* src = R"( | 
|  | [[stage(fragment)]] | 
|  | fn frag_main1() -> [[builtin(sample_mask)]] u32 { | 
|  | return 7u; | 
|  | } | 
|  |  | 
|  | [[stage(fragment)]] | 
|  | fn frag_main2() -> [[location(0)]] f32 { | 
|  | return 1.0; | 
|  | } | 
|  |  | 
|  | [[stage(vertex)]] | 
|  | fn vert_main1() -> [[builtin(position)]] vec4<f32> { | 
|  | return vec4<f32>(); | 
|  | } | 
|  |  | 
|  | [[stage(compute), workgroup_size(1)]] | 
|  | fn comp_main1() { | 
|  | } | 
|  | )"; | 
|  |  | 
|  | auto* expect = R"( | 
|  | struct tint_symbol { | 
|  | [[builtin(sample_mask)]] | 
|  | value : u32; | 
|  | }; | 
|  |  | 
|  | [[stage(fragment)]] | 
|  | fn frag_main1() -> tint_symbol { | 
|  | return tint_symbol((7u & 3u)); | 
|  | } | 
|  |  | 
|  | struct tint_symbol_2 { | 
|  | [[location(0)]] | 
|  | value : f32; | 
|  | [[builtin(sample_mask)]] | 
|  | tint_symbol_1 : u32; | 
|  | }; | 
|  |  | 
|  | [[stage(fragment)]] | 
|  | fn frag_main2() -> tint_symbol_2 { | 
|  | return tint_symbol_2(1.0, 3u); | 
|  | } | 
|  |  | 
|  | struct tint_symbol_3 { | 
|  | [[builtin(position)]] | 
|  | value : vec4<f32>; | 
|  | }; | 
|  |  | 
|  | [[stage(vertex)]] | 
|  | fn vert_main1() -> tint_symbol_3 { | 
|  | return tint_symbol_3(vec4<f32>()); | 
|  | } | 
|  |  | 
|  | [[stage(compute), workgroup_size(1)]] | 
|  | fn comp_main1() { | 
|  | } | 
|  | )"; | 
|  |  | 
|  | DataMap data; | 
|  | data.Add<CanonicalizeEntryPointIO::Config>( | 
|  | CanonicalizeEntryPointIO::BuiltinStyle::kParameter, 0x03u); | 
|  | auto got = Run<CanonicalizeEntryPointIO>(src, data); | 
|  |  | 
|  | EXPECT_EQ(expect, str(got)); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  | }  // namespace transform | 
|  | }  // namespace tint |