1 // Copyright (c) 2019 Google LLC
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 "source/fuzz/transformation_load.h"
16
17 #include "gtest/gtest.h"
18 #include "source/fuzz/fuzzer_util.h"
19 #include "source/fuzz/instruction_descriptor.h"
20 #include "test/fuzz/fuzz_test_util.h"
21
22 namespace spvtools {
23 namespace fuzz {
24 namespace {
25
TEST(TransformationLoadTest,BasicTest)26 TEST(TransformationLoadTest, BasicTest) {
27 std::string shader = R"(
28 OpCapability Shader
29 OpCapability VariablePointers
30 %1 = OpExtInstImport "GLSL.std.450"
31 OpMemoryModel Logical GLSL450
32 OpEntryPoint Fragment %4 "main"
33 OpExecutionMode %4 OriginUpperLeft
34 OpSource ESSL 310
35 %2 = OpTypeVoid
36 %3 = OpTypeFunction %2
37 %6 = OpTypeInt 32 1
38 %7 = OpTypeFloat 32
39 %8 = OpTypeStruct %6 %7
40 %9 = OpTypePointer Function %8
41 %10 = OpTypeFunction %6 %9
42 %14 = OpConstant %6 0
43 %15 = OpTypePointer Function %6
44 %51 = OpTypePointer Private %6
45 %21 = OpConstant %6 2
46 %23 = OpConstant %6 1
47 %24 = OpConstant %7 1
48 %25 = OpTypePointer Function %7
49 %50 = OpTypePointer Private %7
50 %34 = OpTypeBool
51 %35 = OpConstantFalse %34
52 %60 = OpConstantNull %50
53 %52 = OpVariable %50 Private
54 %53 = OpVariable %51 Private
55 %4 = OpFunction %2 None %3
56 %5 = OpLabel
57 %20 = OpVariable %9 Function
58 %27 = OpVariable %9 Function ; irrelevant
59 %22 = OpAccessChain %15 %20 %14
60 %44 = OpCopyObject %9 %20
61 %26 = OpAccessChain %25 %20 %23
62 %29 = OpFunctionCall %6 %12 %27
63 %30 = OpAccessChain %15 %20 %14
64 %45 = OpCopyObject %15 %30
65 %33 = OpAccessChain %15 %20 %14
66 OpSelectionMerge %37 None
67 OpBranchConditional %35 %36 %37
68 %36 = OpLabel
69 %38 = OpAccessChain %15 %20 %14
70 %40 = OpAccessChain %15 %20 %14
71 %43 = OpAccessChain %15 %20 %14
72 OpBranch %37
73 %37 = OpLabel
74 OpReturn
75 OpFunctionEnd
76 %12 = OpFunction %6 None %10
77 %11 = OpFunctionParameter %9 ; irrelevant
78 %13 = OpLabel
79 %46 = OpCopyObject %9 %11 ; irrelevant
80 %16 = OpAccessChain %15 %11 %14 ; irrelevant
81 OpReturnValue %21
82 OpFunctionEnd
83 )";
84
85 const auto env = SPV_ENV_UNIVERSAL_1_4;
86 const auto consumer = nullptr;
87 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
88 spvtools::ValidatorOptions validator_options;
89 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
90 kConsoleMessageConsumer));
91 TransformationContext transformation_context(
92 MakeUnique<FactManager>(context.get()), validator_options);
93 transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
94 27);
95 transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
96 11);
97 transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
98 46);
99 transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
100 16);
101 transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
102 52);
103
104 transformation_context.GetFactManager()->AddFactBlockIsDead(36);
105
106 // Variables with pointee types:
107 // 52 - ptr_to(7)
108 // 53 - ptr_to(6)
109 // 20 - ptr_to(8)
110 // 27 - ptr_to(8) - irrelevant
111
112 // Access chains with pointee type:
113 // 22 - ptr_to(6)
114 // 26 - ptr_to(6)
115 // 30 - ptr_to(6)
116 // 33 - ptr_to(6)
117 // 38 - ptr_to(6)
118 // 40 - ptr_to(6)
119 // 43 - ptr_to(6)
120 // 16 - ptr_to(6) - irrelevant
121
122 // Copied object with pointee type:
123 // 44 - ptr_to(8)
124 // 45 - ptr_to(6)
125 // 46 - ptr_to(8) - irrelevant
126
127 // Function parameters with pointee type:
128 // 11 - ptr_to(8) - irrelevant
129
130 // Pointers that cannot be used:
131 // 60 - null
132
133 // Bad: id is not fresh
134 ASSERT_FALSE(TransformationLoad(
135 33, 33, false, 0, 0,
136 MakeInstructionDescriptor(38, spv::Op::OpAccessChain, 0))
137 .IsApplicable(context.get(), transformation_context));
138 // Bad: attempt to load from 11 from outside its function
139 ASSERT_FALSE(TransformationLoad(
140 100, 11, false, 0, 0,
141 MakeInstructionDescriptor(38, spv::Op::OpAccessChain, 0))
142 .IsApplicable(context.get(), transformation_context));
143
144 // Bad: pointer is not available
145 ASSERT_FALSE(TransformationLoad(
146 100, 33, false, 0, 0,
147 MakeInstructionDescriptor(45, spv::Op::OpCopyObject, 0))
148 .IsApplicable(context.get(), transformation_context));
149
150 // Bad: attempt to insert before OpVariable
151 ASSERT_FALSE(
152 TransformationLoad(100, 27, false, 0, 0,
153 MakeInstructionDescriptor(27, spv::Op::OpVariable, 0))
154 .IsApplicable(context.get(), transformation_context));
155
156 // Bad: pointer id does not exist
157 ASSERT_FALSE(TransformationLoad(
158 100, 1000, false, 0, 0,
159 MakeInstructionDescriptor(38, spv::Op::OpAccessChain, 0))
160 .IsApplicable(context.get(), transformation_context));
161
162 // Bad: pointer id exists but does not have a type
163 ASSERT_FALSE(TransformationLoad(
164 100, 5, false, 0, 0,
165 MakeInstructionDescriptor(38, spv::Op::OpAccessChain, 0))
166 .IsApplicable(context.get(), transformation_context));
167
168 // Bad: pointer id exists and has a type, but is not a pointer
169 ASSERT_FALSE(TransformationLoad(
170 100, 24, false, 0, 0,
171 MakeInstructionDescriptor(38, spv::Op::OpAccessChain, 0))
172 .IsApplicable(context.get(), transformation_context));
173
174 // Bad: attempt to load from null pointer
175 ASSERT_FALSE(TransformationLoad(
176 100, 60, false, 0, 0,
177 MakeInstructionDescriptor(38, spv::Op::OpAccessChain, 0))
178 .IsApplicable(context.get(), transformation_context));
179
180 // Bad: %40 is not available at the program point
181 ASSERT_FALSE(
182 TransformationLoad(100, 40, false, 0, 0,
183 MakeInstructionDescriptor(37, spv::Op::OpReturn, 0))
184 .IsApplicable(context.get(), transformation_context));
185
186 // Bad: The described instruction does not exist
187 ASSERT_FALSE(
188 TransformationLoad(100, 33, false, 0, 0,
189 MakeInstructionDescriptor(1000, spv::Op::OpReturn, 0))
190 .IsApplicable(context.get(), transformation_context));
191
192 {
193 TransformationLoad transformation(
194 100, 33, false, 0, 0,
195 MakeInstructionDescriptor(38, spv::Op::OpAccessChain, 0));
196 ASSERT_TRUE(
197 transformation.IsApplicable(context.get(), transformation_context));
198 ApplyAndCheckFreshIds(transformation, context.get(),
199 &transformation_context);
200 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
201 context.get(), validator_options, kConsoleMessageConsumer));
202 }
203
204 {
205 TransformationLoad transformation(
206 101, 46, false, 0, 0,
207 MakeInstructionDescriptor(16, spv::Op::OpReturnValue, 0));
208 ASSERT_TRUE(
209 transformation.IsApplicable(context.get(), transformation_context));
210 ApplyAndCheckFreshIds(transformation, context.get(),
211 &transformation_context);
212 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
213 context.get(), validator_options, kConsoleMessageConsumer));
214 }
215
216 {
217 TransformationLoad transformation(
218 102, 16, false, 0, 0,
219 MakeInstructionDescriptor(16, spv::Op::OpReturnValue, 0));
220 ASSERT_TRUE(
221 transformation.IsApplicable(context.get(), transformation_context));
222 ApplyAndCheckFreshIds(transformation, context.get(),
223 &transformation_context);
224 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
225 context.get(), validator_options, kConsoleMessageConsumer));
226 }
227
228 {
229 TransformationLoad transformation(
230 103, 40, false, 0, 0,
231 MakeInstructionDescriptor(43, spv::Op::OpAccessChain, 0));
232 ASSERT_TRUE(
233 transformation.IsApplicable(context.get(), transformation_context));
234 ApplyAndCheckFreshIds(transformation, context.get(),
235 &transformation_context);
236 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
237 context.get(), validator_options, kConsoleMessageConsumer));
238 }
239
240 std::string after_transformation = R"(
241 OpCapability Shader
242 OpCapability VariablePointers
243 %1 = OpExtInstImport "GLSL.std.450"
244 OpMemoryModel Logical GLSL450
245 OpEntryPoint Fragment %4 "main"
246 OpExecutionMode %4 OriginUpperLeft
247 OpSource ESSL 310
248 %2 = OpTypeVoid
249 %3 = OpTypeFunction %2
250 %6 = OpTypeInt 32 1
251 %7 = OpTypeFloat 32
252 %8 = OpTypeStruct %6 %7
253 %9 = OpTypePointer Function %8
254 %10 = OpTypeFunction %6 %9
255 %14 = OpConstant %6 0
256 %15 = OpTypePointer Function %6
257 %51 = OpTypePointer Private %6
258 %21 = OpConstant %6 2
259 %23 = OpConstant %6 1
260 %24 = OpConstant %7 1
261 %25 = OpTypePointer Function %7
262 %50 = OpTypePointer Private %7
263 %34 = OpTypeBool
264 %35 = OpConstantFalse %34
265 %60 = OpConstantNull %50
266 %52 = OpVariable %50 Private
267 %53 = OpVariable %51 Private
268 %4 = OpFunction %2 None %3
269 %5 = OpLabel
270 %20 = OpVariable %9 Function
271 %27 = OpVariable %9 Function ; irrelevant
272 %22 = OpAccessChain %15 %20 %14
273 %44 = OpCopyObject %9 %20
274 %26 = OpAccessChain %25 %20 %23
275 %29 = OpFunctionCall %6 %12 %27
276 %30 = OpAccessChain %15 %20 %14
277 %45 = OpCopyObject %15 %30
278 %33 = OpAccessChain %15 %20 %14
279 OpSelectionMerge %37 None
280 OpBranchConditional %35 %36 %37
281 %36 = OpLabel
282 %100 = OpLoad %6 %33
283 %38 = OpAccessChain %15 %20 %14
284 %40 = OpAccessChain %15 %20 %14
285 %103 = OpLoad %6 %40
286 %43 = OpAccessChain %15 %20 %14
287 OpBranch %37
288 %37 = OpLabel
289 OpReturn
290 OpFunctionEnd
291 %12 = OpFunction %6 None %10
292 %11 = OpFunctionParameter %9 ; irrelevant
293 %13 = OpLabel
294 %46 = OpCopyObject %9 %11 ; irrelevant
295 %16 = OpAccessChain %15 %11 %14 ; irrelevant
296 %101 = OpLoad %8 %46
297 %102 = OpLoad %6 %16
298 OpReturnValue %21
299 OpFunctionEnd
300 )";
301 ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
302 }
303
TEST(TransformationLoadTest,AtomicLoadTestCase)304 TEST(TransformationLoadTest, AtomicLoadTestCase) {
305 const std::string shader = R"(
306 OpCapability Shader
307 OpCapability Int8
308 %1 = OpExtInstImport "GLSL.std.450"
309 OpMemoryModel Logical GLSL450
310 OpEntryPoint Fragment %4 "main"
311 OpExecutionMode %4 OriginUpperLeft
312 OpSource ESSL 320
313 %2 = OpTypeVoid
314 %3 = OpTypeFunction %2
315 %6 = OpTypeInt 32 1
316 %7 = OpTypeInt 8 1
317 %9 = OpTypeInt 32 0
318 %26 = OpTypeFloat 32
319 %8 = OpTypeStruct %6
320 %10 = OpTypePointer StorageBuffer %8
321 %11 = OpVariable %10 StorageBuffer
322 %19 = OpConstant %26 0
323 %18 = OpConstant %9 1
324 %12 = OpConstant %6 0
325 %13 = OpTypePointer StorageBuffer %6
326 %15 = OpConstant %6 4
327 %16 = OpConstant %6 7
328 %17 = OpConstant %7 4
329 %20 = OpConstant %9 64
330 %4 = OpFunction %2 None %3
331 %5 = OpLabel
332 %14 = OpAccessChain %13 %11 %12
333 %24 = OpAccessChain %13 %11 %12
334 OpReturn
335 OpFunctionEnd
336 )";
337
338 const auto env = SPV_ENV_UNIVERSAL_1_3;
339 const auto consumer = nullptr;
340 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
341 spvtools::ValidatorOptions validator_options;
342 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
343 kConsoleMessageConsumer));
344 TransformationContext transformation_context(
345 MakeUnique<FactManager>(context.get()), validator_options);
346
347 // Bad: id is not fresh.
348 ASSERT_FALSE(TransformationLoad(
349 14, 14, true, 15, 20,
350 MakeInstructionDescriptor(24, spv::Op::OpAccessChain, 0))
351 .IsApplicable(context.get(), transformation_context));
352
353 // Bad: id 100 of memory scope instruction does not exist.
354 ASSERT_FALSE(TransformationLoad(
355 21, 14, true, 100, 20,
356 MakeInstructionDescriptor(24, spv::Op::OpAccessChain, 0))
357 .IsApplicable(context.get(), transformation_context));
358 // Bad: id 100 of memory semantics instruction does not exist.
359 ASSERT_FALSE(TransformationLoad(
360 21, 14, true, 15, 100,
361 MakeInstructionDescriptor(24, spv::Op::OpAccessChain, 0))
362 .IsApplicable(context.get(), transformation_context));
363 // Bad: memory scope should be |OpConstant| opcode.
364 ASSERT_FALSE(TransformationLoad(
365 21, 14, true, 5, 20,
366 MakeInstructionDescriptor(24, spv::Op::OpAccessChain, 0))
367 .IsApplicable(context.get(), transformation_context));
368 // Bad: memory semantics should be |OpConstant| opcode.
369 ASSERT_FALSE(TransformationLoad(
370 21, 14, true, 15, 5,
371 MakeInstructionDescriptor(24, spv::Op::OpAccessChain, 0))
372 .IsApplicable(context.get(), transformation_context));
373
374 // Bad: The memory scope instruction must have an Integer operand.
375 ASSERT_FALSE(TransformationLoad(
376 21, 14, true, 15, 19,
377 MakeInstructionDescriptor(24, spv::Op::OpAccessChain, 0))
378 .IsApplicable(context.get(), transformation_context));
379 // Bad: The memory memory semantics instruction must have an Integer operand.
380 ASSERT_FALSE(TransformationLoad(
381 21, 14, true, 19, 20,
382 MakeInstructionDescriptor(24, spv::Op::OpAccessChain, 0))
383 .IsApplicable(context.get(), transformation_context));
384
385 // Bad: Integer size of the memory scope must be equal to 32 bits.
386 ASSERT_FALSE(TransformationLoad(
387 21, 14, true, 17, 20,
388 MakeInstructionDescriptor(24, spv::Op::OpAccessChain, 0))
389 .IsApplicable(context.get(), transformation_context));
390
391 // Bad: Integer size of memory semantics must be equal to 32 bits.
392 ASSERT_FALSE(TransformationLoad(
393 21, 14, true, 15, 17,
394 MakeInstructionDescriptor(24, spv::Op::OpAccessChain, 0))
395 .IsApplicable(context.get(), transformation_context));
396
397 // Bad: memory scope value must be 4 (spv::Scope::Invocation).
398 ASSERT_FALSE(TransformationLoad(
399 21, 14, true, 16, 20,
400 MakeInstructionDescriptor(24, spv::Op::OpAccessChain, 0))
401 .IsApplicable(context.get(), transformation_context));
402
403 // Bad: memory semantics value must be either:
404 // 64 (SpvMemorySemanticsUniformMemoryMask)
405 // 256 (SpvMemorySemanticsWorkgroupMemoryMask)
406 ASSERT_FALSE(TransformationLoad(
407 21, 14, true, 15, 16,
408 MakeInstructionDescriptor(24, spv::Op::OpAccessChain, 0))
409 .IsApplicable(context.get(), transformation_context));
410
411 // Bad: The described instruction does not exist
412 ASSERT_FALSE(TransformationLoad(
413 21, 14, false, 15, 20,
414 MakeInstructionDescriptor(150, spv::Op::OpAccessChain, 0))
415 .IsApplicable(context.get(), transformation_context));
416
417 // Successful transformations.
418 {
419 TransformationLoad transformation(
420 21, 14, true, 15, 20,
421 MakeInstructionDescriptor(24, spv::Op::OpAccessChain, 0));
422 ASSERT_TRUE(
423 transformation.IsApplicable(context.get(), transformation_context));
424 ApplyAndCheckFreshIds(transformation, context.get(),
425 &transformation_context);
426 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
427 context.get(), validator_options, kConsoleMessageConsumer));
428 }
429
430 const std::string after_transformation = R"(
431 OpCapability Shader
432 OpCapability Int8
433 %1 = OpExtInstImport "GLSL.std.450"
434 OpMemoryModel Logical GLSL450
435 OpEntryPoint Fragment %4 "main"
436 OpExecutionMode %4 OriginUpperLeft
437 OpSource ESSL 320
438 %2 = OpTypeVoid
439 %3 = OpTypeFunction %2
440 %6 = OpTypeInt 32 1
441 %7 = OpTypeInt 8 1
442 %9 = OpTypeInt 32 0
443 %26 = OpTypeFloat 32
444 %8 = OpTypeStruct %6
445 %10 = OpTypePointer StorageBuffer %8
446 %11 = OpVariable %10 StorageBuffer
447 %19 = OpConstant %26 0
448 %18 = OpConstant %9 1
449 %12 = OpConstant %6 0
450 %13 = OpTypePointer StorageBuffer %6
451 %15 = OpConstant %6 4
452 %16 = OpConstant %6 7
453 %17 = OpConstant %7 4
454 %20 = OpConstant %9 64
455 %4 = OpFunction %2 None %3
456 %5 = OpLabel
457 %14 = OpAccessChain %13 %11 %12
458 %21 = OpAtomicLoad %6 %14 %15 %20
459 %24 = OpAccessChain %13 %11 %12
460 OpReturn
461 OpFunctionEnd
462 )";
463
464 ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
465 }
466
TEST(TransformationLoadTest,AtomicLoadTestCaseForWorkgroupMemory)467 TEST(TransformationLoadTest, AtomicLoadTestCaseForWorkgroupMemory) {
468 std::string shader = R"(
469 OpCapability Shader
470 OpCapability Int8
471 %1 = OpExtInstImport "GLSL.std.450"
472 OpMemoryModel Logical GLSL450
473 OpEntryPoint Fragment %4 "main"
474 OpExecutionMode %4 OriginUpperLeft
475 OpSource ESSL 310
476 %2 = OpTypeVoid
477 %3 = OpTypeFunction %2
478 %6 = OpTypeInt 32 1
479 %26 = OpTypeFloat 32
480 %27 = OpTypeInt 8 1
481 %7 = OpTypeInt 32 0 ; 0 means unsigned
482 %8 = OpConstant %7 0
483 %17 = OpConstant %27 4
484 %19 = OpConstant %26 0
485 %9 = OpTypePointer Function %6
486 %13 = OpTypeStruct %6
487 %12 = OpTypePointer Workgroup %13
488 %11 = OpVariable %12 Workgroup
489 %14 = OpConstant %6 0
490 %15 = OpTypePointer Function %6
491 %51 = OpTypePointer Private %6
492 %21 = OpConstant %6 4
493 %23 = OpConstant %6 256
494 %25 = OpTypePointer Function %7
495 %50 = OpTypePointer Workgroup %6
496 %34 = OpTypeBool
497 %35 = OpConstantFalse %34
498 %53 = OpVariable %51 Private
499 %4 = OpFunction %2 None %3
500 %5 = OpLabel
501 OpSelectionMerge %37 None
502 OpBranchConditional %35 %36 %37
503 %36 = OpLabel
504 %38 = OpAccessChain %50 %11 %14
505 %40 = OpAccessChain %50 %11 %14
506 OpBranch %37
507 %37 = OpLabel
508 OpReturn
509 OpFunctionEnd
510 )";
511
512 const auto env = SPV_ENV_UNIVERSAL_1_3;
513 const auto consumer = nullptr;
514 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
515 spvtools::ValidatorOptions validator_options;
516 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
517 kConsoleMessageConsumer));
518 TransformationContext transformation_context(
519 MakeUnique<FactManager>(context.get()), validator_options);
520
521 // Bad: Can't insert OpAccessChain before the id 23 of memory scope.
522 ASSERT_FALSE(TransformationLoad(
523 60, 38, true, 21, 23,
524 MakeInstructionDescriptor(23, spv::Op::OpAccessChain, 0))
525 .IsApplicable(context.get(), transformation_context));
526
527 // Bad: Can't insert OpAccessChain before the id 23 of memory semantics.
528 ASSERT_FALSE(TransformationLoad(
529 60, 38, true, 21, 23,
530 MakeInstructionDescriptor(21, spv::Op::OpAccessChain, 0))
531 .IsApplicable(context.get(), transformation_context));
532
533 // Successful transformations.
534 {
535 TransformationLoad transformation(
536 60, 38, true, 21, 23,
537 MakeInstructionDescriptor(40, spv::Op::OpAccessChain, 0));
538 ASSERT_TRUE(
539 transformation.IsApplicable(context.get(), transformation_context));
540 ApplyAndCheckFreshIds(transformation, context.get(),
541 &transformation_context);
542 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
543 context.get(), validator_options, kConsoleMessageConsumer));
544 }
545
546 std::string after_transformation = R"(
547 OpCapability Shader
548 OpCapability Int8
549 %1 = OpExtInstImport "GLSL.std.450"
550 OpMemoryModel Logical GLSL450
551 OpEntryPoint Fragment %4 "main"
552 OpExecutionMode %4 OriginUpperLeft
553 OpSource ESSL 310
554 %2 = OpTypeVoid
555 %3 = OpTypeFunction %2
556 %6 = OpTypeInt 32 1
557 %26 = OpTypeFloat 32
558 %27 = OpTypeInt 8 1
559 %7 = OpTypeInt 32 0 ; 0 means unsigned
560 %8 = OpConstant %7 0
561 %17 = OpConstant %27 4
562 %19 = OpConstant %26 0
563 %9 = OpTypePointer Function %6
564 %13 = OpTypeStruct %6
565 %12 = OpTypePointer Workgroup %13
566 %11 = OpVariable %12 Workgroup
567 %14 = OpConstant %6 0
568 %15 = OpTypePointer Function %6
569 %51 = OpTypePointer Private %6
570 %21 = OpConstant %6 4
571 %23 = OpConstant %6 256
572 %25 = OpTypePointer Function %7
573 %50 = OpTypePointer Workgroup %6
574 %34 = OpTypeBool
575 %35 = OpConstantFalse %34
576 %53 = OpVariable %51 Private
577 %4 = OpFunction %2 None %3
578 %5 = OpLabel
579 OpSelectionMerge %37 None
580 OpBranchConditional %35 %36 %37
581 %36 = OpLabel
582 %38 = OpAccessChain %50 %11 %14
583 %60 = OpAtomicLoad %6 %38 %21 %23
584 %40 = OpAccessChain %50 %11 %14
585 OpBranch %37
586 %37 = OpLabel
587 OpReturn
588 OpFunctionEnd
589 )";
590
591 ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
592 }
593
594 } // namespace
595 } // namespace fuzz
596 } // namespace spvtools
597