• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 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 "gmock/gmock.h"
16 #include "src/reader/spirv/function.h"
17 #include "src/reader/spirv/parser_impl_test_helper.h"
18 #include "src/reader/spirv/spirv_tools_helpers_test.h"
19 #include "src/utils/string.h"
20 
21 namespace tint {
22 namespace reader {
23 namespace spirv {
24 namespace {
25 
26 using SpvModuleScopeVarParserTest = SpvParserTest;
27 
28 using ::testing::ElementsAre;
29 using ::testing::Eq;
30 using ::testing::HasSubstr;
31 using ::testing::Not;
32 
Preamble()33 std::string Preamble() {
34   return R"(
35    OpCapability Shader
36    OpMemoryModel Logical Simple
37 )";
38 }
39 
FragMain()40 std::string FragMain() {
41   return R"(
42    OpEntryPoint Fragment %main "main"
43    OpExecutionMode %main OriginUpperLeft
44 )";
45 }
46 
MainBody()47 std::string MainBody() {
48   return R"(
49    %main = OpFunction %void None %voidfn
50    %main_entry = OpLabel
51    OpReturn
52    OpFunctionEnd
53 )";
54 }
55 
CommonCapabilities()56 std::string CommonCapabilities() {
57   return R"(
58     OpCapability Shader
59     OpCapability SampleRateShading
60     OpMemoryModel Logical Simple
61 )";
62 }
63 
CommonTypes()64 std::string CommonTypes() {
65   return R"(
66     %void = OpTypeVoid
67     %voidfn = OpTypeFunction %void
68 
69     %bool = OpTypeBool
70     %float = OpTypeFloat 32
71     %uint = OpTypeInt 32 0
72     %int = OpTypeInt 32 1
73 
74     %ptr_bool = OpTypePointer Private %bool
75     %ptr_float = OpTypePointer Private %float
76     %ptr_uint = OpTypePointer Private %uint
77     %ptr_int = OpTypePointer Private %int
78 
79     %true = OpConstantTrue %bool
80     %false = OpConstantFalse %bool
81     %float_0 = OpConstant %float 0.0
82     %float_1p5 = OpConstant %float 1.5
83     %uint_1 = OpConstant %uint 1
84     %int_m1 = OpConstant %int -1
85     %int_14 = OpConstant %int 14
86     %uint_2 = OpConstant %uint 2
87 
88     %v2bool = OpTypeVector %bool 2
89     %v2uint = OpTypeVector %uint 2
90     %v2int = OpTypeVector %int 2
91     %v2float = OpTypeVector %float 2
92     %v4float = OpTypeVector %float 4
93     %m3v2float = OpTypeMatrix %v2float 3
94 
95     %arr2uint = OpTypeArray %uint %uint_2
96   )";
97 }
98 
StructTypes()99 std::string StructTypes() {
100   return R"(
101     %strct = OpTypeStruct %uint %float %arr2uint
102 )";
103 }
104 
105 // Returns layout annotations for types in StructTypes()
CommonLayout()106 std::string CommonLayout() {
107   return R"(
108     OpMemberDecorate %strct 0 Offset 0
109     OpMemberDecorate %strct 1 Offset 4
110     OpMemberDecorate %strct 2 Offset 8
111     OpDecorate %arr2uint ArrayStride 4
112 )";
113 }
114 
TEST_F(SpvModuleScopeVarParserTest,NoVar)115 TEST_F(SpvModuleScopeVarParserTest, NoVar) {
116   auto assembly = Preamble() + FragMain() + CommonTypes() + MainBody();
117   auto p = parser(test::Assemble(assembly));
118   EXPECT_TRUE(p->BuildAndParseInternalModule()) << assembly;
119   EXPECT_TRUE(p->error().empty());
120   const auto module_ast = test::ToString(p->program());
121   EXPECT_THAT(module_ast, Not(HasSubstr("Variable"))) << module_ast;
122 }
123 
TEST_F(SpvModuleScopeVarParserTest,BadStorageClass_NotAWebGPUStorageClass)124 TEST_F(SpvModuleScopeVarParserTest, BadStorageClass_NotAWebGPUStorageClass) {
125   auto p = parser(test::Assemble(Preamble() + FragMain() + R"(
126     %float = OpTypeFloat 32
127     %ptr = OpTypePointer CrossWorkgroup %float
128     %52 = OpVariable %ptr CrossWorkgroup
129     %void = OpTypeVoid
130     %voidfn = OpTypeFunction %void
131   )" + MainBody()));
132   EXPECT_TRUE(p->BuildInternalModule());
133   // Normally we should run ParserImpl::RegisterTypes before emitting
134   // variables. But defensive coding in EmitModuleScopeVariables lets
135   // us catch this error.
136   EXPECT_FALSE(p->EmitModuleScopeVariables()) << p->error();
137   EXPECT_THAT(p->error(), HasSubstr("unknown SPIR-V storage class: 5"));
138 }
139 
TEST_F(SpvModuleScopeVarParserTest,BadStorageClass_Function)140 TEST_F(SpvModuleScopeVarParserTest, BadStorageClass_Function) {
141   auto p = parser(test::Assemble(Preamble() + FragMain() + R"(
142     %float = OpTypeFloat 32
143     %ptr = OpTypePointer Function %float
144     %52 = OpVariable %ptr Function
145     %void = OpTypeVoid
146     %voidfn = OpTypeFunction %void
147   )" + MainBody()));
148   EXPECT_TRUE(p->BuildInternalModule());
149   // Normally we should run ParserImpl::RegisterTypes before emitting
150   // variables. But defensive coding in EmitModuleScopeVariables lets
151   // us catch this error.
152   EXPECT_FALSE(p->EmitModuleScopeVariables()) << p->error();
153   EXPECT_THAT(p->error(),
154               HasSubstr("invalid SPIR-V storage class 7 for module scope "
155                         "variable: %52 = OpVariable %3 Function"));
156 }
157 
TEST_F(SpvModuleScopeVarParserTest,BadPointerType)158 TEST_F(SpvModuleScopeVarParserTest, BadPointerType) {
159   auto p = parser(test::Assemble(Preamble() + FragMain() + R"(
160     %float = OpTypeFloat 32
161     %fn_ty = OpTypeFunction %float
162     %3 = OpTypePointer Private %fn_ty
163     %52 = OpVariable %3 Private
164     %void = OpTypeVoid
165     %voidfn = OpTypeFunction %void
166   )" + MainBody()));
167   EXPECT_TRUE(p->BuildInternalModule());
168   // Normally we should run ParserImpl::RegisterTypes before emitting
169   // variables. But defensive coding in EmitModuleScopeVariables lets
170   // us catch this error.
171   EXPECT_FALSE(p->EmitModuleScopeVariables());
172   EXPECT_THAT(p->error(), HasSubstr("internal error: failed to register Tint "
173                                     "AST type for SPIR-V type with ID: 3"));
174 }
175 
TEST_F(SpvModuleScopeVarParserTest,NonPointerType)176 TEST_F(SpvModuleScopeVarParserTest, NonPointerType) {
177   auto p = parser(test::Assemble(Preamble() + FragMain() + R"(
178     %float = OpTypeFloat 32
179     %5 = OpTypeFunction %float
180     %3 = OpTypePointer Private %5
181     %52 = OpVariable %float Private
182     %void = OpTypeVoid
183     %voidfn = OpTypeFunction %void
184   )" + MainBody()));
185   EXPECT_TRUE(p->BuildInternalModule());
186   EXPECT_FALSE(p->RegisterTypes());
187   EXPECT_THAT(
188       p->error(),
189       HasSubstr("SPIR-V pointer type with ID 3 has invalid pointee type 5"));
190 }
191 
TEST_F(SpvModuleScopeVarParserTest,AnonWorkgroupVar)192 TEST_F(SpvModuleScopeVarParserTest, AnonWorkgroupVar) {
193   auto p = parser(test::Assemble(Preamble() + FragMain() + R"(
194     %float = OpTypeFloat 32
195     %ptr = OpTypePointer Workgroup %float
196     %52 = OpVariable %ptr Workgroup
197     %void = OpTypeVoid
198     %voidfn = OpTypeFunction %void
199   )" + MainBody()));
200 
201   EXPECT_TRUE(p->BuildAndParseInternalModule());
202   EXPECT_TRUE(p->error().empty());
203   const auto module_str = test::ToString(p->program());
204   EXPECT_THAT(module_str, HasSubstr("var<workgroup> x_52 : f32;"));
205 }
206 
TEST_F(SpvModuleScopeVarParserTest,NamedWorkgroupVar)207 TEST_F(SpvModuleScopeVarParserTest, NamedWorkgroupVar) {
208   auto p = parser(test::Assemble(Preamble() + FragMain() + R"(
209     OpName %52 "the_counter"
210     %float = OpTypeFloat 32
211     %ptr = OpTypePointer Workgroup %float
212     %52 = OpVariable %ptr Workgroup
213     %void = OpTypeVoid
214     %voidfn = OpTypeFunction %void
215   )" + MainBody()));
216 
217   EXPECT_TRUE(p->BuildAndParseInternalModule());
218   EXPECT_TRUE(p->error().empty());
219   const auto module_str = test::ToString(p->program());
220   EXPECT_THAT(module_str, HasSubstr("var<workgroup> the_counter : f32;"));
221 }
222 
TEST_F(SpvModuleScopeVarParserTest,PrivateVar)223 TEST_F(SpvModuleScopeVarParserTest, PrivateVar) {
224   auto p = parser(test::Assemble(Preamble() + FragMain() + R"(
225     OpName %52 "my_own_private_idaho"
226     %float = OpTypeFloat 32
227     %ptr = OpTypePointer Private %float
228     %52 = OpVariable %ptr Private
229     %void = OpTypeVoid
230     %voidfn = OpTypeFunction %void
231   )" + MainBody()));
232 
233   EXPECT_TRUE(p->BuildAndParseInternalModule());
234   EXPECT_TRUE(p->error().empty());
235   const auto module_str = test::ToString(p->program());
236   EXPECT_THAT(module_str,
237               HasSubstr("var<private> my_own_private_idaho : f32;"));
238 }
239 
TEST_F(SpvModuleScopeVarParserTest,BuiltinVertexIndex)240 TEST_F(SpvModuleScopeVarParserTest, BuiltinVertexIndex) {
241   // This is the simple case for the vertex_index builtin,
242   // where the SPIR-V uses the same store type as in WGSL.
243   // See later for tests where the SPIR-V store type is signed
244   // integer, as in GLSL.
245   auto p = parser(test::Assemble(Preamble() + R"(
246     OpEntryPoint Vertex %main "main" %52 %position
247     OpName %position "position"
248     OpDecorate %position BuiltIn Position
249     OpDecorate %52 BuiltIn VertexIndex
250     %uint = OpTypeInt 32 0
251     %ptr = OpTypePointer Input %uint
252     %52 = OpVariable %ptr Input
253     %void = OpTypeVoid
254     %voidfn = OpTypeFunction %void
255     %float = OpTypeFloat 32
256     %v4float = OpTypeVector %float 4
257     %posty = OpTypePointer Output %v4float
258     %position = OpVariable %posty Output
259   )" + MainBody()));
260 
261   EXPECT_TRUE(p->BuildAndParseInternalModule());
262   EXPECT_TRUE(p->error().empty());
263   const auto module_str = test::ToString(p->program());
264   EXPECT_THAT(module_str, HasSubstr("var<private> x_52 : u32;"));
265 }
266 
PerVertexPreamble()267 std::string PerVertexPreamble() {
268   return R"(
269     OpCapability Shader
270     OpMemoryModel Logical Simple
271     OpEntryPoint Vertex %main "main" %1
272 
273     OpMemberDecorate %10 0 BuiltIn Position
274     OpMemberDecorate %10 1 BuiltIn PointSize
275     OpMemberDecorate %10 2 BuiltIn ClipDistance
276     OpMemberDecorate %10 3 BuiltIn CullDistance
277     %void = OpTypeVoid
278     %voidfn = OpTypeFunction %void
279     %float = OpTypeFloat 32
280     %12 = OpTypeVector %float 4
281     %uint = OpTypeInt 32 0
282     %uint_0 = OpConstant %uint 0
283     %uint_1 = OpConstant %uint 1
284     %arr = OpTypeArray %float %uint_1
285     %10 = OpTypeStruct %12 %float %arr %arr
286     %11 = OpTypePointer Output %10
287     %1 = OpVariable %11 Output
288 )";
289 }
290 
TEST_F(SpvModuleScopeVarParserTest,BuiltinPosition_StoreWholeStruct_NotSupported)291 TEST_F(SpvModuleScopeVarParserTest,
292        BuiltinPosition_StoreWholeStruct_NotSupported) {
293   // Glslang does not generate this code pattern.
294   const std::string assembly = PerVertexPreamble() + R"(
295   %nil = OpConstantNull %10 ; the whole struct
296 
297   %main = OpFunction %void None %voidfn
298   %entry = OpLabel
299   OpStore %1 %nil  ; store the whole struct
300   OpReturn
301   OpFunctionEnd
302   )";
303   auto p = parser(test::Assemble(assembly));
304   EXPECT_FALSE(p->BuildAndParseInternalModule()) << assembly;
305   EXPECT_THAT(p->error(), Eq("storing to the whole per-vertex structure is not "
306                              "supported: OpStore %1 %13"))
307       << p->error();
308 }
309 
TEST_F(SpvModuleScopeVarParserTest,BuiltinPosition_IntermediateWholeStruct_NotSupported)310 TEST_F(SpvModuleScopeVarParserTest,
311        BuiltinPosition_IntermediateWholeStruct_NotSupported) {
312   const std::string assembly = PerVertexPreamble() + R"(
313   %main = OpFunction %void None %voidfn
314   %entry = OpLabel
315   %1000 = OpUndef %10
316   OpReturn
317   OpFunctionEnd
318   )";
319   auto p = parser(test::Assemble(assembly));
320   EXPECT_FALSE(p->BuildAndParseInternalModule()) << assembly;
321   EXPECT_THAT(p->error(), Eq("operations producing a per-vertex structure are "
322                              "not supported: %1000 = OpUndef %10"))
323       << p->error();
324 }
325 
TEST_F(SpvModuleScopeVarParserTest,BuiltinPosition_IntermediatePtrWholeStruct_NotSupported)326 TEST_F(SpvModuleScopeVarParserTest,
327        BuiltinPosition_IntermediatePtrWholeStruct_NotSupported) {
328   const std::string assembly = PerVertexPreamble() + R"(
329   %main = OpFunction %void None %voidfn
330   %entry = OpLabel
331   %1000 = OpCopyObject %11 %1
332   OpReturn
333   OpFunctionEnd
334   )";
335   auto p = parser(test::Assemble(assembly));
336   EXPECT_FALSE(p->BuildAndParseInternalModule());
337   EXPECT_THAT(p->error(),
338               Eq("operations producing a pointer to a per-vertex structure are "
339                  "not supported: %1000 = OpCopyObject %11 %1"))
340       << p->error();
341 }
342 
TEST_F(SpvModuleScopeVarParserTest,BuiltinPosition_StorePosition)343 TEST_F(SpvModuleScopeVarParserTest, BuiltinPosition_StorePosition) {
344   const std::string assembly = PerVertexPreamble() + R"(
345   %ptr_v4float = OpTypePointer Output %12
346   %nil = OpConstantNull %12
347 
348   %main = OpFunction %void None %voidfn
349   %entry = OpLabel
350   %100 = OpAccessChain %ptr_v4float %1 %uint_0 ; address of the Position member
351   OpStore %100 %nil
352   OpReturn
353   OpFunctionEnd
354   )";
355   auto p = parser(test::Assemble(assembly));
356   EXPECT_TRUE(p->BuildAndParseInternalModule());
357   EXPECT_TRUE(p->error().empty());
358   const auto module_str = test::ToString(p->program());
359   EXPECT_THAT(module_str,
360               HasSubstr("gl_Position = vec4<f32>(0.0, 0.0, 0.0, 0.0);"))
361       << module_str;
362 }
363 
TEST_F(SpvModuleScopeVarParserTest,BuiltinPosition_StorePosition_PerVertexStructOutOfOrderDecl)364 TEST_F(SpvModuleScopeVarParserTest,
365        BuiltinPosition_StorePosition_PerVertexStructOutOfOrderDecl) {
366   const std::string assembly = R"(
367   OpCapability Shader
368   OpMemoryModel Logical Simple
369   OpEntryPoint Vertex %main "main" %1
370 
371  ;  scramble the member indices
372   OpMemberDecorate %10 0 BuiltIn ClipDistance
373   OpMemberDecorate %10 1 BuiltIn CullDistance
374   OpMemberDecorate %10 2 BuiltIn Position
375   OpMemberDecorate %10 3 BuiltIn PointSize
376   %void = OpTypeVoid
377   %voidfn = OpTypeFunction %void
378   %float = OpTypeFloat 32
379   %12 = OpTypeVector %float 4
380   %uint = OpTypeInt 32 0
381   %uint_0 = OpConstant %uint 0
382   %uint_1 = OpConstant %uint 1
383   %uint_2 = OpConstant %uint 2
384   %arr = OpTypeArray %float %uint_1
385   %10 = OpTypeStruct %arr %arr %12 %float
386   %11 = OpTypePointer Output %10
387   %1 = OpVariable %11 Output
388 
389   %ptr_v4float = OpTypePointer Output %12
390   %nil = OpConstantNull %12
391 
392   %main = OpFunction %void None %voidfn
393   %entry = OpLabel
394   %100 = OpAccessChain %ptr_v4float %1 %uint_2 ; address of the Position member
395   OpStore %100 %nil
396   OpReturn
397   OpFunctionEnd
398   )";
399   auto p = parser(test::Assemble(assembly));
400   EXPECT_TRUE(p->BuildAndParseInternalModule());
401   EXPECT_TRUE(p->error().empty());
402   const auto module_str = test::ToString(p->program());
403   EXPECT_THAT(module_str,
404               HasSubstr("gl_Position = vec4<f32>(0.0, 0.0, 0.0, 0.0);"))
405       << module_str;
406 }
407 
TEST_F(SpvModuleScopeVarParserTest,BuiltinPosition_StorePositionMember_OneAccessChain)408 TEST_F(SpvModuleScopeVarParserTest,
409        BuiltinPosition_StorePositionMember_OneAccessChain) {
410   const std::string assembly = PerVertexPreamble() + R"(
411   %ptr_float = OpTypePointer Output %float
412   %nil = OpConstantNull %float
413 
414   %main = OpFunction %void None %voidfn
415   %entry = OpLabel
416   %100 = OpAccessChain %ptr_float %1 %uint_0 %uint_1 ; address of the Position.y member
417   OpStore %100 %nil
418   OpReturn
419   OpFunctionEnd
420   )";
421   auto p = parser(test::Assemble(assembly));
422   EXPECT_TRUE(p->BuildAndParseInternalModule());
423   EXPECT_TRUE(p->error().empty());
424   const auto module_str = test::ToString(p->program());
425   EXPECT_THAT(module_str, HasSubstr("gl_Position.y = 0.0;")) << module_str;
426 }
427 
TEST_F(SpvModuleScopeVarParserTest,BuiltinPosition_StorePositionMember_TwoAccessChain)428 TEST_F(SpvModuleScopeVarParserTest,
429        BuiltinPosition_StorePositionMember_TwoAccessChain) {
430   // The algorithm is smart enough to collapse it down.
431   const std::string assembly = PerVertexPreamble() + R"(
432   %ptr = OpTypePointer Output %12
433   %ptr_float = OpTypePointer Output %float
434   %nil = OpConstantNull %float
435 
436   %main = OpFunction %void None %voidfn
437   %entry = OpLabel
438   %100 = OpAccessChain %ptr %1 %uint_0 ; address of the Position member
439   %101 = OpAccessChain %ptr_float %100 %uint_1 ; address of the Position.y member
440   OpStore %101 %nil
441   OpReturn
442   OpFunctionEnd
443   )";
444   auto p = parser(test::Assemble(assembly));
445   EXPECT_TRUE(p->BuildAndParseInternalModule());
446   EXPECT_TRUE(p->error().empty());
447   const auto module_str = test::ToString(p->program());
448   EXPECT_THAT(module_str, HasSubstr("gl_Position.y = 0.0;")) << module_str;
449 }
450 
TEST_F(SpvModuleScopeVarParserTest,BuiltinPointSize_Write1_IsErased)451 TEST_F(SpvModuleScopeVarParserTest, BuiltinPointSize_Write1_IsErased) {
452   const std::string assembly = PerVertexPreamble() + R"(
453   %ptr = OpTypePointer Output %float
454   %one = OpConstant %float 1.0
455 
456   %main = OpFunction %void None %voidfn
457   %entry = OpLabel
458   %100 = OpAccessChain %ptr %1 %uint_1 ; address of the PointSize member
459   OpStore %100 %one
460   OpReturn
461   OpFunctionEnd
462   )";
463   auto p = parser(test::Assemble(assembly));
464   EXPECT_TRUE(p->BuildAndParseInternalModule());
465   EXPECT_TRUE(p->error().empty());
466   const auto module_str = test::ToString(p->program());
467   EXPECT_EQ(module_str, R"(var<private> gl_Position : vec4<f32>;
468 
469 fn main_1() {
470   return;
471 }
472 
473 struct main_out {
474   [[builtin(position)]]
475   gl_Position : vec4<f32>;
476 };
477 
478 [[stage(vertex)]]
479 fn main() -> main_out {
480   main_1();
481   return main_out(gl_Position);
482 }
483 )") << module_str;
484 }
485 
TEST_F(SpvModuleScopeVarParserTest,BuiltinPointSize_WriteNon1_IsError)486 TEST_F(SpvModuleScopeVarParserTest, BuiltinPointSize_WriteNon1_IsError) {
487   const std::string assembly = PerVertexPreamble() + R"(
488   %ptr = OpTypePointer Output %float
489   %999 = OpConstant %float 2.0
490 
491   %main = OpFunction %void None %voidfn
492   %entry = OpLabel
493   %100 = OpAccessChain %ptr %1 %uint_1 ; address of the PointSize member
494   OpStore %100 %999
495   OpReturn
496   OpFunctionEnd
497   )";
498   auto p = parser(test::Assemble(assembly));
499   EXPECT_FALSE(p->BuildAndParseInternalModule());
500   EXPECT_THAT(p->error(),
501               HasSubstr("cannot store a value other than constant 1.0 to "
502                         "PointSize builtin: OpStore %100 %999"));
503 }
504 
TEST_F(SpvModuleScopeVarParserTest,BuiltinPointSize_ReadReplaced)505 TEST_F(SpvModuleScopeVarParserTest, BuiltinPointSize_ReadReplaced) {
506   const std::string assembly = PerVertexPreamble() + R"(
507   %ptr = OpTypePointer Output %float
508   %nil = OpConstantNull %12
509   %private_ptr = OpTypePointer Private %float
510   %900 = OpVariable %private_ptr Private
511 
512   %main = OpFunction %void None %voidfn
513   %entry = OpLabel
514   %100 = OpAccessChain %ptr %1 %uint_1 ; address of the PointSize member
515   %99 = OpLoad %float %100
516   OpStore %900 %99
517   OpReturn
518   OpFunctionEnd
519   )";
520   auto p = parser(test::Assemble(assembly));
521   EXPECT_TRUE(p->BuildAndParseInternalModule());
522   EXPECT_TRUE(p->error().empty());
523   const auto module_str = test::ToString(p->program());
524   EXPECT_EQ(module_str, R"(var<private> x_900 : f32;
525 
526 var<private> gl_Position : vec4<f32>;
527 
528 fn main_1() {
529   x_900 = 1.0;
530   return;
531 }
532 
533 struct main_out {
534   [[builtin(position)]]
535   gl_Position : vec4<f32>;
536 };
537 
538 [[stage(vertex)]]
539 fn main() -> main_out {
540   main_1();
541   return main_out(gl_Position);
542 }
543 )") << module_str;
544 }
545 
TEST_F(SpvModuleScopeVarParserTest,BuiltinPointSize_WriteViaCopyObjectPriorAccess_Unsupported)546 TEST_F(SpvModuleScopeVarParserTest,
547        BuiltinPointSize_WriteViaCopyObjectPriorAccess_Unsupported) {
548   const std::string assembly = PerVertexPreamble() + R"(
549   %ptr = OpTypePointer Output %float
550   %nil = OpConstantNull %12
551 
552   %main = OpFunction %void None %voidfn
553   %entry = OpLabel
554   %20 = OpCopyObject %11 %1
555   %100 = OpAccessChain %20 %1 %uint_1 ; address of the PointSize member
556   OpStore %100 %nil
557   OpReturn
558   OpFunctionEnd
559   )";
560   auto p = parser(test::Assemble(assembly));
561   EXPECT_FALSE(p->BuildAndParseInternalModule()) << p->error();
562   EXPECT_THAT(
563       p->error(),
564       HasSubstr("operations producing a pointer to a per-vertex structure are "
565                 "not supported: %20 = OpCopyObject %11 %1"));
566 }
567 
TEST_F(SpvModuleScopeVarParserTest,BuiltinPointSize_WriteViaCopyObjectPostAccessChainErased)568 TEST_F(SpvModuleScopeVarParserTest,
569        BuiltinPointSize_WriteViaCopyObjectPostAccessChainErased) {
570   const std::string assembly = PerVertexPreamble() + R"(
571   %ptr = OpTypePointer Output %float
572   %one = OpConstant %float 1.0
573 
574   %main = OpFunction %void None %voidfn
575   %entry = OpLabel
576   %100 = OpAccessChain %ptr %1 %uint_1 ; address of the PointSize member
577   %101 = OpCopyObject %ptr %100
578   OpStore %101 %one
579   OpReturn
580   OpFunctionEnd
581   )";
582   auto p = parser(test::Assemble(assembly));
583   EXPECT_TRUE(p->BuildAndParseInternalModule()) << p->error();
584   EXPECT_TRUE(p->error().empty());
585   const auto module_str = test::ToString(p->program());
586   EXPECT_EQ(module_str, R"(var<private> gl_Position : vec4<f32>;
587 
588 fn main_1() {
589   return;
590 }
591 
592 struct main_out {
593   [[builtin(position)]]
594   gl_Position : vec4<f32>;
595 };
596 
597 [[stage(vertex)]]
598 fn main() -> main_out {
599   main_1();
600   return main_out(gl_Position);
601 }
602 )") << module_str;
603 }
604 
LoosePointSizePreamble(std::string stage="Vertex")605 std::string LoosePointSizePreamble(std::string stage = "Vertex") {
606   return R"(
607     OpCapability Shader
608     OpMemoryModel Logical Simple
609     OpEntryPoint )" +
610          stage + R"( %500 "main" %1
611 )" + (stage == "Vertex" ? " %2 " : "") +
612          +(stage == "Fragment" ? "OpExecutionMode %500 OriginUpperLeft" : "") +
613          +(stage == "Vertex" ? " OpDecorate %2 BuiltIn Position " : "") +
614          R"(
615     OpDecorate %1 BuiltIn PointSize
616     %void = OpTypeVoid
617     %voidfn = OpTypeFunction %void
618     %float = OpTypeFloat 32
619     %v4float = OpTypeVector %float 4
620     %uint = OpTypeInt 32 0
621     %uint_0 = OpConstant %uint 0
622     %uint_1 = OpConstant %uint 1
623     %11 = OpTypePointer Output %float
624     %1 = OpVariable %11 Output
625     %12 = OpTypePointer Output %v4float
626     %2 = OpVariable %12 Output
627 )";
628 }
629 
TEST_F(SpvModuleScopeVarParserTest,BuiltinPointSize_Loose_Write1_IsErased)630 TEST_F(SpvModuleScopeVarParserTest, BuiltinPointSize_Loose_Write1_IsErased) {
631   const std::string assembly = LoosePointSizePreamble() + R"(
632   %ptr = OpTypePointer Output %float
633   %one = OpConstant %float 1.0
634 
635   %500 = OpFunction %void None %voidfn
636   %entry = OpLabel
637   OpStore %1 %one
638   OpReturn
639   OpFunctionEnd
640   )";
641   auto p = parser(test::Assemble(assembly));
642   EXPECT_TRUE(p->BuildAndParseInternalModule());
643   EXPECT_TRUE(p->error().empty());
644   const auto module_str = test::ToString(p->program());
645   EXPECT_EQ(module_str, R"(var<private> x_2 : vec4<f32>;
646 
647 fn main_1() {
648   return;
649 }
650 
651 struct main_out {
652   [[builtin(position)]]
653   x_2_1 : vec4<f32>;
654 };
655 
656 [[stage(vertex)]]
657 fn main() -> main_out {
658   main_1();
659   return main_out(x_2);
660 }
661 )") << module_str;
662 }
663 
TEST_F(SpvModuleScopeVarParserTest,BuiltinPointSize_Loose_WriteNon1_IsError)664 TEST_F(SpvModuleScopeVarParserTest, BuiltinPointSize_Loose_WriteNon1_IsError) {
665   const std::string assembly = LoosePointSizePreamble() + R"(
666   %ptr = OpTypePointer Output %float
667   %999 = OpConstant %float 2.0
668 
669   %500 = OpFunction %void None %voidfn
670   %entry = OpLabel
671   OpStore %1 %999
672   OpReturn
673   OpFunctionEnd
674   )";
675   auto p = parser(test::Assemble(assembly));
676   EXPECT_FALSE(p->BuildAndParseInternalModule());
677   EXPECT_THAT(p->error(),
678               HasSubstr("cannot store a value other than constant 1.0 to "
679                         "PointSize builtin: OpStore %1 %999"));
680 }
681 
TEST_F(SpvModuleScopeVarParserTest,BuiltinPointSize_Loose_ReadReplaced_Vertex)682 TEST_F(SpvModuleScopeVarParserTest,
683        BuiltinPointSize_Loose_ReadReplaced_Vertex) {
684   const std::string assembly = LoosePointSizePreamble() + R"(
685   %ptr = OpTypePointer Private %float
686   %900 = OpVariable %ptr Private
687 
688   %500 = OpFunction %void None %voidfn
689   %entry = OpLabel
690   %99 = OpLoad %float %1
691   OpStore %900 %99
692   OpReturn
693   OpFunctionEnd
694   )";
695   auto p = parser(test::Assemble(assembly));
696 
697   EXPECT_TRUE(p->BuildAndParseInternalModule());
698   EXPECT_TRUE(p->error().empty());
699   const auto module_str = test::ToString(p->program());
700   EXPECT_EQ(module_str, R"(var<private> x_2 : vec4<f32>;
701 
702 var<private> x_900 : f32;
703 
704 fn main_1() {
705   x_900 = 1.0;
706   return;
707 }
708 
709 struct main_out {
710   [[builtin(position)]]
711   x_2_1 : vec4<f32>;
712 };
713 
714 [[stage(vertex)]]
715 fn main() -> main_out {
716   main_1();
717   return main_out(x_2);
718 }
719 )") << module_str;
720 }
721 
TEST_F(SpvModuleScopeVarParserTest,BuiltinPointSize_Loose_ReadReplaced_Fragment)722 TEST_F(SpvModuleScopeVarParserTest,
723        BuiltinPointSize_Loose_ReadReplaced_Fragment) {
724   const std::string assembly = LoosePointSizePreamble("Fragment") + R"(
725   %ptr = OpTypePointer Private %float
726   %900 = OpVariable %ptr Private
727 
728   %500 = OpFunction %void None %voidfn
729   %entry = OpLabel
730   %99 = OpLoad %float %1
731   OpStore %900 %99
732   OpReturn
733   OpFunctionEnd
734   )";
735   auto p = parser(test::Assemble(assembly));
736 
737   // This example is invalid because you PointSize is not valid in Vulkan
738   // Fragment shaders.
739   EXPECT_FALSE(p->Parse());
740   EXPECT_FALSE(p->success());
741   EXPECT_THAT(p->error(), HasSubstr("VUID-PointSize-PointSize-04314"));
742 }
743 
TEST_F(SpvModuleScopeVarParserTest,BuiltinPointSize_Loose_WriteViaCopyObjectPriorAccess_Erased)744 TEST_F(SpvModuleScopeVarParserTest,
745        BuiltinPointSize_Loose_WriteViaCopyObjectPriorAccess_Erased) {
746   const std::string assembly = LoosePointSizePreamble() + R"(
747   %one = OpConstant %float 1.0
748 
749   %500 = OpFunction %void None %voidfn
750   %entry = OpLabel
751   %20 = OpCopyObject %11 %1
752   %100 = OpAccessChain %11 %20
753   OpStore %100 %one
754   OpReturn
755   OpFunctionEnd
756   )";
757   auto p = parser(test::Assemble(assembly));
758   EXPECT_TRUE(p->BuildAndParseInternalModule()) << p->error();
759   EXPECT_TRUE(p->error().empty());
760   const auto module_str = test::ToString(p->program());
761   EXPECT_EQ(module_str, R"(var<private> x_2 : vec4<f32>;
762 
763 fn main_1() {
764   return;
765 }
766 
767 struct main_out {
768   [[builtin(position)]]
769   x_2_1 : vec4<f32>;
770 };
771 
772 [[stage(vertex)]]
773 fn main() -> main_out {
774   main_1();
775   return main_out(x_2);
776 }
777 )") << module_str;
778 }
779 
TEST_F(SpvModuleScopeVarParserTest,BuiltinPointSize_Loose_WriteViaCopyObjectPostAccessChainErased)780 TEST_F(SpvModuleScopeVarParserTest,
781        BuiltinPointSize_Loose_WriteViaCopyObjectPostAccessChainErased) {
782   const std::string assembly = LoosePointSizePreamble() + R"(
783   %one = OpConstant %float 1.0
784 
785   %500 = OpFunction %void None %voidfn
786   %entry = OpLabel
787   %100 = OpAccessChain %11 %1
788   %101 = OpCopyObject %11 %100
789   OpStore %101 %one
790   OpReturn
791   OpFunctionEnd
792   )";
793   auto p = parser(test::Assemble(assembly));
794   EXPECT_TRUE(p->BuildAndParseInternalModule()) << p->error();
795   EXPECT_TRUE(p->error().empty()) << p->error();
796   const auto module_str = test::ToString(p->program());
797   EXPECT_EQ(module_str, R"(var<private> x_2 : vec4<f32>;
798 
799 fn main_1() {
800   return;
801 }
802 
803 struct main_out {
804   [[builtin(position)]]
805   x_2_1 : vec4<f32>;
806 };
807 
808 [[stage(vertex)]]
809 fn main() -> main_out {
810   main_1();
811   return main_out(x_2);
812 }
813 )") << module_str;
814 }
815 
TEST_F(SpvModuleScopeVarParserTest,BuiltinClipDistance_NotSupported)816 TEST_F(SpvModuleScopeVarParserTest, BuiltinClipDistance_NotSupported) {
817   const std::string assembly = PerVertexPreamble() + R"(
818   %ptr_float = OpTypePointer Output %float
819   %nil = OpConstantNull %float
820   %uint_2 = OpConstant %uint 2
821 
822   %main = OpFunction %void None %voidfn
823   %entry = OpLabel
824 ; address of the first entry in ClipDistance
825   %100 = OpAccessChain %ptr_float %1 %uint_2 %uint_0
826   OpStore %100 %nil
827   OpReturn
828   OpFunctionEnd
829   )";
830   auto p = parser(test::Assemble(assembly));
831   EXPECT_FALSE(p->BuildAndParseInternalModule());
832   EXPECT_EQ(p->error(),
833             "accessing per-vertex member 2 is not supported. Only Position is "
834             "supported, and PointSize is ignored");
835 }
836 
TEST_F(SpvModuleScopeVarParserTest,BuiltinCullDistance_NotSupported)837 TEST_F(SpvModuleScopeVarParserTest, BuiltinCullDistance_NotSupported) {
838   const std::string assembly = PerVertexPreamble() + R"(
839   %ptr_float = OpTypePointer Output %float
840   %nil = OpConstantNull %float
841   %uint_3 = OpConstant %uint 3
842 
843   %main = OpFunction %void None %voidfn
844   %entry = OpLabel
845 ; address of the first entry in CullDistance
846   %100 = OpAccessChain %ptr_float %1 %uint_3 %uint_0
847   OpStore %100 %nil
848   OpReturn
849   OpFunctionEnd
850   )";
851   auto p = parser(test::Assemble(assembly));
852   EXPECT_FALSE(p->BuildAndParseInternalModule());
853   EXPECT_EQ(p->error(),
854             "accessing per-vertex member 3 is not supported. Only Position is "
855             "supported, and PointSize is ignored");
856 }
857 
TEST_F(SpvModuleScopeVarParserTest,BuiltinPerVertex_MemberIndex_NotConstant)858 TEST_F(SpvModuleScopeVarParserTest, BuiltinPerVertex_MemberIndex_NotConstant) {
859   const std::string assembly = PerVertexPreamble() + R"(
860   %ptr_float = OpTypePointer Output %float
861   %nil = OpConstantNull %float
862 
863   %main = OpFunction %void None %voidfn
864   %entry = OpLabel
865   %sum = OpIAdd %uint %uint_0 %uint_0
866   %100 = OpAccessChain %ptr_float %1 %sum
867   OpStore %100 %nil
868   OpReturn
869   OpFunctionEnd
870   )";
871   auto p = parser(test::Assemble(assembly));
872   EXPECT_FALSE(p->BuildAndParseInternalModule());
873   EXPECT_THAT(p->error(),
874               Eq("first index of access chain into per-vertex structure is not "
875                  "a constant: %100 = OpAccessChain %13 %1 %16"));
876 }
877 
TEST_F(SpvModuleScopeVarParserTest,BuiltinPerVertex_MemberIndex_NotConstantInteger)878 TEST_F(SpvModuleScopeVarParserTest,
879        BuiltinPerVertex_MemberIndex_NotConstantInteger) {
880   const std::string assembly = PerVertexPreamble() + R"(
881   %ptr_float = OpTypePointer Output %float
882   %nil = OpConstantNull %float
883 
884   %main = OpFunction %void None %voidfn
885   %entry = OpLabel
886 ; nil is bad here!
887   %100 = OpAccessChain %ptr_float %1 %nil
888   OpStore %100 %nil
889   OpReturn
890   OpFunctionEnd
891   )";
892   auto p = parser(test::Assemble(assembly));
893   EXPECT_FALSE(p->BuildAndParseInternalModule());
894   EXPECT_THAT(p->error(),
895               Eq("first index of access chain into per-vertex structure is not "
896                  "a constant integer: %100 = OpAccessChain %13 %1 %14"));
897 }
898 
TEST_F(SpvModuleScopeVarParserTest,ScalarInitializers)899 TEST_F(SpvModuleScopeVarParserTest, ScalarInitializers) {
900   auto p = parser(test::Assemble(Preamble() + FragMain() + CommonTypes() + R"(
901      %1 = OpVariable %ptr_bool Private %true
902      %2 = OpVariable %ptr_bool Private %false
903      %3 = OpVariable %ptr_int Private %int_m1
904      %4 = OpVariable %ptr_uint Private %uint_1
905      %5 = OpVariable %ptr_float Private %float_1p5
906   )" + MainBody()));
907   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
908   EXPECT_TRUE(p->error().empty());
909   const auto module_str = test::ToString(p->program());
910   EXPECT_THAT(module_str, HasSubstr(R"(var<private> x_1 : bool = true;
911 
912 var<private> x_2 : bool = false;
913 
914 var<private> x_3 : i32 = -1;
915 
916 var<private> x_4 : u32 = 1u;
917 
918 var<private> x_5 : f32 = 1.5;
919 )"));
920 }
921 
TEST_F(SpvModuleScopeVarParserTest,ScalarNullInitializers)922 TEST_F(SpvModuleScopeVarParserTest, ScalarNullInitializers) {
923   auto p = parser(test::Assemble(Preamble() + FragMain() + CommonTypes() + R"(
924      %null_bool = OpConstantNull %bool
925      %null_int = OpConstantNull %int
926      %null_uint = OpConstantNull %uint
927      %null_float = OpConstantNull %float
928 
929      %1 = OpVariable %ptr_bool Private %null_bool
930      %2 = OpVariable %ptr_int Private %null_int
931      %3 = OpVariable %ptr_uint Private %null_uint
932      %4 = OpVariable %ptr_float Private %null_float
933   )" + MainBody()));
934   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
935   EXPECT_TRUE(p->error().empty());
936   const auto module_str = test::ToString(p->program());
937   EXPECT_THAT(module_str, HasSubstr(R"(var<private> x_1 : bool = false;
938 
939 var<private> x_2 : i32 = 0;
940 
941 var<private> x_3 : u32 = 0u;
942 
943 var<private> x_4 : f32 = 0.0;
944 )"));
945 }
946 
TEST_F(SpvModuleScopeVarParserTest,ScalarUndefInitializers)947 TEST_F(SpvModuleScopeVarParserTest, ScalarUndefInitializers) {
948   auto p = parser(test::Assemble(Preamble() + FragMain() + CommonTypes() + R"(
949      %undef_bool = OpUndef %bool
950      %undef_int = OpUndef %int
951      %undef_uint = OpUndef %uint
952      %undef_float = OpUndef %float
953 
954      %1 = OpVariable %ptr_bool Private %undef_bool
955      %2 = OpVariable %ptr_int Private %undef_int
956      %3 = OpVariable %ptr_uint Private %undef_uint
957      %4 = OpVariable %ptr_float Private %undef_float
958   )" + MainBody()));
959   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
960   EXPECT_TRUE(p->error().empty());
961   const auto module_str = test::ToString(p->program());
962   EXPECT_THAT(module_str, HasSubstr(R"(var<private> x_1 : bool = false;
963 
964 var<private> x_2 : i32 = 0;
965 
966 var<private> x_3 : u32 = 0u;
967 
968 var<private> x_4 : f32 = 0.0;
969 )"));
970 
971   // This example module emits ok, but is not valid SPIR-V in the first place.
972   p->DeliberatelyInvalidSpirv();
973 }
974 
TEST_F(SpvModuleScopeVarParserTest,VectorInitializer)975 TEST_F(SpvModuleScopeVarParserTest, VectorInitializer) {
976   auto p = parser(test::Assemble(Preamble() + FragMain() + CommonTypes() + R"(
977      %ptr = OpTypePointer Private %v2float
978      %two = OpConstant %float 2.0
979      %const = OpConstantComposite %v2float %float_1p5 %two
980      %200 = OpVariable %ptr Private %const
981   )" + MainBody()));
982   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
983   EXPECT_TRUE(p->error().empty());
984   const auto module_str = test::ToString(p->program());
985   EXPECT_THAT(
986       module_str,
987       HasSubstr("var<private> x_200 : vec2<f32> = vec2<f32>(1.5, 2.0);"));
988 }
989 
TEST_F(SpvModuleScopeVarParserTest,VectorBoolNullInitializer)990 TEST_F(SpvModuleScopeVarParserTest, VectorBoolNullInitializer) {
991   auto p = parser(test::Assemble(Preamble() + FragMain() + CommonTypes() + R"(
992      %ptr = OpTypePointer Private %v2bool
993      %const = OpConstantNull %v2bool
994      %200 = OpVariable %ptr Private %const
995   )" + MainBody()));
996   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
997   EXPECT_TRUE(p->error().empty());
998   const auto module_str = test::ToString(p->program());
999   EXPECT_THAT(
1000       module_str,
1001       HasSubstr("var<private> x_200 : vec2<bool> = vec2<bool>(false, false);"));
1002 }
1003 
TEST_F(SpvModuleScopeVarParserTest,VectorBoolUndefInitializer)1004 TEST_F(SpvModuleScopeVarParserTest, VectorBoolUndefInitializer) {
1005   auto p = parser(test::Assemble(Preamble() + FragMain() + CommonTypes() + R"(
1006      %ptr = OpTypePointer Private %v2bool
1007      %const = OpUndef %v2bool
1008      %200 = OpVariable %ptr Private %const
1009   )" + MainBody()));
1010   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
1011   EXPECT_TRUE(p->error().empty());
1012   const auto module_str = test::ToString(p->program());
1013   EXPECT_THAT(
1014       module_str,
1015       HasSubstr("var<private> x_200 : vec2<bool> = vec2<bool>(false, false);"));
1016 
1017   // This example module emits ok, but is not valid SPIR-V in the first place.
1018   p->DeliberatelyInvalidSpirv();
1019 }
1020 
TEST_F(SpvModuleScopeVarParserTest,VectorUintNullInitializer)1021 TEST_F(SpvModuleScopeVarParserTest, VectorUintNullInitializer) {
1022   auto p = parser(test::Assemble(Preamble() + FragMain() + CommonTypes() + R"(
1023      %ptr = OpTypePointer Private %v2uint
1024      %const = OpConstantNull %v2uint
1025      %200 = OpVariable %ptr Private %const
1026   )" + MainBody()));
1027   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
1028   EXPECT_TRUE(p->error().empty());
1029   const auto module_str = test::ToString(p->program());
1030   EXPECT_THAT(module_str,
1031               HasSubstr("var<private> x_200 : vec2<u32> = vec2<u32>(0u, 0u);"));
1032 }
1033 
TEST_F(SpvModuleScopeVarParserTest,VectorUintUndefInitializer)1034 TEST_F(SpvModuleScopeVarParserTest, VectorUintUndefInitializer) {
1035   auto p = parser(test::Assemble(Preamble() + FragMain() + CommonTypes() + R"(
1036      %ptr = OpTypePointer Private %v2uint
1037      %const = OpUndef %v2uint
1038      %200 = OpVariable %ptr Private %const
1039   )" + MainBody()));
1040   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
1041   EXPECT_TRUE(p->error().empty());
1042   const auto module_str = test::ToString(p->program());
1043   EXPECT_THAT(module_str,
1044               HasSubstr("var<private> x_200 : vec2<u32> = vec2<u32>(0u, 0u);"));
1045 
1046   // This example module emits ok, but is not valid SPIR-V in the first place.
1047   p->DeliberatelyInvalidSpirv();
1048 }
1049 
TEST_F(SpvModuleScopeVarParserTest,VectorIntNullInitializer)1050 TEST_F(SpvModuleScopeVarParserTest, VectorIntNullInitializer) {
1051   auto p = parser(test::Assemble(Preamble() + FragMain() + CommonTypes() + R"(
1052      %ptr = OpTypePointer Private %v2int
1053      %const = OpConstantNull %v2int
1054      %200 = OpVariable %ptr Private %const
1055   )" + MainBody()));
1056   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
1057   EXPECT_TRUE(p->error().empty());
1058   const auto module_str = test::ToString(p->program());
1059   EXPECT_THAT(module_str,
1060               HasSubstr("var<private> x_200 : vec2<i32> = vec2<i32>(0, 0);"));
1061 }
1062 
TEST_F(SpvModuleScopeVarParserTest,VectorIntUndefInitializer)1063 TEST_F(SpvModuleScopeVarParserTest, VectorIntUndefInitializer) {
1064   auto p = parser(test::Assemble(Preamble() + FragMain() + CommonTypes() + R"(
1065      %ptr = OpTypePointer Private %v2int
1066      %const = OpUndef %v2int
1067      %200 = OpVariable %ptr Private %const
1068   )" + MainBody()));
1069   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
1070   EXPECT_TRUE(p->error().empty());
1071   const auto module_str = test::ToString(p->program());
1072   EXPECT_THAT(module_str,
1073               HasSubstr("var<private> x_200 : vec2<i32> = vec2<i32>(0, 0);"));
1074 
1075   // This example module emits ok, but is not valid SPIR-V in the first place.
1076   p->DeliberatelyInvalidSpirv();
1077 }
1078 
TEST_F(SpvModuleScopeVarParserTest,VectorFloatNullInitializer)1079 TEST_F(SpvModuleScopeVarParserTest, VectorFloatNullInitializer) {
1080   auto p = parser(test::Assemble(Preamble() + FragMain() + CommonTypes() + R"(
1081      %ptr = OpTypePointer Private %v2float
1082      %const = OpConstantNull %v2float
1083      %200 = OpVariable %ptr Private %const
1084   )" + MainBody()));
1085   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
1086   EXPECT_TRUE(p->error().empty());
1087   const auto module_str = test::ToString(p->program());
1088   EXPECT_THAT(
1089       module_str,
1090       HasSubstr("var<private> x_200 : vec2<f32> = vec2<f32>(0.0, 0.0);"));
1091 }
1092 
TEST_F(SpvModuleScopeVarParserTest,VectorFloatUndefInitializer)1093 TEST_F(SpvModuleScopeVarParserTest, VectorFloatUndefInitializer) {
1094   auto p = parser(test::Assemble(Preamble() + FragMain() + CommonTypes() + R"(
1095      %ptr = OpTypePointer Private %v2float
1096      %const = OpUndef %v2float
1097      %200 = OpVariable %ptr Private %const
1098   )" + MainBody()));
1099   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
1100   EXPECT_TRUE(p->error().empty());
1101   const auto module_str = test::ToString(p->program());
1102   EXPECT_THAT(
1103       module_str,
1104       HasSubstr("var<private> x_200 : vec2<f32> = vec2<f32>(0.0, 0.0);"));
1105 
1106   // This example module emits ok, but is not valid SPIR-V in the first place.
1107   p->DeliberatelyInvalidSpirv();
1108 }
1109 
TEST_F(SpvModuleScopeVarParserTest,MatrixInitializer)1110 TEST_F(SpvModuleScopeVarParserTest, MatrixInitializer) {
1111   auto p = parser(test::Assemble(Preamble() + FragMain() + CommonTypes() + R"(
1112      %ptr = OpTypePointer Private %m3v2float
1113      %two = OpConstant %float 2.0
1114      %three = OpConstant %float 3.0
1115      %four = OpConstant %float 4.0
1116      %v0 = OpConstantComposite %v2float %float_1p5 %two
1117      %v1 = OpConstantComposite %v2float %two %three
1118      %v2 = OpConstantComposite %v2float %three %four
1119      %const = OpConstantComposite %m3v2float %v0 %v1 %v2
1120      %200 = OpVariable %ptr Private %const
1121   )" + MainBody()));
1122   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
1123   EXPECT_TRUE(p->error().empty());
1124   const auto module_str = test::ToString(p->program());
1125   EXPECT_THAT(module_str,
1126               HasSubstr("var<private> x_200 : mat3x2<f32> = mat3x2<f32>("
1127                         "vec2<f32>(1.5, 2.0), "
1128                         "vec2<f32>(2.0, 3.0), "
1129                         "vec2<f32>(3.0, 4.0));"));
1130 }
1131 
TEST_F(SpvModuleScopeVarParserTest,MatrixNullInitializer)1132 TEST_F(SpvModuleScopeVarParserTest, MatrixNullInitializer) {
1133   auto p = parser(test::Assemble(Preamble() + FragMain() + CommonTypes() + R"(
1134      %ptr = OpTypePointer Private %m3v2float
1135      %const = OpConstantNull %m3v2float
1136      %200 = OpVariable %ptr Private %const
1137   )" + MainBody()));
1138   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
1139   EXPECT_TRUE(p->error().empty());
1140   const auto module_str = test::ToString(p->program());
1141   EXPECT_THAT(module_str,
1142               HasSubstr("var<private> x_200 : mat3x2<f32> = mat3x2<f32>("
1143                         "vec2<f32>(0.0, 0.0), "
1144                         "vec2<f32>(0.0, 0.0), "
1145                         "vec2<f32>(0.0, 0.0));"));
1146 }
1147 
TEST_F(SpvModuleScopeVarParserTest,MatrixUndefInitializer)1148 TEST_F(SpvModuleScopeVarParserTest, MatrixUndefInitializer) {
1149   auto p = parser(test::Assemble(Preamble() + FragMain() + CommonTypes() + R"(
1150      %ptr = OpTypePointer Private %m3v2float
1151      %const = OpUndef %m3v2float
1152      %200 = OpVariable %ptr Private %const
1153   )" + MainBody()));
1154   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
1155   EXPECT_TRUE(p->error().empty());
1156   const auto module_str = test::ToString(p->program());
1157   EXPECT_THAT(module_str,
1158               HasSubstr("var<private> x_200 : mat3x2<f32> = mat3x2<f32>("
1159                         "vec2<f32>(0.0, 0.0), "
1160                         "vec2<f32>(0.0, 0.0), "
1161                         "vec2<f32>(0.0, 0.0));"));
1162 
1163   // This example module emits ok, but is not valid SPIR-V in the first place.
1164   p->DeliberatelyInvalidSpirv();
1165 }
1166 
TEST_F(SpvModuleScopeVarParserTest,ArrayInitializer)1167 TEST_F(SpvModuleScopeVarParserTest, ArrayInitializer) {
1168   auto p = parser(test::Assemble(Preamble() + FragMain() + CommonTypes() + R"(
1169      %ptr = OpTypePointer Private %arr2uint
1170      %two = OpConstant %uint 2
1171      %const = OpConstantComposite %arr2uint %uint_1 %two
1172      %200 = OpVariable %ptr Private %const
1173   )" + MainBody()));
1174   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
1175   EXPECT_TRUE(p->error().empty());
1176   const auto module_str = test::ToString(p->program());
1177   EXPECT_THAT(
1178       module_str,
1179       HasSubstr(
1180           "var<private> x_200 : array<u32, 2u> = array<u32, 2u>(1u, 2u);"));
1181 }
1182 
TEST_F(SpvModuleScopeVarParserTest,ArrayNullInitializer)1183 TEST_F(SpvModuleScopeVarParserTest, ArrayNullInitializer) {
1184   auto p = parser(test::Assemble(Preamble() + FragMain() + CommonTypes() + R"(
1185      %ptr = OpTypePointer Private %arr2uint
1186      %const = OpConstantNull %arr2uint
1187      %200 = OpVariable %ptr Private %const
1188   )" + MainBody()));
1189   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
1190   EXPECT_TRUE(p->error().empty());
1191   const auto module_str = test::ToString(p->program());
1192   EXPECT_THAT(
1193       module_str,
1194       HasSubstr(
1195           "var<private> x_200 : array<u32, 2u> = array<u32, 2u>(0u, 0u);"));
1196 }
1197 
TEST_F(SpvModuleScopeVarParserTest,ArrayUndefInitializer)1198 TEST_F(SpvModuleScopeVarParserTest, ArrayUndefInitializer) {
1199   auto p = parser(test::Assemble(Preamble() + FragMain() + CommonTypes() + R"(
1200      %ptr = OpTypePointer Private %arr2uint
1201      %const = OpUndef %arr2uint
1202      %200 = OpVariable %ptr Private %const
1203   )" + MainBody()));
1204   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
1205   EXPECT_TRUE(p->error().empty());
1206   const auto module_str = test::ToString(p->program());
1207   EXPECT_THAT(
1208       module_str,
1209       HasSubstr(
1210           "var<private> x_200 : array<u32, 2u> = array<u32, 2u>(0u, 0u);"));
1211 
1212   // This example module emits ok, but is not valid SPIR-V in the first place.
1213   p->DeliberatelyInvalidSpirv();
1214 }
1215 
TEST_F(SpvModuleScopeVarParserTest,StructInitializer)1216 TEST_F(SpvModuleScopeVarParserTest, StructInitializer) {
1217   auto p = parser(test::Assemble(Preamble() + FragMain() + CommonTypes() +
1218                                  StructTypes() + R"(
1219      %ptr = OpTypePointer Private %strct
1220      %two = OpConstant %uint 2
1221      %arrconst = OpConstantComposite %arr2uint %uint_1 %two
1222      %const = OpConstantComposite %strct %uint_1 %float_1p5 %arrconst
1223      %200 = OpVariable %ptr Private %const
1224   )" + MainBody()));
1225   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
1226   EXPECT_TRUE(p->error().empty());
1227   const auto module_str = test::ToString(p->program());
1228   EXPECT_THAT(
1229       module_str,
1230       HasSubstr("var<private> x_200 : S = S(1u, 1.5, array<u32, 2u>(1u, 2u));"))
1231       << module_str;
1232 }
1233 
TEST_F(SpvModuleScopeVarParserTest,StructNullInitializer)1234 TEST_F(SpvModuleScopeVarParserTest, StructNullInitializer) {
1235   auto p = parser(test::Assemble(Preamble() + FragMain() + CommonTypes() +
1236                                  StructTypes() + R"(
1237      %ptr = OpTypePointer Private %strct
1238      %const = OpConstantNull %strct
1239      %200 = OpVariable %ptr Private %const
1240   )" + MainBody()));
1241   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
1242   EXPECT_TRUE(p->error().empty());
1243   const auto module_str = test::ToString(p->program());
1244   EXPECT_THAT(
1245       module_str,
1246       HasSubstr("var<private> x_200 : S = S(0u, 0.0, array<u32, 2u>(0u, 0u));"))
1247       << module_str;
1248 }
1249 
TEST_F(SpvModuleScopeVarParserTest,StructUndefInitializer)1250 TEST_F(SpvModuleScopeVarParserTest, StructUndefInitializer) {
1251   auto p = parser(test::Assemble(Preamble() + FragMain() + CommonTypes() +
1252                                  StructTypes() + R"(
1253      %ptr = OpTypePointer Private %strct
1254      %const = OpUndef %strct
1255      %200 = OpVariable %ptr Private %const
1256   )" + MainBody()));
1257   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
1258   EXPECT_TRUE(p->error().empty());
1259 
1260   const auto module_str = test::ToString(p->program());
1261   EXPECT_THAT(
1262       module_str,
1263       HasSubstr("var<private> x_200 : S = S(0u, 0.0, array<u32, 2u>(0u, 0u));"))
1264       << module_str;
1265 
1266   // This example module emits ok, but is not valid SPIR-V in the first place.
1267   p->DeliberatelyInvalidSpirv();
1268 }
1269 
TEST_F(SpvModuleScopeVarParserTest,LocationDecoration_MissingOperandWontAssemble)1270 TEST_F(SpvModuleScopeVarParserTest,
1271        LocationDecoration_MissingOperandWontAssemble) {
1272   const auto assembly = Preamble() + FragMain() + R"(
1273      OpName %myvar "myvar"
1274      OpDecorate %myvar Location
1275 )" + CommonTypes() + R"(
1276      %ptr = OpTypePointer Input %uint
1277      %myvar = OpVariable %ptr Input
1278   )" + MainBody();
1279   EXPECT_THAT(test::AssembleFailure(assembly),
1280               Eq("10:4: Expected operand, found next instruction instead."));
1281 }
1282 
TEST_F(SpvModuleScopeVarParserTest,LocationDecoration_TwoOperandsWontAssemble)1283 TEST_F(SpvModuleScopeVarParserTest,
1284        LocationDecoration_TwoOperandsWontAssemble) {
1285   const auto assembly = Preamble() + FragMain() + R"(
1286      OpName %myvar "myvar"
1287      OpDecorate %myvar Location 3 4
1288 )" + CommonTypes() + R"(
1289      %ptr = OpTypePointer Input %uint
1290      %myvar = OpVariable %ptr Input
1291   )" + MainBody();
1292   EXPECT_THAT(
1293       test::AssembleFailure(assembly),
1294       Eq("8:34: Expected <opcode> or <result-id> at the beginning of an "
1295          "instruction, found '4'."));
1296 }
1297 
TEST_F(SpvModuleScopeVarParserTest,DescriptorGroupDecoration_Valid)1298 TEST_F(SpvModuleScopeVarParserTest, DescriptorGroupDecoration_Valid) {
1299   auto p = parser(test::Assemble(Preamble() + FragMain() + CommonLayout() + R"(
1300      OpDecorate %1 DescriptorSet 3
1301      OpDecorate %1 Binding 9 ; Required to pass WGSL validation
1302      OpDecorate %strct Block
1303 )" + CommonTypes() + StructTypes() +
1304                                  R"(
1305      %ptr_sb_strct = OpTypePointer StorageBuffer %strct
1306      %1 = OpVariable %ptr_sb_strct StorageBuffer
1307   )" + MainBody()));
1308   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
1309   EXPECT_TRUE(p->error().empty());
1310   const auto module_str = test::ToString(p->program());
1311   EXPECT_THAT(
1312       module_str,
1313       HasSubstr("[[group(3), binding(9)]] var<storage, read_write> x_1 : S;"))
1314       << module_str;
1315 }
1316 
TEST_F(SpvModuleScopeVarParserTest,DescriptorGroupDecoration_MissingOperandWontAssemble)1317 TEST_F(SpvModuleScopeVarParserTest,
1318        DescriptorGroupDecoration_MissingOperandWontAssemble) {
1319   const auto assembly = Preamble() + FragMain() + CommonLayout() + R"(
1320      OpDecorate %1 DescriptorSet
1321      OpDecorate %strct Block
1322 )" + CommonTypes() + StructTypes() +
1323                         R"(
1324      %ptr_sb_strct = OpTypePointer StorageBuffer %strct
1325      %1 = OpVariable %ptr_sb_strct StorageBuffer
1326   )" + MainBody();
1327   EXPECT_THAT(test::AssembleFailure(assembly),
1328               Eq("13:5: Expected operand, found next instruction instead."));
1329 }
1330 
TEST_F(SpvModuleScopeVarParserTest,DescriptorGroupDecoration_TwoOperandsWontAssemble)1331 TEST_F(SpvModuleScopeVarParserTest,
1332        DescriptorGroupDecoration_TwoOperandsWontAssemble) {
1333   const auto assembly = Preamble() + FragMain() + R"(
1334      OpName %myvar "myvar"
1335      OpDecorate %myvar DescriptorSet 3 4
1336      OpDecorate %strct Block
1337 )" + CommonTypes() + StructTypes() +
1338                         R"(
1339      %ptr_sb_strct = OpTypePointer StorageBuffer %strct
1340      %myvar = OpVariable %ptr_sb_strct StorageBuffer
1341   )" + MainBody();
1342   EXPECT_THAT(
1343       test::AssembleFailure(assembly),
1344       Eq("8:39: Expected <opcode> or <result-id> at the beginning of an "
1345          "instruction, found '4'."));
1346 }
1347 
TEST_F(SpvModuleScopeVarParserTest,BindingDecoration_Valid)1348 TEST_F(SpvModuleScopeVarParserTest, BindingDecoration_Valid) {
1349   auto p = parser(test::Assemble(Preamble() + FragMain() + R"(
1350      OpDecorate %1 DescriptorSet 0 ; WGSL validation requires this already
1351      OpDecorate %1 Binding 3
1352      OpDecorate %strct Block
1353 )" + CommonLayout() + CommonTypes() +
1354                                  StructTypes() +
1355                                  R"(
1356      %ptr_sb_strct = OpTypePointer StorageBuffer %strct
1357      %1 = OpVariable %ptr_sb_strct StorageBuffer
1358   )" + MainBody()));
1359   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
1360   EXPECT_TRUE(p->error().empty());
1361   const auto module_str = test::ToString(p->program());
1362   EXPECT_THAT(
1363       module_str,
1364       HasSubstr("[[group(0), binding(3)]] var<storage, read_write> x_1 : S;"))
1365       << module_str;
1366 }
1367 
TEST_F(SpvModuleScopeVarParserTest,BindingDecoration_MissingOperandWontAssemble)1368 TEST_F(SpvModuleScopeVarParserTest,
1369        BindingDecoration_MissingOperandWontAssemble) {
1370   const auto assembly = Preamble() + FragMain() + R"(
1371      OpName %myvar "myvar"
1372      OpDecorate %myvar Binding
1373      OpDecorate %strct Block
1374 )" + CommonTypes() + StructTypes() +
1375                         R"(
1376      %ptr_sb_strct = OpTypePointer StorageBuffer %strct
1377      %myvar = OpVariable %ptr_sb_strct StorageBuffer
1378   )" + MainBody();
1379   EXPECT_THAT(test::AssembleFailure(assembly),
1380               Eq("9:5: Expected operand, found next instruction instead."));
1381 }
1382 
TEST_F(SpvModuleScopeVarParserTest,BindingDecoration_TwoOperandsWontAssemble)1383 TEST_F(SpvModuleScopeVarParserTest, BindingDecoration_TwoOperandsWontAssemble) {
1384   const auto assembly = Preamble() + FragMain() + R"(
1385      OpName %myvar "myvar"
1386      OpDecorate %myvar Binding 3 4
1387      OpDecorate %strct Block
1388 )" + CommonTypes() + StructTypes() +
1389                         R"(
1390      %ptr_sb_strct = OpTypePointer StorageBuffer %strct
1391      %myvar = OpVariable %ptr_sb_strct StorageBuffer
1392   )" + MainBody();
1393   EXPECT_THAT(
1394       test::AssembleFailure(assembly),
1395       Eq("8:33: Expected <opcode> or <result-id> at the beginning of an "
1396          "instruction, found '4'."));
1397 }
1398 
TEST_F(SpvModuleScopeVarParserTest,StructMember_NonReadableDecoration_Dropped)1399 TEST_F(SpvModuleScopeVarParserTest,
1400        StructMember_NonReadableDecoration_Dropped) {
1401   auto p = parser(test::Assemble(Preamble() + FragMain() + R"(
1402      OpDecorate %1 DescriptorSet 0
1403      OpDecorate %1 Binding 0
1404      OpDecorate %strct Block
1405      OpMemberDecorate %strct 0 NonReadable
1406 )" + CommonLayout() + CommonTypes() +
1407                                  StructTypes() + R"(
1408      %ptr_sb_strct = OpTypePointer StorageBuffer %strct
1409      %1 = OpVariable %ptr_sb_strct StorageBuffer
1410   )" + MainBody()));
1411   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
1412   EXPECT_TRUE(p->error().empty());
1413   const auto module_str = test::ToString(p->program());
1414   EXPECT_THAT(module_str, HasSubstr(R"(type Arr = [[stride(4)]] array<u32, 2u>;
1415 
1416 [[block]]
1417 struct S {
1418   field0 : u32;
1419   field1 : f32;
1420   field2 : Arr;
1421 };
1422 
1423 [[group(0), binding(0)]] var<storage, read_write> x_1 : S;
1424 )")) << module_str;
1425 }
1426 
TEST_F(SpvModuleScopeVarParserTest,ColMajorDecoration_Dropped)1427 TEST_F(SpvModuleScopeVarParserTest, ColMajorDecoration_Dropped) {
1428   auto p = parser(test::Assemble(Preamble() + FragMain() + R"(
1429      OpName %myvar "myvar"
1430      OpDecorate %myvar DescriptorSet 0
1431      OpDecorate %myvar Binding 0
1432      OpDecorate %s Block
1433      OpMemberDecorate %s 0 ColMajor
1434      OpMemberDecorate %s 0 Offset 0
1435      OpMemberDecorate %s 0 MatrixStride 8
1436      %float = OpTypeFloat 32
1437      %v2float = OpTypeVector %float 2
1438      %m3v2float = OpTypeMatrix %v2float 3
1439 
1440      %s = OpTypeStruct %m3v2float
1441      %ptr_sb_s = OpTypePointer StorageBuffer %s
1442      %myvar = OpVariable %ptr_sb_s StorageBuffer
1443      %void = OpTypeVoid
1444      %voidfn = OpTypeFunction %void
1445   )" + MainBody()));
1446   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
1447   EXPECT_TRUE(p->error().empty());
1448   const auto module_str = test::ToString(p->program());
1449   EXPECT_THAT(module_str, HasSubstr(R"([[block]]
1450 struct S {
1451   field0 : mat3x2<f32>;
1452 };
1453 
1454 [[group(0), binding(0)]] var<storage, read_write> myvar : S;
1455 )")) << module_str;
1456 }
1457 
TEST_F(SpvModuleScopeVarParserTest,MatrixStrideDecoration_Natural_Dropped)1458 TEST_F(SpvModuleScopeVarParserTest, MatrixStrideDecoration_Natural_Dropped) {
1459   auto p = parser(test::Assemble(Preamble() + FragMain() + R"(
1460      OpName %myvar "myvar"
1461      OpDecorate %myvar DescriptorSet 0
1462      OpDecorate %myvar Binding 0
1463      OpDecorate %s Block
1464      OpMemberDecorate %s 0 MatrixStride 8
1465      OpMemberDecorate %s 0 Offset 0
1466      %void = OpTypeVoid
1467      %voidfn = OpTypeFunction %void
1468      %float = OpTypeFloat 32
1469      %v2float = OpTypeVector %float 2
1470      %m3v2float = OpTypeMatrix %v2float 3
1471 
1472      %s = OpTypeStruct %m3v2float
1473      %ptr_sb_s = OpTypePointer StorageBuffer %s
1474      %myvar = OpVariable %ptr_sb_s StorageBuffer
1475   )" + MainBody()));
1476   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
1477   EXPECT_TRUE(p->error().empty());
1478   const auto module_str = test::ToString(p->program());
1479   EXPECT_THAT(module_str, HasSubstr(R"([[block]]
1480 struct S {
1481   field0 : mat3x2<f32>;
1482 };
1483 
1484 [[group(0), binding(0)]] var<storage, read_write> myvar : S;
1485 )")) << module_str;
1486 }
1487 
TEST_F(SpvModuleScopeVarParserTest,MatrixStrideDecoration)1488 TEST_F(SpvModuleScopeVarParserTest, MatrixStrideDecoration) {
1489   auto p = parser(test::Assemble(Preamble() + FragMain() + R"(
1490      OpName %myvar "myvar"
1491      OpDecorate %myvar DescriptorSet 0
1492      OpDecorate %myvar Binding 0
1493      OpDecorate %s Block
1494      OpMemberDecorate %s 0 MatrixStride 64
1495      OpMemberDecorate %s 0 Offset 0
1496      %void = OpTypeVoid
1497      %voidfn = OpTypeFunction %void
1498      %float = OpTypeFloat 32
1499      %v2float = OpTypeVector %float 2
1500      %m3v2float = OpTypeMatrix %v2float 3
1501 
1502      %s = OpTypeStruct %m3v2float
1503      %ptr_sb_s = OpTypePointer StorageBuffer %s
1504      %myvar = OpVariable %ptr_sb_s StorageBuffer
1505   )" + MainBody()));
1506   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
1507   EXPECT_TRUE(p->error().empty());
1508   const auto module_str = test::ToString(p->program());
1509   EXPECT_THAT(module_str, HasSubstr(R"([[block]]
1510 struct S {
1511   [[stride(64), internal(disable_validation__ignore_stride)]]
1512   field0 : mat3x2<f32>;
1513 };
1514 
1515 [[group(0), binding(0)]] var<storage, read_write> myvar : S;
1516 )")) << module_str;
1517 }
1518 
TEST_F(SpvModuleScopeVarParserTest,RowMajorDecoration_IsError)1519 TEST_F(SpvModuleScopeVarParserTest, RowMajorDecoration_IsError) {
1520   auto p = parser(test::Assemble(Preamble() + FragMain() + R"(
1521      OpName %myvar "myvar"
1522      OpDecorate %s Block
1523      OpMemberDecorate %s 0 RowMajor
1524      OpMemberDecorate %s 0 Offset 0
1525      %void = OpTypeVoid
1526      %voidfn = OpTypeFunction %void
1527      %float = OpTypeFloat 32
1528      %v2float = OpTypeVector %float 2
1529      %m3v2float = OpTypeMatrix %v2float 3
1530 
1531      %s = OpTypeStruct %m3v2float
1532      %ptr_sb_s = OpTypePointer StorageBuffer %s
1533      %myvar = OpVariable %ptr_sb_s StorageBuffer
1534   )" + MainBody()));
1535   EXPECT_FALSE(p->BuildAndParseInternalModuleExceptFunctions());
1536   EXPECT_THAT(
1537       p->error(),
1538       Eq(R"(WGSL does not support row-major matrices: can't translate member 0 of %3 = OpTypeStruct %8)"))
1539       << p->error();
1540 }
1541 
TEST_F(SpvModuleScopeVarParserTest,StorageBuffer_NonWritable_AllMembers)1542 TEST_F(SpvModuleScopeVarParserTest, StorageBuffer_NonWritable_AllMembers) {
1543   // Variable should have access(read)
1544   auto p = parser(test::Assemble(Preamble() + FragMain() + R"(
1545      OpDecorate %s Block
1546      OpDecorate %1 DescriptorSet 0
1547      OpDecorate %1 Binding 0
1548      OpMemberDecorate %s 0 NonWritable
1549      OpMemberDecorate %s 1 NonWritable
1550      OpMemberDecorate %s 0 Offset 0
1551      OpMemberDecorate %s 1 Offset 4
1552      %void = OpTypeVoid
1553      %voidfn = OpTypeFunction %void
1554      %float = OpTypeFloat 32
1555 
1556      %s = OpTypeStruct %float %float
1557      %ptr_sb_s = OpTypePointer StorageBuffer %s
1558      %1 = OpVariable %ptr_sb_s StorageBuffer
1559   )" + MainBody()));
1560   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
1561   EXPECT_TRUE(p->error().empty());
1562   const auto module_str = test::ToString(p->program());
1563   EXPECT_THAT(module_str, HasSubstr(R"([[block]]
1564 struct S {
1565   field0 : f32;
1566   field1 : f32;
1567 };
1568 
1569 [[group(0), binding(0)]] var<storage, read> x_1 : S;
1570 )")) << module_str;
1571 }
1572 
TEST_F(SpvModuleScopeVarParserTest,StorageBuffer_NonWritable_NotAllMembers)1573 TEST_F(SpvModuleScopeVarParserTest, StorageBuffer_NonWritable_NotAllMembers) {
1574   // Variable should have access(read_write)
1575   auto p = parser(test::Assemble(Preamble() + FragMain() + R"(
1576      OpDecorate %1 DescriptorSet 0
1577      OpDecorate %1 Binding 0
1578      OpDecorate %s Block
1579      OpMemberDecorate %s 0 NonWritable
1580      OpMemberDecorate %s 0 Offset 0
1581      OpMemberDecorate %s 1 Offset 4
1582      %void = OpTypeVoid
1583      %voidfn = OpTypeFunction %void
1584      %float = OpTypeFloat 32
1585 
1586      %s = OpTypeStruct %float %float
1587      %ptr_sb_s = OpTypePointer StorageBuffer %s
1588      %1 = OpVariable %ptr_sb_s StorageBuffer
1589   )" + MainBody()));
1590   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
1591   EXPECT_TRUE(p->error().empty());
1592   const auto module_str = test::ToString(p->program());
1593   EXPECT_THAT(module_str, HasSubstr(R"([[block]]
1594 struct S {
1595   field0 : f32;
1596   field1 : f32;
1597 };
1598 
1599 [[group(0), binding(0)]] var<storage, read_write> x_1 : S;
1600 )")) << module_str;
1601 }
1602 
TEST_F(SpvModuleScopeVarParserTest,StorageBuffer_NonWritable_NotAllMembers_DuplicatedOnSameMember)1603 TEST_F(
1604     SpvModuleScopeVarParserTest,
1605     StorageBuffer_NonWritable_NotAllMembers_DuplicatedOnSameMember) {  // NOLINT
1606   // Variable should have access(read_write)
1607   auto p = parser(test::Assemble(Preamble() + FragMain() + R"(
1608      OpDecorate %s Block
1609      OpDecorate %1 DescriptorSet 0
1610      OpDecorate %1 Binding 0
1611      OpMemberDecorate %s 0 NonWritable
1612      OpMemberDecorate %s 0 NonWritable ; same member. Don't double-count it
1613      OpMemberDecorate %s 0 Offset 0
1614      OpMemberDecorate %s 1 Offset 4
1615      %void = OpTypeVoid
1616      %voidfn = OpTypeFunction %void
1617      %float = OpTypeFloat 32
1618 
1619      %s = OpTypeStruct %float %float
1620      %ptr_sb_s = OpTypePointer StorageBuffer %s
1621      %1 = OpVariable %ptr_sb_s StorageBuffer
1622   )" + MainBody()));
1623   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
1624   EXPECT_TRUE(p->error().empty());
1625   const auto module_str = test::ToString(p->program());
1626   EXPECT_THAT(module_str, HasSubstr(R"([[block]]
1627 struct S {
1628   field0 : f32;
1629   field1 : f32;
1630 };
1631 
1632 [[group(0), binding(0)]] var<storage, read_write> x_1 : S;
1633 )")) << module_str;
1634 }
1635 
TEST_F(SpvModuleScopeVarParserTest,ScalarSpecConstant_DeclareConst_Id_TooBig)1636 TEST_F(SpvModuleScopeVarParserTest, ScalarSpecConstant_DeclareConst_Id_TooBig) {
1637   // Override IDs must be between 0 and 65535
1638   auto p = parser(test::Assemble(Preamble() + FragMain() + R"(
1639      OpDecorate %1 SpecId 65536
1640      %bool = OpTypeBool
1641      %1 = OpSpecConstantTrue %bool
1642      %void = OpTypeVoid
1643      %voidfn = OpTypeFunction %void
1644   )" + MainBody()));
1645   EXPECT_FALSE(p->Parse());
1646   EXPECT_EQ(p->error(),
1647             "SpecId too large. WGSL override IDs must be between 0 and 65535: "
1648             "ID %1 has SpecId 65536");
1649 }
1650 
TEST_F(SpvModuleScopeVarParserTest,ScalarSpecConstant_DeclareConst_Id_MaxValid)1651 TEST_F(SpvModuleScopeVarParserTest,
1652        ScalarSpecConstant_DeclareConst_Id_MaxValid) {
1653   // Override IDs must be between 0 and 65535
1654   auto p = parser(test::Assemble(Preamble() + FragMain() + R"(
1655      OpDecorate %1 SpecId 65535
1656      %bool = OpTypeBool
1657      %1 = OpSpecConstantTrue %bool
1658      %void = OpTypeVoid
1659      %voidfn = OpTypeFunction %void
1660   )" + MainBody()));
1661   EXPECT_TRUE(p->Parse());
1662   EXPECT_EQ(p->error(), "");
1663 }
1664 
TEST_F(SpvModuleScopeVarParserTest,ScalarSpecConstant_DeclareConst_True)1665 TEST_F(SpvModuleScopeVarParserTest, ScalarSpecConstant_DeclareConst_True) {
1666   auto p = parser(test::Assemble(Preamble() + FragMain() + R"(
1667      OpName %c "myconst"
1668      OpDecorate %c SpecId 12
1669      %bool = OpTypeBool
1670      %c = OpSpecConstantTrue %bool
1671      %void = OpTypeVoid
1672      %voidfn = OpTypeFunction %void
1673   )" + MainBody()));
1674   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
1675   EXPECT_TRUE(p->error().empty());
1676   const auto module_str = test::ToString(p->program());
1677   EXPECT_THAT(module_str,
1678               HasSubstr("[[override(12)]] let myconst : bool = true;"))
1679       << module_str;
1680 }
1681 
TEST_F(SpvModuleScopeVarParserTest,ScalarSpecConstant_DeclareConst_False)1682 TEST_F(SpvModuleScopeVarParserTest, ScalarSpecConstant_DeclareConst_False) {
1683   auto p = parser(test::Assemble(Preamble() + FragMain() + R"(
1684      OpName %c "myconst"
1685      OpDecorate %c SpecId 12
1686      %bool = OpTypeBool
1687      %c = OpSpecConstantFalse %bool
1688      %void = OpTypeVoid
1689      %voidfn = OpTypeFunction %void
1690   )" + MainBody()));
1691   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
1692   EXPECT_TRUE(p->error().empty());
1693   const auto module_str = test::ToString(p->program());
1694   EXPECT_THAT(module_str,
1695               HasSubstr("[[override(12)]] let myconst : bool = false;"))
1696       << module_str;
1697 }
1698 
TEST_F(SpvModuleScopeVarParserTest,ScalarSpecConstant_DeclareConst_U32)1699 TEST_F(SpvModuleScopeVarParserTest, ScalarSpecConstant_DeclareConst_U32) {
1700   auto p = parser(test::Assemble(Preamble() + FragMain() + R"(
1701      OpName %c "myconst"
1702      OpDecorate %c SpecId 12
1703      %uint = OpTypeInt 32 0
1704      %c = OpSpecConstant %uint 42
1705      %void = OpTypeVoid
1706      %voidfn = OpTypeFunction %void
1707   )" + MainBody()));
1708   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
1709   EXPECT_TRUE(p->error().empty());
1710   const auto module_str = test::ToString(p->program());
1711   EXPECT_THAT(module_str,
1712               HasSubstr("[[override(12)]] let myconst : u32 = 42u;"))
1713       << module_str;
1714 }
1715 
TEST_F(SpvModuleScopeVarParserTest,ScalarSpecConstant_DeclareConst_I32)1716 TEST_F(SpvModuleScopeVarParserTest, ScalarSpecConstant_DeclareConst_I32) {
1717   auto p = parser(test::Assemble(Preamble() + FragMain() + R"(
1718      OpName %c "myconst"
1719      OpDecorate %c SpecId 12
1720      %int = OpTypeInt 32 1
1721      %c = OpSpecConstant %int 42
1722      %void = OpTypeVoid
1723      %voidfn = OpTypeFunction %void
1724   )" + MainBody()));
1725   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
1726   EXPECT_TRUE(p->error().empty());
1727   const auto module_str = test::ToString(p->program());
1728   EXPECT_THAT(module_str, HasSubstr("[[override(12)]] let myconst : i32 = 42;"))
1729       << module_str;
1730 }
1731 
TEST_F(SpvModuleScopeVarParserTest,ScalarSpecConstant_DeclareConst_F32)1732 TEST_F(SpvModuleScopeVarParserTest, ScalarSpecConstant_DeclareConst_F32) {
1733   auto p = parser(test::Assemble(Preamble() + FragMain() + R"(
1734      OpName %c "myconst"
1735      OpDecorate %c SpecId 12
1736      %float = OpTypeFloat 32
1737      %c = OpSpecConstant %float 2.5
1738      %void = OpTypeVoid
1739      %voidfn = OpTypeFunction %void
1740   )" + MainBody()));
1741   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
1742   EXPECT_TRUE(p->error().empty());
1743   const auto module_str = test::ToString(p->program());
1744   EXPECT_THAT(module_str,
1745               HasSubstr("[[override(12)]] let myconst : f32 = 2.5;"))
1746       << module_str;
1747 }
1748 
TEST_F(SpvModuleScopeVarParserTest,ScalarSpecConstant_DeclareConst_F32_WithoutSpecId)1749 TEST_F(SpvModuleScopeVarParserTest,
1750        ScalarSpecConstant_DeclareConst_F32_WithoutSpecId) {
1751   // When we don't have a spec ID, declare an undecorated module-scope constant.
1752   auto p = parser(test::Assemble(Preamble() + FragMain() + R"(
1753      OpName %c "myconst"
1754      %float = OpTypeFloat 32
1755      %c = OpSpecConstant %float 2.5
1756      %void = OpTypeVoid
1757      %voidfn = OpTypeFunction %void
1758   )" + MainBody()));
1759   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
1760   EXPECT_TRUE(p->error().empty());
1761   const auto module_str = test::ToString(p->program());
1762   EXPECT_THAT(module_str, HasSubstr("let myconst : f32 = 2.5;")) << module_str;
1763 }
1764 
TEST_F(SpvModuleScopeVarParserTest,ScalarSpecConstant_UsedInFunction)1765 TEST_F(SpvModuleScopeVarParserTest, ScalarSpecConstant_UsedInFunction) {
1766   const auto assembly = Preamble() + FragMain() + R"(
1767      OpName %c "myconst"
1768      %void = OpTypeVoid
1769      %voidfn = OpTypeFunction %void
1770      %float = OpTypeFloat 32
1771      %c = OpSpecConstant %float 2.5
1772      %floatfn = OpTypeFunction %float
1773      %100 = OpFunction %float None %floatfn
1774      %entry = OpLabel
1775      %1 = OpFAdd %float %c %c
1776      OpReturnValue %1
1777      OpFunctionEnd
1778   )" + MainBody();
1779   auto p = parser(test::Assemble(assembly));
1780   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
1781   auto fe = p->function_emitter(100);
1782   EXPECT_TRUE(fe.EmitBody()) << p->error();
1783   EXPECT_TRUE(p->error().empty());
1784 
1785   Program program = p->program();
1786   const auto got = test::ToString(program, fe.ast_body());
1787 
1788   EXPECT_THAT(got, HasSubstr("return (myconst + myconst);")) << got;
1789 }
1790 
1791 // Returns the start of a shader for testing SampleId,
1792 // parameterized by store type of %int or %uint
SampleIdPreamble(std::string store_type)1793 std::string SampleIdPreamble(std::string store_type) {
1794   return R"(
1795     OpCapability Shader
1796     OpCapability SampleRateShading
1797     OpMemoryModel Logical Simple
1798     OpEntryPoint Fragment %main "main" %1
1799     OpExecutionMode %main OriginUpperLeft
1800     OpDecorate %1 BuiltIn SampleId
1801     %void = OpTypeVoid
1802     %voidfn = OpTypeFunction %void
1803     %float = OpTypeFloat 32
1804     %uint = OpTypeInt 32 0
1805     %int = OpTypeInt 32 1
1806     %ptr_ty = OpTypePointer Input )" +
1807          store_type + R"(
1808     %1 = OpVariable %ptr_ty Input
1809 )";
1810 }
1811 
TEST_F(SpvModuleScopeVarParserTest,SampleId_I32_Load_Direct)1812 TEST_F(SpvModuleScopeVarParserTest, SampleId_I32_Load_Direct) {
1813   const std::string assembly = SampleIdPreamble("%int") + R"(
1814     %main = OpFunction %void None %voidfn
1815     %entry = OpLabel
1816     %2 = OpLoad %int %1
1817     OpReturn
1818     OpFunctionEnd
1819  )";
1820   auto p = parser(test::Assemble(assembly));
1821   ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
1822   EXPECT_TRUE(p->error().empty());
1823   const auto module_str = test::ToString(p->program());
1824   const std::string expected =
1825       R"(var<private> x_1 : i32;
1826 
1827 fn main_1() {
1828   let x_2 : i32 = x_1;
1829   return;
1830 }
1831 
1832 [[stage(fragment)]]
1833 fn main([[builtin(sample_index)]] x_1_param : u32) {
1834   x_1 = bitcast<i32>(x_1_param);
1835   main_1();
1836 }
1837 )";
1838   EXPECT_EQ(module_str, expected) << module_str;
1839 }
1840 
TEST_F(SpvModuleScopeVarParserTest,SampleId_I32_Load_CopyObject)1841 TEST_F(SpvModuleScopeVarParserTest, SampleId_I32_Load_CopyObject) {
1842   const std::string assembly = SampleIdPreamble("%int") + R"(
1843     %main = OpFunction %void None %voidfn
1844     %entry = OpLabel
1845     %copy_ptr = OpCopyObject %ptr_ty %1
1846     %2 = OpLoad %int %copy_ptr
1847     OpReturn
1848     OpFunctionEnd
1849  )";
1850   auto p = parser(test::Assemble(assembly));
1851   ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
1852   EXPECT_TRUE(p->error().empty());
1853   const auto module_str = test::ToString(p->program());
1854   const std::string expected =
1855       R"(Module{
1856   Variable{
1857     x_1
1858     private
1859     undefined
1860     __i32
1861   }
1862   Function main_1 -> __void
1863   ()
1864   {
1865     VariableDeclStatement{
1866       VariableConst{
1867         x_11
1868         none
1869         undefined
1870         __ptr_private__i32
1871         {
1872           UnaryOp[not set]{
1873             address-of
1874             Identifier[not set]{x_1}
1875           }
1876         }
1877       }
1878     }
1879     VariableDeclStatement{
1880       VariableConst{
1881         x_2
1882         none
1883         undefined
1884         __i32
1885         {
1886           UnaryOp[not set]{
1887             indirection
1888             Identifier[not set]{x_14}
1889           }
1890         }
1891       }
1892     }
1893     Return{}
1894   }
1895   Function main -> __void
1896   StageDecoration{fragment}
1897   (
1898     VariableConst{
1899       Decorations{
1900         BuiltinDecoration{sample_index}
1901       }
1902       x_1_param
1903       none
1904       undefined
1905       __u32
1906     }
1907   )
1908   {
1909     Assignment{
1910       Identifier[not set]{x_1}
1911       Bitcast[not set]<__i32>{
1912         Identifier[not set]{x_1_param}
1913       }
1914     }
1915     Call[not set]{
1916       Identifier[not set]{main_1}
1917       (
1918       )
1919     }
1920   }
1921 }
1922 )";
1923 }
1924 
TEST_F(SpvModuleScopeVarParserTest,SampleId_I32_Load_AccessChain)1925 TEST_F(SpvModuleScopeVarParserTest, SampleId_I32_Load_AccessChain) {
1926   const std::string assembly = SampleIdPreamble("%int") + R"(
1927     %main = OpFunction %void None %voidfn
1928     %entry = OpLabel
1929     %copy_ptr = OpAccessChain %ptr_ty %1
1930     %2 = OpLoad %int %copy_ptr
1931     OpReturn
1932     OpFunctionEnd
1933  )";
1934   auto p = parser(test::Assemble(assembly));
1935   ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
1936   EXPECT_TRUE(p->error().empty());
1937   const auto module_str = test::ToString(p->program());
1938   const std::string expected = R"(var<private> x_1 : i32;
1939 
1940 fn main_1() {
1941   let x_2 : i32 = x_1;
1942   return;
1943 }
1944 
1945 [[stage(fragment)]]
1946 fn main([[builtin(sample_index)]] x_1_param : u32) {
1947   x_1 = bitcast<i32>(x_1_param);
1948   main_1();
1949 }
1950 )";
1951   EXPECT_EQ(module_str, expected);
1952 }
1953 
TEST_F(SpvModuleScopeVarParserTest,SampleId_I32_FunctParam)1954 TEST_F(SpvModuleScopeVarParserTest, SampleId_I32_FunctParam) {
1955   const std::string assembly = SampleIdPreamble("%int") + R"(
1956     %helper_ty = OpTypeFunction %int %ptr_ty
1957     %helper = OpFunction %int None %helper_ty
1958     %param = OpFunctionParameter %ptr_ty
1959     %helper_entry = OpLabel
1960     %3 = OpLoad %int %param
1961     OpReturnValue %3
1962     OpFunctionEnd
1963 
1964     %main = OpFunction %void None %voidfn
1965     %entry = OpLabel
1966     %result = OpFunctionCall %int %helper %1
1967     OpReturn
1968     OpFunctionEnd
1969  )";
1970   auto p = parser(test::Assemble(assembly));
1971 
1972   // This example is invalid because you can't pass pointer-to-Input
1973   // as a function parameter.
1974   EXPECT_FALSE(p->Parse());
1975   EXPECT_FALSE(p->success());
1976   EXPECT_THAT(p->error(),
1977               HasSubstr("Invalid storage class for pointer operand 1"));
1978 }
1979 
TEST_F(SpvModuleScopeVarParserTest,SampleId_U32_Load_Direct)1980 TEST_F(SpvModuleScopeVarParserTest, SampleId_U32_Load_Direct) {
1981   const std::string assembly = SampleIdPreamble("%uint") + R"(
1982     %main = OpFunction %void None %voidfn
1983     %entry = OpLabel
1984     %2 = OpLoad %uint %1
1985     OpReturn
1986     OpFunctionEnd
1987  )";
1988   auto p = parser(test::Assemble(assembly));
1989   ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
1990   EXPECT_TRUE(p->error().empty());
1991   const auto module_str = test::ToString(p->program());
1992   const std::string expected = R"(var<private> x_1 : u32;
1993 
1994 fn main_1() {
1995   let x_2 : u32 = x_1;
1996   return;
1997 }
1998 
1999 [[stage(fragment)]]
2000 fn main([[builtin(sample_index)]] x_1_param : u32) {
2001   x_1 = x_1_param;
2002   main_1();
2003 }
2004 )";
2005   EXPECT_EQ(module_str, expected) << module_str;
2006 }
2007 
TEST_F(SpvModuleScopeVarParserTest,SampleId_U32_Load_CopyObject)2008 TEST_F(SpvModuleScopeVarParserTest, SampleId_U32_Load_CopyObject) {
2009   const std::string assembly = SampleIdPreamble("%uint") + R"(
2010     %main = OpFunction %void None %voidfn
2011     %entry = OpLabel
2012     %copy_ptr = OpCopyObject %ptr_ty %1
2013     %2 = OpLoad %uint %copy_ptr
2014     OpReturn
2015     OpFunctionEnd
2016  )";
2017   auto p = parser(test::Assemble(assembly));
2018   ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
2019   EXPECT_TRUE(p->error().empty());
2020   const auto module_str = test::ToString(p->program());
2021   const std::string expected = R"(var<private> x_1 : u32;
2022 
2023 fn main_1() {
2024   let x_11 : ptr<private, u32> = &(x_1);
2025   let x_2 : u32 = *(x_11);
2026   return;
2027 }
2028 
2029 [[stage(fragment)]]
2030 fn main([[builtin(sample_index)]] x_1_param : u32) {
2031   x_1 = x_1_param;
2032   main_1();
2033 }
2034 )";
2035   EXPECT_EQ(module_str, expected) << module_str;
2036 }
2037 
TEST_F(SpvModuleScopeVarParserTest,SampleId_U32_Load_AccessChain)2038 TEST_F(SpvModuleScopeVarParserTest, SampleId_U32_Load_AccessChain) {
2039   const std::string assembly = SampleIdPreamble("%uint") + R"(
2040     %main = OpFunction %void None %voidfn
2041     %entry = OpLabel
2042     %copy_ptr = OpAccessChain %ptr_ty %1
2043     %2 = OpLoad %uint %copy_ptr
2044     OpReturn
2045     OpFunctionEnd
2046  )";
2047   auto p = parser(test::Assemble(assembly));
2048   ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
2049   EXPECT_TRUE(p->error().empty());
2050   const auto module_str = test::ToString(p->program());
2051   const std::string expected = R"(var<private> x_1 : u32;
2052 
2053 fn main_1() {
2054   let x_2 : u32 = x_1;
2055   return;
2056 }
2057 
2058 [[stage(fragment)]]
2059 fn main([[builtin(sample_index)]] x_1_param : u32) {
2060   x_1 = x_1_param;
2061   main_1();
2062 }
2063 )";
2064   EXPECT_EQ(module_str, expected) << module_str;
2065 }
2066 
TEST_F(SpvModuleScopeVarParserTest,SampleId_U32_FunctParam)2067 TEST_F(SpvModuleScopeVarParserTest, SampleId_U32_FunctParam) {
2068   const std::string assembly = SampleIdPreamble("%uint") + R"(
2069     %helper_ty = OpTypeFunction %uint %ptr_ty
2070     %helper = OpFunction %uint None %helper_ty
2071     %param = OpFunctionParameter %ptr_ty
2072     %helper_entry = OpLabel
2073     %3 = OpLoad %uint %param
2074     OpReturnValue %3
2075     OpFunctionEnd
2076 
2077     %main = OpFunction %void None %voidfn
2078     %entry = OpLabel
2079     %result = OpFunctionCall %uint %helper %1
2080     OpReturn
2081     OpFunctionEnd
2082  )";
2083   auto p = parser(test::Assemble(assembly));
2084   // This example is invalid because you can't pass pointer-to-Input
2085   // as a function parameter.
2086   EXPECT_FALSE(p->Parse());
2087   EXPECT_THAT(p->error(),
2088               HasSubstr("Invalid storage class for pointer operand 1"));
2089 }
2090 
2091 // Returns the start of a shader for testing SampleMask
2092 // parameterized by store type.
SampleMaskPreamble(std::string store_type,uint32_t stride=0u)2093 std::string SampleMaskPreamble(std::string store_type, uint32_t stride = 0u) {
2094   return std::string(R"(
2095     OpCapability Shader
2096     OpMemoryModel Logical Simple
2097     OpEntryPoint Fragment %main "main" %1
2098     OpExecutionMode %main OriginUpperLeft
2099     OpDecorate %1 BuiltIn SampleMask
2100 )") +
2101          (stride > 0u ? R"(
2102     OpDecorate %uarr1 ArrayStride 4
2103     OpDecorate %uarr2 ArrayStride 4
2104     OpDecorate %iarr1 ArrayStride 4
2105     OpDecorate %iarr2 ArrayStride 4
2106 )"
2107                       : "") +
2108          R"(
2109     %void = OpTypeVoid
2110     %voidfn = OpTypeFunction %void
2111     %float = OpTypeFloat 32
2112     %uint = OpTypeInt 32 0
2113     %int = OpTypeInt 32 1
2114     %int_12 = OpConstant %int 12
2115     %uint_0 = OpConstant %uint 0
2116     %uint_1 = OpConstant %uint 1
2117     %uint_2 = OpConstant %uint 2
2118     %uarr1 = OpTypeArray %uint %uint_1
2119     %uarr2 = OpTypeArray %uint %uint_2
2120     %iarr1 = OpTypeArray %int %uint_1
2121     %iarr2 = OpTypeArray %int %uint_2
2122     %iptr_in_ty = OpTypePointer Input %int
2123     %uptr_in_ty = OpTypePointer Input %uint
2124     %iptr_out_ty = OpTypePointer Output %int
2125     %uptr_out_ty = OpTypePointer Output %uint
2126     %in_ty = OpTypePointer Input )" +
2127          store_type + R"(
2128     %out_ty = OpTypePointer Output )" +
2129          store_type + R"(
2130 )";
2131 }
2132 
TEST_F(SpvModuleScopeVarParserTest,SampleMask_In_ArraySize2_Error)2133 TEST_F(SpvModuleScopeVarParserTest, SampleMask_In_ArraySize2_Error) {
2134   const std::string assembly = SampleMaskPreamble("%uarr2") + R"(
2135     %1 = OpVariable %in_ty Input
2136 
2137     %main = OpFunction %void None %voidfn
2138     %entry = OpLabel
2139     %2 = OpAccessChain %uptr_in_ty %1 %uint_0
2140     %3 = OpLoad %int %2
2141     OpReturn
2142     OpFunctionEnd
2143  )";
2144   auto p = parser(test::Assemble(assembly));
2145   ASSERT_FALSE(p->BuildAndParseInternalModule());
2146   EXPECT_THAT(p->error(),
2147               HasSubstr("WGSL supports a sample mask of at most 32 bits. "
2148                         "SampleMask must be an array of 1 element"))
2149       << p->error() << assembly;
2150 }
2151 
TEST_F(SpvModuleScopeVarParserTest,SampleMask_In_U32_Direct)2152 TEST_F(SpvModuleScopeVarParserTest, SampleMask_In_U32_Direct) {
2153   const std::string assembly = SampleMaskPreamble("%uarr1") + R"(
2154     %1 = OpVariable %in_ty Input
2155 
2156     %main = OpFunction %void None %voidfn
2157     %entry = OpLabel
2158     %2 = OpAccessChain %uptr_in_ty %1 %uint_0
2159     %3 = OpLoad %uint %2
2160     OpReturn
2161     OpFunctionEnd
2162  )";
2163   auto p = parser(test::Assemble(assembly));
2164   ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
2165   EXPECT_TRUE(p->error().empty());
2166   const auto module_str = test::ToString(p->program());
2167   const std::string expected = R"(var<private> x_1 : array<u32, 1u>;
2168 
2169 fn main_1() {
2170   let x_3 : u32 = x_1[0];
2171   return;
2172 }
2173 
2174 [[stage(fragment)]]
2175 fn main([[builtin(sample_mask)]] x_1_param : u32) {
2176   x_1[0] = x_1_param;
2177   main_1();
2178 }
2179 )";
2180   EXPECT_EQ(module_str, expected);
2181 }
2182 
TEST_F(SpvModuleScopeVarParserTest,SampleMask_In_U32_CopyObject)2183 TEST_F(SpvModuleScopeVarParserTest, SampleMask_In_U32_CopyObject) {
2184   const std::string assembly = SampleMaskPreamble("%uarr1") + R"(
2185     %1 = OpVariable %in_ty Input
2186 
2187     %main = OpFunction %void None %voidfn
2188     %entry = OpLabel
2189     %2 = OpAccessChain %uptr_in_ty %1 %uint_0
2190     %3 = OpCopyObject %uptr_in_ty %2
2191     %4 = OpLoad %uint %3
2192     OpReturn
2193     OpFunctionEnd
2194  )";
2195   auto p = parser(test::Assemble(assembly));
2196   ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
2197   EXPECT_TRUE(p->error().empty());
2198   const auto module_str = test::ToString(p->program());
2199   const std::string expected = R"(var<private> x_1 : array<u32, 1u>;
2200 
2201 fn main_1() {
2202   let x_4 : u32 = x_1[0];
2203   return;
2204 }
2205 
2206 [[stage(fragment)]]
2207 fn main([[builtin(sample_mask)]] x_1_param : u32) {
2208   x_1[0] = x_1_param;
2209   main_1();
2210 }
2211 )";
2212   EXPECT_EQ(module_str, expected) << module_str;
2213 }
2214 
TEST_F(SpvModuleScopeVarParserTest,SampleMask_In_U32_AccessChain)2215 TEST_F(SpvModuleScopeVarParserTest, SampleMask_In_U32_AccessChain) {
2216   const std::string assembly = SampleMaskPreamble("%uarr1") + R"(
2217     %1 = OpVariable %in_ty Input
2218 
2219     %main = OpFunction %void None %voidfn
2220     %entry = OpLabel
2221     %2 = OpAccessChain %uptr_in_ty %1 %uint_0
2222     %3 = OpAccessChain %uptr_in_ty %2
2223     %4 = OpLoad %uint %3
2224     OpReturn
2225     OpFunctionEnd
2226  )";
2227   auto p = parser(test::Assemble(assembly));
2228   ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
2229   EXPECT_TRUE(p->error().empty());
2230   const auto module_str = test::ToString(p->program());
2231   const std::string expected = R"(var<private> x_1 : array<u32, 1u>;
2232 
2233 fn main_1() {
2234   let x_4 : u32 = x_1[0];
2235   return;
2236 }
2237 
2238 [[stage(fragment)]]
2239 fn main([[builtin(sample_mask)]] x_1_param : u32) {
2240   x_1[0] = x_1_param;
2241   main_1();
2242 }
2243 )";
2244   EXPECT_EQ(module_str, expected);
2245 }
2246 
TEST_F(SpvModuleScopeVarParserTest,SampleMask_In_I32_Direct)2247 TEST_F(SpvModuleScopeVarParserTest, SampleMask_In_I32_Direct) {
2248   const std::string assembly = SampleMaskPreamble("%iarr1") + R"(
2249     %1 = OpVariable %in_ty Input
2250 
2251     %main = OpFunction %void None %voidfn
2252     %entry = OpLabel
2253     %2 = OpAccessChain %iptr_in_ty %1 %uint_0
2254     %3 = OpLoad %int %2
2255     OpReturn
2256     OpFunctionEnd
2257  )";
2258   auto p = parser(test::Assemble(assembly));
2259   ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
2260   EXPECT_TRUE(p->error().empty());
2261   const auto module_str = test::ToString(p->program());
2262   const std::string expected = R"(var<private> x_1 : array<i32, 1u>;
2263 
2264 fn main_1() {
2265   let x_3 : i32 = x_1[0];
2266   return;
2267 }
2268 
2269 [[stage(fragment)]]
2270 fn main([[builtin(sample_mask)]] x_1_param : u32) {
2271   x_1[0] = bitcast<i32>(x_1_param);
2272   main_1();
2273 }
2274 )";
2275   EXPECT_EQ(module_str, expected) << module_str;
2276 }
2277 
TEST_F(SpvModuleScopeVarParserTest,SampleMask_In_I32_CopyObject)2278 TEST_F(SpvModuleScopeVarParserTest, SampleMask_In_I32_CopyObject) {
2279   const std::string assembly = SampleMaskPreamble("%iarr1") + R"(
2280     %1 = OpVariable %in_ty Input
2281 
2282     %main = OpFunction %void None %voidfn
2283     %entry = OpLabel
2284     %2 = OpAccessChain %iptr_in_ty %1 %uint_0
2285     %3 = OpCopyObject %iptr_in_ty %2
2286     %4 = OpLoad %int %3
2287     OpReturn
2288     OpFunctionEnd
2289  )";
2290   auto p = parser(test::Assemble(assembly));
2291   ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
2292   EXPECT_TRUE(p->error().empty());
2293   const auto module_str = test::ToString(p->program());
2294   const std::string expected = R"(var<private> x_1 : array<i32, 1u>;
2295 
2296 fn main_1() {
2297   let x_4 : i32 = x_1[0];
2298   return;
2299 }
2300 
2301 [[stage(fragment)]]
2302 fn main([[builtin(sample_mask)]] x_1_param : u32) {
2303   x_1[0] = bitcast<i32>(x_1_param);
2304   main_1();
2305 }
2306 )";
2307   EXPECT_EQ(module_str, expected) << module_str;
2308 }
2309 
TEST_F(SpvModuleScopeVarParserTest,SampleMask_In_I32_AccessChain)2310 TEST_F(SpvModuleScopeVarParserTest, SampleMask_In_I32_AccessChain) {
2311   const std::string assembly = SampleMaskPreamble("%iarr1") + R"(
2312     %1 = OpVariable %in_ty Input
2313 
2314     %main = OpFunction %void None %voidfn
2315     %entry = OpLabel
2316     %2 = OpAccessChain %iptr_in_ty %1 %uint_0
2317     %3 = OpAccessChain %iptr_in_ty %2
2318     %4 = OpLoad %int %3
2319     OpReturn
2320     OpFunctionEnd
2321  )";
2322   auto p = parser(test::Assemble(assembly));
2323   ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
2324   EXPECT_TRUE(p->error().empty());
2325   const auto module_str = test::ToString(p->program());
2326   const std::string expected = R"(var<private> x_1 : array<i32, 1u>;
2327 
2328 fn main_1() {
2329   let x_4 : i32 = x_1[0];
2330   return;
2331 }
2332 
2333 [[stage(fragment)]]
2334 fn main([[builtin(sample_mask)]] x_1_param : u32) {
2335   x_1[0] = bitcast<i32>(x_1_param);
2336   main_1();
2337 }
2338 )";
2339   EXPECT_EQ(module_str, expected) << module_str;
2340 }
2341 
TEST_F(SpvModuleScopeVarParserTest,SampleMask_Out_ArraySize2_Error)2342 TEST_F(SpvModuleScopeVarParserTest, SampleMask_Out_ArraySize2_Error) {
2343   const std::string assembly = SampleMaskPreamble("%uarr2") + R"(
2344     %1 = OpVariable %out_ty Output
2345 
2346     %main = OpFunction %void None %voidfn
2347     %entry = OpLabel
2348     %2 = OpAccessChain %uptr_out_ty %1 %uint_0
2349     OpStore %2 %uint_0
2350     OpReturn
2351     OpFunctionEnd
2352  )";
2353   auto p = parser(test::Assemble(assembly));
2354   ASSERT_FALSE(p->BuildAndParseInternalModule());
2355   EXPECT_THAT(p->error(),
2356               HasSubstr("WGSL supports a sample mask of at most 32 bits. "
2357                         "SampleMask must be an array of 1 element"))
2358       << p->error() << assembly;
2359 }
2360 
TEST_F(SpvModuleScopeVarParserTest,SampleMask_Out_U32_Direct)2361 TEST_F(SpvModuleScopeVarParserTest, SampleMask_Out_U32_Direct) {
2362   const std::string assembly = SampleMaskPreamble("%uarr1") + R"(
2363     %1 = OpVariable %out_ty Output
2364 
2365     %main = OpFunction %void None %voidfn
2366     %entry = OpLabel
2367     %2 = OpAccessChain %uptr_out_ty %1 %uint_0
2368     OpStore %2 %uint_0
2369     OpReturn
2370     OpFunctionEnd
2371  )";
2372   auto p = parser(test::Assemble(assembly));
2373   ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
2374   EXPECT_TRUE(p->error().empty());
2375   const auto module_str = test::ToString(p->program());
2376   const std::string expected = R"(var<private> x_1 : array<u32, 1u>;
2377 
2378 fn main_1() {
2379   x_1[0] = 0u;
2380   return;
2381 }
2382 
2383 struct main_out {
2384   [[builtin(sample_mask)]]
2385   x_1_1 : u32;
2386 };
2387 
2388 [[stage(fragment)]]
2389 fn main() -> main_out {
2390   main_1();
2391   return main_out(x_1[0]);
2392 }
2393 )";
2394   EXPECT_EQ(module_str, expected);
2395 }
2396 
TEST_F(SpvModuleScopeVarParserTest,SampleMask_Out_U32_CopyObject)2397 TEST_F(SpvModuleScopeVarParserTest, SampleMask_Out_U32_CopyObject) {
2398   const std::string assembly = SampleMaskPreamble("%uarr1") + R"(
2399     %1 = OpVariable %out_ty Output
2400 
2401     %main = OpFunction %void None %voidfn
2402     %entry = OpLabel
2403     %2 = OpAccessChain %uptr_out_ty %1 %uint_0
2404     %3 = OpCopyObject %uptr_out_ty %2
2405     OpStore %2 %uint_0
2406     OpReturn
2407     OpFunctionEnd
2408  )";
2409   auto p = parser(test::Assemble(assembly));
2410   ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
2411   EXPECT_TRUE(p->error().empty());
2412   const auto module_str = test::ToString(p->program());
2413   const std::string expected = R"(var<private> x_1 : array<u32, 1u>;
2414 
2415 fn main_1() {
2416   x_1[0] = 0u;
2417   return;
2418 }
2419 
2420 struct main_out {
2421   [[builtin(sample_mask)]]
2422   x_1_1 : u32;
2423 };
2424 
2425 [[stage(fragment)]]
2426 fn main() -> main_out {
2427   main_1();
2428   return main_out(x_1[0]);
2429 }
2430 )";
2431   EXPECT_EQ(module_str, expected);
2432 }
2433 
TEST_F(SpvModuleScopeVarParserTest,SampleMask_Out_U32_AccessChain)2434 TEST_F(SpvModuleScopeVarParserTest, SampleMask_Out_U32_AccessChain) {
2435   const std::string assembly = SampleMaskPreamble("%uarr1") + R"(
2436     %1 = OpVariable %out_ty Output
2437 
2438     %main = OpFunction %void None %voidfn
2439     %entry = OpLabel
2440     %2 = OpAccessChain %uptr_out_ty %1 %uint_0
2441     %3 = OpAccessChain %uptr_out_ty %2
2442     OpStore %2 %uint_0
2443     OpReturn
2444     OpFunctionEnd
2445  )";
2446   auto p = parser(test::Assemble(assembly));
2447   ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
2448   EXPECT_TRUE(p->error().empty());
2449   const auto module_str = test::ToString(p->program());
2450   const std::string expected = R"(var<private> x_1 : array<u32, 1u>;
2451 
2452 fn main_1() {
2453   x_1[0] = 0u;
2454   return;
2455 }
2456 
2457 struct main_out {
2458   [[builtin(sample_mask)]]
2459   x_1_1 : u32;
2460 };
2461 
2462 [[stage(fragment)]]
2463 fn main() -> main_out {
2464   main_1();
2465   return main_out(x_1[0]);
2466 }
2467 )";
2468   EXPECT_EQ(module_str, expected);
2469 }
2470 
TEST_F(SpvModuleScopeVarParserTest,SampleMask_Out_I32_Direct)2471 TEST_F(SpvModuleScopeVarParserTest, SampleMask_Out_I32_Direct) {
2472   const std::string assembly = SampleMaskPreamble("%iarr1") + R"(
2473     %1 = OpVariable %out_ty Output
2474 
2475     %main = OpFunction %void None %voidfn
2476     %entry = OpLabel
2477     %2 = OpAccessChain %iptr_out_ty %1 %uint_0
2478     OpStore %2 %int_12
2479     OpReturn
2480     OpFunctionEnd
2481  )";
2482   auto p = parser(test::Assemble(assembly));
2483   ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
2484   EXPECT_TRUE(p->error().empty());
2485   const auto module_str = test::ToString(p->program());
2486   const std::string expected = R"(var<private> x_1 : array<i32, 1u>;
2487 
2488 fn main_1() {
2489   x_1[0] = 12;
2490   return;
2491 }
2492 
2493 struct main_out {
2494   [[builtin(sample_mask)]]
2495   x_1_1 : u32;
2496 };
2497 
2498 [[stage(fragment)]]
2499 fn main() -> main_out {
2500   main_1();
2501   return main_out(bitcast<u32>(x_1[0]));
2502 }
2503 )";
2504   EXPECT_EQ(module_str, expected);
2505 }
2506 
TEST_F(SpvModuleScopeVarParserTest,SampleMask_Out_I32_CopyObject)2507 TEST_F(SpvModuleScopeVarParserTest, SampleMask_Out_I32_CopyObject) {
2508   const std::string assembly = SampleMaskPreamble("%iarr1") + R"(
2509     %1 = OpVariable %out_ty Output
2510 
2511     %main = OpFunction %void None %voidfn
2512     %entry = OpLabel
2513     %2 = OpAccessChain %iptr_out_ty %1 %uint_0
2514     %3 = OpCopyObject %iptr_out_ty %2
2515     OpStore %2 %int_12
2516     OpReturn
2517     OpFunctionEnd
2518  )";
2519   auto p = parser(test::Assemble(assembly));
2520   ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
2521   EXPECT_TRUE(p->error().empty());
2522   const auto module_str = test::ToString(p->program());
2523   const std::string expected = R"(var<private> x_1 : array<i32, 1u>;
2524 
2525 fn main_1() {
2526   x_1[0] = 12;
2527   return;
2528 }
2529 
2530 struct main_out {
2531   [[builtin(sample_mask)]]
2532   x_1_1 : u32;
2533 };
2534 
2535 [[stage(fragment)]]
2536 fn main() -> main_out {
2537   main_1();
2538   return main_out(bitcast<u32>(x_1[0]));
2539 }
2540 )";
2541   EXPECT_EQ(module_str, expected);
2542 }
2543 
TEST_F(SpvModuleScopeVarParserTest,SampleMask_Out_I32_AccessChain)2544 TEST_F(SpvModuleScopeVarParserTest, SampleMask_Out_I32_AccessChain) {
2545   const std::string assembly = SampleMaskPreamble("%iarr1") + R"(
2546     %1 = OpVariable %out_ty Output
2547 
2548     %main = OpFunction %void None %voidfn
2549     %entry = OpLabel
2550     %2 = OpAccessChain %iptr_out_ty %1 %uint_0
2551     %3 = OpAccessChain %iptr_out_ty %2
2552     OpStore %2 %int_12
2553     OpReturn
2554     OpFunctionEnd
2555  )";
2556   auto p = parser(test::Assemble(assembly));
2557   ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
2558   EXPECT_TRUE(p->error().empty());
2559   const auto module_str = test::ToString(p->program());
2560   const std::string expected = R"(var<private> x_1 : array<i32, 1u>;
2561 
2562 fn main_1() {
2563   x_1[0] = 12;
2564   return;
2565 }
2566 
2567 struct main_out {
2568   [[builtin(sample_mask)]]
2569   x_1_1 : u32;
2570 };
2571 
2572 [[stage(fragment)]]
2573 fn main() -> main_out {
2574   main_1();
2575   return main_out(bitcast<u32>(x_1[0]));
2576 }
2577 )";
2578   EXPECT_EQ(module_str, expected);
2579 }
2580 
TEST_F(SpvModuleScopeVarParserTest,SampleMask_In_WithStride)2581 TEST_F(SpvModuleScopeVarParserTest, SampleMask_In_WithStride) {
2582   const std::string assembly = SampleMaskPreamble("%uarr1", 4u) + R"(
2583     %1 = OpVariable %in_ty Input
2584 
2585     %main = OpFunction %void None %voidfn
2586     %entry = OpLabel
2587     %2 = OpAccessChain %uptr_in_ty %1 %uint_0
2588     %3 = OpLoad %uint %2
2589     OpReturn
2590     OpFunctionEnd
2591  )";
2592   auto p = parser(test::Assemble(assembly));
2593   ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
2594   EXPECT_TRUE(p->error().empty());
2595   const auto module_str = test::ToString(p->program());
2596   const std::string expected = R"(type Arr = [[stride(4)]] array<u32, 1u>;
2597 
2598 type Arr_1 = [[stride(4)]] array<u32, 2u>;
2599 
2600 type Arr_2 = [[stride(4)]] array<i32, 1u>;
2601 
2602 type Arr_3 = [[stride(4)]] array<i32, 2u>;
2603 
2604 var<private> x_1 : Arr;
2605 
2606 fn main_1() {
2607   let x_3 : u32 = x_1[0];
2608   return;
2609 }
2610 
2611 [[stage(fragment)]]
2612 fn main([[builtin(sample_mask)]] x_1_param : u32) {
2613   x_1[0] = x_1_param;
2614   main_1();
2615 }
2616 )";
2617   EXPECT_EQ(module_str, expected);
2618 }
2619 
TEST_F(SpvModuleScopeVarParserTest,SampleMask_Out_WithStride)2620 TEST_F(SpvModuleScopeVarParserTest, SampleMask_Out_WithStride) {
2621   const std::string assembly = SampleMaskPreamble("%uarr1", 4u) + R"(
2622     %1 = OpVariable %out_ty Output
2623 
2624     %main = OpFunction %void None %voidfn
2625     %entry = OpLabel
2626     %2 = OpAccessChain %uptr_out_ty %1 %uint_0
2627     OpStore %2 %uint_0
2628     OpReturn
2629     OpFunctionEnd
2630  )";
2631   auto p = parser(test::Assemble(assembly));
2632   ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
2633   EXPECT_TRUE(p->error().empty());
2634   const auto module_str = test::ToString(p->program());
2635   const std::string expected = R"(type Arr = [[stride(4)]] array<u32, 1u>;
2636 
2637 type Arr_1 = [[stride(4)]] array<u32, 2u>;
2638 
2639 type Arr_2 = [[stride(4)]] array<i32, 1u>;
2640 
2641 type Arr_3 = [[stride(4)]] array<i32, 2u>;
2642 
2643 var<private> x_1 : Arr;
2644 
2645 fn main_1() {
2646   x_1[0] = 0u;
2647   return;
2648 }
2649 
2650 struct main_out {
2651   [[builtin(sample_mask)]]
2652   x_1_1 : u32;
2653 };
2654 
2655 [[stage(fragment)]]
2656 fn main() -> main_out {
2657   main_1();
2658   return main_out(x_1[0]);
2659 }
2660 )";
2661   EXPECT_EQ(module_str, expected);
2662 }
2663 
2664 // Returns the start of a shader for testing VertexIndex,
2665 // parameterized by store type of %int or %uint
VertexIndexPreamble(std::string store_type)2666 std::string VertexIndexPreamble(std::string store_type) {
2667   return R"(
2668     OpCapability Shader
2669     OpMemoryModel Logical Simple
2670     OpEntryPoint Vertex %main "main" %position %1
2671     OpDecorate %position BuiltIn Position
2672     OpDecorate %1 BuiltIn VertexIndex
2673     %void = OpTypeVoid
2674     %voidfn = OpTypeFunction %void
2675     %float = OpTypeFloat 32
2676     %uint = OpTypeInt 32 0
2677     %int = OpTypeInt 32 1
2678     %ptr_ty = OpTypePointer Input )" +
2679          store_type + R"(
2680     %1 = OpVariable %ptr_ty Input
2681     %v4float = OpTypeVector %float 4
2682     %posty = OpTypePointer Output %v4float
2683     %position = OpVariable %posty Output
2684 )";
2685 }
2686 
TEST_F(SpvModuleScopeVarParserTest,VertexIndex_I32_Load_Direct)2687 TEST_F(SpvModuleScopeVarParserTest, VertexIndex_I32_Load_Direct) {
2688   const std::string assembly = VertexIndexPreamble("%int") + R"(
2689     %main = OpFunction %void None %voidfn
2690     %entry = OpLabel
2691     %2 = OpLoad %int %1
2692     OpReturn
2693     OpFunctionEnd
2694  )";
2695   auto p = parser(test::Assemble(assembly));
2696   ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
2697   EXPECT_TRUE(p->error().empty());
2698   const auto module_str = test::ToString(p->program());
2699   const std::string expected = R"(var<private> x_1 : i32;
2700 
2701 var<private> x_4 : vec4<f32>;
2702 
2703 fn main_1() {
2704   let x_2 : i32 = x_1;
2705   return;
2706 }
2707 
2708 struct main_out {
2709   [[builtin(position)]]
2710   x_4_1 : vec4<f32>;
2711 };
2712 
2713 [[stage(vertex)]]
2714 fn main([[builtin(vertex_index)]] x_1_param : u32) -> main_out {
2715   x_1 = bitcast<i32>(x_1_param);
2716   main_1();
2717   return main_out(x_4);
2718 }
2719 )";
2720   EXPECT_EQ(module_str, expected) << module_str;
2721 }
2722 
TEST_F(SpvModuleScopeVarParserTest,VertexIndex_I32_Load_CopyObject)2723 TEST_F(SpvModuleScopeVarParserTest, VertexIndex_I32_Load_CopyObject) {
2724   const std::string assembly = VertexIndexPreamble("%int") + R"(
2725     %main = OpFunction %void None %voidfn
2726     %entry = OpLabel
2727     %copy_ptr = OpCopyObject %ptr_ty %1
2728     %2 = OpLoad %int %copy_ptr
2729     OpReturn
2730     OpFunctionEnd
2731  )";
2732   auto p = parser(test::Assemble(assembly));
2733   ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
2734   EXPECT_TRUE(p->error().empty());
2735   const auto module_str = test::ToString(p->program());
2736   const std::string expected = R"(var<private> x_1 : i32;
2737 
2738 var<private> x_4 : vec4<f32>;
2739 
2740 fn main_1() {
2741   let x_14 : ptr<private, i32> = &(x_1);
2742   let x_2 : i32 = *(x_14);
2743   return;
2744 }
2745 
2746 struct main_out {
2747   [[builtin(position)]]
2748   x_4_1 : vec4<f32>;
2749 };
2750 
2751 [[stage(vertex)]]
2752 fn main([[builtin(vertex_index)]] x_1_param : u32) -> main_out {
2753   x_1 = bitcast<i32>(x_1_param);
2754   main_1();
2755   return main_out(x_4);
2756 }
2757 )";
2758   EXPECT_EQ(module_str, expected);
2759 }
2760 
TEST_F(SpvModuleScopeVarParserTest,VertexIndex_I32_Load_AccessChain)2761 TEST_F(SpvModuleScopeVarParserTest, VertexIndex_I32_Load_AccessChain) {
2762   const std::string assembly = VertexIndexPreamble("%int") + R"(
2763     %main = OpFunction %void None %voidfn
2764     %entry = OpLabel
2765     %copy_ptr = OpAccessChain %ptr_ty %1
2766     %2 = OpLoad %int %copy_ptr
2767     OpReturn
2768     OpFunctionEnd
2769  )";
2770   auto p = parser(test::Assemble(assembly));
2771   ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
2772   EXPECT_TRUE(p->error().empty());
2773   const auto module_str = test::ToString(p->program());
2774   const std::string expected = R"(var<private> x_1 : i32;
2775 
2776 var<private> x_4 : vec4<f32>;
2777 
2778 fn main_1() {
2779   let x_2 : i32 = x_1;
2780   return;
2781 }
2782 
2783 struct main_out {
2784   [[builtin(position)]]
2785   x_4_1 : vec4<f32>;
2786 };
2787 
2788 [[stage(vertex)]]
2789 fn main([[builtin(vertex_index)]] x_1_param : u32) -> main_out {
2790   x_1 = bitcast<i32>(x_1_param);
2791   main_1();
2792   return main_out(x_4);
2793 }
2794 )";
2795   EXPECT_EQ(module_str, expected);
2796 }
2797 
TEST_F(SpvModuleScopeVarParserTest,VertexIndex_U32_Load_Direct)2798 TEST_F(SpvModuleScopeVarParserTest, VertexIndex_U32_Load_Direct) {
2799   const std::string assembly = VertexIndexPreamble("%uint") + R"(
2800     %main = OpFunction %void None %voidfn
2801     %entry = OpLabel
2802     %2 = OpLoad %uint %1
2803     OpReturn
2804     OpFunctionEnd
2805  )";
2806   auto p = parser(test::Assemble(assembly));
2807   ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
2808   EXPECT_TRUE(p->error().empty());
2809   const auto module_str = test::ToString(p->program());
2810   const std::string expected = R"(var<private> x_1 : u32;
2811 
2812 var<private> x_4 : vec4<f32>;
2813 
2814 fn main_1() {
2815   let x_2 : u32 = x_1;
2816   return;
2817 }
2818 
2819 struct main_out {
2820   [[builtin(position)]]
2821   x_4_1 : vec4<f32>;
2822 };
2823 
2824 [[stage(vertex)]]
2825 fn main([[builtin(vertex_index)]] x_1_param : u32) -> main_out {
2826   x_1 = x_1_param;
2827   main_1();
2828   return main_out(x_4);
2829 }
2830 )";
2831   EXPECT_EQ(module_str, expected);
2832 }
2833 
TEST_F(SpvModuleScopeVarParserTest,VertexIndex_U32_Load_CopyObject)2834 TEST_F(SpvModuleScopeVarParserTest, VertexIndex_U32_Load_CopyObject) {
2835   const std::string assembly = VertexIndexPreamble("%uint") + R"(
2836     %main = OpFunction %void None %voidfn
2837     %entry = OpLabel
2838     %copy_ptr = OpCopyObject %ptr_ty %1
2839     %2 = OpLoad %uint %copy_ptr
2840     OpReturn
2841     OpFunctionEnd
2842  )";
2843   auto p = parser(test::Assemble(assembly));
2844   ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
2845   EXPECT_TRUE(p->error().empty());
2846   const auto module_str = test::ToString(p->program());
2847   const std::string expected = R"(var<private> x_1 : u32;
2848 
2849 var<private> x_4 : vec4<f32>;
2850 
2851 fn main_1() {
2852   let x_14 : ptr<private, u32> = &(x_1);
2853   let x_2 : u32 = *(x_14);
2854   return;
2855 }
2856 
2857 struct main_out {
2858   [[builtin(position)]]
2859   x_4_1 : vec4<f32>;
2860 };
2861 
2862 [[stage(vertex)]]
2863 fn main([[builtin(vertex_index)]] x_1_param : u32) -> main_out {
2864   x_1 = x_1_param;
2865   main_1();
2866   return main_out(x_4);
2867 }
2868 )";
2869   EXPECT_EQ(module_str, expected);
2870 }
2871 
TEST_F(SpvModuleScopeVarParserTest,VertexIndex_U32_Load_AccessChain)2872 TEST_F(SpvModuleScopeVarParserTest, VertexIndex_U32_Load_AccessChain) {
2873   const std::string assembly = VertexIndexPreamble("%uint") + R"(
2874     %main = OpFunction %void None %voidfn
2875     %entry = OpLabel
2876     %copy_ptr = OpAccessChain %ptr_ty %1
2877     %2 = OpLoad %uint %copy_ptr
2878     OpReturn
2879     OpFunctionEnd
2880  )";
2881   auto p = parser(test::Assemble(assembly));
2882   ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
2883   EXPECT_TRUE(p->error().empty());
2884   const auto module_str = test::ToString(p->program());
2885   const std::string expected = R"(var<private> x_1 : u32;
2886 
2887 var<private> x_4 : vec4<f32>;
2888 
2889 fn main_1() {
2890   let x_2 : u32 = x_1;
2891   return;
2892 }
2893 
2894 struct main_out {
2895   [[builtin(position)]]
2896   x_4_1 : vec4<f32>;
2897 };
2898 
2899 [[stage(vertex)]]
2900 fn main([[builtin(vertex_index)]] x_1_param : u32) -> main_out {
2901   x_1 = x_1_param;
2902   main_1();
2903   return main_out(x_4);
2904 }
2905 )";
2906   EXPECT_EQ(module_str, expected);
2907 }
2908 
TEST_F(SpvModuleScopeVarParserTest,VertexIndex_U32_FunctParam)2909 TEST_F(SpvModuleScopeVarParserTest, VertexIndex_U32_FunctParam) {
2910   const std::string assembly = VertexIndexPreamble("%uint") + R"(
2911     %helper_ty = OpTypeFunction %uint %ptr_ty
2912     %helper = OpFunction %uint None %helper_ty
2913     %param = OpFunctionParameter %ptr_ty
2914     %helper_entry = OpLabel
2915     %3 = OpLoad %uint %param
2916     OpReturnValue %3
2917     OpFunctionEnd
2918 
2919     %main = OpFunction %void None %voidfn
2920     %entry = OpLabel
2921     %result = OpFunctionCall %uint %helper %1
2922     OpReturn
2923     OpFunctionEnd
2924  )";
2925   auto p = parser(test::Assemble(assembly));
2926 
2927   // This example is invalid because you can't pass pointer-to-Input
2928   // as a function parameter.
2929   EXPECT_FALSE(p->Parse());
2930   EXPECT_THAT(p->error(),
2931               HasSubstr("Invalid storage class for pointer operand 1"));
2932 }
2933 
2934 // Returns the start of a shader for testing InstanceIndex,
2935 // parameterized by store type of %int or %uint
InstanceIndexPreamble(std::string store_type)2936 std::string InstanceIndexPreamble(std::string store_type) {
2937   return R"(
2938     OpCapability Shader
2939     OpMemoryModel Logical Simple
2940     OpEntryPoint Vertex %main "main" %position %1
2941     OpName %position "position"
2942     OpDecorate %position BuiltIn Position
2943     OpDecorate %1 BuiltIn InstanceIndex
2944     %void = OpTypeVoid
2945     %voidfn = OpTypeFunction %void
2946     %float = OpTypeFloat 32
2947     %uint = OpTypeInt 32 0
2948     %int = OpTypeInt 32 1
2949     %ptr_ty = OpTypePointer Input )" +
2950          store_type + R"(
2951     %1 = OpVariable %ptr_ty Input
2952     %v4float = OpTypeVector %float 4
2953     %posty = OpTypePointer Output %v4float
2954     %position = OpVariable %posty Output
2955 )";
2956 }
2957 
TEST_F(SpvModuleScopeVarParserTest,InstanceIndex_I32_Load_Direct)2958 TEST_F(SpvModuleScopeVarParserTest, InstanceIndex_I32_Load_Direct) {
2959   const std::string assembly = InstanceIndexPreamble("%int") + R"(
2960     %main = OpFunction %void None %voidfn
2961     %entry = OpLabel
2962     %2 = OpLoad %int %1
2963     OpReturn
2964     OpFunctionEnd
2965  )";
2966   auto p = parser(test::Assemble(assembly));
2967   ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
2968   EXPECT_TRUE(p->error().empty());
2969   const auto module_str = test::ToString(p->program());
2970   const std::string expected = R"(var<private> x_1 : i32;
2971 
2972 var<private> position : vec4<f32>;
2973 
2974 fn main_1() {
2975   let x_2 : i32 = x_1;
2976   return;
2977 }
2978 
2979 struct main_out {
2980   [[builtin(position)]]
2981   position_1 : vec4<f32>;
2982 };
2983 
2984 [[stage(vertex)]]
2985 fn main([[builtin(instance_index)]] x_1_param : u32) -> main_out {
2986   x_1 = bitcast<i32>(x_1_param);
2987   main_1();
2988   return main_out(position);
2989 }
2990 )";
2991   EXPECT_EQ(module_str, expected) << module_str;
2992 }
2993 
TEST_F(SpvModuleScopeVarParserTest,InstanceIndex_I32_Load_CopyObject)2994 TEST_F(SpvModuleScopeVarParserTest, InstanceIndex_I32_Load_CopyObject) {
2995   const std::string assembly = InstanceIndexPreamble("%int") + R"(
2996     %main = OpFunction %void None %voidfn
2997     %entry = OpLabel
2998     %copy_ptr = OpCopyObject %ptr_ty %1
2999     %2 = OpLoad %int %copy_ptr
3000     OpReturn
3001     OpFunctionEnd
3002  )";
3003   auto p = parser(test::Assemble(assembly));
3004   ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
3005   EXPECT_TRUE(p->error().empty());
3006   const auto module_str = test::ToString(p->program());
3007   const std::string expected = R"(var<private> x_1 : i32;
3008 
3009 var<private> position : vec4<f32>;
3010 
3011 fn main_1() {
3012   let x_14 : ptr<private, i32> = &(x_1);
3013   let x_2 : i32 = *(x_14);
3014   return;
3015 }
3016 
3017 struct main_out {
3018   [[builtin(position)]]
3019   position_1 : vec4<f32>;
3020 };
3021 
3022 [[stage(vertex)]]
3023 fn main([[builtin(instance_index)]] x_1_param : u32) -> main_out {
3024   x_1 = bitcast<i32>(x_1_param);
3025   main_1();
3026   return main_out(position);
3027 }
3028 )";
3029   EXPECT_EQ(module_str, expected) << module_str;
3030 }
3031 
TEST_F(SpvModuleScopeVarParserTest,InstanceIndex_I32_Load_AccessChain)3032 TEST_F(SpvModuleScopeVarParserTest, InstanceIndex_I32_Load_AccessChain) {
3033   const std::string assembly = InstanceIndexPreamble("%int") + R"(
3034     %main = OpFunction %void None %voidfn
3035     %entry = OpLabel
3036     %copy_ptr = OpAccessChain %ptr_ty %1
3037     %2 = OpLoad %int %copy_ptr
3038     OpReturn
3039     OpFunctionEnd
3040  )";
3041   auto p = parser(test::Assemble(assembly));
3042   ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
3043   EXPECT_TRUE(p->error().empty());
3044   const auto module_str = test::ToString(p->program());
3045   const std::string expected = R"(var<private> x_1 : i32;
3046 
3047 var<private> position : vec4<f32>;
3048 
3049 fn main_1() {
3050   let x_2 : i32 = x_1;
3051   return;
3052 }
3053 
3054 struct main_out {
3055   [[builtin(position)]]
3056   position_1 : vec4<f32>;
3057 };
3058 
3059 [[stage(vertex)]]
3060 fn main([[builtin(instance_index)]] x_1_param : u32) -> main_out {
3061   x_1 = bitcast<i32>(x_1_param);
3062   main_1();
3063   return main_out(position);
3064 }
3065 )";
3066   EXPECT_EQ(module_str, expected) << module_str;
3067 }
3068 
TEST_F(SpvModuleScopeVarParserTest,InstanceIndex_I32_FunctParam)3069 TEST_F(SpvModuleScopeVarParserTest, InstanceIndex_I32_FunctParam) {
3070   const std::string assembly = InstanceIndexPreamble("%int") + R"(
3071     %helper_ty = OpTypeFunction %int %ptr_ty
3072     %helper = OpFunction %int None %helper_ty
3073     %param = OpFunctionParameter %ptr_ty
3074     %helper_entry = OpLabel
3075     %3 = OpLoad %int %param
3076     OpReturnValue %3
3077     OpFunctionEnd
3078 
3079     %main = OpFunction %void None %voidfn
3080     %entry = OpLabel
3081     %result = OpFunctionCall %int %helper %1
3082     OpReturn
3083     OpFunctionEnd
3084  )";
3085   auto p = parser(test::Assemble(assembly));
3086   // This example is invalid because you can't pass pointer-to-Input
3087   // as a function parameter.
3088   EXPECT_FALSE(p->Parse());
3089   EXPECT_THAT(p->error(),
3090               HasSubstr("Invalid storage class for pointer operand 1"));
3091 }
3092 
TEST_F(SpvModuleScopeVarParserTest,InstanceIndex_U32_Load_Direct)3093 TEST_F(SpvModuleScopeVarParserTest, InstanceIndex_U32_Load_Direct) {
3094   const std::string assembly = InstanceIndexPreamble("%uint") + R"(
3095     %main = OpFunction %void None %voidfn
3096     %entry = OpLabel
3097     %2 = OpLoad %uint %1
3098     OpReturn
3099     OpFunctionEnd
3100  )";
3101   auto p = parser(test::Assemble(assembly));
3102   ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
3103   EXPECT_TRUE(p->error().empty());
3104   const auto module_str = test::ToString(p->program());
3105   const std::string expected = R"(var<private> x_1 : u32;
3106 
3107 var<private> position : vec4<f32>;
3108 
3109 fn main_1() {
3110   let x_2 : u32 = x_1;
3111   return;
3112 }
3113 
3114 struct main_out {
3115   [[builtin(position)]]
3116   position_1 : vec4<f32>;
3117 };
3118 
3119 [[stage(vertex)]]
3120 fn main([[builtin(instance_index)]] x_1_param : u32) -> main_out {
3121   x_1 = x_1_param;
3122   main_1();
3123   return main_out(position);
3124 }
3125 )";
3126   EXPECT_EQ(module_str, expected);
3127 }
3128 
TEST_F(SpvModuleScopeVarParserTest,InstanceIndex_U32_Load_CopyObject)3129 TEST_F(SpvModuleScopeVarParserTest, InstanceIndex_U32_Load_CopyObject) {
3130   const std::string assembly = InstanceIndexPreamble("%uint") + R"(
3131     %main = OpFunction %void None %voidfn
3132     %entry = OpLabel
3133     %copy_ptr = OpCopyObject %ptr_ty %1
3134     %2 = OpLoad %uint %copy_ptr
3135     OpReturn
3136     OpFunctionEnd
3137  )";
3138   auto p = parser(test::Assemble(assembly));
3139   ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
3140   EXPECT_TRUE(p->error().empty());
3141   const auto module_str = test::ToString(p->program());
3142   const std::string expected = R"(var<private> x_1 : u32;
3143 
3144 var<private> position : vec4<f32>;
3145 
3146 fn main_1() {
3147   let x_14 : ptr<private, u32> = &(x_1);
3148   let x_2 : u32 = *(x_14);
3149   return;
3150 }
3151 
3152 struct main_out {
3153   [[builtin(position)]]
3154   position_1 : vec4<f32>;
3155 };
3156 
3157 [[stage(vertex)]]
3158 fn main([[builtin(instance_index)]] x_1_param : u32) -> main_out {
3159   x_1 = x_1_param;
3160   main_1();
3161   return main_out(position);
3162 }
3163 )";
3164   EXPECT_EQ(module_str, expected);
3165 }
3166 
TEST_F(SpvModuleScopeVarParserTest,InstanceIndex_U32_Load_AccessChain)3167 TEST_F(SpvModuleScopeVarParserTest, InstanceIndex_U32_Load_AccessChain) {
3168   const std::string assembly = InstanceIndexPreamble("%uint") + R"(
3169     %main = OpFunction %void None %voidfn
3170     %entry = OpLabel
3171     %copy_ptr = OpAccessChain %ptr_ty %1
3172     %2 = OpLoad %uint %copy_ptr
3173     OpReturn
3174     OpFunctionEnd
3175  )";
3176   auto p = parser(test::Assemble(assembly));
3177   ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
3178   EXPECT_TRUE(p->error().empty());
3179   const auto module_str = test::ToString(p->program());
3180   const std::string expected = R"(var<private> x_1 : u32;
3181 
3182 var<private> position : vec4<f32>;
3183 
3184 fn main_1() {
3185   let x_2 : u32 = x_1;
3186   return;
3187 }
3188 
3189 struct main_out {
3190   [[builtin(position)]]
3191   position_1 : vec4<f32>;
3192 };
3193 
3194 [[stage(vertex)]]
3195 fn main([[builtin(instance_index)]] x_1_param : u32) -> main_out {
3196   x_1 = x_1_param;
3197   main_1();
3198   return main_out(position);
3199 }
3200 )";
3201   EXPECT_EQ(module_str, expected);
3202 }
3203 
TEST_F(SpvModuleScopeVarParserTest,InstanceIndex_U32_FunctParam)3204 TEST_F(SpvModuleScopeVarParserTest, InstanceIndex_U32_FunctParam) {
3205   const std::string assembly = InstanceIndexPreamble("%uint") + R"(
3206     %helper_ty = OpTypeFunction %uint %ptr_ty
3207     %helper = OpFunction %uint None %helper_ty
3208     %param = OpFunctionParameter %ptr_ty
3209     %helper_entry = OpLabel
3210     %3 = OpLoad %uint %param
3211     OpReturnValue %3
3212     OpFunctionEnd
3213 
3214     %main = OpFunction %void None %voidfn
3215     %entry = OpLabel
3216     %result = OpFunctionCall %uint %helper %1
3217     OpReturn
3218     OpFunctionEnd
3219  )";
3220   auto p = parser(test::Assemble(assembly));
3221   // This example is invalid because you can't pass pointer-to-Input
3222   // as a function parameter.
3223   EXPECT_FALSE(p->Parse());
3224   EXPECT_THAT(p->error(),
3225               HasSubstr("Invalid storage class for pointer operand 1"));
3226 }
3227 
3228 // Returns the start of a shader for testing LocalInvocationIndex,
3229 // parameterized by store type of %int or %uint
ComputeBuiltinInputPreamble(std::string builtin,std::string store_type)3230 std::string ComputeBuiltinInputPreamble(std::string builtin,
3231                                         std::string store_type) {
3232   return R"(
3233     OpCapability Shader
3234     OpMemoryModel Logical Simple
3235     OpEntryPoint GLCompute %main "main" %1
3236     OpExecutionMode %main LocalSize 1 1 1
3237     OpDecorate %1 BuiltIn )" +
3238          builtin + R"(
3239     %void = OpTypeVoid
3240     %voidfn = OpTypeFunction %void
3241     %float = OpTypeFloat 32
3242     %uint = OpTypeInt 32 0
3243     %int = OpTypeInt 32 1
3244     %v3uint = OpTypeVector %uint 3
3245     %v3int = OpTypeVector %int 3
3246     %ptr_ty = OpTypePointer Input )" +
3247          store_type + R"(
3248     %1 = OpVariable %ptr_ty Input
3249 )";
3250 }
3251 
3252 struct ComputeBuiltinInputCase {
3253   std::string spirv_builtin;
3254   std::string spirv_store_type;
3255   std::string wgsl_builtin;
3256 };
operator <<(std::ostream & o,ComputeBuiltinInputCase c)3257 inline std::ostream& operator<<(std::ostream& o, ComputeBuiltinInputCase c) {
3258   return o << "ComputeBuiltinInputCase(" << c.spirv_builtin << " "
3259            << c.spirv_store_type << " " << c.wgsl_builtin << ")";
3260 }
3261 
WgslType(std::string spirv_type)3262 std::string WgslType(std::string spirv_type) {
3263   if (spirv_type == "%uint") {
3264     return "u32";
3265   }
3266   if (spirv_type == "%int") {
3267     return "i32";
3268   }
3269   if (spirv_type == "%v3uint") {
3270     return "vec3<u32>";
3271   }
3272   if (spirv_type == "%v3int") {
3273     return "vec3<i32>";
3274   }
3275   return "error";
3276 }
3277 
UnsignedWgslType(std::string wgsl_type)3278 std::string UnsignedWgslType(std::string wgsl_type) {
3279   if (wgsl_type == "u32") {
3280     return "u32";
3281   }
3282   if (wgsl_type == "i32") {
3283     return "u32";
3284   }
3285   if (wgsl_type == "vec3<u32>") {
3286     return "vec3<u32>";
3287   }
3288   if (wgsl_type == "vec3<i32>") {
3289     return "vec3<u32>";
3290   }
3291   return "error";
3292 }
3293 
SignedWgslType(std::string wgsl_type)3294 std::string SignedWgslType(std::string wgsl_type) {
3295   if (wgsl_type == "u32") {
3296     return "i32";
3297   }
3298   if (wgsl_type == "i32") {
3299     return "i32";
3300   }
3301   if (wgsl_type == "vec3<u32>") {
3302     return "vec3<i32>";
3303   }
3304   if (wgsl_type == "vec3<i32>") {
3305     return "vec3<i32>";
3306   }
3307   return "error";
3308 }
3309 
3310 using SpvModuleScopeVarParserTest_ComputeBuiltin =
3311     SpvParserTestBase<::testing::TestWithParam<ComputeBuiltinInputCase>>;
3312 
TEST_P(SpvModuleScopeVarParserTest_ComputeBuiltin,Load_Direct)3313 TEST_P(SpvModuleScopeVarParserTest_ComputeBuiltin, Load_Direct) {
3314   const auto wgsl_type = WgslType(GetParam().spirv_store_type);
3315   const auto wgsl_builtin = GetParam().wgsl_builtin;
3316   const auto unsigned_wgsl_type = UnsignedWgslType(wgsl_type);
3317   const auto signed_wgsl_type = SignedWgslType(wgsl_type);
3318   const std::string assembly =
3319       ComputeBuiltinInputPreamble(GetParam().spirv_builtin,
3320                                   GetParam().spirv_store_type) +
3321       R"(
3322     %main = OpFunction %void None %voidfn
3323     %entry = OpLabel
3324     %2 = OpLoad )" +
3325       GetParam().spirv_store_type + R"( %1
3326     OpReturn
3327     OpFunctionEnd
3328  )";
3329   auto p = parser(test::Assemble(assembly));
3330   ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
3331   EXPECT_TRUE(p->error().empty());
3332   const auto module_str = test::ToString(p->program());
3333   std::string expected = R"(var<private> x_1 : ${wgsl_type};
3334 
3335 fn main_1() {
3336   let x_2 : ${wgsl_type} = x_1;
3337   return;
3338 }
3339 
3340 [[stage(compute), workgroup_size(1, 1, 1)]]
3341 fn main([[builtin(${wgsl_builtin})]] x_1_param : ${unsigned_wgsl_type}) {
3342   x_1 = ${assignment_value};
3343   main_1();
3344 }
3345 )";
3346 
3347   expected = utils::ReplaceAll(expected, "${wgsl_type}", wgsl_type);
3348   expected =
3349       utils::ReplaceAll(expected, "${unsigned_wgsl_type}", unsigned_wgsl_type);
3350   expected = utils::ReplaceAll(expected, "${wgsl_builtin}", wgsl_builtin);
3351   expected =
3352       utils::ReplaceAll(expected, "${assignment_value}",
3353                         (wgsl_type == unsigned_wgsl_type)
3354                             ? "x_1_param"
3355                             : "bitcast<" + signed_wgsl_type + ">(x_1_param)");
3356 
3357   EXPECT_EQ(module_str, expected) << module_str;
3358 }
3359 
TEST_P(SpvModuleScopeVarParserTest_ComputeBuiltin,Load_CopyObject)3360 TEST_P(SpvModuleScopeVarParserTest_ComputeBuiltin, Load_CopyObject) {
3361   const auto wgsl_type = WgslType(GetParam().spirv_store_type);
3362   const auto wgsl_builtin = GetParam().wgsl_builtin;
3363   const auto unsigned_wgsl_type = UnsignedWgslType(wgsl_type);
3364   const auto signed_wgsl_type = SignedWgslType(wgsl_type);
3365   const std::string assembly =
3366       ComputeBuiltinInputPreamble(GetParam().spirv_builtin,
3367                                   GetParam().spirv_store_type) +
3368       R"(
3369     %main = OpFunction %void None %voidfn
3370     %entry = OpLabel
3371     %13 = OpCopyObject %ptr_ty %1
3372     %2 = OpLoad )" +
3373       GetParam().spirv_store_type + R"( %13
3374     OpReturn
3375     OpFunctionEnd
3376  )";
3377   auto p = parser(test::Assemble(assembly));
3378   ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
3379   EXPECT_TRUE(p->error().empty());
3380   const auto module_str = test::ToString(p->program());
3381   std::string expected = R"(var<private> x_1 : ${wgsl_type};
3382 
3383 fn main_1() {
3384   let x_13 : ptr<private, ${wgsl_type}> = &(x_1);
3385   let x_2 : ${wgsl_type} = *(x_13);
3386   return;
3387 }
3388 
3389 [[stage(compute), workgroup_size(1, 1, 1)]]
3390 fn main([[builtin(${wgsl_builtin})]] x_1_param : ${unsigned_wgsl_type}) {
3391   x_1 = ${assignment_value};
3392   main_1();
3393 }
3394 )";
3395 
3396   expected = utils::ReplaceAll(expected, "${wgsl_type}", wgsl_type);
3397   expected =
3398       utils::ReplaceAll(expected, "${unsigned_wgsl_type}", unsigned_wgsl_type);
3399   expected = utils::ReplaceAll(expected, "${wgsl_builtin}", wgsl_builtin);
3400   expected =
3401       utils::ReplaceAll(expected, "${assignment_value}",
3402                         (wgsl_type == unsigned_wgsl_type)
3403                             ? "x_1_param"
3404                             : "bitcast<" + signed_wgsl_type + ">(x_1_param)");
3405 
3406   EXPECT_EQ(module_str, expected) << module_str;
3407 }
3408 
TEST_P(SpvModuleScopeVarParserTest_ComputeBuiltin,Load_AccessChain)3409 TEST_P(SpvModuleScopeVarParserTest_ComputeBuiltin, Load_AccessChain) {
3410   const auto wgsl_type = WgslType(GetParam().spirv_store_type);
3411   const auto wgsl_builtin = GetParam().wgsl_builtin;
3412   const auto unsigned_wgsl_type = UnsignedWgslType(wgsl_type);
3413   const auto signed_wgsl_type = SignedWgslType(wgsl_type);
3414   const std::string assembly =
3415       ComputeBuiltinInputPreamble(GetParam().spirv_builtin,
3416                                   GetParam().spirv_store_type) +
3417       R"(
3418     %main = OpFunction %void None %voidfn
3419     %entry = OpLabel
3420     %13 = OpAccessChain %ptr_ty %1
3421     %2 = OpLoad )" +
3422       GetParam().spirv_store_type + R"( %13
3423     OpReturn
3424     OpFunctionEnd
3425  )";
3426   auto p = parser(test::Assemble(assembly));
3427   ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
3428   EXPECT_TRUE(p->error().empty());
3429   const auto module_str = test::ToString(p->program());
3430   std::string expected = R"(var<private> x_1 : ${wgsl_type};
3431 
3432 fn main_1() {
3433   let x_2 : ${wgsl_type} = x_1;
3434   return;
3435 }
3436 
3437 [[stage(compute), workgroup_size(1, 1, 1)]]
3438 fn main([[builtin(${wgsl_builtin})]] x_1_param : ${unsigned_wgsl_type}) {
3439   x_1 = ${assignment_value};
3440   main_1();
3441 }
3442 )";
3443 
3444   expected = utils::ReplaceAll(expected, "${wgsl_type}", wgsl_type);
3445   expected =
3446       utils::ReplaceAll(expected, "${unsigned_wgsl_type}", unsigned_wgsl_type);
3447   expected = utils::ReplaceAll(expected, "${wgsl_builtin}", wgsl_builtin);
3448   expected =
3449       utils::ReplaceAll(expected, "${assignment_value}",
3450                         (wgsl_type == unsigned_wgsl_type)
3451                             ? "x_1_param"
3452                             : "bitcast<" + signed_wgsl_type + ">(x_1_param)");
3453 
3454   EXPECT_EQ(module_str, expected) << module_str;
3455 }
3456 
3457 INSTANTIATE_TEST_SUITE_P(
3458     Samples,
3459     SpvModuleScopeVarParserTest_ComputeBuiltin,
3460     ::testing::ValuesIn(std::vector<ComputeBuiltinInputCase>{
3461         {"LocalInvocationIndex", "%uint", "local_invocation_index"},
3462         {"LocalInvocationIndex", "%int", "local_invocation_index"},
3463         {"LocalInvocationId", "%v3uint", "local_invocation_id"},
3464         {"LocalInvocationId", "%v3int", "local_invocation_id"},
3465         {"GlobalInvocationId", "%v3uint", "global_invocation_id"},
3466         {"GlobalInvocationId", "%v3int", "global_invocation_id"},
3467         {"WorkgroupId", "%v3uint", "workgroup_id"},
3468         {"WorkgroupId", "%v3int", "workgroup_id"}}));
3469 
3470 // TODO(dneto): crbug.com/tint/752
3471 // NumWorkgroups support is blocked by crbug.com/tint/752
3472 // When the AST supports NumWorkgroups, add these cases:
3473 //        {"NumWorkgroups", "%uint", "num_workgroups"}
3474 //        {"NumWorkgroups", "%int", "num_workgroups"}
3475 
TEST_F(SpvModuleScopeVarParserTest,RegisterInputOutputVars)3476 TEST_F(SpvModuleScopeVarParserTest, RegisterInputOutputVars) {
3477   const std::string assembly =
3478       R"(
3479     OpCapability Shader
3480     OpMemoryModel Logical Simple
3481     OpEntryPoint Fragment %1000 "w1000"
3482     OpEntryPoint Fragment %1100 "w1100" %1
3483     OpEntryPoint Fragment %1200 "w1200" %2 %15
3484     ; duplication is tolerated prior to SPIR-V 1.4
3485     OpEntryPoint Fragment %1300 "w1300" %1 %15 %2 %1
3486     OpExecutionMode %1000 OriginUpperLeft
3487     OpExecutionMode %1100 OriginUpperLeft
3488     OpExecutionMode %1200 OriginUpperLeft
3489     OpExecutionMode %1300 OriginUpperLeft
3490 
3491     OpDecorate %1 Location 1
3492     OpDecorate %2 Location 2
3493     OpDecorate %5 Location 5
3494     OpDecorate %11 Location 1
3495     OpDecorate %12 Location 2
3496     OpDecorate %15 Location 5
3497 
3498 )" + CommonTypes() +
3499       R"(
3500 
3501     %ptr_in_uint = OpTypePointer Input %uint
3502     %ptr_out_uint = OpTypePointer Output %uint
3503 
3504     %1 = OpVariable %ptr_in_uint Input
3505     %2 = OpVariable %ptr_in_uint Input
3506     %5 = OpVariable %ptr_in_uint Input
3507     %11 = OpVariable %ptr_out_uint Output
3508     %12 = OpVariable %ptr_out_uint Output
3509     %15 = OpVariable %ptr_out_uint Output
3510 
3511     %100 = OpFunction %void None %voidfn
3512     %entry_100 = OpLabel
3513     %load_100 = OpLoad %uint %1
3514     OpReturn
3515     OpFunctionEnd
3516 
3517     %200 = OpFunction %void None %voidfn
3518     %entry_200 = OpLabel
3519     %load_200 = OpLoad %uint %2
3520     OpStore %15 %load_200
3521     OpStore %15 %load_200
3522     OpReturn
3523     OpFunctionEnd
3524 
3525     %300 = OpFunction %void None %voidfn
3526     %entry_300 = OpLabel
3527     %dummy_300_1 = OpFunctionCall %void %100
3528     %dummy_300_2 = OpFunctionCall %void %200
3529     OpReturn
3530     OpFunctionEnd
3531 
3532     ; Call nothing
3533     %1000 = OpFunction %void None %voidfn
3534     %entry_1000 = OpLabel
3535     OpReturn
3536     OpFunctionEnd
3537 
3538     ; Call %100
3539     %1100 = OpFunction %void None %voidfn
3540     %entry_1100 = OpLabel
3541     %dummy_1100_1 = OpFunctionCall %void %100
3542     OpReturn
3543     OpFunctionEnd
3544 
3545     ; Call %200
3546     %1200 = OpFunction %void None %voidfn
3547     %entry_1200 = OpLabel
3548     %dummy_1200_1 = OpFunctionCall %void %200
3549     OpReturn
3550     OpFunctionEnd
3551 
3552     ; Call %300
3553     %1300 = OpFunction %void None %voidfn
3554     %entry_1300 = OpLabel
3555     %dummy_1300_1 = OpFunctionCall %void %300
3556     OpReturn
3557     OpFunctionEnd
3558 
3559  )";
3560   auto p = parser(test::Assemble(assembly));
3561   ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
3562   EXPECT_TRUE(p->error().empty());
3563 
3564   const auto& info_1000 = p->GetEntryPointInfo(1000);
3565   EXPECT_EQ(1u, info_1000.size());
3566   EXPECT_TRUE(info_1000[0].inputs.empty());
3567   EXPECT_TRUE(info_1000[0].outputs.empty());
3568 
3569   const auto& info_1100 = p->GetEntryPointInfo(1100);
3570   EXPECT_EQ(1u, info_1100.size());
3571   EXPECT_THAT(info_1100[0].inputs, ElementsAre(1));
3572   EXPECT_TRUE(info_1100[0].outputs.empty());
3573 
3574   const auto& info_1200 = p->GetEntryPointInfo(1200);
3575   EXPECT_EQ(1u, info_1200.size());
3576   EXPECT_THAT(info_1200[0].inputs, ElementsAre(2));
3577   EXPECT_THAT(info_1200[0].outputs, ElementsAre(15));
3578 
3579   const auto& info_1300 = p->GetEntryPointInfo(1300);
3580   EXPECT_EQ(1u, info_1300.size());
3581   EXPECT_THAT(info_1300[0].inputs, ElementsAre(1, 2));
3582   EXPECT_THAT(info_1300[0].outputs, ElementsAre(15));
3583 
3584   // Validation incorrectly reports an overlap for the duplicated variable %1 on
3585   // shader %1300
3586   p->SkipDumpingPending(
3587       "https://github.com/KhronosGroup/SPIRV-Tools/issues/4403");
3588 }
3589 
TEST_F(SpvModuleScopeVarParserTest,InputVarsConvertedToPrivate)3590 TEST_F(SpvModuleScopeVarParserTest, InputVarsConvertedToPrivate) {
3591   const auto assembly = Preamble() + FragMain() + CommonTypes() + R"(
3592      %ptr_in_uint = OpTypePointer Input %uint
3593      %1 = OpVariable %ptr_in_uint Input
3594   )" + MainBody();
3595   auto p = parser(test::Assemble(assembly));
3596 
3597   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
3598   EXPECT_TRUE(p->error().empty());
3599   const auto got = test::ToString(p->program());
3600   const std::string expected = "var<private> x_1 : u32;";
3601   EXPECT_THAT(got, HasSubstr(expected)) << got;
3602 }
3603 
TEST_F(SpvModuleScopeVarParserTest,OutputVarsConvertedToPrivate)3604 TEST_F(SpvModuleScopeVarParserTest, OutputVarsConvertedToPrivate) {
3605   const auto assembly = Preamble() + FragMain() + CommonTypes() + R"(
3606      %ptr_out_uint = OpTypePointer Output %uint
3607      %1 = OpVariable %ptr_out_uint Output
3608   )" + MainBody();
3609   auto p = parser(test::Assemble(assembly));
3610 
3611   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
3612   EXPECT_TRUE(p->error().empty());
3613   const auto got = test::ToString(p->program());
3614   const std::string expected = "var<private> x_1 : u32;";
3615   EXPECT_THAT(got, HasSubstr(expected)) << got;
3616 }
3617 
TEST_F(SpvModuleScopeVarParserTest,OutputVarsConvertedToPrivate_WithInitializer)3618 TEST_F(SpvModuleScopeVarParserTest,
3619        OutputVarsConvertedToPrivate_WithInitializer) {
3620   const auto assembly = Preamble() + FragMain() + CommonTypes() + R"(
3621      %ptr_out_uint = OpTypePointer Output %uint
3622      %1 = OpVariable %ptr_out_uint Output %uint_1
3623   )" + MainBody();
3624   auto p = parser(test::Assemble(assembly));
3625 
3626   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
3627   EXPECT_TRUE(p->error().empty());
3628   const auto got = test::ToString(p->program());
3629   const std::string expected = "var<private> x_1 : u32 = 1u;";
3630   EXPECT_THAT(got, HasSubstr(expected)) << got;
3631 }
3632 
TEST_F(SpvModuleScopeVarParserTest,Builtin_Output_Initializer_SameSignednessAsWGSL)3633 TEST_F(SpvModuleScopeVarParserTest,
3634        Builtin_Output_Initializer_SameSignednessAsWGSL) {
3635   // Only outputs can have initializers.
3636   // WGSL sample_mask store type is u32.
3637   const auto assembly = Preamble() + FragMain() + R"(
3638      OpDecorate %1 BuiltIn SampleMask
3639 )" + CommonTypes() + R"(
3640      %arr_ty = OpTypeArray %uint %uint_1
3641      %ptr_ty = OpTypePointer Output %arr_ty
3642      %arr_init = OpConstantComposite %arr_ty %uint_2
3643      %1 = OpVariable %ptr_ty Output %arr_init
3644   )" + MainBody();
3645   auto p = parser(test::Assemble(assembly));
3646 
3647   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
3648   EXPECT_TRUE(p->error().empty());
3649   const auto got = test::ToString(p->program());
3650   const std::string expected =
3651       "var<private> x_1 : array<u32, 1u> = array<u32, 1u>(2u);";
3652   EXPECT_THAT(got, HasSubstr(expected)) << got;
3653 }
3654 
TEST_F(SpvModuleScopeVarParserTest,Builtin_Output_Initializer_OppositeSignednessAsWGSL)3655 TEST_F(SpvModuleScopeVarParserTest,
3656        Builtin_Output_Initializer_OppositeSignednessAsWGSL) {
3657   // Only outputs can have initializers.
3658   // WGSL sample_mask store type is u32.  Use i32 in SPIR-V
3659   const auto assembly = Preamble() + FragMain() + R"(
3660      OpDecorate %1 BuiltIn SampleMask
3661 )" + CommonTypes() + R"(
3662      %arr_ty = OpTypeArray %int %uint_1
3663      %ptr_ty = OpTypePointer Output %arr_ty
3664      %arr_init = OpConstantComposite %arr_ty %int_14
3665      %1 = OpVariable %ptr_ty Output %arr_init
3666   )" + MainBody();
3667   auto p = parser(test::Assemble(assembly));
3668 
3669   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
3670   EXPECT_TRUE(p->error().empty());
3671   const auto got = test::ToString(p->program());
3672   const std::string expected =
3673       "var<private> x_1 : array<i32, 1u> = array<i32, 1u>(14);";
3674   EXPECT_THAT(got, HasSubstr(expected)) << got;
3675 }
3676 
TEST_F(SpvModuleScopeVarParserTest,Builtin_Input_SameSignednessAsWGSL)3677 TEST_F(SpvModuleScopeVarParserTest, Builtin_Input_SameSignednessAsWGSL) {
3678   // WGSL vertex_index store type is u32.
3679   const auto assembly = Preamble() + FragMain() + R"(
3680      OpDecorate %1 BuiltIn VertexIndex
3681 )" + CommonTypes() + R"(
3682      %ptr_ty = OpTypePointer Input %uint
3683      %1 = OpVariable %ptr_ty Input
3684   )" + MainBody();
3685   auto p = parser(test::Assemble(assembly));
3686 
3687   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
3688   EXPECT_TRUE(p->error().empty());
3689   const auto got = test::ToString(p->program());
3690   const std::string expected = "var<private> x_1 : u32;";
3691   EXPECT_THAT(got, HasSubstr(expected)) << got;
3692 }
3693 
TEST_F(SpvModuleScopeVarParserTest,Builtin_Input_OppositeSignednessAsWGSL)3694 TEST_F(SpvModuleScopeVarParserTest, Builtin_Input_OppositeSignednessAsWGSL) {
3695   // WGSL vertex_index store type is u32.  Use i32 in SPIR-V.
3696   const auto assembly = Preamble() + FragMain() + R"(
3697      OpDecorate %1 BuiltIn VertexIndex
3698 )" + CommonTypes() + R"(
3699      %ptr_ty = OpTypePointer Input %int
3700      %1 = OpVariable %ptr_ty Input
3701   )" + MainBody();
3702   auto p = parser(test::Assemble(assembly));
3703 
3704   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
3705   EXPECT_TRUE(p->error().empty());
3706   const auto got = test::ToString(p->program());
3707   const std::string expected = "var<private> x_1 : i32;";
3708   EXPECT_THAT(got, HasSubstr(expected)) << got;
3709 }
3710 
TEST_F(SpvModuleScopeVarParserTest,EntryPointWrapping_IOLocations)3711 TEST_F(SpvModuleScopeVarParserTest, EntryPointWrapping_IOLocations) {
3712   const auto assembly = CommonCapabilities() + R"(
3713      OpEntryPoint Fragment %main "main" %1 %2 %3 %4
3714      OpExecutionMode %main OriginUpperLeft
3715      OpDecorate %1 Location 0
3716      OpDecorate %2 Location 0
3717      OpDecorate %3 Location 30
3718      OpDecorate %4 Location 6
3719 )" + CommonTypes() +
3720                         R"(
3721      %ptr_in_uint = OpTypePointer Input %uint
3722      %ptr_out_uint = OpTypePointer Output %uint
3723      %1 = OpVariable %ptr_in_uint Input
3724      %2 = OpVariable %ptr_out_uint Output
3725      %3 = OpVariable %ptr_in_uint Input
3726      %4 = OpVariable %ptr_out_uint Output
3727 
3728      %main = OpFunction %void None %voidfn
3729      %entry = OpLabel
3730      OpReturn
3731      OpFunctionEnd
3732   )";
3733   auto p = parser(test::Assemble(assembly));
3734 
3735   ASSERT_TRUE(p->BuildAndParseInternalModule());
3736   EXPECT_TRUE(p->error().empty());
3737   const auto got = test::ToString(p->program());
3738   const std::string expected =
3739       R"(var<private> x_1 : u32;
3740 
3741 var<private> x_2 : u32;
3742 
3743 var<private> x_3 : u32;
3744 
3745 var<private> x_4 : u32;
3746 
3747 fn main_1() {
3748   return;
3749 }
3750 
3751 struct main_out {
3752   [[location(0)]]
3753   x_2_1 : u32;
3754   [[location(6)]]
3755   x_4_1 : u32;
3756 };
3757 
3758 [[stage(fragment)]]
3759 fn main([[location(0)]] x_1_param : u32, [[location(30)]] x_3_param : u32) -> main_out {
3760   x_1 = x_1_param;
3761   x_3 = x_3_param;
3762   main_1();
3763   return main_out(x_2, x_4);
3764 }
3765 )";
3766   EXPECT_THAT(got, HasSubstr(expected)) << got;
3767 }
3768 
TEST_F(SpvModuleScopeVarParserTest,EntryPointWrapping_BuiltinVar_Input_SameSignedness)3769 TEST_F(SpvModuleScopeVarParserTest,
3770        EntryPointWrapping_BuiltinVar_Input_SameSignedness) {
3771   // instance_index is u32 in WGSL. Use uint in SPIR-V.
3772   // No bitcasts are used for parameter formation or return value.
3773   const auto assembly = CommonCapabilities() + R"(
3774      OpEntryPoint Vertex %main "main" %1 %position
3775      OpDecorate %position BuiltIn Position
3776      OpDecorate %1 BuiltIn InstanceIndex
3777 )" + CommonTypes() +
3778                         R"(
3779      %ptr_in_uint = OpTypePointer Input %uint
3780      %1 = OpVariable %ptr_in_uint Input
3781      %posty = OpTypePointer Output %v4float
3782      %position = OpVariable %posty Output
3783 
3784      %main = OpFunction %void None %voidfn
3785      %entry = OpLabel
3786      %2 = OpLoad %uint %1 ; load same signedness
3787      ;;;; %3 = OpLoad %int %1 ; loading different signedness is invalid.
3788      OpReturn
3789      OpFunctionEnd
3790   )";
3791   auto p = parser(test::Assemble(assembly));
3792 
3793   ASSERT_TRUE(p->Parse()) << p->error() << assembly;
3794   EXPECT_TRUE(p->error().empty());
3795   const auto got = test::ToString(p->program());
3796   const std::string expected = R"(var<private> x_1 : u32;
3797 
3798 var<private> x_4 : vec4<f32>;
3799 
3800 fn main_1() {
3801   let x_2 : u32 = x_1;
3802   return;
3803 }
3804 
3805 struct main_out {
3806   [[builtin(position)]]
3807   x_4_1 : vec4<f32>;
3808 };
3809 
3810 [[stage(vertex)]]
3811 fn main([[builtin(instance_index)]] x_1_param : u32) -> main_out {
3812   x_1 = x_1_param;
3813   main_1();
3814   return main_out(x_4);
3815 }
3816 )";
3817   EXPECT_EQ(got, expected) << got;
3818 }
3819 
TEST_F(SpvModuleScopeVarParserTest,EntryPointWrapping_BuiltinVar_Input_OppositeSignedness)3820 TEST_F(SpvModuleScopeVarParserTest,
3821        EntryPointWrapping_BuiltinVar_Input_OppositeSignedness) {
3822   // instance_index is u32 in WGSL. Use int in SPIR-V.
3823   const auto assembly = CommonCapabilities() + R"(
3824      OpEntryPoint Vertex %main "main" %position %1
3825      OpDecorate %position BuiltIn Position
3826      OpDecorate %1 BuiltIn InstanceIndex
3827 )" + CommonTypes() +
3828                         R"(
3829      %ptr_in_int = OpTypePointer Input %int
3830      %1 = OpVariable %ptr_in_int Input
3831      %posty = OpTypePointer Output %v4float
3832      %position = OpVariable %posty Output
3833 
3834      %main = OpFunction %void None %voidfn
3835      %entry = OpLabel
3836      %2 = OpLoad %int %1 ; load same signedness
3837      ;;; %3 = OpLoad %uint %1 ; loading different signedness is invalid
3838      OpReturn
3839      OpFunctionEnd
3840   )";
3841   auto p = parser(test::Assemble(assembly));
3842 
3843   ASSERT_TRUE(p->Parse()) << p->error() << assembly;
3844   EXPECT_TRUE(p->error().empty());
3845   const auto got = test::ToString(p->program());
3846   const std::string expected = R"(var<private> x_1 : i32;
3847 
3848 var<private> x_4 : vec4<f32>;
3849 
3850 fn main_1() {
3851   let x_2 : i32 = x_1;
3852   return;
3853 }
3854 
3855 struct main_out {
3856   [[builtin(position)]]
3857   x_4_1 : vec4<f32>;
3858 };
3859 
3860 [[stage(vertex)]]
3861 fn main([[builtin(instance_index)]] x_1_param : u32) -> main_out {
3862   x_1 = bitcast<i32>(x_1_param);
3863   main_1();
3864   return main_out(x_4);
3865 }
3866 )";
3867   EXPECT_EQ(got, expected) << got;
3868 }
3869 
3870 // SampleMask is an array in Vulkan SPIR-V, but a scalar in WGSL.
TEST_F(SpvModuleScopeVarParserTest,EntryPointWrapping_BuiltinVar_SampleMask_In_Unsigned)3871 TEST_F(SpvModuleScopeVarParserTest,
3872        EntryPointWrapping_BuiltinVar_SampleMask_In_Unsigned) {
3873   // SampleMask is u32 in WGSL.
3874   // Use unsigned array element in Vulkan.
3875   const auto assembly = CommonCapabilities() + R"(
3876      OpEntryPoint Fragment %main "main" %1
3877      OpExecutionMode %main OriginUpperLeft
3878      OpDecorate %1 BuiltIn SampleMask
3879 )" + CommonTypes() +
3880                         R"(
3881      %arr = OpTypeArray %uint %uint_1
3882      %ptr_ty = OpTypePointer Input %arr
3883      %1 = OpVariable %ptr_ty Input
3884 
3885      %main = OpFunction %void None %voidfn
3886      %entry = OpLabel
3887      OpReturn
3888      OpFunctionEnd
3889   )";
3890   auto p = parser(test::Assemble(assembly));
3891 
3892   ASSERT_TRUE(p->Parse()) << p->error() << assembly;
3893   EXPECT_TRUE(p->error().empty());
3894   const auto got = test::ToString(p->program());
3895   const std::string expected = R"(var<private> x_1 : array<u32, 1u>;
3896 
3897 fn main_1() {
3898   return;
3899 }
3900 
3901 [[stage(fragment)]]
3902 fn main([[builtin(sample_mask)]] x_1_param : u32) {
3903   x_1[0] = x_1_param;
3904   main_1();
3905 }
3906 )";
3907   EXPECT_EQ(got, expected) << got;
3908 }
3909 
TEST_F(SpvModuleScopeVarParserTest,EntryPointWrapping_BuiltinVar_SampleMask_In_Signed)3910 TEST_F(SpvModuleScopeVarParserTest,
3911        EntryPointWrapping_BuiltinVar_SampleMask_In_Signed) {
3912   // SampleMask is u32 in WGSL.
3913   // Use signed array element in Vulkan.
3914   const auto assembly = CommonCapabilities() + R"(
3915      OpEntryPoint Fragment %main "main" %1
3916      OpExecutionMode %main OriginUpperLeft
3917      OpDecorate %1 BuiltIn SampleMask
3918 )" + CommonTypes() +
3919                         R"(
3920      %arr = OpTypeArray %int %uint_1
3921      %ptr_ty = OpTypePointer Input %arr
3922      %1 = OpVariable %ptr_ty Input
3923 
3924      %main = OpFunction %void None %voidfn
3925      %entry = OpLabel
3926      OpReturn
3927      OpFunctionEnd
3928   )";
3929   auto p = parser(test::Assemble(assembly));
3930 
3931   ASSERT_TRUE(p->Parse()) << p->error() << assembly;
3932   EXPECT_TRUE(p->error().empty());
3933   const auto got = test::ToString(p->program());
3934   const std::string expected = R"(var<private> x_1 : array<i32, 1u>;
3935 
3936 fn main_1() {
3937   return;
3938 }
3939 
3940 [[stage(fragment)]]
3941 fn main([[builtin(sample_mask)]] x_1_param : u32) {
3942   x_1[0] = bitcast<i32>(x_1_param);
3943   main_1();
3944 }
3945 )";
3946   EXPECT_EQ(got, expected) << got;
3947 }
3948 
TEST_F(SpvModuleScopeVarParserTest,EntryPointWrapping_BuiltinVar_SampleMask_Out_Unsigned_Initializer)3949 TEST_F(SpvModuleScopeVarParserTest,
3950        EntryPointWrapping_BuiltinVar_SampleMask_Out_Unsigned_Initializer) {
3951   // SampleMask is u32 in WGSL.
3952   // Use unsigned array element in Vulkan.
3953   const auto assembly = CommonCapabilities() + R"(
3954      OpEntryPoint Fragment %main "main" %1
3955      OpExecutionMode %main OriginUpperLeft
3956      OpDecorate %1 BuiltIn SampleMask
3957 )" + CommonTypes() +
3958                         R"(
3959      %arr = OpTypeArray %uint %uint_1
3960      %ptr_ty = OpTypePointer Output %arr
3961      %zero = OpConstantNull %arr
3962      %1 = OpVariable %ptr_ty Output %zero
3963 
3964      %main = OpFunction %void None %voidfn
3965      %entry = OpLabel
3966      OpReturn
3967      OpFunctionEnd
3968   )";
3969   auto p = parser(test::Assemble(assembly));
3970 
3971   ASSERT_TRUE(p->Parse()) << p->error() << assembly;
3972   EXPECT_TRUE(p->error().empty());
3973   const auto got = test::ToString(p->program());
3974   const std::string expected =
3975       R"(var<private> x_1 : array<u32, 1u> = array<u32, 1u>(0u);
3976 
3977 fn main_1() {
3978   return;
3979 }
3980 
3981 struct main_out {
3982   [[builtin(sample_mask)]]
3983   x_1_1 : u32;
3984 };
3985 
3986 [[stage(fragment)]]
3987 fn main() -> main_out {
3988   main_1();
3989   return main_out(x_1[0]);
3990 }
3991 )";
3992   EXPECT_EQ(got, expected) << got;
3993 }
3994 
TEST_F(SpvModuleScopeVarParserTest,EntryPointWrapping_BuiltinVar_SampleMask_Out_Signed_Initializer)3995 TEST_F(SpvModuleScopeVarParserTest,
3996        EntryPointWrapping_BuiltinVar_SampleMask_Out_Signed_Initializer) {
3997   // SampleMask is u32 in WGSL.
3998   // Use signed array element in Vulkan.
3999   const auto assembly = CommonCapabilities() + R"(
4000      OpEntryPoint Fragment %main "main" %1
4001      OpExecutionMode %main OriginUpperLeft
4002      OpDecorate %1 BuiltIn SampleMask
4003 )" + CommonTypes() +
4004                         R"(
4005      %arr = OpTypeArray %int %uint_1
4006      %ptr_ty = OpTypePointer Output %arr
4007      %zero = OpConstantNull %arr
4008      %1 = OpVariable %ptr_ty Output %zero
4009 
4010      %main = OpFunction %void None %voidfn
4011      %entry = OpLabel
4012      OpReturn
4013      OpFunctionEnd
4014   )";
4015   auto p = parser(test::Assemble(assembly));
4016 
4017   ASSERT_TRUE(p->Parse()) << p->error() << assembly;
4018   EXPECT_TRUE(p->error().empty());
4019   const auto got = test::ToString(p->program());
4020   const std::string expected =
4021       R"(var<private> x_1 : array<i32, 1u> = array<i32, 1u>(0);
4022 
4023 fn main_1() {
4024   return;
4025 }
4026 
4027 struct main_out {
4028   [[builtin(sample_mask)]]
4029   x_1_1 : u32;
4030 };
4031 
4032 [[stage(fragment)]]
4033 fn main() -> main_out {
4034   main_1();
4035   return main_out(bitcast<u32>(x_1[0]));
4036 }
4037 )";
4038   EXPECT_EQ(got, expected) << got;
4039 }
4040 
TEST_F(SpvModuleScopeVarParserTest,EntryPointWrapping_BuiltinVar_FragDepth_Out_Initializer)4041 TEST_F(SpvModuleScopeVarParserTest,
4042        EntryPointWrapping_BuiltinVar_FragDepth_Out_Initializer) {
4043   // FragDepth does not require conversion, because it's f32.
4044   // The member of the return type is just the identifier corresponding
4045   // to the module-scope private variable.
4046   const auto assembly = CommonCapabilities() + R"(
4047      OpEntryPoint Fragment %main "main" %1
4048      OpExecutionMode %main OriginUpperLeft
4049      OpDecorate %1 BuiltIn FragDepth
4050 )" + CommonTypes() +
4051                         R"(
4052      %ptr_ty = OpTypePointer Output %float
4053      %1 = OpVariable %ptr_ty Output %float_0
4054 
4055      %main = OpFunction %void None %voidfn
4056      %entry = OpLabel
4057      OpReturn
4058      OpFunctionEnd
4059   )";
4060   auto p = parser(test::Assemble(assembly));
4061 
4062   ASSERT_TRUE(p->Parse()) << p->error() << assembly;
4063   EXPECT_TRUE(p->error().empty());
4064   const auto got = test::ToString(p->program());
4065   const std::string expected = R"(var<private> x_1 : f32 = 0.0;
4066 
4067 fn main_1() {
4068   return;
4069 }
4070 
4071 struct main_out {
4072   [[builtin(frag_depth)]]
4073   x_1_1 : f32;
4074 };
4075 
4076 [[stage(fragment)]]
4077 fn main() -> main_out {
4078   main_1();
4079   return main_out(x_1);
4080 }
4081 )";
4082   EXPECT_EQ(got, expected) << got;
4083 }
4084 
TEST_F(SpvModuleScopeVarParserTest,BuiltinPosition_BuiltIn_Position)4085 TEST_F(SpvModuleScopeVarParserTest, BuiltinPosition_BuiltIn_Position) {
4086   // In Vulkan SPIR-V, Position is the first member of gl_PerVertex
4087   const std::string assembly = PerVertexPreamble() + R"(
4088   %main = OpFunction %void None %voidfn
4089   %entry = OpLabel
4090   OpReturn
4091   OpFunctionEnd
4092 )";
4093   auto p = parser(test::Assemble(assembly));
4094 
4095   ASSERT_TRUE(p->Parse()) << p->error() << assembly;
4096   EXPECT_TRUE(p->error().empty());
4097 
4098   const auto got = test::ToString(p->program());
4099   const std::string expected = R"(var<private> gl_Position : vec4<f32>;
4100 
4101 fn main_1() {
4102   return;
4103 }
4104 
4105 struct main_out {
4106   [[builtin(position)]]
4107   gl_Position : vec4<f32>;
4108 };
4109 
4110 [[stage(vertex)]]
4111 fn main() -> main_out {
4112   main_1();
4113   return main_out(gl_Position);
4114 }
4115 )";
4116   EXPECT_EQ(got, expected) << got;
4117 }
4118 
TEST_F(SpvModuleScopeVarParserTest,BuiltinPosition_BuiltIn_Position_Initializer)4119 TEST_F(SpvModuleScopeVarParserTest,
4120        BuiltinPosition_BuiltIn_Position_Initializer) {
4121   const std::string assembly = R"(
4122     OpCapability Shader
4123     OpMemoryModel Logical Simple
4124     OpEntryPoint Vertex %main "main" %1
4125 
4126     OpMemberDecorate %10 0 BuiltIn Position
4127     OpMemberDecorate %10 1 BuiltIn PointSize
4128     OpMemberDecorate %10 2 BuiltIn ClipDistance
4129     OpMemberDecorate %10 3 BuiltIn CullDistance
4130     %void = OpTypeVoid
4131     %voidfn = OpTypeFunction %void
4132     %float = OpTypeFloat 32
4133     %v4float = OpTypeVector %float 4
4134     %uint = OpTypeInt 32 0
4135     %uint_0 = OpConstant %uint 0
4136     %uint_1 = OpConstant %uint 1
4137     %arr = OpTypeArray %float %uint_1
4138     %10 = OpTypeStruct %v4float %float %arr %arr
4139     %11 = OpTypePointer Output %10
4140 
4141     %float_1 = OpConstant %float 1
4142     %float_2 = OpConstant %float 2
4143     %float_3 = OpConstant %float 3
4144     %float_4 = OpConstant %float 4
4145     %float_5 = OpConstant %float 5
4146     %float_6 = OpConstant %float 6
4147     %float_7 = OpConstant %float 7
4148 
4149     %init_pos = OpConstantComposite %v4float %float_1 %float_2 %float_3 %float_4
4150     %init_clip = OpConstantComposite %arr %float_6
4151     %init_cull = OpConstantComposite %arr %float_7
4152     %init_per_vertex = OpConstantComposite %10 %init_pos %float_5 %init_clip %init_cull
4153 
4154     %1 = OpVariable %11 Output %init_per_vertex
4155 
4156     %main = OpFunction %void None %voidfn
4157     %entry = OpLabel
4158     OpReturn
4159     OpFunctionEnd
4160 )";
4161   auto p = parser(test::Assemble(assembly));
4162 
4163   ASSERT_TRUE(p->Parse()) << p->error() << assembly;
4164   EXPECT_TRUE(p->error().empty());
4165 
4166   const auto got = test::ToString(p->program());
4167   const std::string expected =
4168       R"(var<private> gl_Position : vec4<f32> = vec4<f32>(1.0, 2.0, 3.0, 4.0);
4169 
4170 fn main_1() {
4171   return;
4172 }
4173 
4174 struct main_out {
4175   [[builtin(position)]]
4176   gl_Position : vec4<f32>;
4177 };
4178 
4179 [[stage(vertex)]]
4180 fn main() -> main_out {
4181   main_1();
4182   return main_out(gl_Position);
4183 }
4184 )";
4185   EXPECT_EQ(got, expected) << got;
4186 }
4187 
TEST_F(SpvModuleScopeVarParserTest,Input_FlattenArray_OneLevel)4188 TEST_F(SpvModuleScopeVarParserTest, Input_FlattenArray_OneLevel) {
4189   const std::string assembly = R"(
4190     OpCapability Shader
4191     OpMemoryModel Logical Simple
4192     OpEntryPoint Vertex %main "main" %1 %2
4193     OpDecorate %1 Location 4
4194     OpDecorate %2 BuiltIn Position
4195 
4196     %void = OpTypeVoid
4197     %voidfn = OpTypeFunction %void
4198     %float = OpTypeFloat 32
4199     %v4float = OpTypeVector %float 4
4200     %uint = OpTypeInt 32 0
4201     %uint_0 = OpConstant %uint 0
4202     %uint_1 = OpConstant %uint 1
4203     %uint_3 = OpConstant %uint 3
4204     %arr = OpTypeArray %float %uint_3
4205     %11 = OpTypePointer Input %arr
4206 
4207     %1 = OpVariable %11 Input
4208 
4209     %12 = OpTypePointer Output %v4float
4210     %2 = OpVariable %12 Output
4211 
4212     %main = OpFunction %void None %voidfn
4213     %entry = OpLabel
4214     OpReturn
4215     OpFunctionEnd
4216 )";
4217   auto p = parser(test::Assemble(assembly));
4218 
4219   ASSERT_TRUE(p->Parse()) << p->error() << assembly;
4220   EXPECT_TRUE(p->error().empty());
4221 
4222   const auto got = test::ToString(p->program());
4223   const std::string expected = R"(var<private> x_1 : array<f32, 3u>;
4224 
4225 var<private> x_2 : vec4<f32>;
4226 
4227 fn main_1() {
4228   return;
4229 }
4230 
4231 struct main_out {
4232   [[builtin(position)]]
4233   x_2_1 : vec4<f32>;
4234 };
4235 
4236 [[stage(vertex)]]
4237 fn main([[location(4)]] x_1_param : f32, [[location(5)]] x_1_param_1 : f32, [[location(6)]] x_1_param_2 : f32) -> main_out {
4238   x_1[0] = x_1_param;
4239   x_1[1] = x_1_param_1;
4240   x_1[2] = x_1_param_2;
4241   main_1();
4242   return main_out(x_2);
4243 }
4244 )";
4245   EXPECT_EQ(got, expected) << got;
4246 }
4247 
TEST_F(SpvModuleScopeVarParserTest,Input_FlattenMatrix)4248 TEST_F(SpvModuleScopeVarParserTest, Input_FlattenMatrix) {
4249   const std::string assembly = R"(
4250     OpCapability Shader
4251     OpMemoryModel Logical Simple
4252     OpEntryPoint Vertex %main "main" %1 %2
4253     OpDecorate %1 Location 9
4254     OpDecorate %2 BuiltIn Position
4255 
4256     %void = OpTypeVoid
4257     %voidfn = OpTypeFunction %void
4258     %float = OpTypeFloat 32
4259     %v4float = OpTypeVector %float 4
4260     %m2v4float = OpTypeMatrix %v4float 2
4261     %uint = OpTypeInt 32 0
4262 
4263     %11 = OpTypePointer Input %m2v4float
4264 
4265     %1 = OpVariable %11 Input
4266 
4267     %12 = OpTypePointer Output %v4float
4268     %2 = OpVariable %12 Output
4269 
4270     %main = OpFunction %void None %voidfn
4271     %entry = OpLabel
4272     OpReturn
4273     OpFunctionEnd
4274 )";
4275   auto p = parser(test::Assemble(assembly));
4276 
4277   ASSERT_TRUE(p->Parse()) << p->error() << assembly;
4278   EXPECT_TRUE(p->error().empty());
4279 
4280   const auto got = test::ToString(p->program());
4281   const std::string expected = R"(var<private> x_1 : mat2x4<f32>;
4282 
4283 var<private> x_2 : vec4<f32>;
4284 
4285 fn main_1() {
4286   return;
4287 }
4288 
4289 struct main_out {
4290   [[builtin(position)]]
4291   x_2_1 : vec4<f32>;
4292 };
4293 
4294 [[stage(vertex)]]
4295 fn main([[location(9)]] x_1_param : vec4<f32>, [[location(10)]] x_1_param_1 : vec4<f32>) -> main_out {
4296   x_1[0] = x_1_param;
4297   x_1[1] = x_1_param_1;
4298   main_1();
4299   return main_out(x_2);
4300 }
4301 )";
4302   EXPECT_EQ(got, expected) << got;
4303 }
4304 
TEST_F(SpvModuleScopeVarParserTest,Input_FlattenStruct_LocOnVariable)4305 TEST_F(SpvModuleScopeVarParserTest, Input_FlattenStruct_LocOnVariable) {
4306   const std::string assembly = R"(
4307     OpCapability Shader
4308     OpMemoryModel Logical Simple
4309     OpEntryPoint Vertex %main "main" %1 %2
4310 
4311     OpName %strct "Communicators"
4312     OpMemberName %strct 0 "alice"
4313     OpMemberName %strct 1 "bob"
4314 
4315     OpDecorate %1 Location 9
4316     OpDecorate %2 BuiltIn Position
4317 
4318 
4319     %void = OpTypeVoid
4320     %voidfn = OpTypeFunction %void
4321     %float = OpTypeFloat 32
4322     %v4float = OpTypeVector %float 4
4323     %strct = OpTypeStruct %float %v4float
4324 
4325     %11 = OpTypePointer Input %strct
4326 
4327     %1 = OpVariable %11 Input
4328 
4329     %12 = OpTypePointer Output %v4float
4330     %2 = OpVariable %12 Output
4331 
4332     %main = OpFunction %void None %voidfn
4333     %entry = OpLabel
4334     OpReturn
4335     OpFunctionEnd
4336 )";
4337   auto p = parser(test::Assemble(assembly));
4338 
4339   ASSERT_TRUE(p->Parse()) << p->error() << assembly;
4340   EXPECT_TRUE(p->error().empty());
4341 
4342   const auto got = test::ToString(p->program());
4343   const std::string expected = R"(struct Communicators {
4344   alice : f32;
4345   bob : vec4<f32>;
4346 };
4347 
4348 var<private> x_1 : Communicators;
4349 
4350 var<private> x_2 : vec4<f32>;
4351 
4352 fn main_1() {
4353   return;
4354 }
4355 
4356 struct main_out {
4357   [[builtin(position)]]
4358   x_2_1 : vec4<f32>;
4359 };
4360 
4361 [[stage(vertex)]]
4362 fn main([[location(9)]] x_1_param : f32, [[location(10)]] x_1_param_1 : vec4<f32>) -> main_out {
4363   x_1.alice = x_1_param;
4364   x_1.bob = x_1_param_1;
4365   main_1();
4366   return main_out(x_2);
4367 }
4368 )";
4369   EXPECT_EQ(got, expected) << got;
4370 }
4371 
TEST_F(SpvModuleScopeVarParserTest,Input_FlattenNested)4372 TEST_F(SpvModuleScopeVarParserTest, Input_FlattenNested) {
4373   const std::string assembly = R"(
4374     OpCapability Shader
4375     OpMemoryModel Logical Simple
4376     OpEntryPoint Vertex %main "main" %1 %2
4377     OpDecorate %1 Location 7
4378     OpDecorate %2 BuiltIn Position
4379 
4380     %void = OpTypeVoid
4381     %voidfn = OpTypeFunction %void
4382     %float = OpTypeFloat 32
4383     %v4float = OpTypeVector %float 4
4384     %m2v4float = OpTypeMatrix %v4float 2
4385     %uint = OpTypeInt 32 0
4386     %uint_2 = OpConstant %uint 2
4387 
4388     %arr = OpTypeArray %m2v4float %uint_2
4389 
4390     %11 = OpTypePointer Input %arr
4391     %1 = OpVariable %11 Input
4392 
4393     %12 = OpTypePointer Output %v4float
4394     %2 = OpVariable %12 Output
4395 
4396     %main = OpFunction %void None %voidfn
4397     %entry = OpLabel
4398     OpReturn
4399     OpFunctionEnd
4400 )";
4401   auto p = parser(test::Assemble(assembly));
4402 
4403   ASSERT_TRUE(p->Parse()) << p->error() << assembly;
4404   EXPECT_TRUE(p->error().empty());
4405 
4406   const auto got = test::ToString(p->program());
4407   const std::string expected = R"(var<private> x_1 : array<mat2x4<f32>, 2u>;
4408 
4409 var<private> x_2 : vec4<f32>;
4410 
4411 fn main_1() {
4412   return;
4413 }
4414 
4415 struct main_out {
4416   [[builtin(position)]]
4417   x_2_1 : vec4<f32>;
4418 };
4419 
4420 [[stage(vertex)]]
4421 fn main([[location(7)]] x_1_param : vec4<f32>, [[location(8)]] x_1_param_1 : vec4<f32>, [[location(9)]] x_1_param_2 : vec4<f32>, [[location(10)]] x_1_param_3 : vec4<f32>) -> main_out {
4422   x_1[0][0] = x_1_param;
4423   x_1[0][1] = x_1_param_1;
4424   x_1[1][0] = x_1_param_2;
4425   x_1[1][1] = x_1_param_3;
4426   main_1();
4427   return main_out(x_2);
4428 }
4429 )";
4430   EXPECT_EQ(got, expected) << got;
4431 }
4432 
TEST_F(SpvModuleScopeVarParserTest,Output_FlattenArray_OneLevel)4433 TEST_F(SpvModuleScopeVarParserTest, Output_FlattenArray_OneLevel) {
4434   const std::string assembly = R"(
4435     OpCapability Shader
4436     OpMemoryModel Logical Simple
4437     OpEntryPoint Vertex %main "main" %1 %2
4438     OpDecorate %1 Location 4
4439     OpDecorate %2 BuiltIn Position
4440 
4441     %void = OpTypeVoid
4442     %voidfn = OpTypeFunction %void
4443     %float = OpTypeFloat 32
4444     %v4float = OpTypeVector %float 4
4445     %uint = OpTypeInt 32 0
4446     %uint_0 = OpConstant %uint 0
4447     %uint_1 = OpConstant %uint 1
4448     %uint_3 = OpConstant %uint 3
4449     %arr = OpTypeArray %float %uint_3
4450     %11 = OpTypePointer Output %arr
4451 
4452     %1 = OpVariable %11 Output
4453 
4454     %12 = OpTypePointer Output %v4float
4455     %2 = OpVariable %12 Output
4456 
4457     %main = OpFunction %void None %voidfn
4458     %entry = OpLabel
4459     OpReturn
4460     OpFunctionEnd
4461 )";
4462   auto p = parser(test::Assemble(assembly));
4463 
4464   ASSERT_TRUE(p->Parse()) << p->error() << assembly;
4465   EXPECT_TRUE(p->error().empty());
4466 
4467   const auto got = test::ToString(p->program());
4468   const std::string expected = R"(var<private> x_1 : array<f32, 3u>;
4469 
4470 var<private> x_2 : vec4<f32>;
4471 
4472 fn main_1() {
4473   return;
4474 }
4475 
4476 struct main_out {
4477   [[location(4)]]
4478   x_1_1 : f32;
4479   [[location(5)]]
4480   x_1_2 : f32;
4481   [[location(6)]]
4482   x_1_3 : f32;
4483   [[builtin(position)]]
4484   x_2_1 : vec4<f32>;
4485 };
4486 
4487 [[stage(vertex)]]
4488 fn main() -> main_out {
4489   main_1();
4490   return main_out(x_1[0], x_1[1], x_1[2], x_2);
4491 }
4492 )";
4493   EXPECT_EQ(got, expected) << got;
4494 }
4495 
TEST_F(SpvModuleScopeVarParserTest,Output_FlattenMatrix)4496 TEST_F(SpvModuleScopeVarParserTest, Output_FlattenMatrix) {
4497   const std::string assembly = R"(
4498     OpCapability Shader
4499     OpMemoryModel Logical Simple
4500     OpEntryPoint Vertex %main "main" %1 %2
4501     OpDecorate %1 Location 9
4502     OpDecorate %2 BuiltIn Position
4503 
4504     %void = OpTypeVoid
4505     %voidfn = OpTypeFunction %void
4506     %float = OpTypeFloat 32
4507     %v4float = OpTypeVector %float 4
4508     %m2v4float = OpTypeMatrix %v4float 2
4509     %uint = OpTypeInt 32 0
4510 
4511     %11 = OpTypePointer Output %m2v4float
4512 
4513     %1 = OpVariable %11 Output
4514 
4515     %12 = OpTypePointer Output %v4float
4516     %2 = OpVariable %12 Output
4517 
4518     %main = OpFunction %void None %voidfn
4519     %entry = OpLabel
4520     OpReturn
4521     OpFunctionEnd
4522 )";
4523   auto p = parser(test::Assemble(assembly));
4524 
4525   ASSERT_TRUE(p->Parse()) << p->error() << assembly;
4526   EXPECT_TRUE(p->error().empty());
4527 
4528   const auto got = test::ToString(p->program());
4529   const std::string expected = R"(var<private> x_1 : mat2x4<f32>;
4530 
4531 var<private> x_2 : vec4<f32>;
4532 
4533 fn main_1() {
4534   return;
4535 }
4536 
4537 struct main_out {
4538   [[location(9)]]
4539   x_1_1 : vec4<f32>;
4540   [[location(10)]]
4541   x_1_2 : vec4<f32>;
4542   [[builtin(position)]]
4543   x_2_1 : vec4<f32>;
4544 };
4545 
4546 [[stage(vertex)]]
4547 fn main() -> main_out {
4548   main_1();
4549   return main_out(x_1[0], x_1[1], x_2);
4550 }
4551 )";
4552   EXPECT_EQ(got, expected) << got;
4553 }
4554 
TEST_F(SpvModuleScopeVarParserTest,Output_FlattenStruct_LocOnVariable)4555 TEST_F(SpvModuleScopeVarParserTest, Output_FlattenStruct_LocOnVariable) {
4556   const std::string assembly = R"(
4557     OpCapability Shader
4558     OpMemoryModel Logical Simple
4559     OpEntryPoint Vertex %main "main" %1 %2
4560 
4561     OpName %strct "Communicators"
4562     OpMemberName %strct 0 "alice"
4563     OpMemberName %strct 1 "bob"
4564 
4565     OpDecorate %1 Location 9
4566     OpDecorate %2 BuiltIn Position
4567 
4568 
4569     %void = OpTypeVoid
4570     %voidfn = OpTypeFunction %void
4571     %float = OpTypeFloat 32
4572     %v4float = OpTypeVector %float 4
4573     %strct = OpTypeStruct %float %v4float
4574 
4575     %11 = OpTypePointer Output %strct
4576 
4577     %1 = OpVariable %11 Output
4578 
4579     %12 = OpTypePointer Output %v4float
4580     %2 = OpVariable %12 Output
4581 
4582     %main = OpFunction %void None %voidfn
4583     %entry = OpLabel
4584     OpReturn
4585     OpFunctionEnd
4586 )";
4587   auto p = parser(test::Assemble(assembly));
4588 
4589   ASSERT_TRUE(p->Parse()) << p->error() << assembly;
4590   EXPECT_TRUE(p->error().empty());
4591 
4592   const auto got = test::ToString(p->program());
4593   const std::string expected = R"(struct Communicators {
4594   alice : f32;
4595   bob : vec4<f32>;
4596 };
4597 
4598 var<private> x_1 : Communicators;
4599 
4600 var<private> x_2 : vec4<f32>;
4601 
4602 fn main_1() {
4603   return;
4604 }
4605 
4606 struct main_out {
4607   [[location(9)]]
4608   x_1_1 : f32;
4609   [[location(10)]]
4610   x_1_2 : vec4<f32>;
4611   [[builtin(position)]]
4612   x_2_1 : vec4<f32>;
4613 };
4614 
4615 [[stage(vertex)]]
4616 fn main() -> main_out {
4617   main_1();
4618   return main_out(x_1.alice, x_1.bob, x_2);
4619 }
4620 )";
4621   EXPECT_EQ(got, expected) << got;
4622 }
4623 
TEST_F(SpvModuleScopeVarParserTest,FlattenStruct_LocOnMembers)4624 TEST_F(SpvModuleScopeVarParserTest, FlattenStruct_LocOnMembers) {
4625   // Block-decorated struct may have its members decorated with Location.
4626   const std::string assembly = R"(
4627     OpCapability Shader
4628     OpMemoryModel Logical Simple
4629     OpEntryPoint Vertex %main "main" %1 %2 %3
4630 
4631     OpName %strct "Communicators"
4632     OpMemberName %strct 0 "alice"
4633     OpMemberName %strct 1 "bob"
4634 
4635     OpMemberDecorate %strct 0 Location 9
4636     OpMemberDecorate %strct 1 Location 11
4637     OpDecorate %strct Block
4638     OpDecorate %2 BuiltIn Position
4639 
4640     %void = OpTypeVoid
4641     %voidfn = OpTypeFunction %void
4642     %float = OpTypeFloat 32
4643     %v4float = OpTypeVector %float 4
4644     %strct = OpTypeStruct %float %v4float
4645 
4646     %11 = OpTypePointer Input %strct
4647     %13 = OpTypePointer Output %strct
4648 
4649     %1 = OpVariable %11 Input
4650     %3 = OpVariable %13 Output
4651 
4652     %12 = OpTypePointer Output %v4float
4653     %2 = OpVariable %12 Output
4654 
4655     %main = OpFunction %void None %voidfn
4656     %entry = OpLabel
4657     OpReturn
4658     OpFunctionEnd
4659 )";
4660   auto p = parser(test::Assemble(assembly));
4661 
4662   ASSERT_TRUE(p->Parse()) << p->error() << assembly;
4663   EXPECT_TRUE(p->error().empty());
4664 
4665   const auto got = test::ToString(p->program());
4666   const std::string expected = R"(struct Communicators {
4667   alice : f32;
4668   bob : vec4<f32>;
4669 };
4670 
4671 var<private> x_1 : Communicators;
4672 
4673 var<private> x_3 : Communicators;
4674 
4675 var<private> x_2 : vec4<f32>;
4676 
4677 fn main_1() {
4678   return;
4679 }
4680 
4681 struct main_out {
4682   [[builtin(position)]]
4683   x_2_1 : vec4<f32>;
4684   [[location(9)]]
4685   x_3_1 : f32;
4686   [[location(11)]]
4687   x_3_2 : vec4<f32>;
4688 };
4689 
4690 [[stage(vertex)]]
4691 fn main([[location(9)]] x_1_param : f32, [[location(11)]] x_1_param_1 : vec4<f32>) -> main_out {
4692   x_1.alice = x_1_param;
4693   x_1.bob = x_1_param_1;
4694   main_1();
4695   return main_out(x_2, x_3.alice, x_3.bob);
4696 }
4697 )";
4698   EXPECT_EQ(got, expected) << got;
4699 }
4700 
TEST_F(SpvModuleScopeVarParserTest,EntryPointWrapping_Interpolation_Flat_Vertex_In)4701 TEST_F(SpvModuleScopeVarParserTest,
4702        EntryPointWrapping_Interpolation_Flat_Vertex_In) {
4703   // Flat decorations are dropped for integral
4704   const auto assembly = CommonCapabilities() + R"(
4705      OpEntryPoint Vertex %main "main" %1 %2 %3 %4 %5 %6 %10
4706      OpDecorate %1 Location 1
4707      OpDecorate %2 Location 2
4708      OpDecorate %3 Location 3
4709      OpDecorate %4 Location 4
4710      OpDecorate %5 Location 5
4711      OpDecorate %6 Location 6
4712      OpDecorate %1 Flat
4713      OpDecorate %2 Flat
4714      OpDecorate %3 Flat
4715      OpDecorate %4 Flat
4716      OpDecorate %5 Flat
4717      OpDecorate %6 Flat
4718      OpDecorate %10 BuiltIn Position
4719 )" + CommonTypes() +
4720                         R"(
4721      %ptr_in_uint = OpTypePointer Input %uint
4722      %ptr_in_v2uint = OpTypePointer Input %v2uint
4723      %ptr_in_int = OpTypePointer Input %int
4724      %ptr_in_v2int = OpTypePointer Input %v2int
4725      %ptr_in_float = OpTypePointer Input %float
4726      %ptr_in_v2float = OpTypePointer Input %v2float
4727      %1 = OpVariable %ptr_in_uint Input
4728      %2 = OpVariable %ptr_in_v2uint Input
4729      %3 = OpVariable %ptr_in_int Input
4730      %4 = OpVariable %ptr_in_v2int Input
4731      %5 = OpVariable %ptr_in_float Input
4732      %6 = OpVariable %ptr_in_v2float Input
4733 
4734      %ptr_out_v4float = OpTypePointer Output %v4float
4735      %10 = OpVariable %ptr_out_v4float Output
4736 
4737      %main = OpFunction %void None %voidfn
4738      %entry = OpLabel
4739      OpReturn
4740      OpFunctionEnd
4741   )";
4742   auto p = parser(test::Assemble(assembly));
4743 
4744   ASSERT_TRUE(p->BuildAndParseInternalModule());
4745   EXPECT_TRUE(p->error().empty());
4746   const auto got = test::ToString(p->program());
4747   const std::string expected =
4748       R"(var<private> x_1 : u32;
4749 
4750 var<private> x_2 : vec2<u32>;
4751 
4752 var<private> x_3 : i32;
4753 
4754 var<private> x_4 : vec2<i32>;
4755 
4756 var<private> x_5 : f32;
4757 
4758 var<private> x_6 : vec2<f32>;
4759 
4760 var<private> x_10 : vec4<f32>;
4761 
4762 fn main_1() {
4763   return;
4764 }
4765 
4766 struct main_out {
4767   [[builtin(position)]]
4768   x_10_1 : vec4<f32>;
4769 };
4770 
4771 [[stage(vertex)]]
4772 fn main([[location(1), interpolate(flat)]] x_1_param : u32, [[location(2), interpolate(flat)]] x_2_param : vec2<u32>, [[location(3), interpolate(flat)]] x_3_param : i32, [[location(4), interpolate(flat)]] x_4_param : vec2<i32>, [[location(5), interpolate(flat)]] x_5_param : f32, [[location(6), interpolate(flat)]] x_6_param : vec2<f32>) -> main_out {
4773   x_1 = x_1_param;
4774   x_2 = x_2_param;
4775   x_3 = x_3_param;
4776   x_4 = x_4_param;
4777   x_5 = x_5_param;
4778   x_6 = x_6_param;
4779   main_1();
4780   return main_out(x_10);
4781 }
4782 )";
4783   EXPECT_EQ(got, expected) << got;
4784 }
4785 
TEST_F(SpvModuleScopeVarParserTest,EntryPointWrapping_Interpolation_Flat_Vertex_Output)4786 TEST_F(SpvModuleScopeVarParserTest,
4787        EntryPointWrapping_Interpolation_Flat_Vertex_Output) {
4788   // Flat decorations are dropped for integral
4789   const auto assembly = CommonCapabilities() + R"(
4790      OpEntryPoint Vertex %main "main" %1 %2 %3 %4 %5 %6 %10
4791      OpDecorate %1 Location 1
4792      OpDecorate %2 Location 2
4793      OpDecorate %3 Location 3
4794      OpDecorate %4 Location 4
4795      OpDecorate %5 Location 5
4796      OpDecorate %6 Location 6
4797      OpDecorate %1 Flat
4798      OpDecorate %2 Flat
4799      OpDecorate %3 Flat
4800      OpDecorate %4 Flat
4801      OpDecorate %5 Flat
4802      OpDecorate %6 Flat
4803      OpDecorate %10 BuiltIn Position
4804 )" + CommonTypes() +
4805                         R"(
4806      %ptr_out_uint = OpTypePointer Output %uint
4807      %ptr_out_v2uint = OpTypePointer Output %v2uint
4808      %ptr_out_int = OpTypePointer Output %int
4809      %ptr_out_v2int = OpTypePointer Output %v2int
4810      %ptr_out_float = OpTypePointer Output %float
4811      %ptr_out_v2float = OpTypePointer Output %v2float
4812      %1 = OpVariable %ptr_out_uint Output
4813      %2 = OpVariable %ptr_out_v2uint Output
4814      %3 = OpVariable %ptr_out_int Output
4815      %4 = OpVariable %ptr_out_v2int Output
4816      %5 = OpVariable %ptr_out_float Output
4817      %6 = OpVariable %ptr_out_v2float Output
4818 
4819      %ptr_out_v4float = OpTypePointer Output %v4float
4820      %10 = OpVariable %ptr_out_v4float Output
4821 
4822      %main = OpFunction %void None %voidfn
4823      %entry = OpLabel
4824      OpReturn
4825      OpFunctionEnd
4826   )";
4827   auto p = parser(test::Assemble(assembly));
4828 
4829   ASSERT_TRUE(p->BuildAndParseInternalModule());
4830   EXPECT_TRUE(p->error().empty());
4831   const auto got = test::ToString(p->program());
4832   const std::string expected =
4833       R"(var<private> x_1 : u32;
4834 
4835 var<private> x_2 : vec2<u32>;
4836 
4837 var<private> x_3 : i32;
4838 
4839 var<private> x_4 : vec2<i32>;
4840 
4841 var<private> x_5 : f32;
4842 
4843 var<private> x_6 : vec2<f32>;
4844 
4845 var<private> x_10 : vec4<f32>;
4846 
4847 fn main_1() {
4848   return;
4849 }
4850 
4851 struct main_out {
4852   [[location(1), interpolate(flat)]]
4853   x_1_1 : u32;
4854   [[location(2), interpolate(flat)]]
4855   x_2_1 : vec2<u32>;
4856   [[location(3), interpolate(flat)]]
4857   x_3_1 : i32;
4858   [[location(4), interpolate(flat)]]
4859   x_4_1 : vec2<i32>;
4860   [[location(5), interpolate(flat)]]
4861   x_5_1 : f32;
4862   [[location(6), interpolate(flat)]]
4863   x_6_1 : vec2<f32>;
4864   [[builtin(position)]]
4865   x_10_1 : vec4<f32>;
4866 };
4867 
4868 [[stage(vertex)]]
4869 fn main() -> main_out {
4870   main_1();
4871   return main_out(x_1, x_2, x_3, x_4, x_5, x_6, x_10);
4872 }
4873 )";
4874   EXPECT_EQ(got, expected) << got;
4875 }
4876 
TEST_F(SpvModuleScopeVarParserTest,EntryPointWrapping_Flatten_Interpolation_Flat_Fragment_In)4877 TEST_F(SpvModuleScopeVarParserTest,
4878        EntryPointWrapping_Flatten_Interpolation_Flat_Fragment_In) {
4879   // Flat decorations are dropped for integral
4880   const auto assembly = CommonCapabilities() + R"(
4881      OpEntryPoint Fragment %main "main" %1 %2
4882      OpExecutionMode %main OriginUpperLeft
4883      OpDecorate %1 Location 1
4884      OpDecorate %2 Location 5
4885      OpDecorate %1 Flat
4886      OpDecorate %2 Flat
4887 )" + CommonTypes() +
4888                         R"(
4889      %arr = OpTypeArray %float %uint_2
4890      %strct = OpTypeStruct %float %float
4891      %ptr_in_arr = OpTypePointer Input %arr
4892      %ptr_in_strct = OpTypePointer Input %strct
4893      %1 = OpVariable %ptr_in_arr Input
4894      %2 = OpVariable %ptr_in_strct Input
4895 
4896      %main = OpFunction %void None %voidfn
4897      %entry = OpLabel
4898      OpReturn
4899      OpFunctionEnd
4900   )";
4901   auto p = parser(test::Assemble(assembly));
4902 
4903   ASSERT_TRUE(p->BuildAndParseInternalModule());
4904   EXPECT_TRUE(p->error().empty());
4905   const auto got = test::ToString(p->program());
4906   const std::string expected =
4907       R"(struct S {
4908   field0 : f32;
4909   field1 : f32;
4910 };
4911 
4912 var<private> x_1 : array<f32, 2u>;
4913 
4914 var<private> x_2 : S;
4915 
4916 fn main_1() {
4917   return;
4918 }
4919 
4920 [[stage(fragment)]]
4921 fn main([[location(1), interpolate(flat)]] x_1_param : f32, [[location(2), interpolate(flat)]] x_1_param_1 : f32, [[location(5), interpolate(flat)]] x_2_param : f32, [[location(6), interpolate(flat)]] x_2_param_1 : f32) {
4922   x_1[0] = x_1_param;
4923   x_1[1] = x_1_param_1;
4924   x_2.field0 = x_2_param;
4925   x_2.field1 = x_2_param_1;
4926   main_1();
4927 }
4928 )";
4929   EXPECT_EQ(got, expected) << got;
4930 }
4931 
TEST_F(SpvModuleScopeVarParserTest,EntryPointWrapping_Interpolation_Floating_Fragment_In)4932 TEST_F(SpvModuleScopeVarParserTest,
4933        EntryPointWrapping_Interpolation_Floating_Fragment_In) {
4934   // Flat decorations are dropped for integral
4935   const auto assembly = CommonCapabilities() + R"(
4936      OpEntryPoint Fragment %main "main" %1 %2 %3 %4 %5 %6
4937      OpExecutionMode %main OriginUpperLeft
4938      OpDecorate %1 Location 1
4939      OpDecorate %2 Location 2
4940      OpDecorate %3 Location 3
4941      OpDecorate %4 Location 4
4942      OpDecorate %5 Location 5
4943      OpDecorate %6 Location 6
4944 
4945      ; %1 perspective center
4946 
4947      OpDecorate %2 Centroid ; perspective centroid
4948 
4949      OpDecorate %3 Sample ; perspective sample
4950 
4951      OpDecorate %4 NoPerspective; linear center
4952 
4953      OpDecorate %5 NoPerspective ; linear centroid
4954      OpDecorate %5 Centroid
4955 
4956      OpDecorate %6 NoPerspective ; linear sample
4957      OpDecorate %6 Sample
4958 
4959 )" + CommonTypes() +
4960                         R"(
4961      %ptr_in_float = OpTypePointer Input %float
4962      %1 = OpVariable %ptr_in_float Input
4963      %2 = OpVariable %ptr_in_float Input
4964      %3 = OpVariable %ptr_in_float Input
4965      %4 = OpVariable %ptr_in_float Input
4966      %5 = OpVariable %ptr_in_float Input
4967      %6 = OpVariable %ptr_in_float Input
4968 
4969      %main = OpFunction %void None %voidfn
4970      %entry = OpLabel
4971      OpReturn
4972      OpFunctionEnd
4973   )";
4974   auto p = parser(test::Assemble(assembly));
4975 
4976   ASSERT_TRUE(p->BuildAndParseInternalModule());
4977   EXPECT_TRUE(p->error().empty());
4978   const auto got = test::ToString(p->program());
4979   const std::string expected =
4980       R"(var<private> x_1 : f32;
4981 
4982 var<private> x_2 : f32;
4983 
4984 var<private> x_3 : f32;
4985 
4986 var<private> x_4 : f32;
4987 
4988 var<private> x_5 : f32;
4989 
4990 var<private> x_6 : f32;
4991 
4992 fn main_1() {
4993   return;
4994 }
4995 
4996 [[stage(fragment)]]
4997 fn main([[location(1)]] x_1_param : f32, [[location(2), interpolate(perspective, centroid)]] x_2_param : f32, [[location(3), interpolate(perspective, sample)]] x_3_param : f32, [[location(4), interpolate(linear)]] x_4_param : f32, [[location(5), interpolate(linear, centroid)]] x_5_param : f32, [[location(6), interpolate(linear, sample)]] x_6_param : f32) {
4998   x_1 = x_1_param;
4999   x_2 = x_2_param;
5000   x_3 = x_3_param;
5001   x_4 = x_4_param;
5002   x_5 = x_5_param;
5003   x_6 = x_6_param;
5004   main_1();
5005 }
5006 )";
5007   EXPECT_EQ(got, expected) << got;
5008 }
5009 
TEST_F(SpvModuleScopeVarParserTest,EntryPointWrapping_Flatten_Interpolation_Floating_Fragment_In)5010 TEST_F(SpvModuleScopeVarParserTest,
5011        EntryPointWrapping_Flatten_Interpolation_Floating_Fragment_In) {
5012   const auto assembly = CommonCapabilities() + R"(
5013      OpEntryPoint Fragment %main "main" %1
5014      OpExecutionMode %main OriginUpperLeft
5015      OpDecorate %1 Location 1
5016 
5017      ; member 0 perspective center
5018 
5019      OpMemberDecorate %10 1 Centroid ; perspective centroid
5020 
5021      OpMemberDecorate %10 2 Sample ; perspective sample
5022 
5023      OpMemberDecorate %10 3 NoPerspective; linear center
5024 
5025      OpMemberDecorate %10 4 NoPerspective ; linear centroid
5026      OpMemberDecorate %10 4 Centroid
5027 
5028      OpMemberDecorate %10 5 NoPerspective ; linear sample
5029      OpMemberDecorate %10 5 Sample
5030 
5031 )" + CommonTypes() +
5032                         R"(
5033 
5034      %10 = OpTypeStruct %float %float %float %float %float %float
5035      %ptr_in_strct = OpTypePointer Input %10
5036      %1 = OpVariable %ptr_in_strct Input
5037 
5038      %main = OpFunction %void None %voidfn
5039      %entry = OpLabel
5040      OpReturn
5041      OpFunctionEnd
5042   )";
5043   auto p = parser(test::Assemble(assembly));
5044 
5045   ASSERT_TRUE(p->BuildAndParseInternalModule()) << assembly << p->error();
5046   EXPECT_TRUE(p->error().empty());
5047   const auto got = test::ToString(p->program());
5048   const std::string expected =
5049       R"(struct S {
5050   field0 : f32;
5051   field1 : f32;
5052   field2 : f32;
5053   field3 : f32;
5054   field4 : f32;
5055   field5 : f32;
5056 };
5057 
5058 var<private> x_1 : S;
5059 
5060 fn main_1() {
5061   return;
5062 }
5063 
5064 [[stage(fragment)]]
5065 fn main([[location(1)]] x_1_param : f32, [[location(2), interpolate(perspective, centroid)]] x_1_param_1 : f32, [[location(3), interpolate(perspective, sample)]] x_1_param_2 : f32, [[location(4), interpolate(linear)]] x_1_param_3 : f32, [[location(5), interpolate(linear, centroid)]] x_1_param_4 : f32, [[location(6), interpolate(linear, sample)]] x_1_param_5 : f32) {
5066   x_1.field0 = x_1_param;
5067   x_1.field1 = x_1_param_1;
5068   x_1.field2 = x_1_param_2;
5069   x_1.field3 = x_1_param_3;
5070   x_1.field4 = x_1_param_4;
5071   x_1.field5 = x_1_param_5;
5072   main_1();
5073 }
5074 )";
5075   EXPECT_EQ(got, expected) << got;
5076 }
5077 
TEST_F(SpvModuleScopeVarParserTest,EntryPointWrapping_Interpolation_Floating_Fragment_Out)5078 TEST_F(SpvModuleScopeVarParserTest,
5079        EntryPointWrapping_Interpolation_Floating_Fragment_Out) {
5080   // Flat decorations are dropped for integral
5081   const auto assembly = CommonCapabilities() + R"(
5082      OpEntryPoint Fragment %main "main" %1 %2 %3 %4 %5 %6
5083      OpExecutionMode %main OriginUpperLeft
5084      OpDecorate %1 Location 1
5085      OpDecorate %2 Location 2
5086      OpDecorate %3 Location 3
5087      OpDecorate %4 Location 4
5088      OpDecorate %5 Location 5
5089      OpDecorate %6 Location 6
5090 
5091      ; %1 perspective center
5092 
5093      OpDecorate %2 Centroid ; perspective centroid
5094 
5095      OpDecorate %3 Sample ; perspective sample
5096 
5097      OpDecorate %4 NoPerspective; linear center
5098 
5099      OpDecorate %5 NoPerspective ; linear centroid
5100      OpDecorate %5 Centroid
5101 
5102      OpDecorate %6 NoPerspective ; linear sample
5103      OpDecorate %6 Sample
5104 
5105 )" + CommonTypes() +
5106                         R"(
5107      %ptr_out_float = OpTypePointer Output %float
5108      %1 = OpVariable %ptr_out_float Output
5109      %2 = OpVariable %ptr_out_float Output
5110      %3 = OpVariable %ptr_out_float Output
5111      %4 = OpVariable %ptr_out_float Output
5112      %5 = OpVariable %ptr_out_float Output
5113      %6 = OpVariable %ptr_out_float Output
5114 
5115      %main = OpFunction %void None %voidfn
5116      %entry = OpLabel
5117      OpReturn
5118      OpFunctionEnd
5119   )";
5120   auto p = parser(test::Assemble(assembly));
5121 
5122   ASSERT_TRUE(p->BuildAndParseInternalModule());
5123   EXPECT_TRUE(p->error().empty());
5124   const auto got = test::ToString(p->program());
5125   const std::string expected =
5126       R"(var<private> x_1 : f32;
5127 
5128 var<private> x_2 : f32;
5129 
5130 var<private> x_3 : f32;
5131 
5132 var<private> x_4 : f32;
5133 
5134 var<private> x_5 : f32;
5135 
5136 var<private> x_6 : f32;
5137 
5138 fn main_1() {
5139   return;
5140 }
5141 
5142 struct main_out {
5143   [[location(1)]]
5144   x_1_1 : f32;
5145   [[location(2), interpolate(perspective, centroid)]]
5146   x_2_1 : f32;
5147   [[location(3), interpolate(perspective, sample)]]
5148   x_3_1 : f32;
5149   [[location(4), interpolate(linear)]]
5150   x_4_1 : f32;
5151   [[location(5), interpolate(linear, centroid)]]
5152   x_5_1 : f32;
5153   [[location(6), interpolate(linear, sample)]]
5154   x_6_1 : f32;
5155 };
5156 
5157 [[stage(fragment)]]
5158 fn main() -> main_out {
5159   main_1();
5160   return main_out(x_1, x_2, x_3, x_4, x_5, x_6);
5161 }
5162 )";
5163   EXPECT_EQ(got, expected) << got;
5164 }
5165 
TEST_F(SpvModuleScopeVarParserTest,EntryPointWrapping_Flatten_Interpolation_Floating_Fragment_Out)5166 TEST_F(SpvModuleScopeVarParserTest,
5167        EntryPointWrapping_Flatten_Interpolation_Floating_Fragment_Out) {
5168   const auto assembly = CommonCapabilities() + R"(
5169      OpEntryPoint Fragment %main "main" %1
5170      OpExecutionMode %main OriginUpperLeft
5171 
5172      OpDecorate %1 Location 1
5173 
5174      ; member 0 perspective center
5175 
5176      OpMemberDecorate %10 1 Centroid ; perspective centroid
5177 
5178      OpMemberDecorate %10 2 Sample ; perspective sample
5179 
5180      OpMemberDecorate %10 3 NoPerspective; linear center
5181 
5182      OpMemberDecorate %10 4 NoPerspective ; linear centroid
5183      OpMemberDecorate %10 4 Centroid
5184 
5185      OpMemberDecorate %10 5 NoPerspective ; linear sample
5186      OpMemberDecorate %10 5 Sample
5187 
5188 )" + CommonTypes() +
5189                         R"(
5190 
5191      %10 = OpTypeStruct %float %float %float %float %float %float
5192      %ptr_in_strct = OpTypePointer Output %10
5193      %1 = OpVariable %ptr_in_strct Output
5194 
5195      %main = OpFunction %void None %voidfn
5196      %entry = OpLabel
5197      OpReturn
5198      OpFunctionEnd
5199   )";
5200   auto p = parser(test::Assemble(assembly));
5201 
5202   ASSERT_TRUE(p->BuildAndParseInternalModule());
5203   EXPECT_TRUE(p->error().empty());
5204   const auto got = test::ToString(p->program());
5205   const std::string expected =
5206       R"(struct S {
5207   field0 : f32;
5208   field1 : f32;
5209   field2 : f32;
5210   field3 : f32;
5211   field4 : f32;
5212   field5 : f32;
5213 };
5214 
5215 var<private> x_1 : S;
5216 
5217 fn main_1() {
5218   return;
5219 }
5220 
5221 struct main_out {
5222   [[location(1)]]
5223   x_1_1 : f32;
5224   [[location(2), interpolate(perspective, centroid)]]
5225   x_1_2 : f32;
5226   [[location(3), interpolate(perspective, sample)]]
5227   x_1_3 : f32;
5228   [[location(4), interpolate(linear)]]
5229   x_1_4 : f32;
5230   [[location(5), interpolate(linear, centroid)]]
5231   x_1_5 : f32;
5232   [[location(6), interpolate(linear, sample)]]
5233   x_1_6 : f32;
5234 };
5235 
5236 [[stage(fragment)]]
5237 fn main() -> main_out {
5238   main_1();
5239   return main_out(x_1.field0, x_1.field1, x_1.field2, x_1.field3, x_1.field4, x_1.field5);
5240 }
5241 )";
5242   EXPECT_EQ(got, expected) << got;
5243 }
5244 
5245 }  // namespace
5246 }  // namespace spirv
5247 }  // namespace reader
5248 }  // namespace tint
5249