• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2021 The Tint Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "src/transform/canonicalize_entry_point_io.h"
16 
17 #include "src/transform/test_helper.h"
18 #include "src/transform/unshadow.h"
19 
20 namespace tint {
21 namespace transform {
22 namespace {
23 
24 using CanonicalizeEntryPointIOTest = TransformTest;
25 
TEST_F(CanonicalizeEntryPointIOTest,Error_MissingUnshadow)26 TEST_F(CanonicalizeEntryPointIOTest, Error_MissingUnshadow) {
27   auto* src = "";
28 
29   auto* expect =
30       "error: tint::transform::CanonicalizeEntryPointIO depends on "
31       "tint::transform::Unshadow but the dependency was not run";
32 
33   auto got = Run<CanonicalizeEntryPointIO>(src);
34 
35   EXPECT_EQ(expect, str(got));
36 }
37 
TEST_F(CanonicalizeEntryPointIOTest,Error_MissingTransformData)38 TEST_F(CanonicalizeEntryPointIOTest, Error_MissingTransformData) {
39   auto* src = "";
40 
41   auto* expect =
42       "error: missing transform data for "
43       "tint::transform::CanonicalizeEntryPointIO";
44 
45   auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src);
46 
47   EXPECT_EQ(expect, str(got));
48 }
49 
TEST_F(CanonicalizeEntryPointIOTest,NoShaderIO)50 TEST_F(CanonicalizeEntryPointIOTest, NoShaderIO) {
51   // Test that we do not introduce wrapper functions when there is no shader IO
52   // to process.
53   auto* src = R"(
54 [[stage(fragment)]]
55 fn frag_main() {
56 }
57 
58 [[stage(compute), workgroup_size(1)]]
59 fn comp_main() {
60 }
61 )";
62 
63   auto* expect = src;
64 
65   DataMap data;
66   data.Add<CanonicalizeEntryPointIO::Config>(
67       CanonicalizeEntryPointIO::ShaderStyle::kMsl);
68   auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
69 
70   EXPECT_EQ(expect, str(got));
71 }
72 
TEST_F(CanonicalizeEntryPointIOTest,Parameters_Spirv)73 TEST_F(CanonicalizeEntryPointIOTest, Parameters_Spirv) {
74   auto* src = R"(
75 [[stage(fragment)]]
76 fn frag_main([[location(1)]] loc1 : f32,
77              [[location(2)]] loc2 : vec4<u32>,
78              [[builtin(position)]] coord : vec4<f32>) {
79   var col : f32 = (coord.x * loc1);
80 }
81 )";
82 
83   auto* expect = R"(
84 [[location(1), internal(disable_validation__ignore_storage_class)]] var<in> loc1_1 : f32;
85 
86 [[location(2), interpolate(flat), internal(disable_validation__ignore_storage_class)]] var<in> loc2_1 : vec4<u32>;
87 
88 [[builtin(position), internal(disable_validation__ignore_storage_class)]] var<in> coord_1 : vec4<f32>;
89 
90 fn frag_main_inner(loc1 : f32, loc2 : vec4<u32>, coord : vec4<f32>) {
91   var col : f32 = (coord.x * loc1);
92 }
93 
94 [[stage(fragment)]]
95 fn frag_main() {
96   frag_main_inner(loc1_1, loc2_1, coord_1);
97 }
98 )";
99 
100   DataMap data;
101   data.Add<CanonicalizeEntryPointIO::Config>(
102       CanonicalizeEntryPointIO::ShaderStyle::kSpirv);
103   auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
104 
105   EXPECT_EQ(expect, str(got));
106 }
107 
TEST_F(CanonicalizeEntryPointIOTest,Parameters_Msl)108 TEST_F(CanonicalizeEntryPointIOTest, Parameters_Msl) {
109   auto* src = R"(
110 [[stage(fragment)]]
111 fn frag_main([[location(1)]] loc1 : f32,
112              [[location(2)]] loc2 : vec4<u32>,
113              [[builtin(position)]] coord : vec4<f32>) {
114   var col : f32 = (coord.x * loc1);
115 }
116 )";
117 
118   auto* expect = R"(
119 struct tint_symbol_1 {
120   [[location(1)]]
121   loc1 : f32;
122   [[location(2)]]
123   loc2 : vec4<u32>;
124 };
125 
126 fn frag_main_inner(loc1 : f32, loc2 : vec4<u32>, coord : vec4<f32>) {
127   var col : f32 = (coord.x * loc1);
128 }
129 
130 [[stage(fragment)]]
131 fn frag_main([[builtin(position)]] coord : vec4<f32>, tint_symbol : tint_symbol_1) {
132   frag_main_inner(tint_symbol.loc1, tint_symbol.loc2, coord);
133 }
134 )";
135 
136   DataMap data;
137   data.Add<CanonicalizeEntryPointIO::Config>(
138       CanonicalizeEntryPointIO::ShaderStyle::kMsl);
139   auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
140 
141   EXPECT_EQ(expect, str(got));
142 }
143 
TEST_F(CanonicalizeEntryPointIOTest,Parameters_Hlsl)144 TEST_F(CanonicalizeEntryPointIOTest, Parameters_Hlsl) {
145   auto* src = R"(
146 [[stage(fragment)]]
147 fn frag_main([[location(1)]] loc1 : f32,
148              [[location(2)]] loc2 : vec4<u32>,
149              [[builtin(position)]] coord : vec4<f32>) {
150   var col : f32 = (coord.x * loc1);
151 }
152 )";
153 
154   auto* expect = R"(
155 struct tint_symbol_1 {
156   [[location(1)]]
157   loc1 : f32;
158   [[location(2)]]
159   loc2 : vec4<u32>;
160   [[builtin(position)]]
161   coord : vec4<f32>;
162 };
163 
164 fn frag_main_inner(loc1 : f32, loc2 : vec4<u32>, coord : vec4<f32>) {
165   var col : f32 = (coord.x * loc1);
166 }
167 
168 [[stage(fragment)]]
169 fn frag_main(tint_symbol : tint_symbol_1) {
170   frag_main_inner(tint_symbol.loc1, tint_symbol.loc2, tint_symbol.coord);
171 }
172 )";
173 
174   DataMap data;
175   data.Add<CanonicalizeEntryPointIO::Config>(
176       CanonicalizeEntryPointIO::ShaderStyle::kHlsl);
177   auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
178 
179   EXPECT_EQ(expect, str(got));
180 }
181 
TEST_F(CanonicalizeEntryPointIOTest,Parameter_TypeAlias)182 TEST_F(CanonicalizeEntryPointIOTest, Parameter_TypeAlias) {
183   auto* src = R"(
184 type myf32 = f32;
185 
186 [[stage(fragment)]]
187 fn frag_main([[location(1)]] loc1 : myf32) {
188   var x : myf32 = loc1;
189 }
190 )";
191 
192   auto* expect = R"(
193 type myf32 = f32;
194 
195 struct tint_symbol_1 {
196   [[location(1)]]
197   loc1 : f32;
198 };
199 
200 fn frag_main_inner(loc1 : myf32) {
201   var x : myf32 = loc1;
202 }
203 
204 [[stage(fragment)]]
205 fn frag_main(tint_symbol : tint_symbol_1) {
206   frag_main_inner(tint_symbol.loc1);
207 }
208 )";
209 
210   DataMap data;
211   data.Add<CanonicalizeEntryPointIO::Config>(
212       CanonicalizeEntryPointIO::ShaderStyle::kMsl);
213   auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
214 
215   EXPECT_EQ(expect, str(got));
216 }
217 
TEST_F(CanonicalizeEntryPointIOTest,StructParameters_Spirv)218 TEST_F(CanonicalizeEntryPointIOTest, StructParameters_Spirv) {
219   auto* src = R"(
220 struct FragBuiltins {
221   [[builtin(position)]] coord : vec4<f32>;
222 };
223 struct FragLocations {
224   [[location(1)]] loc1 : f32;
225   [[location(2)]] loc2 : vec4<u32>;
226 };
227 
228 [[stage(fragment)]]
229 fn frag_main([[location(0)]] loc0 : f32,
230              locations : FragLocations,
231              builtins : FragBuiltins) {
232   var col : f32 = ((builtins.coord.x * locations.loc1) + loc0);
233 }
234 )";
235 
236   auto* expect = R"(
237 [[location(0), internal(disable_validation__ignore_storage_class)]] var<in> loc0_1 : f32;
238 
239 [[location(1), internal(disable_validation__ignore_storage_class)]] var<in> loc1_1 : f32;
240 
241 [[location(2), interpolate(flat), internal(disable_validation__ignore_storage_class)]] var<in> loc2_1 : vec4<u32>;
242 
243 [[builtin(position), internal(disable_validation__ignore_storage_class)]] var<in> coord_1 : vec4<f32>;
244 
245 struct FragBuiltins {
246   coord : vec4<f32>;
247 };
248 
249 struct FragLocations {
250   loc1 : f32;
251   loc2 : vec4<u32>;
252 };
253 
254 fn frag_main_inner(loc0 : f32, locations : FragLocations, builtins : FragBuiltins) {
255   var col : f32 = ((builtins.coord.x * locations.loc1) + loc0);
256 }
257 
258 [[stage(fragment)]]
259 fn frag_main() {
260   frag_main_inner(loc0_1, FragLocations(loc1_1, loc2_1), FragBuiltins(coord_1));
261 }
262 )";
263 
264   DataMap data;
265   data.Add<CanonicalizeEntryPointIO::Config>(
266       CanonicalizeEntryPointIO::ShaderStyle::kSpirv);
267   auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
268 
269   EXPECT_EQ(expect, str(got));
270 }
271 
TEST_F(CanonicalizeEntryPointIOTest,StructParameters_kMsl)272 TEST_F(CanonicalizeEntryPointIOTest, StructParameters_kMsl) {
273   auto* src = R"(
274 struct FragBuiltins {
275   [[builtin(position)]] coord : vec4<f32>;
276 };
277 struct FragLocations {
278   [[location(1)]] loc1 : f32;
279   [[location(2)]] loc2 : vec4<u32>;
280 };
281 
282 [[stage(fragment)]]
283 fn frag_main([[location(0)]] loc0 : f32,
284              locations : FragLocations,
285              builtins : FragBuiltins) {
286   var col : f32 = ((builtins.coord.x * locations.loc1) + loc0);
287 }
288 )";
289 
290   auto* expect = R"(
291 struct FragBuiltins {
292   coord : vec4<f32>;
293 };
294 
295 struct FragLocations {
296   loc1 : f32;
297   loc2 : vec4<u32>;
298 };
299 
300 struct tint_symbol_1 {
301   [[location(0)]]
302   loc0 : f32;
303   [[location(1)]]
304   loc1 : f32;
305   [[location(2)]]
306   loc2 : vec4<u32>;
307 };
308 
309 fn frag_main_inner(loc0 : f32, locations : FragLocations, builtins : FragBuiltins) {
310   var col : f32 = ((builtins.coord.x * locations.loc1) + loc0);
311 }
312 
313 [[stage(fragment)]]
314 fn frag_main([[builtin(position)]] coord : vec4<f32>, tint_symbol : tint_symbol_1) {
315   frag_main_inner(tint_symbol.loc0, FragLocations(tint_symbol.loc1, tint_symbol.loc2), FragBuiltins(coord));
316 }
317 )";
318 
319   DataMap data;
320   data.Add<CanonicalizeEntryPointIO::Config>(
321       CanonicalizeEntryPointIO::ShaderStyle::kMsl);
322   auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
323 
324   EXPECT_EQ(expect, str(got));
325 }
326 
TEST_F(CanonicalizeEntryPointIOTest,StructParameters_Hlsl)327 TEST_F(CanonicalizeEntryPointIOTest, StructParameters_Hlsl) {
328   auto* src = R"(
329 struct FragBuiltins {
330   [[builtin(position)]] coord : vec4<f32>;
331 };
332 struct FragLocations {
333   [[location(1)]] loc1 : f32;
334   [[location(2)]] loc2 : vec4<u32>;
335 };
336 
337 [[stage(fragment)]]
338 fn frag_main([[location(0)]] loc0 : f32,
339              locations : FragLocations,
340              builtins : FragBuiltins) {
341   var col : f32 = ((builtins.coord.x * locations.loc1) + loc0);
342 }
343 )";
344 
345   auto* expect = R"(
346 struct FragBuiltins {
347   coord : vec4<f32>;
348 };
349 
350 struct FragLocations {
351   loc1 : f32;
352   loc2 : vec4<u32>;
353 };
354 
355 struct tint_symbol_1 {
356   [[location(0)]]
357   loc0 : f32;
358   [[location(1)]]
359   loc1 : f32;
360   [[location(2)]]
361   loc2 : vec4<u32>;
362   [[builtin(position)]]
363   coord : vec4<f32>;
364 };
365 
366 fn frag_main_inner(loc0 : f32, locations : FragLocations, builtins : FragBuiltins) {
367   var col : f32 = ((builtins.coord.x * locations.loc1) + loc0);
368 }
369 
370 [[stage(fragment)]]
371 fn frag_main(tint_symbol : tint_symbol_1) {
372   frag_main_inner(tint_symbol.loc0, FragLocations(tint_symbol.loc1, tint_symbol.loc2), FragBuiltins(tint_symbol.coord));
373 }
374 )";
375 
376   DataMap data;
377   data.Add<CanonicalizeEntryPointIO::Config>(
378       CanonicalizeEntryPointIO::ShaderStyle::kHlsl);
379   auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
380 
381   EXPECT_EQ(expect, str(got));
382 }
383 
TEST_F(CanonicalizeEntryPointIOTest,Return_NonStruct_Spirv)384 TEST_F(CanonicalizeEntryPointIOTest, Return_NonStruct_Spirv) {
385   auto* src = R"(
386 [[stage(fragment)]]
387 fn frag_main() -> [[builtin(frag_depth)]] f32 {
388   return 1.0;
389 }
390 )";
391 
392   auto* expect = R"(
393 [[builtin(frag_depth), internal(disable_validation__ignore_storage_class)]] var<out> value : f32;
394 
395 fn frag_main_inner() -> f32 {
396   return 1.0;
397 }
398 
399 [[stage(fragment)]]
400 fn frag_main() {
401   let inner_result = frag_main_inner();
402   value = inner_result;
403 }
404 )";
405 
406   DataMap data;
407   data.Add<CanonicalizeEntryPointIO::Config>(
408       CanonicalizeEntryPointIO::ShaderStyle::kSpirv);
409   auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
410 
411   EXPECT_EQ(expect, str(got));
412 }
413 
TEST_F(CanonicalizeEntryPointIOTest,Return_NonStruct_Msl)414 TEST_F(CanonicalizeEntryPointIOTest, Return_NonStruct_Msl) {
415   auto* src = R"(
416 [[stage(fragment)]]
417 fn frag_main() -> [[builtin(frag_depth)]] f32 {
418   return 1.0;
419 }
420 )";
421 
422   auto* expect = R"(
423 struct tint_symbol {
424   [[builtin(frag_depth)]]
425   value : f32;
426 };
427 
428 fn frag_main_inner() -> f32 {
429   return 1.0;
430 }
431 
432 [[stage(fragment)]]
433 fn frag_main() -> tint_symbol {
434   let inner_result = frag_main_inner();
435   var wrapper_result : tint_symbol;
436   wrapper_result.value = inner_result;
437   return wrapper_result;
438 }
439 )";
440 
441   DataMap data;
442   data.Add<CanonicalizeEntryPointIO::Config>(
443       CanonicalizeEntryPointIO::ShaderStyle::kMsl);
444   auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
445 
446   EXPECT_EQ(expect, str(got));
447 }
448 
TEST_F(CanonicalizeEntryPointIOTest,Return_NonStruct_Hlsl)449 TEST_F(CanonicalizeEntryPointIOTest, Return_NonStruct_Hlsl) {
450   auto* src = R"(
451 [[stage(fragment)]]
452 fn frag_main() -> [[builtin(frag_depth)]] f32 {
453   return 1.0;
454 }
455 )";
456 
457   auto* expect = R"(
458 struct tint_symbol {
459   [[builtin(frag_depth)]]
460   value : f32;
461 };
462 
463 fn frag_main_inner() -> f32 {
464   return 1.0;
465 }
466 
467 [[stage(fragment)]]
468 fn frag_main() -> tint_symbol {
469   let inner_result = frag_main_inner();
470   var wrapper_result : tint_symbol;
471   wrapper_result.value = inner_result;
472   return wrapper_result;
473 }
474 )";
475 
476   DataMap data;
477   data.Add<CanonicalizeEntryPointIO::Config>(
478       CanonicalizeEntryPointIO::ShaderStyle::kHlsl);
479   auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
480 
481   EXPECT_EQ(expect, str(got));
482 }
483 
TEST_F(CanonicalizeEntryPointIOTest,Return_Struct_Spirv)484 TEST_F(CanonicalizeEntryPointIOTest, Return_Struct_Spirv) {
485   auto* src = R"(
486 struct FragOutput {
487   [[location(0)]] color : vec4<f32>;
488   [[builtin(frag_depth)]] depth : f32;
489   [[builtin(sample_mask)]] mask : u32;
490 };
491 
492 [[stage(fragment)]]
493 fn frag_main() -> FragOutput {
494   var output : FragOutput;
495   output.depth = 1.0;
496   output.mask = 7u;
497   output.color = vec4<f32>(0.5, 0.5, 0.5, 1.0);
498   return output;
499 }
500 )";
501 
502   auto* expect = R"(
503 [[location(0), internal(disable_validation__ignore_storage_class)]] var<out> color_1 : vec4<f32>;
504 
505 [[builtin(frag_depth), internal(disable_validation__ignore_storage_class)]] var<out> depth_1 : f32;
506 
507 [[builtin(sample_mask), internal(disable_validation__ignore_storage_class)]] var<out> mask_1 : array<u32, 1>;
508 
509 struct FragOutput {
510   color : vec4<f32>;
511   depth : f32;
512   mask : u32;
513 };
514 
515 fn frag_main_inner() -> FragOutput {
516   var output : FragOutput;
517   output.depth = 1.0;
518   output.mask = 7u;
519   output.color = vec4<f32>(0.5, 0.5, 0.5, 1.0);
520   return output;
521 }
522 
523 [[stage(fragment)]]
524 fn frag_main() {
525   let inner_result = frag_main_inner();
526   color_1 = inner_result.color;
527   depth_1 = inner_result.depth;
528   mask_1[0] = inner_result.mask;
529 }
530 )";
531 
532   DataMap data;
533   data.Add<CanonicalizeEntryPointIO::Config>(
534       CanonicalizeEntryPointIO::ShaderStyle::kSpirv);
535   auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
536 
537   EXPECT_EQ(expect, str(got));
538 }
539 
TEST_F(CanonicalizeEntryPointIOTest,Return_Struct_Msl)540 TEST_F(CanonicalizeEntryPointIOTest, Return_Struct_Msl) {
541   auto* src = R"(
542 struct FragOutput {
543   [[location(0)]] color : vec4<f32>;
544   [[builtin(frag_depth)]] depth : f32;
545   [[builtin(sample_mask)]] mask : u32;
546 };
547 
548 [[stage(fragment)]]
549 fn frag_main() -> FragOutput {
550   var output : FragOutput;
551   output.depth = 1.0;
552   output.mask = 7u;
553   output.color = vec4<f32>(0.5, 0.5, 0.5, 1.0);
554   return output;
555 }
556 )";
557 
558   auto* expect = R"(
559 struct FragOutput {
560   color : vec4<f32>;
561   depth : f32;
562   mask : u32;
563 };
564 
565 struct tint_symbol {
566   [[location(0)]]
567   color : vec4<f32>;
568   [[builtin(frag_depth)]]
569   depth : f32;
570   [[builtin(sample_mask)]]
571   mask : u32;
572 };
573 
574 fn frag_main_inner() -> FragOutput {
575   var output : FragOutput;
576   output.depth = 1.0;
577   output.mask = 7u;
578   output.color = vec4<f32>(0.5, 0.5, 0.5, 1.0);
579   return output;
580 }
581 
582 [[stage(fragment)]]
583 fn frag_main() -> tint_symbol {
584   let inner_result = frag_main_inner();
585   var wrapper_result : tint_symbol;
586   wrapper_result.color = inner_result.color;
587   wrapper_result.depth = inner_result.depth;
588   wrapper_result.mask = inner_result.mask;
589   return wrapper_result;
590 }
591 )";
592 
593   DataMap data;
594   data.Add<CanonicalizeEntryPointIO::Config>(
595       CanonicalizeEntryPointIO::ShaderStyle::kMsl);
596   auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
597 
598   EXPECT_EQ(expect, str(got));
599 }
600 
TEST_F(CanonicalizeEntryPointIOTest,Return_Struct_Hlsl)601 TEST_F(CanonicalizeEntryPointIOTest, Return_Struct_Hlsl) {
602   auto* src = R"(
603 struct FragOutput {
604   [[location(0)]] color : vec4<f32>;
605   [[builtin(frag_depth)]] depth : f32;
606   [[builtin(sample_mask)]] mask : u32;
607 };
608 
609 [[stage(fragment)]]
610 fn frag_main() -> FragOutput {
611   var output : FragOutput;
612   output.depth = 1.0;
613   output.mask = 7u;
614   output.color = vec4<f32>(0.5, 0.5, 0.5, 1.0);
615   return output;
616 }
617 )";
618 
619   auto* expect = R"(
620 struct FragOutput {
621   color : vec4<f32>;
622   depth : f32;
623   mask : u32;
624 };
625 
626 struct tint_symbol {
627   [[location(0)]]
628   color : vec4<f32>;
629   [[builtin(frag_depth)]]
630   depth : f32;
631   [[builtin(sample_mask)]]
632   mask : u32;
633 };
634 
635 fn frag_main_inner() -> FragOutput {
636   var output : FragOutput;
637   output.depth = 1.0;
638   output.mask = 7u;
639   output.color = vec4<f32>(0.5, 0.5, 0.5, 1.0);
640   return output;
641 }
642 
643 [[stage(fragment)]]
644 fn frag_main() -> tint_symbol {
645   let inner_result = frag_main_inner();
646   var wrapper_result : tint_symbol;
647   wrapper_result.color = inner_result.color;
648   wrapper_result.depth = inner_result.depth;
649   wrapper_result.mask = inner_result.mask;
650   return wrapper_result;
651 }
652 )";
653 
654   DataMap data;
655   data.Add<CanonicalizeEntryPointIO::Config>(
656       CanonicalizeEntryPointIO::ShaderStyle::kHlsl);
657   auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
658 
659   EXPECT_EQ(expect, str(got));
660 }
661 
TEST_F(CanonicalizeEntryPointIOTest,StructParameters_SharedDeviceFunction_Spirv)662 TEST_F(CanonicalizeEntryPointIOTest,
663        StructParameters_SharedDeviceFunction_Spirv) {
664   auto* src = R"(
665 struct FragmentInput {
666   [[location(0)]] value : f32;
667   [[location(1)]] mul : f32;
668 };
669 
670 fn foo(x : FragmentInput) -> f32 {
671   return x.value * x.mul;
672 }
673 
674 [[stage(fragment)]]
675 fn frag_main1(inputs : FragmentInput) {
676   var x : f32 = foo(inputs);
677 }
678 
679 [[stage(fragment)]]
680 fn frag_main2(inputs : FragmentInput) {
681   var x : f32 = foo(inputs);
682 }
683 )";
684 
685   auto* expect = R"(
686 [[location(0), internal(disable_validation__ignore_storage_class)]] var<in> value_1 : f32;
687 
688 [[location(1), internal(disable_validation__ignore_storage_class)]] var<in> mul_1 : f32;
689 
690 [[location(0), internal(disable_validation__ignore_storage_class)]] var<in> value_2 : f32;
691 
692 [[location(1), internal(disable_validation__ignore_storage_class)]] var<in> mul_2 : f32;
693 
694 struct FragmentInput {
695   value : f32;
696   mul : f32;
697 };
698 
699 fn foo(x : FragmentInput) -> f32 {
700   return (x.value * x.mul);
701 }
702 
703 fn frag_main1_inner(inputs : FragmentInput) {
704   var x : f32 = foo(inputs);
705 }
706 
707 [[stage(fragment)]]
708 fn frag_main1() {
709   frag_main1_inner(FragmentInput(value_1, mul_1));
710 }
711 
712 fn frag_main2_inner(inputs : FragmentInput) {
713   var x : f32 = foo(inputs);
714 }
715 
716 [[stage(fragment)]]
717 fn frag_main2() {
718   frag_main2_inner(FragmentInput(value_2, mul_2));
719 }
720 )";
721 
722   DataMap data;
723   data.Add<CanonicalizeEntryPointIO::Config>(
724       CanonicalizeEntryPointIO::ShaderStyle::kSpirv);
725   auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
726 
727   EXPECT_EQ(expect, str(got));
728 }
729 
TEST_F(CanonicalizeEntryPointIOTest,StructParameters_SharedDeviceFunction_Msl)730 TEST_F(CanonicalizeEntryPointIOTest,
731        StructParameters_SharedDeviceFunction_Msl) {
732   auto* src = R"(
733 struct FragmentInput {
734   [[location(0)]] value : f32;
735   [[location(1)]] mul : f32;
736 };
737 
738 fn foo(x : FragmentInput) -> f32 {
739   return x.value * x.mul;
740 }
741 
742 [[stage(fragment)]]
743 fn frag_main1(inputs : FragmentInput) {
744   var x : f32 = foo(inputs);
745 }
746 
747 [[stage(fragment)]]
748 fn frag_main2(inputs : FragmentInput) {
749   var x : f32 = foo(inputs);
750 }
751 )";
752 
753   auto* expect = R"(
754 struct FragmentInput {
755   value : f32;
756   mul : f32;
757 };
758 
759 fn foo(x : FragmentInput) -> f32 {
760   return (x.value * x.mul);
761 }
762 
763 struct tint_symbol_1 {
764   [[location(0)]]
765   value : f32;
766   [[location(1)]]
767   mul : f32;
768 };
769 
770 fn frag_main1_inner(inputs : FragmentInput) {
771   var x : f32 = foo(inputs);
772 }
773 
774 [[stage(fragment)]]
775 fn frag_main1(tint_symbol : tint_symbol_1) {
776   frag_main1_inner(FragmentInput(tint_symbol.value, tint_symbol.mul));
777 }
778 
779 struct tint_symbol_3 {
780   [[location(0)]]
781   value : f32;
782   [[location(1)]]
783   mul : f32;
784 };
785 
786 fn frag_main2_inner(inputs : FragmentInput) {
787   var x : f32 = foo(inputs);
788 }
789 
790 [[stage(fragment)]]
791 fn frag_main2(tint_symbol_2 : tint_symbol_3) {
792   frag_main2_inner(FragmentInput(tint_symbol_2.value, tint_symbol_2.mul));
793 }
794 )";
795 
796   DataMap data;
797   data.Add<CanonicalizeEntryPointIO::Config>(
798       CanonicalizeEntryPointIO::ShaderStyle::kMsl);
799   auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
800 
801   EXPECT_EQ(expect, str(got));
802 }
803 
TEST_F(CanonicalizeEntryPointIOTest,StructParameters_SharedDeviceFunction_Hlsl)804 TEST_F(CanonicalizeEntryPointIOTest,
805        StructParameters_SharedDeviceFunction_Hlsl) {
806   auto* src = R"(
807 struct FragmentInput {
808   [[location(0)]] value : f32;
809   [[location(1)]] mul : f32;
810 };
811 
812 fn foo(x : FragmentInput) -> f32 {
813   return x.value * x.mul;
814 }
815 
816 [[stage(fragment)]]
817 fn frag_main1(inputs : FragmentInput) {
818   var x : f32 = foo(inputs);
819 }
820 
821 [[stage(fragment)]]
822 fn frag_main2(inputs : FragmentInput) {
823   var x : f32 = foo(inputs);
824 }
825 )";
826 
827   auto* expect = R"(
828 struct FragmentInput {
829   value : f32;
830   mul : f32;
831 };
832 
833 fn foo(x : FragmentInput) -> f32 {
834   return (x.value * x.mul);
835 }
836 
837 struct tint_symbol_1 {
838   [[location(0)]]
839   value : f32;
840   [[location(1)]]
841   mul : f32;
842 };
843 
844 fn frag_main1_inner(inputs : FragmentInput) {
845   var x : f32 = foo(inputs);
846 }
847 
848 [[stage(fragment)]]
849 fn frag_main1(tint_symbol : tint_symbol_1) {
850   frag_main1_inner(FragmentInput(tint_symbol.value, tint_symbol.mul));
851 }
852 
853 struct tint_symbol_3 {
854   [[location(0)]]
855   value : f32;
856   [[location(1)]]
857   mul : f32;
858 };
859 
860 fn frag_main2_inner(inputs : FragmentInput) {
861   var x : f32 = foo(inputs);
862 }
863 
864 [[stage(fragment)]]
865 fn frag_main2(tint_symbol_2 : tint_symbol_3) {
866   frag_main2_inner(FragmentInput(tint_symbol_2.value, tint_symbol_2.mul));
867 }
868 )";
869 
870   DataMap data;
871   data.Add<CanonicalizeEntryPointIO::Config>(
872       CanonicalizeEntryPointIO::ShaderStyle::kHlsl);
873   auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
874 
875   EXPECT_EQ(expect, str(got));
876 }
877 
TEST_F(CanonicalizeEntryPointIOTest,Struct_ModuleScopeVariable)878 TEST_F(CanonicalizeEntryPointIOTest, Struct_ModuleScopeVariable) {
879   auto* src = R"(
880 struct FragmentInput {
881   [[location(0)]] col1 : f32;
882   [[location(1)]] col2 : f32;
883 };
884 
885 var<private> global_inputs : FragmentInput;
886 
887 fn foo() -> f32 {
888   return global_inputs.col1 * 0.5;
889 }
890 
891 fn bar() -> f32 {
892   return global_inputs.col2 * 2.0;
893 }
894 
895 [[stage(fragment)]]
896 fn frag_main1(inputs : FragmentInput) {
897  global_inputs = inputs;
898  var r : f32 = foo();
899  var g : f32 = bar();
900 }
901 )";
902 
903   auto* expect = R"(
904 struct FragmentInput {
905   col1 : f32;
906   col2 : f32;
907 };
908 
909 var<private> global_inputs : FragmentInput;
910 
911 fn foo() -> f32 {
912   return (global_inputs.col1 * 0.5);
913 }
914 
915 fn bar() -> f32 {
916   return (global_inputs.col2 * 2.0);
917 }
918 
919 struct tint_symbol_1 {
920   [[location(0)]]
921   col1 : f32;
922   [[location(1)]]
923   col2 : f32;
924 };
925 
926 fn frag_main1_inner(inputs : FragmentInput) {
927   global_inputs = inputs;
928   var r : f32 = foo();
929   var g : f32 = bar();
930 }
931 
932 [[stage(fragment)]]
933 fn frag_main1(tint_symbol : tint_symbol_1) {
934   frag_main1_inner(FragmentInput(tint_symbol.col1, tint_symbol.col2));
935 }
936 )";
937 
938   DataMap data;
939   data.Add<CanonicalizeEntryPointIO::Config>(
940       CanonicalizeEntryPointIO::ShaderStyle::kMsl);
941   auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
942 
943   EXPECT_EQ(expect, str(got));
944 }
945 
TEST_F(CanonicalizeEntryPointIOTest,Struct_TypeAliases)946 TEST_F(CanonicalizeEntryPointIOTest, Struct_TypeAliases) {
947   auto* src = R"(
948 type myf32 = f32;
949 
950 struct FragmentInput {
951   [[location(0)]] col1 : myf32;
952   [[location(1)]] col2 : myf32;
953 };
954 
955 struct FragmentOutput {
956   [[location(0)]] col1 : myf32;
957   [[location(1)]] col2 : myf32;
958 };
959 
960 type MyFragmentInput = FragmentInput;
961 
962 type MyFragmentOutput = FragmentOutput;
963 
964 fn foo(x : MyFragmentInput) -> myf32 {
965   return x.col1;
966 }
967 
968 [[stage(fragment)]]
969 fn frag_main(inputs : MyFragmentInput) -> MyFragmentOutput {
970   var x : myf32 = foo(inputs);
971   return MyFragmentOutput(x, inputs.col2);
972 }
973 )";
974 
975   auto* expect = R"(
976 type myf32 = f32;
977 
978 struct FragmentInput {
979   col1 : myf32;
980   col2 : myf32;
981 };
982 
983 struct FragmentOutput {
984   col1 : myf32;
985   col2 : myf32;
986 };
987 
988 type MyFragmentInput = FragmentInput;
989 
990 type MyFragmentOutput = FragmentOutput;
991 
992 fn foo(x : MyFragmentInput) -> myf32 {
993   return x.col1;
994 }
995 
996 struct tint_symbol_1 {
997   [[location(0)]]
998   col1 : f32;
999   [[location(1)]]
1000   col2 : f32;
1001 };
1002 
1003 struct tint_symbol_2 {
1004   [[location(0)]]
1005   col1 : f32;
1006   [[location(1)]]
1007   col2 : f32;
1008 };
1009 
1010 fn frag_main_inner(inputs : MyFragmentInput) -> MyFragmentOutput {
1011   var x : myf32 = foo(inputs);
1012   return MyFragmentOutput(x, inputs.col2);
1013 }
1014 
1015 [[stage(fragment)]]
1016 fn frag_main(tint_symbol : tint_symbol_1) -> tint_symbol_2 {
1017   let inner_result = frag_main_inner(MyFragmentInput(tint_symbol.col1, tint_symbol.col2));
1018   var wrapper_result : tint_symbol_2;
1019   wrapper_result.col1 = inner_result.col1;
1020   wrapper_result.col2 = inner_result.col2;
1021   return wrapper_result;
1022 }
1023 )";
1024 
1025   DataMap data;
1026   data.Add<CanonicalizeEntryPointIO::Config>(
1027       CanonicalizeEntryPointIO::ShaderStyle::kMsl);
1028   auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
1029 
1030   EXPECT_EQ(expect, str(got));
1031 }
1032 
TEST_F(CanonicalizeEntryPointIOTest,InterpolateAttributes)1033 TEST_F(CanonicalizeEntryPointIOTest, InterpolateAttributes) {
1034   auto* src = R"(
1035 struct VertexOut {
1036   [[builtin(position)]] pos : vec4<f32>;
1037   [[location(1), interpolate(flat)]] loc1: f32;
1038   [[location(2), interpolate(linear, sample)]] loc2 : f32;
1039   [[location(3), interpolate(perspective, centroid)]] loc3 : f32;
1040 };
1041 
1042 struct FragmentIn {
1043   [[location(1), interpolate(flat)]] loc1: f32;
1044   [[location(2), interpolate(linear, sample)]] loc2 : f32;
1045 };
1046 
1047 [[stage(vertex)]]
1048 fn vert_main() -> VertexOut {
1049   return VertexOut();
1050 }
1051 
1052 [[stage(fragment)]]
1053 fn frag_main(inputs : FragmentIn,
1054              [[location(3), interpolate(perspective, centroid)]] loc3 : f32) {
1055   let x = inputs.loc1 + inputs.loc2 + loc3;
1056 }
1057 )";
1058 
1059   auto* expect = R"(
1060 struct VertexOut {
1061   pos : vec4<f32>;
1062   loc1 : f32;
1063   loc2 : f32;
1064   loc3 : f32;
1065 };
1066 
1067 struct FragmentIn {
1068   loc1 : f32;
1069   loc2 : f32;
1070 };
1071 
1072 struct tint_symbol {
1073   [[location(1), interpolate(flat)]]
1074   loc1 : f32;
1075   [[location(2), interpolate(linear, sample)]]
1076   loc2 : f32;
1077   [[location(3), interpolate(perspective, centroid)]]
1078   loc3 : f32;
1079   [[builtin(position)]]
1080   pos : vec4<f32>;
1081 };
1082 
1083 fn vert_main_inner() -> VertexOut {
1084   return VertexOut();
1085 }
1086 
1087 [[stage(vertex)]]
1088 fn vert_main() -> tint_symbol {
1089   let inner_result = vert_main_inner();
1090   var wrapper_result : tint_symbol;
1091   wrapper_result.pos = inner_result.pos;
1092   wrapper_result.loc1 = inner_result.loc1;
1093   wrapper_result.loc2 = inner_result.loc2;
1094   wrapper_result.loc3 = inner_result.loc3;
1095   return wrapper_result;
1096 }
1097 
1098 struct tint_symbol_2 {
1099   [[location(1), interpolate(flat)]]
1100   loc1 : f32;
1101   [[location(2), interpolate(linear, sample)]]
1102   loc2 : f32;
1103   [[location(3), interpolate(perspective, centroid)]]
1104   loc3 : f32;
1105 };
1106 
1107 fn frag_main_inner(inputs : FragmentIn, loc3 : f32) {
1108   let x = ((inputs.loc1 + inputs.loc2) + loc3);
1109 }
1110 
1111 [[stage(fragment)]]
1112 fn frag_main(tint_symbol_1 : tint_symbol_2) {
1113   frag_main_inner(FragmentIn(tint_symbol_1.loc1, tint_symbol_1.loc2), tint_symbol_1.loc3);
1114 }
1115 )";
1116 
1117   DataMap data;
1118   data.Add<CanonicalizeEntryPointIO::Config>(
1119       CanonicalizeEntryPointIO::ShaderStyle::kHlsl);
1120   auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
1121 
1122   EXPECT_EQ(expect, str(got));
1123 }
1124 
TEST_F(CanonicalizeEntryPointIOTest,InterpolateAttributes_Integers_Spirv)1125 TEST_F(CanonicalizeEntryPointIOTest, InterpolateAttributes_Integers_Spirv) {
1126   // Test that we add a Flat attribute to integers that are vertex outputs and
1127   // fragment inputs, but not vertex inputs or fragment outputs.
1128   auto* src = R"(
1129 struct VertexIn {
1130   [[location(0)]] i : i32;
1131   [[location(1)]] u : u32;
1132   [[location(2)]] vi : vec4<i32>;
1133   [[location(3)]] vu : vec4<u32>;
1134 };
1135 
1136 struct VertexOut {
1137   [[location(0)]] i : i32;
1138   [[location(1)]] u : u32;
1139   [[location(2)]] vi : vec4<i32>;
1140   [[location(3)]] vu : vec4<u32>;
1141   [[builtin(position)]] pos : vec4<f32>;
1142 };
1143 
1144 struct FragmentInterface {
1145   [[location(0)]] i : i32;
1146   [[location(1)]] u : u32;
1147   [[location(2)]] vi : vec4<i32>;
1148   [[location(3)]] vu : vec4<u32>;
1149 };
1150 
1151 [[stage(vertex)]]
1152 fn vert_main(in : VertexIn) -> VertexOut {
1153   return VertexOut(in.i, in.u, in.vi, in.vu, vec4<f32>());
1154 }
1155 
1156 [[stage(fragment)]]
1157 fn frag_main(inputs : FragmentInterface) -> FragmentInterface {
1158   return inputs;
1159 }
1160 )";
1161 
1162   auto* expect =
1163       R"(
1164 [[location(0), internal(disable_validation__ignore_storage_class)]] var<in> i_1 : i32;
1165 
1166 [[location(1), internal(disable_validation__ignore_storage_class)]] var<in> u_1 : u32;
1167 
1168 [[location(2), internal(disable_validation__ignore_storage_class)]] var<in> vi_1 : vec4<i32>;
1169 
1170 [[location(3), internal(disable_validation__ignore_storage_class)]] var<in> vu_1 : vec4<u32>;
1171 
1172 [[location(0), interpolate(flat), internal(disable_validation__ignore_storage_class)]] var<out> i_2 : i32;
1173 
1174 [[location(1), interpolate(flat), internal(disable_validation__ignore_storage_class)]] var<out> u_2 : u32;
1175 
1176 [[location(2), interpolate(flat), internal(disable_validation__ignore_storage_class)]] var<out> vi_2 : vec4<i32>;
1177 
1178 [[location(3), interpolate(flat), internal(disable_validation__ignore_storage_class)]] var<out> vu_2 : vec4<u32>;
1179 
1180 [[builtin(position), internal(disable_validation__ignore_storage_class)]] var<out> pos_1 : vec4<f32>;
1181 
1182 [[location(0), interpolate(flat), internal(disable_validation__ignore_storage_class)]] var<in> i_3 : i32;
1183 
1184 [[location(1), interpolate(flat), internal(disable_validation__ignore_storage_class)]] var<in> u_3 : u32;
1185 
1186 [[location(2), interpolate(flat), internal(disable_validation__ignore_storage_class)]] var<in> vi_3 : vec4<i32>;
1187 
1188 [[location(3), interpolate(flat), internal(disable_validation__ignore_storage_class)]] var<in> vu_3 : vec4<u32>;
1189 
1190 [[location(0), internal(disable_validation__ignore_storage_class)]] var<out> i_4 : i32;
1191 
1192 [[location(1), internal(disable_validation__ignore_storage_class)]] var<out> u_4 : u32;
1193 
1194 [[location(2), internal(disable_validation__ignore_storage_class)]] var<out> vi_4 : vec4<i32>;
1195 
1196 [[location(3), internal(disable_validation__ignore_storage_class)]] var<out> vu_4 : vec4<u32>;
1197 
1198 struct VertexIn {
1199   i : i32;
1200   u : u32;
1201   vi : vec4<i32>;
1202   vu : vec4<u32>;
1203 };
1204 
1205 struct VertexOut {
1206   i : i32;
1207   u : u32;
1208   vi : vec4<i32>;
1209   vu : vec4<u32>;
1210   pos : vec4<f32>;
1211 };
1212 
1213 struct FragmentInterface {
1214   i : i32;
1215   u : u32;
1216   vi : vec4<i32>;
1217   vu : vec4<u32>;
1218 };
1219 
1220 fn vert_main_inner(in : VertexIn) -> VertexOut {
1221   return VertexOut(in.i, in.u, in.vi, in.vu, vec4<f32>());
1222 }
1223 
1224 [[stage(vertex)]]
1225 fn vert_main() {
1226   let inner_result = vert_main_inner(VertexIn(i_1, u_1, vi_1, vu_1));
1227   i_2 = inner_result.i;
1228   u_2 = inner_result.u;
1229   vi_2 = inner_result.vi;
1230   vu_2 = inner_result.vu;
1231   pos_1 = inner_result.pos;
1232 }
1233 
1234 fn frag_main_inner(inputs : FragmentInterface) -> FragmentInterface {
1235   return inputs;
1236 }
1237 
1238 [[stage(fragment)]]
1239 fn frag_main() {
1240   let inner_result_1 = frag_main_inner(FragmentInterface(i_3, u_3, vi_3, vu_3));
1241   i_4 = inner_result_1.i;
1242   u_4 = inner_result_1.u;
1243   vi_4 = inner_result_1.vi;
1244   vu_4 = inner_result_1.vu;
1245 }
1246 )";
1247 
1248   DataMap data;
1249   data.Add<CanonicalizeEntryPointIO::Config>(
1250       CanonicalizeEntryPointIO::ShaderStyle::kSpirv);
1251   auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
1252 
1253   EXPECT_EQ(expect, str(got));
1254 }
1255 
TEST_F(CanonicalizeEntryPointIOTest,InvariantAttributes)1256 TEST_F(CanonicalizeEntryPointIOTest, InvariantAttributes) {
1257   auto* src = R"(
1258 struct VertexOut {
1259   [[builtin(position), invariant]] pos : vec4<f32>;
1260 };
1261 
1262 [[stage(vertex)]]
1263 fn main1() -> VertexOut {
1264   return VertexOut();
1265 }
1266 
1267 [[stage(vertex)]]
1268 fn main2() -> [[builtin(position), invariant]] vec4<f32> {
1269   return vec4<f32>();
1270 }
1271 )";
1272 
1273   auto* expect = R"(
1274 struct VertexOut {
1275   pos : vec4<f32>;
1276 };
1277 
1278 struct tint_symbol {
1279   [[builtin(position), invariant]]
1280   pos : vec4<f32>;
1281 };
1282 
1283 fn main1_inner() -> VertexOut {
1284   return VertexOut();
1285 }
1286 
1287 [[stage(vertex)]]
1288 fn main1() -> tint_symbol {
1289   let inner_result = main1_inner();
1290   var wrapper_result : tint_symbol;
1291   wrapper_result.pos = inner_result.pos;
1292   return wrapper_result;
1293 }
1294 
1295 struct tint_symbol_1 {
1296   [[builtin(position), invariant]]
1297   value : vec4<f32>;
1298 };
1299 
1300 fn main2_inner() -> vec4<f32> {
1301   return vec4<f32>();
1302 }
1303 
1304 [[stage(vertex)]]
1305 fn main2() -> tint_symbol_1 {
1306   let inner_result_1 = main2_inner();
1307   var wrapper_result_1 : tint_symbol_1;
1308   wrapper_result_1.value = inner_result_1;
1309   return wrapper_result_1;
1310 }
1311 )";
1312 
1313   DataMap data;
1314   data.Add<CanonicalizeEntryPointIO::Config>(
1315       CanonicalizeEntryPointIO::ShaderStyle::kHlsl);
1316   auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
1317 
1318   EXPECT_EQ(expect, str(got));
1319 }
1320 
TEST_F(CanonicalizeEntryPointIOTest,Struct_LayoutDecorations)1321 TEST_F(CanonicalizeEntryPointIOTest, Struct_LayoutDecorations) {
1322   auto* src = R"(
1323 [[block]]
1324 struct FragmentInput {
1325   [[size(16), location(1)]] value : f32;
1326   [[builtin(position)]] [[align(32)]] coord : vec4<f32>;
1327   [[location(0), interpolate(linear, sample)]] [[align(128)]] loc0 : f32;
1328 };
1329 
1330 struct FragmentOutput {
1331   [[size(16), location(1), interpolate(flat)]] value : f32;
1332 };
1333 
1334 [[stage(fragment)]]
1335 fn frag_main(inputs : FragmentInput) -> FragmentOutput {
1336   return FragmentOutput(inputs.coord.x * inputs.value + inputs.loc0);
1337 }
1338 )";
1339 
1340   auto* expect = R"(
1341 [[block]]
1342 struct FragmentInput {
1343   [[size(16)]]
1344   value : f32;
1345   [[align(32)]]
1346   coord : vec4<f32>;
1347   [[align(128)]]
1348   loc0 : f32;
1349 };
1350 
1351 struct FragmentOutput {
1352   [[size(16)]]
1353   value : f32;
1354 };
1355 
1356 struct tint_symbol_1 {
1357   [[location(0), interpolate(linear, sample)]]
1358   loc0 : f32;
1359   [[location(1)]]
1360   value : f32;
1361   [[builtin(position)]]
1362   coord : vec4<f32>;
1363 };
1364 
1365 struct tint_symbol_2 {
1366   [[location(1), interpolate(flat)]]
1367   value : f32;
1368 };
1369 
1370 fn frag_main_inner(inputs : FragmentInput) -> FragmentOutput {
1371   return FragmentOutput(((inputs.coord.x * inputs.value) + inputs.loc0));
1372 }
1373 
1374 [[stage(fragment)]]
1375 fn frag_main(tint_symbol : tint_symbol_1) -> tint_symbol_2 {
1376   let inner_result = frag_main_inner(FragmentInput(tint_symbol.value, tint_symbol.coord, tint_symbol.loc0));
1377   var wrapper_result : tint_symbol_2;
1378   wrapper_result.value = inner_result.value;
1379   return wrapper_result;
1380 }
1381 )";
1382 
1383   DataMap data;
1384   data.Add<CanonicalizeEntryPointIO::Config>(
1385       CanonicalizeEntryPointIO::ShaderStyle::kHlsl);
1386   auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
1387 
1388   EXPECT_EQ(expect, str(got));
1389 }
1390 
TEST_F(CanonicalizeEntryPointIOTest,SortedMembers)1391 TEST_F(CanonicalizeEntryPointIOTest, SortedMembers) {
1392   auto* src = R"(
1393 struct VertexOutput {
1394   [[location(1)]] b : u32;
1395   [[builtin(position)]] pos : vec4<f32>;
1396   [[location(3)]] d : u32;
1397   [[location(0)]] a : f32;
1398   [[location(2)]] c : i32;
1399 };
1400 
1401 struct FragmentInputExtra {
1402   [[location(3)]] d : u32;
1403   [[builtin(position)]] pos : vec4<f32>;
1404   [[location(0)]] a : f32;
1405 };
1406 
1407 [[stage(vertex)]]
1408 fn vert_main() -> VertexOutput {
1409   return VertexOutput();
1410 }
1411 
1412 [[stage(fragment)]]
1413 fn frag_main([[builtin(front_facing)]] ff : bool,
1414              [[location(2)]] c : i32,
1415              inputs : FragmentInputExtra,
1416              [[location(1)]] b : u32) {
1417 }
1418 )";
1419 
1420   auto* expect = R"(
1421 struct VertexOutput {
1422   b : u32;
1423   pos : vec4<f32>;
1424   d : u32;
1425   a : f32;
1426   c : i32;
1427 };
1428 
1429 struct FragmentInputExtra {
1430   d : u32;
1431   pos : vec4<f32>;
1432   a : f32;
1433 };
1434 
1435 struct tint_symbol {
1436   [[location(0)]]
1437   a : f32;
1438   [[location(1)]]
1439   b : u32;
1440   [[location(2)]]
1441   c : i32;
1442   [[location(3)]]
1443   d : u32;
1444   [[builtin(position)]]
1445   pos : vec4<f32>;
1446 };
1447 
1448 fn vert_main_inner() -> VertexOutput {
1449   return VertexOutput();
1450 }
1451 
1452 [[stage(vertex)]]
1453 fn vert_main() -> tint_symbol {
1454   let inner_result = vert_main_inner();
1455   var wrapper_result : tint_symbol;
1456   wrapper_result.b = inner_result.b;
1457   wrapper_result.pos = inner_result.pos;
1458   wrapper_result.d = inner_result.d;
1459   wrapper_result.a = inner_result.a;
1460   wrapper_result.c = inner_result.c;
1461   return wrapper_result;
1462 }
1463 
1464 struct tint_symbol_2 {
1465   [[location(0)]]
1466   a : f32;
1467   [[location(1)]]
1468   b : u32;
1469   [[location(2)]]
1470   c : i32;
1471   [[location(3)]]
1472   d : u32;
1473   [[builtin(position)]]
1474   pos : vec4<f32>;
1475   [[builtin(front_facing)]]
1476   ff : bool;
1477 };
1478 
1479 fn frag_main_inner(ff : bool, c : i32, inputs : FragmentInputExtra, b : u32) {
1480 }
1481 
1482 [[stage(fragment)]]
1483 fn frag_main(tint_symbol_1 : tint_symbol_2) {
1484   frag_main_inner(tint_symbol_1.ff, tint_symbol_1.c, FragmentInputExtra(tint_symbol_1.d, tint_symbol_1.pos, tint_symbol_1.a), tint_symbol_1.b);
1485 }
1486 )";
1487 
1488   DataMap data;
1489   data.Add<CanonicalizeEntryPointIO::Config>(
1490       CanonicalizeEntryPointIO::ShaderStyle::kHlsl);
1491   auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
1492 
1493   EXPECT_EQ(expect, str(got));
1494 }
1495 
TEST_F(CanonicalizeEntryPointIOTest,DontRenameSymbols)1496 TEST_F(CanonicalizeEntryPointIOTest, DontRenameSymbols) {
1497   auto* src = R"(
1498 [[stage(fragment)]]
1499 fn tint_symbol_1([[location(0)]] col : f32) {
1500 }
1501 )";
1502 
1503   auto* expect = R"(
1504 struct tint_symbol_2 {
1505   [[location(0)]]
1506   col : f32;
1507 };
1508 
1509 fn tint_symbol_1_inner(col : f32) {
1510 }
1511 
1512 [[stage(fragment)]]
1513 fn tint_symbol_1(tint_symbol : tint_symbol_2) {
1514   tint_symbol_1_inner(tint_symbol.col);
1515 }
1516 )";
1517 
1518   DataMap data;
1519   data.Add<CanonicalizeEntryPointIO::Config>(
1520       CanonicalizeEntryPointIO::ShaderStyle::kMsl);
1521   auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
1522 
1523   EXPECT_EQ(expect, str(got));
1524 }
1525 
TEST_F(CanonicalizeEntryPointIOTest,FixedSampleMask_VoidNoReturn)1526 TEST_F(CanonicalizeEntryPointIOTest, FixedSampleMask_VoidNoReturn) {
1527   auto* src = R"(
1528 [[stage(fragment)]]
1529 fn frag_main() {
1530 }
1531 )";
1532 
1533   auto* expect = R"(
1534 struct tint_symbol {
1535   [[builtin(sample_mask)]]
1536   fixed_sample_mask : u32;
1537 };
1538 
1539 fn frag_main_inner() {
1540 }
1541 
1542 [[stage(fragment)]]
1543 fn frag_main() -> tint_symbol {
1544   frag_main_inner();
1545   var wrapper_result : tint_symbol;
1546   wrapper_result.fixed_sample_mask = 3u;
1547   return wrapper_result;
1548 }
1549 )";
1550 
1551   DataMap data;
1552   data.Add<CanonicalizeEntryPointIO::Config>(
1553       CanonicalizeEntryPointIO::ShaderStyle::kMsl, 0x03u);
1554   auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
1555 
1556   EXPECT_EQ(expect, str(got));
1557 }
1558 
TEST_F(CanonicalizeEntryPointIOTest,FixedSampleMask_VoidWithReturn)1559 TEST_F(CanonicalizeEntryPointIOTest, FixedSampleMask_VoidWithReturn) {
1560   auto* src = R"(
1561 [[stage(fragment)]]
1562 fn frag_main() {
1563   return;
1564 }
1565 )";
1566 
1567   auto* expect = R"(
1568 struct tint_symbol {
1569   [[builtin(sample_mask)]]
1570   fixed_sample_mask : u32;
1571 };
1572 
1573 fn frag_main_inner() {
1574   return;
1575 }
1576 
1577 [[stage(fragment)]]
1578 fn frag_main() -> tint_symbol {
1579   frag_main_inner();
1580   var wrapper_result : tint_symbol;
1581   wrapper_result.fixed_sample_mask = 3u;
1582   return wrapper_result;
1583 }
1584 )";
1585 
1586   DataMap data;
1587   data.Add<CanonicalizeEntryPointIO::Config>(
1588       CanonicalizeEntryPointIO::ShaderStyle::kMsl, 0x03u);
1589   auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
1590 
1591   EXPECT_EQ(expect, str(got));
1592 }
1593 
TEST_F(CanonicalizeEntryPointIOTest,FixedSampleMask_WithAuthoredMask)1594 TEST_F(CanonicalizeEntryPointIOTest, FixedSampleMask_WithAuthoredMask) {
1595   auto* src = R"(
1596 [[stage(fragment)]]
1597 fn frag_main() -> [[builtin(sample_mask)]] u32 {
1598   return 7u;
1599 }
1600 )";
1601 
1602   auto* expect = R"(
1603 struct tint_symbol {
1604   [[builtin(sample_mask)]]
1605   value : u32;
1606 };
1607 
1608 fn frag_main_inner() -> u32 {
1609   return 7u;
1610 }
1611 
1612 [[stage(fragment)]]
1613 fn frag_main() -> tint_symbol {
1614   let inner_result = frag_main_inner();
1615   var wrapper_result : tint_symbol;
1616   wrapper_result.value = (inner_result & 3u);
1617   return wrapper_result;
1618 }
1619 )";
1620 
1621   DataMap data;
1622   data.Add<CanonicalizeEntryPointIO::Config>(
1623       CanonicalizeEntryPointIO::ShaderStyle::kMsl, 0x03u);
1624   auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
1625 
1626   EXPECT_EQ(expect, str(got));
1627 }
1628 
TEST_F(CanonicalizeEntryPointIOTest,FixedSampleMask_WithoutAuthoredMask)1629 TEST_F(CanonicalizeEntryPointIOTest, FixedSampleMask_WithoutAuthoredMask) {
1630   auto* src = R"(
1631 [[stage(fragment)]]
1632 fn frag_main() -> [[location(0)]] f32 {
1633   return 1.0;
1634 }
1635 )";
1636 
1637   auto* expect = R"(
1638 struct tint_symbol {
1639   [[location(0)]]
1640   value : f32;
1641   [[builtin(sample_mask)]]
1642   fixed_sample_mask : u32;
1643 };
1644 
1645 fn frag_main_inner() -> f32 {
1646   return 1.0;
1647 }
1648 
1649 [[stage(fragment)]]
1650 fn frag_main() -> tint_symbol {
1651   let inner_result = frag_main_inner();
1652   var wrapper_result : tint_symbol;
1653   wrapper_result.value = inner_result;
1654   wrapper_result.fixed_sample_mask = 3u;
1655   return wrapper_result;
1656 }
1657 )";
1658 
1659   DataMap data;
1660   data.Add<CanonicalizeEntryPointIO::Config>(
1661       CanonicalizeEntryPointIO::ShaderStyle::kMsl, 0x03u);
1662   auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
1663 
1664   EXPECT_EQ(expect, str(got));
1665 }
1666 
TEST_F(CanonicalizeEntryPointIOTest,FixedSampleMask_StructWithAuthoredMask)1667 TEST_F(CanonicalizeEntryPointIOTest, FixedSampleMask_StructWithAuthoredMask) {
1668   auto* src = R"(
1669 struct Output {
1670   [[builtin(frag_depth)]] depth : f32;
1671   [[builtin(sample_mask)]] mask : u32;
1672   [[location(0)]] value : f32;
1673 };
1674 
1675 [[stage(fragment)]]
1676 fn frag_main() -> Output {
1677   return Output(0.5, 7u, 1.0);
1678 }
1679 )";
1680 
1681   auto* expect = R"(
1682 struct Output {
1683   depth : f32;
1684   mask : u32;
1685   value : f32;
1686 };
1687 
1688 struct tint_symbol {
1689   [[location(0)]]
1690   value : f32;
1691   [[builtin(frag_depth)]]
1692   depth : f32;
1693   [[builtin(sample_mask)]]
1694   mask : u32;
1695 };
1696 
1697 fn frag_main_inner() -> Output {
1698   return Output(0.5, 7u, 1.0);
1699 }
1700 
1701 [[stage(fragment)]]
1702 fn frag_main() -> tint_symbol {
1703   let inner_result = frag_main_inner();
1704   var wrapper_result : tint_symbol;
1705   wrapper_result.depth = inner_result.depth;
1706   wrapper_result.mask = (inner_result.mask & 3u);
1707   wrapper_result.value = inner_result.value;
1708   return wrapper_result;
1709 }
1710 )";
1711 
1712   DataMap data;
1713   data.Add<CanonicalizeEntryPointIO::Config>(
1714       CanonicalizeEntryPointIO::ShaderStyle::kMsl, 0x03u);
1715   auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
1716 
1717   EXPECT_EQ(expect, str(got));
1718 }
1719 
TEST_F(CanonicalizeEntryPointIOTest,FixedSampleMask_StructWithoutAuthoredMask)1720 TEST_F(CanonicalizeEntryPointIOTest,
1721        FixedSampleMask_StructWithoutAuthoredMask) {
1722   auto* src = R"(
1723 struct Output {
1724   [[builtin(frag_depth)]] depth : f32;
1725   [[location(0)]] value : f32;
1726 };
1727 
1728 [[stage(fragment)]]
1729 fn frag_main() -> Output {
1730   return Output(0.5, 1.0);
1731 }
1732 )";
1733 
1734   auto* expect = R"(
1735 struct Output {
1736   depth : f32;
1737   value : f32;
1738 };
1739 
1740 struct tint_symbol {
1741   [[location(0)]]
1742   value : f32;
1743   [[builtin(frag_depth)]]
1744   depth : f32;
1745   [[builtin(sample_mask)]]
1746   fixed_sample_mask : u32;
1747 };
1748 
1749 fn frag_main_inner() -> Output {
1750   return Output(0.5, 1.0);
1751 }
1752 
1753 [[stage(fragment)]]
1754 fn frag_main() -> tint_symbol {
1755   let inner_result = frag_main_inner();
1756   var wrapper_result : tint_symbol;
1757   wrapper_result.depth = inner_result.depth;
1758   wrapper_result.value = inner_result.value;
1759   wrapper_result.fixed_sample_mask = 3u;
1760   return wrapper_result;
1761 }
1762 )";
1763 
1764   DataMap data;
1765   data.Add<CanonicalizeEntryPointIO::Config>(
1766       CanonicalizeEntryPointIO::ShaderStyle::kMsl, 0x03u);
1767   auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
1768 
1769   EXPECT_EQ(expect, str(got));
1770 }
1771 
TEST_F(CanonicalizeEntryPointIOTest,FixedSampleMask_MultipleShaders)1772 TEST_F(CanonicalizeEntryPointIOTest, FixedSampleMask_MultipleShaders) {
1773   auto* src = R"(
1774 [[stage(fragment)]]
1775 fn frag_main1() -> [[builtin(sample_mask)]] u32 {
1776   return 7u;
1777 }
1778 
1779 [[stage(fragment)]]
1780 fn frag_main2() -> [[location(0)]] f32 {
1781   return 1.0;
1782 }
1783 
1784 [[stage(vertex)]]
1785 fn vert_main1() -> [[builtin(position)]] vec4<f32> {
1786   return vec4<f32>();
1787 }
1788 
1789 [[stage(compute), workgroup_size(1)]]
1790 fn comp_main1() {
1791 }
1792 )";
1793 
1794   auto* expect = R"(
1795 struct tint_symbol {
1796   [[builtin(sample_mask)]]
1797   value : u32;
1798 };
1799 
1800 fn frag_main1_inner() -> u32 {
1801   return 7u;
1802 }
1803 
1804 [[stage(fragment)]]
1805 fn frag_main1() -> tint_symbol {
1806   let inner_result = frag_main1_inner();
1807   var wrapper_result : tint_symbol;
1808   wrapper_result.value = (inner_result & 3u);
1809   return wrapper_result;
1810 }
1811 
1812 struct tint_symbol_1 {
1813   [[location(0)]]
1814   value : f32;
1815   [[builtin(sample_mask)]]
1816   fixed_sample_mask : u32;
1817 };
1818 
1819 fn frag_main2_inner() -> f32 {
1820   return 1.0;
1821 }
1822 
1823 [[stage(fragment)]]
1824 fn frag_main2() -> tint_symbol_1 {
1825   let inner_result_1 = frag_main2_inner();
1826   var wrapper_result_1 : tint_symbol_1;
1827   wrapper_result_1.value = inner_result_1;
1828   wrapper_result_1.fixed_sample_mask = 3u;
1829   return wrapper_result_1;
1830 }
1831 
1832 struct tint_symbol_2 {
1833   [[builtin(position)]]
1834   value : vec4<f32>;
1835 };
1836 
1837 fn vert_main1_inner() -> vec4<f32> {
1838   return vec4<f32>();
1839 }
1840 
1841 [[stage(vertex)]]
1842 fn vert_main1() -> tint_symbol_2 {
1843   let inner_result_2 = vert_main1_inner();
1844   var wrapper_result_2 : tint_symbol_2;
1845   wrapper_result_2.value = inner_result_2;
1846   return wrapper_result_2;
1847 }
1848 
1849 [[stage(compute), workgroup_size(1)]]
1850 fn comp_main1() {
1851 }
1852 )";
1853 
1854   DataMap data;
1855   data.Add<CanonicalizeEntryPointIO::Config>(
1856       CanonicalizeEntryPointIO::ShaderStyle::kMsl, 0x03u);
1857   auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
1858 
1859   EXPECT_EQ(expect, str(got));
1860 }
1861 
TEST_F(CanonicalizeEntryPointIOTest,FixedSampleMask_AvoidNameClash)1862 TEST_F(CanonicalizeEntryPointIOTest, FixedSampleMask_AvoidNameClash) {
1863   auto* src = R"(
1864 struct FragOut {
1865   [[location(0)]] fixed_sample_mask : vec4<f32>;
1866   [[location(1)]] fixed_sample_mask_1 : vec4<f32>;
1867 };
1868 
1869 [[stage(fragment)]]
1870 fn frag_main() -> FragOut {
1871   return FragOut();
1872 }
1873 )";
1874 
1875   auto* expect = R"(
1876 struct FragOut {
1877   fixed_sample_mask : vec4<f32>;
1878   fixed_sample_mask_1 : vec4<f32>;
1879 };
1880 
1881 struct tint_symbol {
1882   [[location(0)]]
1883   fixed_sample_mask : vec4<f32>;
1884   [[location(1)]]
1885   fixed_sample_mask_1 : vec4<f32>;
1886   [[builtin(sample_mask)]]
1887   fixed_sample_mask_2 : u32;
1888 };
1889 
1890 fn frag_main_inner() -> FragOut {
1891   return FragOut();
1892 }
1893 
1894 [[stage(fragment)]]
1895 fn frag_main() -> tint_symbol {
1896   let inner_result = frag_main_inner();
1897   var wrapper_result : tint_symbol;
1898   wrapper_result.fixed_sample_mask = inner_result.fixed_sample_mask;
1899   wrapper_result.fixed_sample_mask_1 = inner_result.fixed_sample_mask_1;
1900   wrapper_result.fixed_sample_mask_2 = 3u;
1901   return wrapper_result;
1902 }
1903 )";
1904 
1905   DataMap data;
1906   data.Add<CanonicalizeEntryPointIO::Config>(
1907       CanonicalizeEntryPointIO::ShaderStyle::kMsl, 0x03);
1908   auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
1909 
1910   EXPECT_EQ(expect, str(got));
1911 }
1912 
TEST_F(CanonicalizeEntryPointIOTest,EmitVertexPointSize_ReturnNonStruct_Spirv)1913 TEST_F(CanonicalizeEntryPointIOTest,
1914        EmitVertexPointSize_ReturnNonStruct_Spirv) {
1915   auto* src = R"(
1916 [[stage(vertex)]]
1917 fn vert_main() -> [[builtin(position)]] vec4<f32> {
1918   return vec4<f32>();
1919 }
1920 )";
1921 
1922   auto* expect = R"(
1923 [[builtin(position), internal(disable_validation__ignore_storage_class)]] var<out> value : vec4<f32>;
1924 
1925 [[builtin(pointsize), internal(disable_validation__ignore_storage_class)]] var<out> vertex_point_size : f32;
1926 
1927 fn vert_main_inner() -> vec4<f32> {
1928   return vec4<f32>();
1929 }
1930 
1931 [[stage(vertex)]]
1932 fn vert_main() {
1933   let inner_result = vert_main_inner();
1934   value = inner_result;
1935   vertex_point_size = 1.0;
1936 }
1937 )";
1938 
1939   DataMap data;
1940   data.Add<CanonicalizeEntryPointIO::Config>(
1941       CanonicalizeEntryPointIO::ShaderStyle::kSpirv, 0xFFFFFFFF, true);
1942   auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
1943 
1944   EXPECT_EQ(expect, str(got));
1945 }
1946 
TEST_F(CanonicalizeEntryPointIOTest,EmitVertexPointSize_ReturnNonStruct_Msl)1947 TEST_F(CanonicalizeEntryPointIOTest, EmitVertexPointSize_ReturnNonStruct_Msl) {
1948   auto* src = R"(
1949 [[stage(vertex)]]
1950 fn vert_main() -> [[builtin(position)]] vec4<f32> {
1951   return vec4<f32>();
1952 }
1953 )";
1954 
1955   auto* expect = R"(
1956 struct tint_symbol {
1957   [[builtin(position)]]
1958   value : vec4<f32>;
1959   [[builtin(pointsize)]]
1960   vertex_point_size : f32;
1961 };
1962 
1963 fn vert_main_inner() -> vec4<f32> {
1964   return vec4<f32>();
1965 }
1966 
1967 [[stage(vertex)]]
1968 fn vert_main() -> tint_symbol {
1969   let inner_result = vert_main_inner();
1970   var wrapper_result : tint_symbol;
1971   wrapper_result.value = inner_result;
1972   wrapper_result.vertex_point_size = 1.0;
1973   return wrapper_result;
1974 }
1975 )";
1976 
1977   DataMap data;
1978   data.Add<CanonicalizeEntryPointIO::Config>(
1979       CanonicalizeEntryPointIO::ShaderStyle::kMsl, 0xFFFFFFFF, true);
1980   auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
1981 
1982   EXPECT_EQ(expect, str(got));
1983 }
1984 
TEST_F(CanonicalizeEntryPointIOTest,EmitVertexPointSize_ReturnStruct_Spirv)1985 TEST_F(CanonicalizeEntryPointIOTest, EmitVertexPointSize_ReturnStruct_Spirv) {
1986   auto* src = R"(
1987 struct VertOut {
1988   [[builtin(position)]] pos : vec4<f32>;
1989 };
1990 
1991 [[stage(vertex)]]
1992 fn vert_main() -> VertOut {
1993   return VertOut();
1994 }
1995 )";
1996 
1997   auto* expect = R"(
1998 [[builtin(position), internal(disable_validation__ignore_storage_class)]] var<out> pos_1 : vec4<f32>;
1999 
2000 [[builtin(pointsize), internal(disable_validation__ignore_storage_class)]] var<out> vertex_point_size : f32;
2001 
2002 struct VertOut {
2003   pos : vec4<f32>;
2004 };
2005 
2006 fn vert_main_inner() -> VertOut {
2007   return VertOut();
2008 }
2009 
2010 [[stage(vertex)]]
2011 fn vert_main() {
2012   let inner_result = vert_main_inner();
2013   pos_1 = inner_result.pos;
2014   vertex_point_size = 1.0;
2015 }
2016 )";
2017 
2018   DataMap data;
2019   data.Add<CanonicalizeEntryPointIO::Config>(
2020       CanonicalizeEntryPointIO::ShaderStyle::kSpirv, 0xFFFFFFFF, true);
2021   auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
2022 
2023   EXPECT_EQ(expect, str(got));
2024 }
2025 
TEST_F(CanonicalizeEntryPointIOTest,EmitVertexPointSize_ReturnStruct_Msl)2026 TEST_F(CanonicalizeEntryPointIOTest, EmitVertexPointSize_ReturnStruct_Msl) {
2027   auto* src = R"(
2028 struct VertOut {
2029   [[builtin(position)]] pos : vec4<f32>;
2030 };
2031 
2032 [[stage(vertex)]]
2033 fn vert_main() -> VertOut {
2034   return VertOut();
2035 }
2036 )";
2037 
2038   auto* expect = R"(
2039 struct VertOut {
2040   pos : vec4<f32>;
2041 };
2042 
2043 struct tint_symbol {
2044   [[builtin(position)]]
2045   pos : vec4<f32>;
2046   [[builtin(pointsize)]]
2047   vertex_point_size : f32;
2048 };
2049 
2050 fn vert_main_inner() -> VertOut {
2051   return VertOut();
2052 }
2053 
2054 [[stage(vertex)]]
2055 fn vert_main() -> tint_symbol {
2056   let inner_result = vert_main_inner();
2057   var wrapper_result : tint_symbol;
2058   wrapper_result.pos = inner_result.pos;
2059   wrapper_result.vertex_point_size = 1.0;
2060   return wrapper_result;
2061 }
2062 )";
2063 
2064   DataMap data;
2065   data.Add<CanonicalizeEntryPointIO::Config>(
2066       CanonicalizeEntryPointIO::ShaderStyle::kMsl, 0xFFFFFFFF, true);
2067   auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
2068 
2069   EXPECT_EQ(expect, str(got));
2070 }
2071 
TEST_F(CanonicalizeEntryPointIOTest,EmitVertexPointSize_AvoidNameClash_Spirv)2072 TEST_F(CanonicalizeEntryPointIOTest, EmitVertexPointSize_AvoidNameClash_Spirv) {
2073   auto* src = R"(
2074 var<private> vertex_point_size : f32;
2075 var<private> vertex_point_size_1 : f32;
2076 var<private> vertex_point_size_2 : f32;
2077 
2078 struct VertIn1 {
2079   [[location(0)]] collide : f32;
2080 };
2081 
2082 struct VertIn2 {
2083   [[location(1)]] collide : f32;
2084 };
2085 
2086 struct VertOut {
2087   [[location(0)]] vertex_point_size : f32;
2088   [[builtin(position)]] vertex_point_size_1 : vec4<f32>;
2089 };
2090 
2091 [[stage(vertex)]]
2092 fn vert_main(collide : VertIn1, collide_1 : VertIn2) -> VertOut {
2093   let x = collide.collide + collide_1.collide;
2094   return VertOut();
2095 }
2096 )";
2097 
2098   auto* expect = R"(
2099 [[location(0), internal(disable_validation__ignore_storage_class)]] var<in> collide_2 : f32;
2100 
2101 [[location(1), internal(disable_validation__ignore_storage_class)]] var<in> collide_3 : f32;
2102 
2103 [[location(0), internal(disable_validation__ignore_storage_class)]] var<out> vertex_point_size_3 : f32;
2104 
2105 [[builtin(position), internal(disable_validation__ignore_storage_class)]] var<out> vertex_point_size_1_1 : vec4<f32>;
2106 
2107 [[builtin(pointsize), internal(disable_validation__ignore_storage_class)]] var<out> vertex_point_size_4 : f32;
2108 
2109 var<private> vertex_point_size : f32;
2110 
2111 var<private> vertex_point_size_1 : f32;
2112 
2113 var<private> vertex_point_size_2 : f32;
2114 
2115 struct VertIn1 {
2116   collide : f32;
2117 };
2118 
2119 struct VertIn2 {
2120   collide : f32;
2121 };
2122 
2123 struct VertOut {
2124   vertex_point_size : f32;
2125   vertex_point_size_1 : vec4<f32>;
2126 };
2127 
2128 fn vert_main_inner(collide : VertIn1, collide_1 : VertIn2) -> VertOut {
2129   let x = (collide.collide + collide_1.collide);
2130   return VertOut();
2131 }
2132 
2133 [[stage(vertex)]]
2134 fn vert_main() {
2135   let inner_result = vert_main_inner(VertIn1(collide_2), VertIn2(collide_3));
2136   vertex_point_size_3 = inner_result.vertex_point_size;
2137   vertex_point_size_1_1 = inner_result.vertex_point_size_1;
2138   vertex_point_size_4 = 1.0;
2139 }
2140 )";
2141 
2142   DataMap data;
2143   data.Add<CanonicalizeEntryPointIO::Config>(
2144       CanonicalizeEntryPointIO::ShaderStyle::kSpirv, 0xFFFFFFFF, true);
2145   auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
2146 
2147   EXPECT_EQ(expect, str(got));
2148 }
2149 
TEST_F(CanonicalizeEntryPointIOTest,EmitVertexPointSize_AvoidNameClash_Msl)2150 TEST_F(CanonicalizeEntryPointIOTest, EmitVertexPointSize_AvoidNameClash_Msl) {
2151   auto* src = R"(
2152 struct VertIn1 {
2153   [[location(0)]] collide : f32;
2154 };
2155 
2156 struct VertIn2 {
2157   [[location(1)]] collide : f32;
2158 };
2159 
2160 struct VertOut {
2161   [[location(0)]] vertex_point_size : vec4<f32>;
2162   [[builtin(position)]] vertex_point_size_1 : vec4<f32>;
2163 };
2164 
2165 [[stage(vertex)]]
2166 fn vert_main(collide : VertIn1, collide_1 : VertIn2) -> VertOut {
2167   let x = collide.collide + collide_1.collide;
2168   return VertOut();
2169 }
2170 )";
2171 
2172   auto* expect = R"(
2173 struct VertIn1 {
2174   collide : f32;
2175 };
2176 
2177 struct VertIn2 {
2178   collide : f32;
2179 };
2180 
2181 struct VertOut {
2182   vertex_point_size : vec4<f32>;
2183   vertex_point_size_1 : vec4<f32>;
2184 };
2185 
2186 struct tint_symbol_1 {
2187   [[location(0)]]
2188   collide : f32;
2189   [[location(1)]]
2190   collide_2 : f32;
2191 };
2192 
2193 struct tint_symbol_2 {
2194   [[location(0)]]
2195   vertex_point_size : vec4<f32>;
2196   [[builtin(position)]]
2197   vertex_point_size_1 : vec4<f32>;
2198   [[builtin(pointsize)]]
2199   vertex_point_size_2 : f32;
2200 };
2201 
2202 fn vert_main_inner(collide : VertIn1, collide_1 : VertIn2) -> VertOut {
2203   let x = (collide.collide + collide_1.collide);
2204   return VertOut();
2205 }
2206 
2207 [[stage(vertex)]]
2208 fn vert_main(tint_symbol : tint_symbol_1) -> tint_symbol_2 {
2209   let inner_result = vert_main_inner(VertIn1(tint_symbol.collide), VertIn2(tint_symbol.collide_2));
2210   var wrapper_result : tint_symbol_2;
2211   wrapper_result.vertex_point_size = inner_result.vertex_point_size;
2212   wrapper_result.vertex_point_size_1 = inner_result.vertex_point_size_1;
2213   wrapper_result.vertex_point_size_2 = 1.0;
2214   return wrapper_result;
2215 }
2216 )";
2217 
2218   DataMap data;
2219   data.Add<CanonicalizeEntryPointIO::Config>(
2220       CanonicalizeEntryPointIO::ShaderStyle::kMsl, 0xFFFFFFFF, true);
2221   auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
2222 
2223   EXPECT_EQ(expect, str(got));
2224 }
2225 
TEST_F(CanonicalizeEntryPointIOTest,EmitVertexPointSize_AvoidNameClash_Hlsl)2226 TEST_F(CanonicalizeEntryPointIOTest, EmitVertexPointSize_AvoidNameClash_Hlsl) {
2227   auto* src = R"(
2228 struct VertIn1 {
2229   [[location(0)]] collide : f32;
2230 };
2231 
2232 struct VertIn2 {
2233   [[location(1)]] collide : f32;
2234 };
2235 
2236 struct VertOut {
2237   [[location(0)]] vertex_point_size : vec4<f32>;
2238   [[builtin(position)]] vertex_point_size_1 : vec4<f32>;
2239 };
2240 
2241 [[stage(vertex)]]
2242 fn vert_main(collide : VertIn1, collide_1 : VertIn2) -> VertOut {
2243   let x = collide.collide + collide_1.collide;
2244   return VertOut();
2245 }
2246 )";
2247 
2248   auto* expect = R"(
2249 struct VertIn1 {
2250   collide : f32;
2251 };
2252 
2253 struct VertIn2 {
2254   collide : f32;
2255 };
2256 
2257 struct VertOut {
2258   vertex_point_size : vec4<f32>;
2259   vertex_point_size_1 : vec4<f32>;
2260 };
2261 
2262 struct tint_symbol_1 {
2263   [[location(0)]]
2264   collide : f32;
2265   [[location(1)]]
2266   collide_2 : f32;
2267 };
2268 
2269 struct tint_symbol_2 {
2270   [[location(0)]]
2271   vertex_point_size : vec4<f32>;
2272   [[builtin(position)]]
2273   vertex_point_size_1 : vec4<f32>;
2274   [[builtin(pointsize)]]
2275   vertex_point_size_2 : f32;
2276 };
2277 
2278 fn vert_main_inner(collide : VertIn1, collide_1 : VertIn2) -> VertOut {
2279   let x = (collide.collide + collide_1.collide);
2280   return VertOut();
2281 }
2282 
2283 [[stage(vertex)]]
2284 fn vert_main(tint_symbol : tint_symbol_1) -> tint_symbol_2 {
2285   let inner_result = vert_main_inner(VertIn1(tint_symbol.collide), VertIn2(tint_symbol.collide_2));
2286   var wrapper_result : tint_symbol_2;
2287   wrapper_result.vertex_point_size = inner_result.vertex_point_size;
2288   wrapper_result.vertex_point_size_1 = inner_result.vertex_point_size_1;
2289   wrapper_result.vertex_point_size_2 = 1.0;
2290   return wrapper_result;
2291 }
2292 )";
2293 
2294   DataMap data;
2295   data.Add<CanonicalizeEntryPointIO::Config>(
2296       CanonicalizeEntryPointIO::ShaderStyle::kHlsl, 0xFFFFFFFF, true);
2297   auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
2298 
2299   EXPECT_EQ(expect, str(got));
2300 }
2301 
TEST_F(CanonicalizeEntryPointIOTest,SpirvSampleMaskBuiltins)2302 TEST_F(CanonicalizeEntryPointIOTest, SpirvSampleMaskBuiltins) {
2303   auto* src = R"(
2304 [[stage(fragment)]]
2305 fn main([[builtin(sample_index)]] sample_index : u32,
2306         [[builtin(sample_mask)]] mask_in : u32
2307         ) -> [[builtin(sample_mask)]] u32 {
2308   return mask_in;
2309 }
2310 )";
2311 
2312   auto* expect = R"(
2313 [[builtin(sample_index), internal(disable_validation__ignore_storage_class)]] var<in> sample_index_1 : u32;
2314 
2315 [[builtin(sample_mask), internal(disable_validation__ignore_storage_class)]] var<in> mask_in_1 : array<u32, 1>;
2316 
2317 [[builtin(sample_mask), internal(disable_validation__ignore_storage_class)]] var<out> value : array<u32, 1>;
2318 
2319 fn main_inner(sample_index : u32, mask_in : u32) -> u32 {
2320   return mask_in;
2321 }
2322 
2323 [[stage(fragment)]]
2324 fn main() {
2325   let inner_result = main_inner(sample_index_1, mask_in_1[0]);
2326   value[0] = inner_result;
2327 }
2328 )";
2329 
2330   DataMap data;
2331   data.Add<CanonicalizeEntryPointIO::Config>(
2332       CanonicalizeEntryPointIO::ShaderStyle::kSpirv);
2333   auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src, data);
2334 
2335   EXPECT_EQ(expect, str(got));
2336 }
2337 
2338 }  // namespace
2339 }  // namespace transform
2340 }  // namespace tint
2341