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