• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 The Tint Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "gmock/gmock.h"
16 #include "src/reader/spirv/function.h"
17 #include "src/reader/spirv/parser_impl_test_helper.h"
18 #include "src/reader/spirv/spirv_tools_helpers_test.h"
19 
20 namespace tint {
21 namespace reader {
22 namespace spirv {
23 namespace {
24 
25 using ::testing::Eq;
26 using ::testing::HasSubstr;
27 
28 using SpvParserMemoryTest = SpvParserTest;
29 
Preamble()30 std::string Preamble() {
31   return R"(
32     OpCapability Shader
33     OpMemoryModel Logical Simple
34     OpEntryPoint Fragment %100 "main"
35     OpExecutionMode %100 OriginUpperLeft
36 )";
37 }
38 
TEST_F(SpvParserMemoryTest,EmitStatement_StoreBoolConst)39 TEST_F(SpvParserMemoryTest, EmitStatement_StoreBoolConst) {
40   auto p = parser(test::Assemble(Preamble() + R"(
41      %void = OpTypeVoid
42      %voidfn = OpTypeFunction %void
43      %ty = OpTypeBool
44      %true = OpConstantTrue %ty
45      %false = OpConstantFalse %ty
46      %null = OpConstantNull %ty
47      %ptr_ty = OpTypePointer Function %ty
48      %100 = OpFunction %void None %voidfn
49      %entry = OpLabel
50      %1 = OpVariable %ptr_ty Function
51      OpStore %1 %true
52      OpStore %1 %false
53      OpStore %1 %null
54      OpReturn
55      OpFunctionEnd
56   )"));
57   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
58   auto fe = p->function_emitter(100);
59   EXPECT_TRUE(fe.EmitBody()) << p->error();
60   auto ast_body = fe.ast_body();
61   EXPECT_THAT(test::ToString(p->program(), ast_body), HasSubstr(R"(x_1 = true;
62 x_1 = false;
63 x_1 = false;
64 )"));
65 }
66 
TEST_F(SpvParserMemoryTest,EmitStatement_StoreUintConst)67 TEST_F(SpvParserMemoryTest, EmitStatement_StoreUintConst) {
68   auto p = parser(test::Assemble(Preamble() + R"(
69      %void = OpTypeVoid
70      %voidfn = OpTypeFunction %void
71      %ty = OpTypeInt 32 0
72      %val = OpConstant %ty 42
73      %null = OpConstantNull %ty
74      %ptr_ty = OpTypePointer Function %ty
75      %100 = OpFunction %void None %voidfn
76      %entry = OpLabel
77      %1 = OpVariable %ptr_ty Function
78      OpStore %1 %val
79      OpStore %1 %null
80      OpReturn
81      OpFunctionEnd
82   )"));
83   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
84   auto fe = p->function_emitter(100);
85   EXPECT_TRUE(fe.EmitBody());
86   auto ast_body = fe.ast_body();
87   EXPECT_THAT(test::ToString(p->program(), ast_body), HasSubstr(R"(x_1 = 42u;
88 x_1 = 0u;
89 )"));
90 }
91 
TEST_F(SpvParserMemoryTest,EmitStatement_StoreIntConst)92 TEST_F(SpvParserMemoryTest, EmitStatement_StoreIntConst) {
93   auto p = parser(test::Assemble(Preamble() + R"(
94      %void = OpTypeVoid
95      %voidfn = OpTypeFunction %void
96      %ty = OpTypeInt 32 1
97      %val = OpConstant %ty 42
98      %null = OpConstantNull %ty
99      %ptr_ty = OpTypePointer Function %ty
100      %100 = OpFunction %void None %voidfn
101      %entry = OpLabel
102      %1 = OpVariable %ptr_ty Function
103      OpStore %1 %val
104      OpStore %1 %null
105      OpReturn
106      OpFunctionEnd
107   )"));
108   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
109   auto fe = p->function_emitter(100);
110   EXPECT_TRUE(fe.EmitBody());
111   auto ast_body = fe.ast_body();
112   EXPECT_THAT(test::ToString(p->program(), ast_body), HasSubstr(R"(x_1 = 42;
113 x_1 = 0;
114 )"));
115 }
116 
TEST_F(SpvParserMemoryTest,EmitStatement_StoreFloatConst)117 TEST_F(SpvParserMemoryTest, EmitStatement_StoreFloatConst) {
118   auto p = parser(test::Assemble(Preamble() + R"(
119      %void = OpTypeVoid
120      %voidfn = OpTypeFunction %void
121      %ty = OpTypeFloat 32
122      %val = OpConstant %ty 42
123      %null = OpConstantNull %ty
124      %ptr_ty = OpTypePointer Function %ty
125      %100 = OpFunction %void None %voidfn
126      %entry = OpLabel
127      %1 = OpVariable %ptr_ty Function
128      OpStore %1 %val
129      OpStore %1 %null
130      OpReturn
131      OpFunctionEnd
132   )"));
133   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
134   auto fe = p->function_emitter(100);
135   EXPECT_TRUE(fe.EmitBody());
136   auto ast_body = fe.ast_body();
137   EXPECT_THAT(test::ToString(p->program(), ast_body), HasSubstr(R"(x_1 = 42.0;
138 x_1 = 0.0;
139 )"));
140 }
141 
TEST_F(SpvParserMemoryTest,EmitStatement_LoadBool)142 TEST_F(SpvParserMemoryTest, EmitStatement_LoadBool) {
143   auto p = parser(test::Assemble(Preamble() + R"(
144      %void = OpTypeVoid
145      %voidfn = OpTypeFunction %void
146      %ty = OpTypeBool
147      %true = OpConstantTrue %ty
148      %false = OpConstantFalse %ty
149      %null = OpConstantNull %ty
150      %ptr_ty = OpTypePointer Function %ty
151      %100 = OpFunction %void None %voidfn
152      %entry = OpLabel
153      %1 = OpVariable %ptr_ty Function %true
154      %2 = OpLoad %ty %1
155      OpReturn
156      OpFunctionEnd
157   )"));
158   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
159   auto fe = p->function_emitter(100);
160   EXPECT_TRUE(fe.EmitBody()) << p->error();
161   auto ast_body = fe.ast_body();
162   EXPECT_THAT(test::ToString(p->program(), ast_body),
163               HasSubstr("let x_2 : bool = x_1;"));
164 }
165 
TEST_F(SpvParserMemoryTest,EmitStatement_LoadScalar)166 TEST_F(SpvParserMemoryTest, EmitStatement_LoadScalar) {
167   auto p = parser(test::Assemble(Preamble() + R"(
168      %void = OpTypeVoid
169      %voidfn = OpTypeFunction %void
170      %ty = OpTypeInt 32 0
171      %ty_42 = OpConstant %ty 42
172      %ptr_ty = OpTypePointer Function %ty
173      %100 = OpFunction %void None %voidfn
174      %entry = OpLabel
175      %1 = OpVariable %ptr_ty Function %ty_42
176      %2 = OpLoad %ty %1
177      %3 = OpLoad %ty %1
178      OpReturn
179      OpFunctionEnd
180   )"));
181   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
182   auto fe = p->function_emitter(100);
183   EXPECT_TRUE(fe.EmitBody()) << p->error();
184   auto ast_body = fe.ast_body();
185   EXPECT_THAT(test::ToString(p->program(), ast_body),
186               HasSubstr(R"(let x_2 : u32 = x_1;
187 let x_3 : u32 = x_1;
188 )"));
189 }
190 
TEST_F(SpvParserMemoryTest,EmitStatement_UseLoadedScalarTwice)191 TEST_F(SpvParserMemoryTest, EmitStatement_UseLoadedScalarTwice) {
192   auto p = parser(test::Assemble(Preamble() + R"(
193      %void = OpTypeVoid
194      %voidfn = OpTypeFunction %void
195      %ty = OpTypeInt 32 0
196      %ty_42 = OpConstant %ty 42
197      %ptr_ty = OpTypePointer Function %ty
198      %100 = OpFunction %void None %voidfn
199      %entry = OpLabel
200      %1 = OpVariable %ptr_ty Function %ty_42
201      %2 = OpLoad %ty %1
202      OpStore %1 %2
203      OpStore %1 %2
204      OpReturn
205      OpFunctionEnd
206   )"));
207   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
208   auto fe = p->function_emitter(100);
209   EXPECT_TRUE(fe.EmitBody()) << p->error();
210   auto ast_body = fe.ast_body();
211   EXPECT_THAT(test::ToString(p->program(), ast_body),
212               HasSubstr(R"(let x_2 : u32 = x_1;
213 x_1 = x_2;
214 x_1 = x_2;
215 )"));
216 }
217 
TEST_F(SpvParserMemoryTest,EmitStatement_StoreToModuleScopeVar)218 TEST_F(SpvParserMemoryTest, EmitStatement_StoreToModuleScopeVar) {
219   auto p = parser(test::Assemble(Preamble() + R"(
220      %void = OpTypeVoid
221      %voidfn = OpTypeFunction %void
222      %ty = OpTypeInt 32 0
223      %val = OpConstant %ty 42
224      %ptr_ty = OpTypePointer Private %ty
225      %1 = OpVariable %ptr_ty Private
226      %100 = OpFunction %void None %voidfn
227      %entry = OpLabel
228      OpStore %1 %val
229      OpReturn
230      OpFunctionEnd
231   )"));
232   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
233   auto fe = p->function_emitter(100);
234   EXPECT_TRUE(fe.EmitBody());
235   auto ast_body = fe.ast_body();
236   EXPECT_THAT(test::ToString(p->program(), ast_body), HasSubstr("x_1 = 42u;"));
237 }
238 
TEST_F(SpvParserMemoryTest,EmitStatement_CopyMemory_Scalar_Function_To_Private)239 TEST_F(SpvParserMemoryTest,
240        EmitStatement_CopyMemory_Scalar_Function_To_Private) {
241   auto p = parser(test::Assemble(Preamble() + R"(
242      %void = OpTypeVoid
243      %voidfn = OpTypeFunction %void
244      %ty = OpTypeInt 32 0
245      %val = OpConstant %ty 42
246      %ptr_fn_ty = OpTypePointer Function %ty
247      %ptr_priv_ty = OpTypePointer Private %ty
248      %2 = OpVariable %ptr_priv_ty Private
249      %100 = OpFunction %void None %voidfn
250      %entry = OpLabel
251      %1 = OpVariable %ptr_fn_ty Function
252      OpCopyMemory %2 %1
253      OpReturn
254      OpFunctionEnd
255   )"));
256   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
257   auto fe = p->function_emitter(100);
258   EXPECT_TRUE(fe.EmitBody());
259   auto ast_body = fe.ast_body();
260   const auto got = test::ToString(p->program(), ast_body);
261   const auto* expected = "x_2 = x_1;";
262   EXPECT_THAT(got, HasSubstr(expected));
263 }
264 
TEST_F(SpvParserMemoryTest,EmitStatement_AccessChain_NoOperands)265 TEST_F(SpvParserMemoryTest, EmitStatement_AccessChain_NoOperands) {
266   auto err = test::AssembleFailure(Preamble() + R"(
267      %void = OpTypeVoid
268      %voidfn = OpTypeFunction %void
269      %ty = OpTypeInt 32 0
270      %val = OpConstant %ty 42
271      %ptr_ty = OpTypePointer Private %ty
272      %1 = OpVariable %ptr_ty Private
273      %100 = OpFunction %void None %voidfn
274      %entry = OpLabel
275 
276      %2 = OpAccessChain %ptr_ty  ; Needs a base operand
277      OpStore %1 %val
278      OpReturn
279   )");
280   EXPECT_THAT(err,
281               Eq("16:5: Expected operand, found next instruction instead."));
282 }
283 
TEST_F(SpvParserMemoryTest,EmitStatement_AccessChain_BaseIsNotPointer)284 TEST_F(SpvParserMemoryTest, EmitStatement_AccessChain_BaseIsNotPointer) {
285   auto p = parser(test::Assemble(Preamble() + R"(
286      %void = OpTypeVoid
287      %voidfn = OpTypeFunction %void
288      %10 = OpTypeInt 32 0
289      %val = OpConstant %10 42
290      %ptr_ty = OpTypePointer Private %10
291      %20 = OpVariable %10 Private ; bad pointer type
292      %100 = OpFunction %void None %voidfn
293      %entry = OpLabel
294      %1 = OpAccessChain %ptr_ty %20
295      OpStore %1 %val
296      OpReturn
297   )"));
298   EXPECT_FALSE(p->BuildAndParseInternalModuleExceptFunctions());
299   EXPECT_THAT(p->error(), Eq("variable with ID 20 has non-pointer type 10"));
300 }
301 
TEST_F(SpvParserMemoryTest,EmitStatement_AccessChain_VectorSwizzle)302 TEST_F(SpvParserMemoryTest, EmitStatement_AccessChain_VectorSwizzle) {
303   const std::string assembly = Preamble() + R"(
304      OpName %1 "myvar"
305      %void = OpTypeVoid
306      %voidfn = OpTypeFunction %void
307      %uint = OpTypeInt 32 0
308      %store_ty = OpTypeVector %uint 4
309      %uint_2 = OpConstant %uint 2
310      %uint_42 = OpConstant %uint 42
311      %elem_ty = OpTypePointer Private %uint
312      %var_ty = OpTypePointer Private %store_ty
313      %1 = OpVariable %var_ty Private
314      %100 = OpFunction %void None %voidfn
315      %entry = OpLabel
316      %2 = OpAccessChain %elem_ty %1 %uint_2
317      OpStore %2 %uint_42
318      OpReturn
319      OpFunctionEnd
320   )";
321   auto p = parser(test::Assemble(assembly));
322   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions())
323       << assembly << p->error();
324   auto fe = p->function_emitter(100);
325   EXPECT_TRUE(fe.EmitBody());
326   auto ast_body = fe.ast_body();
327   EXPECT_THAT(test::ToString(p->program(), ast_body),
328               HasSubstr("myvar.z = 42u;"));
329 }
330 
TEST_F(SpvParserMemoryTest,EmitStatement_AccessChain_VectorConstOutOfBounds)331 TEST_F(SpvParserMemoryTest, EmitStatement_AccessChain_VectorConstOutOfBounds) {
332   const std::string assembly = Preamble() + R"(
333      OpName %1 "myvar"
334      %void = OpTypeVoid
335      %voidfn = OpTypeFunction %void
336      %uint = OpTypeInt 32 0
337      %store_ty = OpTypeVector %uint 4
338      %42 = OpConstant %uint 42
339      %uint_99 = OpConstant %uint 99
340      %elem_ty = OpTypePointer Private %uint
341      %var_ty = OpTypePointer Private %store_ty
342      %1 = OpVariable %var_ty Private
343      %100 = OpFunction %void None %voidfn
344      %entry = OpLabel
345      %2 = OpAccessChain %elem_ty %1 %42
346      OpStore %2 %uint_99
347      OpReturn
348      OpFunctionEnd
349   )";
350   auto p = parser(test::Assemble(assembly));
351   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions())
352       << assembly << p->error();
353   auto fe = p->function_emitter(100);
354   EXPECT_FALSE(fe.EmitBody());
355   EXPECT_THAT(p->error(), Eq("Access chain %2 index %42 value 42 is out of "
356                              "bounds for vector of 4 elements"));
357 }
358 
TEST_F(SpvParserMemoryTest,EmitStatement_AccessChain_VectorNonConstIndex)359 TEST_F(SpvParserMemoryTest, EmitStatement_AccessChain_VectorNonConstIndex) {
360   const std::string assembly = Preamble() + R"(
361      OpName %1 "myvar"
362      OpName %13 "a_dynamic_index"
363      %void = OpTypeVoid
364      %voidfn = OpTypeFunction %void
365      %uint = OpTypeInt 32 0
366      %store_ty = OpTypeVector %uint 4
367      %uint_2 = OpConstant %uint 2
368      %uint_42 = OpConstant %uint 42
369      %elem_ty = OpTypePointer Private %uint
370      %var_ty = OpTypePointer Private %store_ty
371      %1 = OpVariable %var_ty Private
372      %10 = OpVariable %var_ty Private
373      %100 = OpFunction %void None %voidfn
374      %entry = OpLabel
375      %11 = OpLoad %store_ty %10
376      %12 = OpCompositeExtract %uint %11 2
377      %13 = OpCopyObject %uint %12
378      %2 = OpAccessChain %elem_ty %1 %13
379      OpStore %2 %uint_42
380      OpReturn
381      OpFunctionEnd
382   )";
383   auto p = parser(test::Assemble(assembly));
384   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions())
385       << assembly << p->error();
386   auto fe = p->function_emitter(100);
387   EXPECT_TRUE(fe.EmitBody());
388   auto ast_body = fe.ast_body();
389   EXPECT_THAT(test::ToString(p->program(), ast_body),
390               HasSubstr("myvar[a_dynamic_index] = 42u;"));
391 }
392 
TEST_F(SpvParserMemoryTest,EmitStatement_AccessChain_VectorComponent_MultiUse)393 TEST_F(SpvParserMemoryTest,
394        EmitStatement_AccessChain_VectorComponent_MultiUse) {
395   // WGSL does not support pointer-to-vector-component, so test that we sink
396   // these pointers into the point of use.
397   const std::string assembly = Preamble() + R"(
398      OpName %1 "myvar"
399      %void = OpTypeVoid
400      %voidfn = OpTypeFunction %void
401      %uint = OpTypeInt 32 0
402      %store_ty = OpTypeVector %uint 4
403      %uint_2 = OpConstant %uint 2
404      %uint_42 = OpConstant %uint 42
405      %elem_ty = OpTypePointer Private %uint
406      %var_ty = OpTypePointer Private %store_ty
407      %1 = OpVariable %var_ty Private
408      %100 = OpFunction %void None %voidfn
409      %entry = OpLabel
410      %ptr = OpAccessChain %elem_ty %1 %uint_2
411      %load = OpLoad %uint %ptr
412      %result = OpIAdd %uint %load %uint_2
413      OpStore %ptr %result
414      OpReturn
415      OpFunctionEnd
416   )";
417   auto p = parser(test::Assemble(assembly));
418   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions())
419       << assembly << p->error();
420   auto fe = p->function_emitter(100);
421   EXPECT_TRUE(fe.EmitBody()) << p->error();
422   auto ast_body = fe.ast_body();
423   auto wgsl = test::ToString(p->program(), ast_body);
424   EXPECT_THAT(wgsl, Not(HasSubstr("&")));
425   EXPECT_THAT(wgsl, HasSubstr(" = myvar.z;"));
426   EXPECT_THAT(wgsl, HasSubstr("myvar.z = "));
427 }
428 
TEST_F(SpvParserMemoryTest,EmitStatement_AccessChain_VectorComponent_MultiUse_NonConstIndex)429 TEST_F(SpvParserMemoryTest,
430        EmitStatement_AccessChain_VectorComponent_MultiUse_NonConstIndex) {
431   // WGSL does not support pointer-to-vector-component, so test that we sink
432   // these pointers into the point of use.
433   const std::string assembly = Preamble() + R"(
434      OpName %1 "myvar"
435      %void = OpTypeVoid
436      %voidfn = OpTypeFunction %void
437      %uint = OpTypeInt 32 0
438      %store_ty = OpTypeVector %uint 4
439      %uint_2 = OpConstant %uint 2
440      %uint_42 = OpConstant %uint 42
441      %elem_ty = OpTypePointer Private %uint
442      %var_ty = OpTypePointer Private %store_ty
443      %1 = OpVariable %var_ty Private
444      %2 = OpVariable %elem_ty Private
445      %100 = OpFunction %void None %voidfn
446      %entry = OpLabel
447      %idx = OpLoad %uint %2
448      %ptr = OpAccessChain %elem_ty %1 %idx
449      %load = OpLoad %uint %ptr
450      %result = OpIAdd %uint %load %uint_2
451      OpStore %ptr %result
452      OpReturn
453      OpFunctionEnd
454   )";
455   auto p = parser(test::Assemble(assembly));
456   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions())
457       << assembly << p->error();
458   auto fe = p->function_emitter(100);
459   EXPECT_TRUE(fe.EmitBody()) << p->error();
460   auto ast_body = fe.ast_body();
461   auto wgsl = test::ToString(p->program(), ast_body);
462   EXPECT_THAT(wgsl, Not(HasSubstr("&")));
463   EXPECT_THAT(wgsl, HasSubstr(" = myvar[x_12];"));
464   EXPECT_THAT(wgsl, HasSubstr("myvar[x_12] = "));
465 }
466 
TEST_F(SpvParserMemoryTest,EmitStatement_AccessChain_VectorComponent_SinkThroughChain)467 TEST_F(SpvParserMemoryTest,
468        EmitStatement_AccessChain_VectorComponent_SinkThroughChain) {
469   // Test that we can sink a pointer-to-vector-component through a chain of
470   // instructions that propagate it.
471   const std::string assembly = Preamble() + R"(
472      OpName %1 "myvar"
473      %void = OpTypeVoid
474      %voidfn = OpTypeFunction %void
475      %uint = OpTypeInt 32 0
476      %store_ty = OpTypeVector %uint 4
477      %uint_2 = OpConstant %uint 2
478      %uint_42 = OpConstant %uint 42
479      %elem_ty = OpTypePointer Private %uint
480      %var_ty = OpTypePointer Private %store_ty
481      %1 = OpVariable %var_ty Private
482      %100 = OpFunction %void None %voidfn
483      %entry = OpLabel
484      %ptr = OpAccessChain %elem_ty %1 %uint_2
485      %ptr2 = OpCopyObject %elem_ty %ptr
486      %ptr3 = OpInBoundsAccessChain %elem_ty %ptr2
487      %ptr4 = OpAccessChain %elem_ty %ptr3
488      %load = OpLoad %uint %ptr3
489      %result = OpIAdd %uint %load %uint_2
490      OpStore %ptr4 %result
491      OpReturn
492      OpFunctionEnd
493   )";
494   auto p = parser(test::Assemble(assembly));
495   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions())
496       << assembly << p->error();
497   auto fe = p->function_emitter(100);
498   EXPECT_TRUE(fe.EmitBody()) << p->error();
499   auto ast_body = fe.ast_body();
500   auto wgsl = test::ToString(p->program(), ast_body);
501   EXPECT_THAT(wgsl, Not(HasSubstr("&")));
502   EXPECT_THAT(wgsl, HasSubstr(" = myvar.z;"));
503   EXPECT_THAT(wgsl, HasSubstr("myvar.z = "));
504 }
505 
TEST_F(SpvParserMemoryTest,EmitStatement_AccessChain_Matrix)506 TEST_F(SpvParserMemoryTest, EmitStatement_AccessChain_Matrix) {
507   const std::string assembly = Preamble() + R"(
508      OpName %1 "myvar"
509      %void = OpTypeVoid
510      %voidfn = OpTypeFunction %void
511      %float = OpTypeFloat 32
512      %v4float = OpTypeVector %float 4
513      %m3v4float = OpTypeMatrix %v4float 3
514      %elem_ty = OpTypePointer Private %v4float
515      %var_ty = OpTypePointer Private %m3v4float
516      %uint = OpTypeInt 32 0
517      %uint_2 = OpConstant %uint 2
518      %float_42 = OpConstant %float 42
519      %v4float_42 = OpConstantComposite %v4float %float_42 %float_42 %float_42 %float_42
520 
521      %1 = OpVariable %var_ty Private
522      %100 = OpFunction %void None %voidfn
523      %entry = OpLabel
524      %2 = OpAccessChain %elem_ty %1 %uint_2
525      OpStore %2 %v4float_42
526      OpReturn
527      OpFunctionEnd
528   )";
529   auto p = parser(test::Assemble(assembly));
530   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions())
531       << assembly << p->error();
532   auto fe = p->function_emitter(100);
533   EXPECT_TRUE(fe.EmitBody());
534   auto ast_body = fe.ast_body();
535   EXPECT_THAT(test::ToString(p->program(), ast_body),
536               HasSubstr("myvar[2u] = vec4<f32>(42.0, 42.0, 42.0, 42.0);"));
537 }
538 
TEST_F(SpvParserMemoryTest,EmitStatement_AccessChain_Array)539 TEST_F(SpvParserMemoryTest, EmitStatement_AccessChain_Array) {
540   const std::string assembly = Preamble() + R"(
541      OpName %1 "myvar"
542      %void = OpTypeVoid
543      %voidfn = OpTypeFunction %void
544      %float = OpTypeFloat 32
545      %v4float = OpTypeVector %float 4
546      %m3v4float = OpTypeMatrix %v4float 3
547      %elem_ty = OpTypePointer Private %v4float
548      %var_ty = OpTypePointer Private %m3v4float
549      %uint = OpTypeInt 32 0
550      %uint_2 = OpConstant %uint 2
551      %float_42 = OpConstant %float 42
552      %v4float_42 = OpConstantComposite %v4float %float_42 %float_42 %float_42 %float_42
553 
554      %1 = OpVariable %var_ty Private
555      %100 = OpFunction %void None %voidfn
556      %entry = OpLabel
557      %2 = OpAccessChain %elem_ty %1 %uint_2
558      OpStore %2 %v4float_42
559      OpReturn
560      OpFunctionEnd
561   )";
562   auto p = parser(test::Assemble(assembly));
563   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions())
564       << assembly << p->error();
565   auto fe = p->function_emitter(100);
566   EXPECT_TRUE(fe.EmitBody());
567   auto ast_body = fe.ast_body();
568   EXPECT_THAT(test::ToString(p->program(), ast_body),
569               HasSubstr("myvar[2u] = vec4<f32>(42.0, 42.0, 42.0, 42.0);"));
570 }
571 
TEST_F(SpvParserMemoryTest,EmitStatement_AccessChain_Struct)572 TEST_F(SpvParserMemoryTest, EmitStatement_AccessChain_Struct) {
573   const std::string assembly = Preamble() + R"(
574      OpName %1 "myvar"
575      OpMemberName %strct 1 "age"
576      %void = OpTypeVoid
577      %voidfn = OpTypeFunction %void
578      %float = OpTypeFloat 32
579      %float_42 = OpConstant %float 42
580      %strct = OpTypeStruct %float %float
581      %elem_ty = OpTypePointer Private %float
582      %var_ty = OpTypePointer Private %strct
583      %uint = OpTypeInt 32 0
584      %uint_1 = OpConstant %uint 1
585 
586      %1 = OpVariable %var_ty Private
587      %100 = OpFunction %void None %voidfn
588      %entry = OpLabel
589      %2 = OpAccessChain %elem_ty %1 %uint_1
590      OpStore %2 %float_42
591      OpReturn
592      OpFunctionEnd
593   )";
594   auto p = parser(test::Assemble(assembly));
595   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions())
596       << assembly << p->error();
597   auto fe = p->function_emitter(100);
598   EXPECT_TRUE(fe.EmitBody());
599   auto ast_body = fe.ast_body();
600   EXPECT_THAT(test::ToString(p->program(), ast_body),
601               HasSubstr("myvar.age = 42.0;"));
602 }
603 
TEST_F(SpvParserMemoryTest,EmitStatement_AccessChain_Struct_DifferOnlyMemberName)604 TEST_F(SpvParserMemoryTest,
605        EmitStatement_AccessChain_Struct_DifferOnlyMemberName) {
606   // The spirv-opt internal representation will map both structs to the
607   // same canonicalized type, because it doesn't care about member names.
608   // But we care about member names when producing a member-access expression.
609   // crbug.com/tint/213
610   const std::string assembly = Preamble() + R"(
611      OpName %1 "myvar"
612      OpName %10 "myvar2"
613      OpMemberName %strct 1 "age"
614      OpMemberName %strct2 1 "ancientness"
615      %void = OpTypeVoid
616      %voidfn = OpTypeFunction %void
617      %float = OpTypeFloat 32
618      %float_42 = OpConstant %float 42
619      %float_420 = OpConstant %float 420
620      %strct = OpTypeStruct %float %float
621      %strct2 = OpTypeStruct %float %float
622      %elem_ty = OpTypePointer Private %float
623      %var_ty = OpTypePointer Private %strct
624      %var2_ty = OpTypePointer Private %strct2
625      %uint = OpTypeInt 32 0
626      %uint_1 = OpConstant %uint 1
627 
628      %1 = OpVariable %var_ty Private
629      %10 = OpVariable %var2_ty Private
630      %100 = OpFunction %void None %voidfn
631      %entry = OpLabel
632      %2 = OpAccessChain %elem_ty %1 %uint_1
633      OpStore %2 %float_42
634      %20 = OpAccessChain %elem_ty %10 %uint_1
635      OpStore %20 %float_420
636      OpReturn
637      OpFunctionEnd
638   )";
639   auto p = parser(test::Assemble(assembly));
640   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions())
641       << assembly << p->error();
642   auto fe = p->function_emitter(100);
643   EXPECT_TRUE(fe.EmitBody());
644   auto ast_body = fe.ast_body();
645   EXPECT_THAT(test::ToString(p->program(), ast_body),
646               HasSubstr(R"(myvar.age = 42.0;
647 myvar2.ancientness = 420.0;
648 )"));
649 }
650 
TEST_F(SpvParserMemoryTest,EmitStatement_AccessChain_StructNonConstIndex)651 TEST_F(SpvParserMemoryTest, EmitStatement_AccessChain_StructNonConstIndex) {
652   const std::string assembly = Preamble() + R"(
653      OpName %1 "myvar"
654      OpMemberName %55 1 "age"
655      %void = OpTypeVoid
656      %voidfn = OpTypeFunction %void
657      %float = OpTypeFloat 32
658      %float_42 = OpConstant %float 42
659      %55 = OpTypeStruct %float %float
660      %elem_ty = OpTypePointer Private %float
661      %var_ty = OpTypePointer Private %55
662      %uint = OpTypeInt 32 0
663      %uint_1 = OpConstant %uint 1
664      %uint_ptr = OpTypePointer Private %uint
665      %uintvar = OpVariable %uint_ptr Private
666 
667      %1 = OpVariable %var_ty Private
668      %100 = OpFunction %void None %voidfn
669      %entry = OpLabel
670      %10 = OpLoad %uint %uintvar
671      %2 = OpAccessChain %elem_ty %1 %10
672      OpStore %2 %float_42
673      OpReturn
674      OpFunctionEnd
675   )";
676   auto p = parser(test::Assemble(assembly));
677   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions())
678       << assembly << p->error();
679   auto fe = p->function_emitter(100);
680   EXPECT_FALSE(fe.EmitBody());
681   EXPECT_THAT(p->error(), Eq("Access chain %2 index %10 is a non-constant "
682                              "index into a structure %55"));
683 }
684 
TEST_F(SpvParserMemoryTest,EmitStatement_AccessChain_StructConstOutOfBounds)685 TEST_F(SpvParserMemoryTest, EmitStatement_AccessChain_StructConstOutOfBounds) {
686   const std::string assembly = Preamble() + R"(
687      OpName %1 "myvar"
688      OpMemberName %55 1 "age"
689      %void = OpTypeVoid
690      %voidfn = OpTypeFunction %void
691      %float = OpTypeFloat 32
692      %float_42 = OpConstant %float 42
693      %55 = OpTypeStruct %float %float
694      %elem_ty = OpTypePointer Private %float
695      %var_ty = OpTypePointer Private %55
696      %uint = OpTypeInt 32 0
697      %uint_99 = OpConstant %uint 99
698 
699      %1 = OpVariable %var_ty Private
700      %100 = OpFunction %void None %voidfn
701      %entry = OpLabel
702      %2 = OpAccessChain %elem_ty %1 %uint_99
703      OpStore %2 %float_42
704      OpReturn
705      OpFunctionEnd
706   )";
707   auto p = parser(test::Assemble(assembly));
708   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions())
709       << assembly << p->error();
710   auto fe = p->function_emitter(100);
711   EXPECT_FALSE(fe.EmitBody());
712   EXPECT_THAT(p->error(), Eq("Access chain %2 index value 99 is out of bounds "
713                              "for structure %55 having 2 members"));
714 }
715 
TEST_F(SpvParserMemoryTest,EmitStatement_AccessChain_Struct_RuntimeArray)716 TEST_F(SpvParserMemoryTest, EmitStatement_AccessChain_Struct_RuntimeArray) {
717   const std::string assembly = Preamble() + R"(
718      OpName %1 "myvar"
719      OpMemberName %strct 1 "age"
720 
721      OpDecorate %1 DescriptorSet 0
722      OpDecorate %1 Binding 0
723      OpDecorate %strct BufferBlock
724      OpMemberDecorate %strct 0 Offset 0
725      OpMemberDecorate %strct 1 Offset 4
726      OpDecorate %rtarr ArrayStride 4
727 
728      %void = OpTypeVoid
729      %voidfn = OpTypeFunction %void
730      %float = OpTypeFloat 32
731      %float_42 = OpConstant %float 42
732      %rtarr = OpTypeRuntimeArray %float
733      %strct = OpTypeStruct %float %rtarr
734      %elem_ty = OpTypePointer Uniform %float
735      %var_ty = OpTypePointer Uniform %strct
736      %uint = OpTypeInt 32 0
737      %uint_1 = OpConstant %uint 1
738      %uint_2 = OpConstant %uint 2
739 
740      %1 = OpVariable %var_ty Uniform
741      %100 = OpFunction %void None %voidfn
742      %entry = OpLabel
743      %2 = OpAccessChain %elem_ty %1 %uint_1 %uint_2
744      OpStore %2 %float_42
745      OpReturn
746      OpFunctionEnd
747   )";
748   auto p = parser(test::Assemble(assembly));
749   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions())
750       << assembly << p->error();
751   auto fe = p->function_emitter(100);
752   EXPECT_TRUE(fe.EmitBody());
753   auto ast_body = fe.ast_body();
754   EXPECT_THAT(test::ToString(p->program(), ast_body),
755               HasSubstr("myvar.age[2u] = 42.0;"));
756 }
757 
TEST_F(SpvParserMemoryTest,EmitStatement_AccessChain_Compound_Matrix_Vector)758 TEST_F(SpvParserMemoryTest, EmitStatement_AccessChain_Compound_Matrix_Vector) {
759   const std::string assembly = Preamble() + R"(
760      OpName %1 "myvar"
761      %void = OpTypeVoid
762      %voidfn = OpTypeFunction %void
763      %float = OpTypeFloat 32
764      %v4float = OpTypeVector %float 4
765      %m3v4float = OpTypeMatrix %v4float 3
766      %elem_ty = OpTypePointer Private %float
767      %var_ty = OpTypePointer Private %m3v4float
768      %uint = OpTypeInt 32 0
769      %uint_2 = OpConstant %uint 2
770      %uint_3 = OpConstant %uint 3
771      %float_42 = OpConstant %float 42
772 
773      %1 = OpVariable %var_ty Private
774      %100 = OpFunction %void None %voidfn
775      %entry = OpLabel
776      %2 = OpAccessChain %elem_ty %1 %uint_2 %uint_3
777      OpStore %2 %float_42
778      OpReturn
779      OpFunctionEnd
780   )";
781   auto p = parser(test::Assemble(assembly));
782   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions())
783       << assembly << p->error();
784   auto fe = p->function_emitter(100);
785   EXPECT_TRUE(fe.EmitBody());
786   auto ast_body = fe.ast_body();
787   EXPECT_THAT(test::ToString(p->program(), ast_body),
788               HasSubstr("myvar[2u].w = 42.0;"));
789 }
790 
TEST_F(SpvParserMemoryTest,EmitStatement_AccessChain_InvalidPointeeType)791 TEST_F(SpvParserMemoryTest, EmitStatement_AccessChain_InvalidPointeeType) {
792   const std::string assembly = Preamble() + R"(
793      OpName %1 "myvar"
794      %55 = OpTypeVoid
795      %voidfn = OpTypeFunction %55
796      %float = OpTypeFloat 32
797      %60 = OpTypePointer Private %55
798      %var_ty = OpTypePointer Private %60
799      %uint = OpTypeInt 32 0
800      %uint_2 = OpConstant %uint 2
801 
802      %1 = OpVariable %var_ty Private
803      %100 = OpFunction %55 None %voidfn
804      %entry = OpLabel
805      %2 = OpAccessChain %60 %1 %uint_2
806      OpReturn
807      OpFunctionEnd
808   )";
809   auto p = parser(test::Assemble(assembly));
810   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions())
811       << assembly << p->error();
812   auto fe = p->function_emitter(100);
813   EXPECT_FALSE(fe.EmitBody());
814   EXPECT_THAT(p->error(),
815               HasSubstr("Access chain with unknown or invalid pointee type "
816                         "%60: %60 = OpTypePointer Private %55"));
817 }
818 
TEST_F(SpvParserMemoryTest,EmitStatement_AccessChain_DereferenceBase)819 TEST_F(SpvParserMemoryTest, EmitStatement_AccessChain_DereferenceBase) {
820   // The base operand to OpAccessChain may have to be dereferenced first.
821   // crbug.com/tint/737
822   const std::string assembly = Preamble() + R"(
823      %void = OpTypeVoid
824      %voidfn = OpTypeFunction %void
825 
826      %uint = OpTypeInt 32 0
827      %v2uint = OpTypeVector %uint 2
828      %elem_ty = OpTypePointer Private %uint
829      %vec_ty = OpTypePointer Private %v2uint
830 
831      %ptrfn = OpTypeFunction %void %vec_ty
832 
833      %uint_0 = OpConstant %uint 0
834 
835      ; The shortest way to make a pointer example is as a function parameter.
836      %200 = OpFunction %void None %ptrfn
837      %1 = OpFunctionParameter %vec_ty
838      %entry = OpLabel
839      %2 = OpAccessChain %elem_ty %1 %uint_0
840      %3 = OpLoad %uint %2
841      OpReturn
842      OpFunctionEnd
843 
844      %100 = OpFunction %void None %voidfn
845      %main_entry = OpLabel
846      OpReturn
847      OpFunctionEnd
848   )";
849   auto p = parser(test::Assemble(assembly));
850   ASSERT_TRUE(p->BuildAndParseInternalModule());
851   const auto got = test::ToString(p->program());
852   const std::string expected = R"(fn x_200(x_1 : ptr<private, vec2<u32>>) {
853   let x_3 : u32 = (*(x_1)).x;
854   return;
855 }
856 
857 fn main_1() {
858   return;
859 }
860 
861 [[stage(fragment)]]
862 fn main() {
863   main_1();
864 }
865 )";
866   EXPECT_EQ(got, expected) << got;
867 }
868 
TEST_F(SpvParserMemoryTest,EmitStatement_AccessChain_InferFunctionStorageClass)869 TEST_F(SpvParserMemoryTest,
870        EmitStatement_AccessChain_InferFunctionStorageClass) {
871   // An access chain can have no indices. When the base is a Function variable,
872   // the reference type has no explicit storage class in the AST representation.
873   // But the pointer type for the let declaration must have an explicit
874   // 'function' storage class. From crbug.com/tint/807
875   const std::string assembly = R"(
876 OpCapability Shader
877 OpMemoryModel Logical Simple
878 OpEntryPoint Fragment %main "main"
879 OpExecutionMode %main OriginUpperLeft
880 
881 %uint = OpTypeInt 32 0
882 %ptr_ty = OpTypePointer Function %uint
883 
884   %void = OpTypeVoid
885 %voidfn = OpTypeFunction %void
886   %main = OpFunction %void None %voidfn
887  %entry = OpLabel
888      %1 = OpVariable %ptr_ty Function
889      %2 = OpAccessChain %ptr_ty %1
890           OpReturn
891           OpFunctionEnd
892 )";
893   auto p = parser(test::Assemble(assembly));
894   ASSERT_TRUE(p->BuildAndParseInternalModule()) << assembly;
895   const auto got = test::ToString(p->program());
896   const std::string expected = R"(fn main_1() {
897   var x_1 : u32;
898   let x_2 : ptr<function, u32> = &(x_1);
899   return;
900 }
901 
902 [[stage(fragment)]]
903 fn main() {
904   main_1();
905 }
906 )";
907   EXPECT_EQ(got, expected) << got;
908 }
909 
OldStorageBufferPreamble()910 std::string OldStorageBufferPreamble() {
911   return Preamble() + R"(
912      OpName %myvar "myvar"
913 
914      OpDecorate %myvar DescriptorSet 0
915      OpDecorate %myvar Binding 0
916 
917      OpDecorate %struct BufferBlock
918      OpMemberDecorate %struct 0 Offset 0
919      OpMemberDecorate %struct 1 Offset 4
920      OpDecorate %arr ArrayStride 4
921 
922      %void = OpTypeVoid
923      %voidfn = OpTypeFunction %void
924      %uint = OpTypeInt 32 0
925 
926      %uint_0 = OpConstant %uint 0
927      %uint_1 = OpConstant %uint 1
928 
929      %arr = OpTypeRuntimeArray %uint
930      %struct = OpTypeStruct %uint %arr
931      %ptr_struct = OpTypePointer Uniform %struct
932      %ptr_uint = OpTypePointer Uniform %uint
933 
934      %myvar = OpVariable %ptr_struct Uniform
935   )";
936 }
937 
TEST_F(SpvParserMemoryTest,RemapStorageBuffer_TypesAndVarDeclarations)938 TEST_F(SpvParserMemoryTest, RemapStorageBuffer_TypesAndVarDeclarations) {
939   // Enusure we get the right module-scope declaration.  This tests translation
940   // of the structure type, arrays of the structure, pointers to them, and
941   // OpVariable of these.
942   const auto assembly = OldStorageBufferPreamble() + R"(
943   ; The preamble declared %100 to be an entry point, so supply it.
944   %100 = OpFunction %void None %voidfn
945   %entry = OpLabel
946   OpReturn
947   OpFunctionEnd
948 )";
949   auto p = parser(test::Assemble(assembly));
950   ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions())
951       << assembly << p->error();
952   const auto module_str = test::ToString(p->program());
953   EXPECT_THAT(module_str, HasSubstr(R"(type RTArr = [[stride(4)]] array<u32>;
954 
955 [[block]]
956 struct S {
957   field0 : u32;
958   field1 : RTArr;
959 };
960 
961 [[group(0), binding(0)]] var<storage, read_write> myvar : S;
962 )"));
963 }
964 
TEST_F(SpvParserMemoryTest,RemapStorageBuffer_ThroughAccessChain_NonCascaded)965 TEST_F(SpvParserMemoryTest, RemapStorageBuffer_ThroughAccessChain_NonCascaded) {
966   const auto assembly = OldStorageBufferPreamble() + R"(
967   %100 = OpFunction %void None %voidfn
968   %entry = OpLabel
969 
970   ; the scalar element
971   %1 = OpAccessChain %ptr_uint %myvar %uint_0
972   OpStore %1 %uint_0
973 
974   ; element in the runtime array
975   %2 = OpAccessChain %ptr_uint %myvar %uint_1 %uint_1
976   OpStore %2 %uint_0
977 
978   OpReturn
979   OpFunctionEnd
980 )";
981   auto p = parser(test::Assemble(assembly));
982   ASSERT_TRUE(p->BuildAndParseInternalModule()) << assembly << p->error();
983   auto fe = p->function_emitter(100);
984   EXPECT_TRUE(fe.EmitBody()) << p->error();
985   auto ast_body = fe.ast_body();
986   const auto got = test::ToString(p->program(), ast_body);
987   EXPECT_THAT(got, HasSubstr(R"(myvar.field0 = 0u;
988 myvar.field1[1u] = 0u;
989 )"));
990 }
991 
TEST_F(SpvParserMemoryTest,RemapStorageBuffer_ThroughAccessChain_NonCascaded_InBoundsAccessChain)992 TEST_F(SpvParserMemoryTest,
993        RemapStorageBuffer_ThroughAccessChain_NonCascaded_InBoundsAccessChain) {
994   // Like the previous test, but using OpInBoundsAccessChain.
995   const auto assembly = OldStorageBufferPreamble() + R"(
996   %100 = OpFunction %void None %voidfn
997   %entry = OpLabel
998 
999   ; the scalar element
1000   %1 = OpInBoundsAccessChain %ptr_uint %myvar %uint_0
1001   OpStore %1 %uint_0
1002 
1003   ; element in the runtime array
1004   %2 = OpInBoundsAccessChain %ptr_uint %myvar %uint_1 %uint_1
1005   OpStore %2 %uint_0
1006 
1007   OpReturn
1008   OpFunctionEnd
1009 )";
1010   auto p = parser(test::Assemble(assembly));
1011   ASSERT_TRUE(p->BuildAndParseInternalModule()) << assembly << p->error();
1012   auto fe = p->function_emitter(100);
1013   EXPECT_TRUE(fe.EmitBody()) << p->error();
1014   auto ast_body = fe.ast_body();
1015   const auto got = test::ToString(p->program(), ast_body);
1016   EXPECT_THAT(got, HasSubstr(R"(myvar.field0 = 0u;
1017 myvar.field1[1u] = 0u;
1018 )")) << got
1019      << p->error();
1020 }
1021 
TEST_F(SpvParserMemoryTest,RemapStorageBuffer_ThroughAccessChain_Cascaded)1022 TEST_F(SpvParserMemoryTest, RemapStorageBuffer_ThroughAccessChain_Cascaded) {
1023   const auto assembly = OldStorageBufferPreamble() + R"(
1024   %ptr_rtarr = OpTypePointer Uniform %arr
1025   %100 = OpFunction %void None %voidfn
1026   %entry = OpLabel
1027 
1028   ; get the runtime array
1029   %1 = OpAccessChain %ptr_rtarr %myvar %uint_1
1030   ; now an element in it
1031   %2 = OpAccessChain %ptr_uint %1 %uint_1
1032   OpStore %2 %uint_0
1033 
1034   OpReturn
1035   OpFunctionEnd
1036 )";
1037   auto p = parser(test::Assemble(assembly));
1038   ASSERT_TRUE(p->BuildAndParseInternalModule()) << assembly << p->error();
1039   auto fe = p->function_emitter(100);
1040   EXPECT_TRUE(fe.EmitBody()) << p->error();
1041   auto ast_body = fe.ast_body();
1042   EXPECT_THAT(test::ToString(p->program(), ast_body),
1043               HasSubstr("myvar.field1[1u] = 0u;"))
1044       << p->error();
1045 }
1046 
TEST_F(SpvParserMemoryTest,RemapStorageBuffer_ThroughCopyObject_WithoutHoisting)1047 TEST_F(SpvParserMemoryTest,
1048        RemapStorageBuffer_ThroughCopyObject_WithoutHoisting) {
1049   // Generates a const declaration directly.
1050   // We have to do a bunch of storage class tracking for locally
1051   // defined values in order to get the right pointer-to-storage-buffer
1052   // value type for the const declration.
1053   const auto assembly = OldStorageBufferPreamble() + R"(
1054   %100 = OpFunction %void None %voidfn
1055   %entry = OpLabel
1056 
1057   %1 = OpAccessChain %ptr_uint %myvar %uint_1 %uint_1
1058   %2 = OpCopyObject %ptr_uint %1
1059   OpStore %2 %uint_0
1060 
1061   OpReturn
1062   OpFunctionEnd
1063 )";
1064   auto p = parser(test::Assemble(assembly));
1065   ASSERT_TRUE(p->BuildAndParseInternalModule()) << assembly << p->error();
1066   auto fe = p->function_emitter(100);
1067   EXPECT_TRUE(fe.EmitBody()) << p->error();
1068   auto ast_body = fe.ast_body();
1069   EXPECT_THAT(test::ToString(p->program(), ast_body),
1070               HasSubstr(R"(let x_2 : ptr<storage, u32> = &(myvar.field1[1u]);
1071 *(x_2) = 0u;
1072 )")) << p->error();
1073 
1074   p->SkipDumpingPending(
1075       "crbug.com/tint/1041 track access mode in spirv-reader parser type");
1076 }
1077 
TEST_F(SpvParserMemoryTest,RemapStorageBuffer_ThroughCopyObject_WithHoisting)1078 TEST_F(SpvParserMemoryTest, RemapStorageBuffer_ThroughCopyObject_WithHoisting) {
1079   // TODO(dneto): Hoisting non-storable values (pointers) is not yet supported.
1080   // It's debatable whether this test should run at all.
1081   // crbug.com/tint/98
1082 
1083   // Like the previous test, but the declaration for the copy-object
1084   // has its declaration hoisted.
1085   const auto assembly = OldStorageBufferPreamble() + R"(
1086   %bool = OpTypeBool
1087   %cond = OpConstantTrue %bool
1088 
1089   %100 = OpFunction %void None %voidfn
1090 
1091   %entry = OpLabel
1092   OpSelectionMerge %99 None
1093   OpBranchConditional %cond %20 %30
1094 
1095   %20 = OpLabel
1096   %1 = OpAccessChain %ptr_uint %myvar %uint_1 %uint_1
1097   ; this definintion dominates the use in %99
1098   %2 = OpCopyObject %ptr_uint %1
1099   OpBranch %99
1100 
1101   %30 = OpLabel
1102   OpReturn
1103 
1104   %99 = OpLabel
1105   OpStore %2 %uint_0
1106   OpReturn
1107 
1108   OpFunctionEnd
1109 )";
1110   auto p = parser(test::Assemble(assembly));
1111   ASSERT_TRUE(p->BuildAndParseInternalModule()) << assembly << p->error();
1112   auto fe = p->function_emitter(100);
1113   EXPECT_TRUE(fe.EmitBody()) << p->error();
1114   auto ast_body = fe.ast_body();
1115   EXPECT_EQ(test::ToString(p->program(), ast_body),
1116             R"(var x_2 : ptr<storage, u32>;
1117 if (true) {
1118   x_2 = &(myvar.field1[1u]);
1119 } else {
1120   return;
1121 }
1122 x_2 = 0u;
1123 return;
1124 )") << p->error();
1125   p->SkipDumpingPending("crbug.com/tint/98");
1126 }
1127 
TEST_F(SpvParserMemoryTest,DISABLED_RemapStorageBuffer_ThroughFunctionCall)1128 TEST_F(SpvParserMemoryTest, DISABLED_RemapStorageBuffer_ThroughFunctionCall) {
1129   // WGSL does not support pointer-to-storage-buffer as function parameter
1130 }
TEST_F(SpvParserMemoryTest,DISABLED_RemapStorageBuffer_ThroughFunctionParameter)1131 TEST_F(SpvParserMemoryTest,
1132        DISABLED_RemapStorageBuffer_ThroughFunctionParameter) {
1133   // WGSL does not support pointer-to-storage-buffer as function parameter
1134 }
1135 
RuntimeArrayPreamble()1136 std::string RuntimeArrayPreamble() {
1137   return R"(
1138      OpCapability Shader
1139      OpMemoryModel Logical Simple
1140      OpEntryPoint Fragment %100 "main"
1141      OpExecutionMode %100 OriginUpperLeft
1142 
1143      OpName %myvar "myvar"
1144      OpMemberName %struct 0 "first"
1145      OpMemberName %struct 1 "rtarr"
1146 
1147      OpDecorate %struct Block
1148      OpMemberDecorate %struct 0 Offset 0
1149      OpMemberDecorate %struct 1 Offset 4
1150      OpDecorate %arr ArrayStride 4
1151 
1152      OpDecorate %myvar DescriptorSet 0
1153      OpDecorate %myvar Binding 0
1154 
1155      %void = OpTypeVoid
1156      %voidfn = OpTypeFunction %void
1157      %uint = OpTypeInt 32 0
1158 
1159      %uint_0 = OpConstant %uint 0
1160      %uint_1 = OpConstant %uint 1
1161 
1162      %arr = OpTypeRuntimeArray %uint
1163      %struct = OpTypeStruct %uint %arr
1164      %ptr_struct = OpTypePointer StorageBuffer %struct
1165      %ptr_uint = OpTypePointer StorageBuffer %uint
1166 
1167      %myvar = OpVariable %ptr_struct StorageBuffer
1168   )";
1169 }
1170 
TEST_F(SpvParserMemoryTest,ArrayLength_FromVar)1171 TEST_F(SpvParserMemoryTest, ArrayLength_FromVar) {
1172   const auto assembly = RuntimeArrayPreamble() + R"(
1173 
1174   %100 = OpFunction %void None %voidfn
1175 
1176   %entry = OpLabel
1177   %1 = OpArrayLength %uint %myvar 1
1178   OpReturn
1179   OpFunctionEnd
1180 )";
1181   auto p = parser(test::Assemble(assembly));
1182   ASSERT_TRUE(p->BuildAndParseInternalModule()) << assembly << p->error();
1183   auto fe = p->function_emitter(100);
1184   EXPECT_TRUE(fe.EmitBody()) << p->error();
1185   auto ast_body = fe.ast_body();
1186   const auto body_str = test::ToString(p->program(), ast_body);
1187   EXPECT_THAT(body_str,
1188               HasSubstr("let x_1 : u32 = arrayLength(&(myvar.rtarr));"))
1189       << body_str;
1190 }
1191 
TEST_F(SpvParserMemoryTest,ArrayLength_FromCopyObject)1192 TEST_F(SpvParserMemoryTest, ArrayLength_FromCopyObject) {
1193   const auto assembly = RuntimeArrayPreamble() + R"(
1194 
1195   %100 = OpFunction %void None %voidfn
1196 
1197   %entry = OpLabel
1198   %2 = OpCopyObject %ptr_struct %myvar
1199   %1 = OpArrayLength %uint %2 1
1200   OpReturn
1201   OpFunctionEnd
1202 )";
1203   auto p = parser(test::Assemble(assembly));
1204   ASSERT_TRUE(p->BuildAndParseInternalModule()) << assembly << p->error();
1205   auto fe = p->function_emitter(100);
1206   EXPECT_TRUE(fe.EmitBody()) << p->error();
1207   auto ast_body = fe.ast_body();
1208   const auto body_str = test::ToString(p->program(), ast_body);
1209   EXPECT_THAT(body_str, HasSubstr(R"(let x_2 : ptr<storage, S> = &(myvar);
1210 let x_1 : u32 = arrayLength(&((*(x_2)).rtarr));
1211 )")) << body_str;
1212 
1213   p->SkipDumpingPending(
1214       "crbug.com/tint/1041 track access mode in spirv-reader parser type");
1215 }
1216 
TEST_F(SpvParserMemoryTest,ArrayLength_FromAccessChain)1217 TEST_F(SpvParserMemoryTest, ArrayLength_FromAccessChain) {
1218   const auto assembly = RuntimeArrayPreamble() + R"(
1219 
1220   %100 = OpFunction %void None %voidfn
1221 
1222   %entry = OpLabel
1223   %2 = OpAccessChain %ptr_struct %myvar ; no indices
1224   %1 = OpArrayLength %uint %2 1
1225   OpReturn
1226   OpFunctionEnd
1227 )";
1228   auto p = parser(test::Assemble(assembly));
1229   ASSERT_TRUE(p->BuildAndParseInternalModule()) << assembly << p->error();
1230   auto fe = p->function_emitter(100);
1231   EXPECT_TRUE(fe.EmitBody()) << p->error();
1232   auto ast_body = fe.ast_body();
1233   const auto body_str = test::ToString(p->program(), ast_body);
1234   EXPECT_THAT(body_str,
1235               HasSubstr("let x_1 : u32 = arrayLength(&(myvar.rtarr));"))
1236       << body_str;
1237 }
1238 
InvalidPointerPreamble()1239 std::string InvalidPointerPreamble() {
1240   return R"(
1241 OpCapability Shader
1242 OpMemoryModel Logical Simple
1243 OpEntryPoint Fragment %main "main"
1244 OpExecutionMode %main OriginUpperLeft
1245 
1246 %uint = OpTypeInt 32 0
1247 %ptr_ty = OpTypePointer Function %uint
1248 
1249   %void = OpTypeVoid
1250 %voidfn = OpTypeFunction %void
1251 )";
1252 }
1253 
TEST_F(SpvParserMemoryTest,InvalidPointer_Undef_ModuleScope_IsError)1254 TEST_F(SpvParserMemoryTest, InvalidPointer_Undef_ModuleScope_IsError) {
1255   const std::string assembly = InvalidPointerPreamble() + R"(
1256  %ptr = OpUndef %ptr_ty
1257 
1258   %main = OpFunction %void None %voidfn
1259  %entry = OpLabel
1260      %1 = OpCopyObject %ptr_ty %ptr
1261      %2 = OpAccessChain %ptr_ty %ptr
1262      %3 = OpInBoundsAccessChain %ptr_ty %ptr
1263 ; now show the invalid pointer propagates
1264      %10 = OpCopyObject %ptr_ty %1
1265      %20 = OpAccessChain %ptr_ty %2
1266      %30 = OpInBoundsAccessChain %ptr_ty %3
1267           OpReturn
1268           OpFunctionEnd
1269 )";
1270   auto p = parser(test::Assemble(assembly));
1271   EXPECT_FALSE(p->BuildAndParseInternalModule()) << assembly;
1272   EXPECT_EQ(p->error(), "undef pointer is not valid: %9 = OpUndef %6");
1273 }
1274 
TEST_F(SpvParserMemoryTest,InvalidPointer_Undef_FunctionScope_IsError)1275 TEST_F(SpvParserMemoryTest, InvalidPointer_Undef_FunctionScope_IsError) {
1276   const std::string assembly = InvalidPointerPreamble() + R"(
1277 
1278   %main = OpFunction %void None %voidfn
1279  %entry = OpLabel
1280    %ptr = OpUndef %ptr_ty
1281           OpReturn
1282           OpFunctionEnd
1283 )";
1284   auto p = parser(test::Assemble(assembly));
1285   EXPECT_FALSE(p->BuildAndParseInternalModule()) << assembly;
1286   EXPECT_EQ(p->error(), "undef pointer is not valid: %7 = OpUndef %3");
1287 }
1288 
TEST_F(SpvParserMemoryTest,InvalidPointer_ConstantNull_IsError)1289 TEST_F(SpvParserMemoryTest, InvalidPointer_ConstantNull_IsError) {
1290   // OpConstantNull on logical pointer requires variable-pointers, which
1291   // is not (yet) supported by WGSL features.
1292   const std::string assembly = InvalidPointerPreamble() + R"(
1293  %ptr = OpConstantNull %ptr_ty
1294 
1295   %main = OpFunction %void None %voidfn
1296  %entry = OpLabel
1297      %1 = OpCopyObject %ptr_ty %ptr
1298      %2 = OpAccessChain %ptr_ty %ptr
1299      %3 = OpInBoundsAccessChain %ptr_ty %ptr
1300 ; now show the invalid pointer propagates
1301      %10 = OpCopyObject %ptr_ty %1
1302      %20 = OpAccessChain %ptr_ty %2
1303      %30 = OpInBoundsAccessChain %ptr_ty %3
1304           OpReturn
1305           OpFunctionEnd
1306 )";
1307   auto p = parser(test::Assemble(assembly));
1308   EXPECT_FALSE(p->BuildAndParseInternalModule());
1309   EXPECT_EQ(p->error(), "null pointer is not valid: %9 = OpConstantNull %6");
1310 }
1311 
1312 }  // namespace
1313 }  // namespace spirv
1314 }  // namespace reader
1315 }  // namespace tint
1316