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