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