1 // Copyright (c) 2020 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_merge_function_returns.h"
16
17 #include "gtest/gtest.h"
18 #include "source/fuzz/counter_overflow_id_source.h"
19 #include "source/fuzz/fuzzer_util.h"
20 #include "test/fuzz/fuzz_test_util.h"
21
22 namespace spvtools {
23 namespace fuzz {
24 namespace {
25
MakeReturnMergingInfo(uint32_t merge_block_id,uint32_t is_returning_id,uint32_t maybe_return_val_id,const std::map<uint32_t,uint32_t> & opphi_to_suitable_id)26 protobufs::ReturnMergingInfo MakeReturnMergingInfo(
27 uint32_t merge_block_id, uint32_t is_returning_id,
28 uint32_t maybe_return_val_id,
29 const std::map<uint32_t, uint32_t>& opphi_to_suitable_id) {
30 protobufs::ReturnMergingInfo result;
31 result.set_merge_block_id(merge_block_id);
32 result.set_is_returning_id(is_returning_id);
33 result.set_maybe_return_val_id(maybe_return_val_id);
34 *result.mutable_opphi_to_suitable_id() =
35 fuzzerutil::MapToRepeatedUInt32Pair(opphi_to_suitable_id);
36 return result;
37 }
38
TEST(TransformationMergeFunctionReturnsTest,SimpleInapplicable)39 TEST(TransformationMergeFunctionReturnsTest, SimpleInapplicable) {
40 std::string shader = R"(
41 OpCapability Shader
42 %1 = OpExtInstImport "GLSL.std.450"
43 OpMemoryModel Logical GLSL450
44 OpEntryPoint Fragment %2 "main"
45 OpExecutionMode %2 OriginUpperLeft
46 OpSource ESSL 310
47 %3 = OpTypeVoid
48 %4 = OpTypeFunction %3
49 %5 = OpTypeInt 32 1
50 %6 = OpTypeFunction %5
51 %7 = OpTypeFloat 32
52 %8 = OpTypeFunction %7
53 %9 = OpTypeBool
54 %10 = OpConstantTrue %9
55 %11 = OpConstantFalse %9
56 %12 = OpConstant %5 0
57 %13 = OpConstant %5 1
58 %2 = OpFunction %3 None %4
59 %14 = OpLabel
60 %15 = OpFunctionCall %3 %16
61 %17 = OpFunctionCall %3 %18
62 %19 = OpFunctionCall %3 %20
63 %21 = OpFunctionCall %7 %22
64 OpReturn
65 OpFunctionEnd
66 %16 = OpFunction %3 None %4
67 %23 = OpLabel
68 OpSelectionMerge %24 None
69 OpBranchConditional %10 %25 %26
70 %25 = OpLabel
71 OpReturn
72 %26 = OpLabel
73 OpReturn
74 %24 = OpLabel
75 OpUnreachable
76 OpFunctionEnd
77 %18 = OpFunction %3 None %4
78 %27 = OpLabel
79 OpBranch %28
80 %28 = OpLabel
81 OpLoopMerge %29 %30 None
82 OpBranch %31
83 %31 = OpLabel
84 OpBranchConditional %10 %32 %29
85 %32 = OpLabel
86 OpReturn
87 %30 = OpLabel
88 OpBranch %28
89 %29 = OpLabel
90 OpReturn
91 OpFunctionEnd
92 %20 = OpFunction %3 None %4
93 %33 = OpLabel
94 OpBranch %34
95 %34 = OpLabel
96 OpLoopMerge %35 %36 None
97 OpBranch %37
98 %37 = OpLabel
99 OpBranchConditional %10 %38 %35
100 %38 = OpLabel
101 OpReturn
102 %36 = OpLabel
103 OpBranch %34
104 %35 = OpLabel
105 %39 = OpFunctionCall %3 %18
106 OpReturn
107 OpFunctionEnd
108 %22 = OpFunction %7 None %8
109 %40 = OpLabel
110 OpBranch %51
111 %51 = OpLabel
112 OpLoopMerge %41 %53 None
113 OpBranchConditional %10 %42 %41
114 %42 = OpLabel
115 %43 = OpConvertSToF %7 %12
116 OpReturnValue %43
117 %41 = OpLabel
118 %44 = OpConvertSToF %7 %13
119 OpReturnValue %44
120 %53 = OpLabel
121 OpBranch %51
122 OpFunctionEnd
123 %45 = OpFunction %5 None %6
124 %46 = OpLabel
125 OpBranch %52
126 %52 = OpLabel
127 %47 = OpConvertSToF %7 %13
128 OpLoopMerge %48 %54 None
129 OpBranchConditional %10 %49 %48
130 %49 = OpLabel
131 OpReturnValue %12
132 %48 = OpLabel
133 %50 = OpCopyObject %5 %12
134 OpReturnValue %13
135 %54 = OpLabel
136 OpBranch %52
137 OpFunctionEnd
138 )";
139
140 const auto env = SPV_ENV_UNIVERSAL_1_5;
141 const auto consumer = nullptr;
142 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
143 spvtools::ValidatorOptions validator_options;
144 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
145 kConsoleMessageConsumer));
146 TransformationContext transformation_context(
147 MakeUnique<FactManager>(context.get()), validator_options);
148
149 // Function %1 does not exist.
150 ASSERT_FALSE(TransformationMergeFunctionReturns(1, 100, 101, 0, 0, {{}})
151 .IsApplicable(context.get(), transformation_context));
152
153 // The entry block (%22) of function %15 does not branch unconditionally to
154 // the following block.
155 ASSERT_FALSE(TransformationMergeFunctionReturns(16, 100, 101, 0, 0, {{}})
156 .IsApplicable(context.get(), transformation_context));
157
158 // Block %28 is the merge block of a loop containing a return instruction, but
159 // it contains an OpReturn instruction (so, it contains instructions that are
160 // not OpLabel, OpPhi or OpBranch).
161 ASSERT_FALSE(
162 TransformationMergeFunctionReturns(
163 18, 100, 101, 0, 0, {{MakeReturnMergingInfo(29, 102, 0, {{}})}})
164 .IsApplicable(context.get(), transformation_context));
165
166 // Block %34 is the merge block of a loop containing a return instruction, but
167 // it contains an OpFunctionCall instruction (so, it contains instructions
168 // that are not OpLabel, OpPhi or OpBranch).
169 ASSERT_FALSE(
170 TransformationMergeFunctionReturns(
171 20, 100, 101, 0, 0, {{MakeReturnMergingInfo(35, 102, 0, {{}})}})
172 .IsApplicable(context.get(), transformation_context));
173
174 // Id %1000 cannot be found in the module and there is no id of the correct
175 // type (float) available at the end of the entry block of function %21.
176 ASSERT_FALSE(TransformationMergeFunctionReturns(22, 100, 101, 102, 1000, {{}})
177 .IsApplicable(context.get(), transformation_context));
178
179 // Id %47 is of type float, while function %45 has return type int.
180 ASSERT_FALSE(TransformationMergeFunctionReturns(45, 100, 101, 102, 47, {{}})
181 .IsApplicable(context.get(), transformation_context));
182
183 // Id %50 is not available at the end of the entry block of function %45.
184 ASSERT_FALSE(TransformationMergeFunctionReturns(45, 100, 101, 102, 50, {{}})
185 .IsApplicable(context.get(), transformation_context));
186 }
187
TEST(TransformationMergeFunctionReturnsTest,MissingBooleans)188 TEST(TransformationMergeFunctionReturnsTest, MissingBooleans) {
189 {
190 // OpConstantTrue is missing.
191 std::string shader = R"(
192 OpCapability Shader
193 %1 = OpExtInstImport "GLSL.std.450"
194 OpMemoryModel Logical GLSL450
195 OpEntryPoint Fragment %2 "main"
196 OpExecutionMode %2 OriginUpperLeft
197 OpSource ESSL 310
198 OpName %2 "main"
199 OpName %3 "A("
200 OpDecorate %3 RelaxedPrecision
201 OpDecorate %4 RelaxedPrecision
202 %5 = OpTypeVoid
203 %6 = OpTypeFunction %5
204 %7 = OpTypeInt 32 1
205 %8 = OpTypeFunction %7
206 %9 = OpTypeBool
207 %10 = OpConstantFalse %9
208 %11 = OpConstant %7 1
209 %12 = OpConstant %7 2
210 %2 = OpFunction %5 None %6
211 %13 = OpLabel
212 %4 = OpFunctionCall %7 %3
213 OpReturn
214 OpFunctionEnd
215 %3 = OpFunction %7 None %8
216 %14 = OpLabel
217 OpBranch %15
218 %15 = OpLabel
219 OpSelectionMerge %16 None
220 OpBranchConditional %10 %17 %16
221 %17 = OpLabel
222 OpReturnValue %11
223 %16 = OpLabel
224 OpReturnValue %12
225 OpFunctionEnd
226 )";
227
228 const auto env = SPV_ENV_UNIVERSAL_1_5;
229 const auto consumer = nullptr;
230 const auto context =
231 BuildModule(env, consumer, shader, kFuzzAssembleOption);
232 spvtools::ValidatorOptions validator_options;
233 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
234 context.get(), validator_options, kConsoleMessageConsumer));
235 TransformationContext transformation_context(
236 MakeUnique<FactManager>(context.get()), validator_options);
237
238 ASSERT_FALSE(TransformationMergeFunctionReturns(3, 100, 101, 0, 0, {{}})
239 .IsApplicable(context.get(), transformation_context));
240 }
241 {
242 // OpConstantFalse is missing.
243 std::string shader = R"(
244 OpCapability Shader
245 %1 = OpExtInstImport "GLSL.std.450"
246 OpMemoryModel Logical GLSL450
247 OpEntryPoint Fragment %2 "main"
248 OpExecutionMode %2 OriginUpperLeft
249 OpSource ESSL 310
250 OpName %2 "main"
251 OpName %3 "A("
252 OpDecorate %3 RelaxedPrecision
253 OpDecorate %4 RelaxedPrecision
254 %5 = OpTypeVoid
255 %6 = OpTypeFunction %5
256 %7 = OpTypeInt 32 1
257 %8 = OpTypeFunction %7
258 %9 = OpTypeBool
259 %10 = OpConstantTrue %9
260 %11 = OpConstant %7 1
261 %12 = OpConstant %7 2
262 %2 = OpFunction %5 None %6
263 %13 = OpLabel
264 %4 = OpFunctionCall %7 %3
265 OpReturn
266 OpFunctionEnd
267 %3 = OpFunction %7 None %8
268 %14 = OpLabel
269 OpBranch %15
270 %15 = OpLabel
271 OpSelectionMerge %16 None
272 OpBranchConditional %10 %17 %16
273 %17 = OpLabel
274 OpReturnValue %11
275 %16 = OpLabel
276 OpReturnValue %12
277 OpFunctionEnd
278 )";
279
280 const auto env = SPV_ENV_UNIVERSAL_1_5;
281 const auto consumer = nullptr;
282 const auto context =
283 BuildModule(env, consumer, shader, kFuzzAssembleOption);
284 spvtools::ValidatorOptions validator_options;
285 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
286 context.get(), validator_options, kConsoleMessageConsumer));
287 TransformationContext transformation_context(
288 MakeUnique<FactManager>(context.get()), validator_options);
289
290 ASSERT_FALSE(TransformationMergeFunctionReturns(3, 100, 101, 0, 0, {{}})
291 .IsApplicable(context.get(), transformation_context));
292 }
293 }
294
TEST(TransformationMergeFunctionReturnsTest,InvalidIds)295 TEST(TransformationMergeFunctionReturnsTest, InvalidIds) {
296 std::string shader = R"(
297 OpCapability Shader
298 %1 = OpExtInstImport "GLSL.std.450"
299 OpMemoryModel Logical GLSL450
300 OpEntryPoint Fragment %2 "main"
301 OpExecutionMode %2 OriginUpperLeft
302 OpSource ESSL 310
303 %3 = OpTypeVoid
304 %4 = OpTypeFunction %3
305 %5 = OpTypeInt 32 1
306 %6 = OpTypeFunction %5
307 %42 = OpTypeFloat 32
308 %7 = OpTypeBool
309 %8 = OpConstantTrue %7
310 %9 = OpConstantFalse %7
311 %10 = OpConstant %5 0
312 %11 = OpConstant %5 1
313 %2 = OpFunction %3 None %4
314 %12 = OpLabel
315 %13 = OpFunctionCall %5 %14
316 %15 = OpFunctionCall %3 %16
317 OpReturn
318 OpFunctionEnd
319 %17 = OpFunction %3 None %4
320 %18 = OpLabel
321 OpBranch %19
322 %19 = OpLabel
323 OpLoopMerge %20 %21 None
324 OpBranch %22
325 %22 = OpLabel
326 OpBranchConditional %8 %23 %20
327 %23 = OpLabel
328 OpReturn
329 %21 = OpLabel
330 OpBranch %19
331 %20 = OpLabel
332 OpBranch %24
333 %24 = OpLabel
334 OpReturn
335 OpFunctionEnd
336 %14 = OpFunction %5 None %6
337 %25 = OpLabel
338 OpBranch %26
339 %26 = OpLabel
340 OpLoopMerge %27 %28 None
341 OpBranch %29
342 %29 = OpLabel
343 OpBranchConditional %8 %30 %27
344 %30 = OpLabel
345 OpReturnValue %10
346 %28 = OpLabel
347 OpBranch %26
348 %27 = OpLabel
349 OpBranch %33
350 %33 = OpLabel
351 OpReturnValue %11
352 OpFunctionEnd
353 %16 = OpFunction %3 None %4
354 %34 = OpLabel
355 OpBranch %35
356 %35 = OpLabel
357 OpLoopMerge %36 %37 None
358 OpBranch %38
359 %38 = OpLabel
360 %43 = OpConvertSToF %42 %10
361 OpBranchConditional %8 %39 %36
362 %39 = OpLabel
363 OpReturn
364 %37 = OpLabel
365 %44 = OpConvertSToF %42 %10
366 OpBranch %35
367 %36 = OpLabel
368 %31 = OpPhi %42 %43 %38
369 %32 = OpPhi %5 %11 %38
370 OpBranch %40
371 %40 = OpLabel
372 %41 = OpFunctionCall %3 %17
373 OpReturn
374 OpFunctionEnd
375 )";
376
377 const auto env = SPV_ENV_UNIVERSAL_1_5;
378 const auto consumer = nullptr;
379 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
380 spvtools::ValidatorOptions validator_options;
381 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
382 kConsoleMessageConsumer));
383 TransformationContext transformation_context(
384 MakeUnique<FactManager>(context.get()), validator_options);
385
386 // Fresh id %100 is used twice.
387 ASSERT_FALSE(
388 TransformationMergeFunctionReturns(
389 17, 100, 100, 0, 0, {{MakeReturnMergingInfo(20, 101, 0, {{}})}})
390 .IsApplicable(context.get(), transformation_context));
391
392 // Fresh id %100 is used twice.
393 ASSERT_FALSE(
394 TransformationMergeFunctionReturns(
395 17, 100, 101, 0, 0, {{MakeReturnMergingInfo(20, 100, 0, {{}})}})
396 .IsApplicable(context.get(), transformation_context));
397
398 // %0 cannot be a fresh id for the new merge block.
399 ASSERT_FALSE(
400 TransformationMergeFunctionReturns(
401 17, 100, 0, 0, 0, {{MakeReturnMergingInfo(20, 101, 0, {{}})}})
402 .IsApplicable(context.get(), transformation_context));
403
404 // %0 cannot be a fresh id for the new header block.
405 ASSERT_FALSE(
406 TransformationMergeFunctionReturns(
407 17, 0, 100, 0, 0, {{MakeReturnMergingInfo(20, 101, 0, {{}})}})
408 .IsApplicable(context.get(), transformation_context));
409
410 // %0 cannot be a fresh id for the new |is_returning| instruction in an
411 // existing merge block.
412 ASSERT_FALSE(
413 TransformationMergeFunctionReturns(
414 17, 100, 101, 0, 0, {{MakeReturnMergingInfo(20, 0, 0, {{}})}})
415 .IsApplicable(context.get(), transformation_context));
416
417 // %0 cannot be a fresh id for the new |return_val| instruction in the new
418 // return block.
419 ASSERT_FALSE(
420 TransformationMergeFunctionReturns(
421 14, 100, 101, 0, 10, {{MakeReturnMergingInfo(27, 102, 103, {{}})}})
422 .IsApplicable(context.get(), transformation_context));
423
424 // %0 cannot be a fresh id for the new |maybe_return_val| instruction in an
425 // existing merge block, inside a non-void function.
426 ASSERT_FALSE(
427 TransformationMergeFunctionReturns(
428 14, 100, 101, 102, 10, {{MakeReturnMergingInfo(27, 103, 0, {{}})}})
429 .IsApplicable(context.get(), transformation_context));
430
431 // Fresh id %102 is repeated.
432 ASSERT_FALSE(
433 TransformationMergeFunctionReturns(
434 14, 100, 101, 102, 10, {{MakeReturnMergingInfo(27, 102, 104, {{}})}})
435 .IsApplicable(context.get(), transformation_context));
436
437 // Id %11 (type int) does not have the correct type (float) for OpPhi
438 // instruction %31.
439 ASSERT_FALSE(
440 TransformationMergeFunctionReturns(
441 16, 100, 101, 0, 0,
442 {{MakeReturnMergingInfo(36, 103, 104, {{{31, 11}, {32, 11}}})}})
443 .IsApplicable(context.get(), transformation_context));
444
445 // Id %11 (type int) does not have the correct type (float) for OpPhi
446 // instruction %31.
447 ASSERT_FALSE(
448 TransformationMergeFunctionReturns(
449 16, 100, 101, 0, 0,
450 {{MakeReturnMergingInfo(36, 102, 0, {{{31, 11}, {32, 11}}})}})
451 .IsApplicable(context.get(), transformation_context));
452
453 // Id %43 is not available at the end of the entry block.
454 ASSERT_FALSE(
455 TransformationMergeFunctionReturns(
456 16, 100, 101, 0, 0,
457 {{MakeReturnMergingInfo(36, 102, 0, {{{31, 44}, {32, 11}}})}})
458 .IsApplicable(context.get(), transformation_context));
459
460 // There is not a mapping for id %31 (float OpPhi instruction in a loop merge
461 // block) and no suitable id is available at the end of the entry block.
462 ASSERT_FALSE(TransformationMergeFunctionReturns(
463 16, 100, 101, 0, 0,
464 {{MakeReturnMergingInfo(36, 102, 0, {{{32, 11}}})}})
465 .IsApplicable(context.get(), transformation_context));
466
467 // Id %1000 cannot be found in the module and no suitable id for OpPhi %31 is
468 // available at the end of the entry block.
469 ASSERT_FALSE(
470 TransformationMergeFunctionReturns(
471 16, 100, 101, 0, 0,
472 {{MakeReturnMergingInfo(36, 102, 0, {{{31, 1000}, {32, 11}}})}})
473 .IsApplicable(context.get(), transformation_context));
474 }
475
TEST(TransformationMergeFunctionReturnsTest,Simple)476 TEST(TransformationMergeFunctionReturnsTest, Simple) {
477 std::string shader = R"(
478 OpCapability Shader
479 %1 = OpExtInstImport "GLSL.std.450"
480 OpMemoryModel Logical GLSL450
481 OpEntryPoint Fragment %2 "main"
482 OpExecutionMode %2 OriginUpperLeft
483 OpSource ESSL 310
484 %3 = OpTypeVoid
485 %4 = OpTypeFunction %3
486 %5 = OpTypeInt 32 1
487 %6 = OpTypeFunction %5
488 %7 = OpTypeFloat 32
489 %8 = OpTypeFunction %7 %7
490 %9 = OpTypeFunction %7
491 %10 = OpTypeBool
492 %11 = OpConstantTrue %10
493 %40 = OpConstantFalse %10
494 %12 = OpConstant %5 1
495 %2 = OpFunction %3 None %4
496 %13 = OpLabel
497 OpReturn
498 OpFunctionEnd
499 %14 = OpFunction %3 None %4
500 %15 = OpLabel
501 OpBranch %16
502 %16 = OpLabel
503 OpSelectionMerge %17 None
504 OpBranchConditional %11 %18 %17
505 %18 = OpLabel
506 OpReturn
507 %17 = OpLabel
508 OpReturn
509 OpFunctionEnd
510 %19 = OpFunction %5 None %6
511 %20 = OpLabel
512 OpBranch %21
513 %21 = OpLabel
514 OpSelectionMerge %22 None
515 OpBranchConditional %11 %23 %24
516 %23 = OpLabel
517 OpReturnValue %12
518 %24 = OpLabel
519 %25 = OpIAdd %5 %12 %12
520 OpReturnValue %25
521 %22 = OpLabel
522 OpUnreachable
523 OpFunctionEnd
524 %26 = OpFunction %7 None %8
525 %27 = OpFunctionParameter %7
526 %28 = OpLabel
527 OpBranch %29
528 %29 = OpLabel
529 OpSelectionMerge %30 None
530 OpBranchConditional %11 %31 %30
531 %31 = OpLabel
532 %32 = OpFAdd %7 %27 %27
533 OpReturnValue %32
534 %30 = OpLabel
535 OpReturnValue %27
536 OpFunctionEnd
537 %33 = OpFunction %7 None %9
538 %34 = OpLabel
539 %35 = OpConvertSToF %7 %12
540 OpBranch %36
541 %36 = OpLabel
542 OpSelectionMerge %37 None
543 OpBranchConditional %11 %38 %37
544 %38 = OpLabel
545 %39 = OpFAdd %7 %35 %35
546 OpReturnValue %39
547 %37 = OpLabel
548 OpReturnValue %35
549 OpFunctionEnd
550 )";
551
552 const auto env = SPV_ENV_UNIVERSAL_1_5;
553 const auto consumer = nullptr;
554 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
555 spvtools::ValidatorOptions validator_options;
556 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
557 kConsoleMessageConsumer));
558 TransformationContext transformation_context(
559 MakeUnique<FactManager>(context.get()), validator_options);
560
561 // The 0s are allowed because the function's return type is void.
562 auto transformation1 =
563 TransformationMergeFunctionReturns(14, 100, 101, 0, 0, {{}});
564 ASSERT_TRUE(
565 transformation1.IsApplicable(context.get(), transformation_context));
566 ApplyAndCheckFreshIds(transformation1, context.get(),
567 &transformation_context);
568 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
569 kConsoleMessageConsumer));
570
571 // %12 is available at the end of the entry block of %19 (it is a global
572 // variable).
573 ASSERT_TRUE(TransformationMergeFunctionReturns(19, 110, 111, 112, 12, {{}})
574 .IsApplicable(context.get(), transformation_context));
575
576 // %1000 cannot be found in the module, but there is a suitable id available
577 // at the end of the entry block (%12).
578 auto transformation2 =
579 TransformationMergeFunctionReturns(19, 110, 111, 112, 1000, {{}});
580 ASSERT_TRUE(
581 transformation2.IsApplicable(context.get(), transformation_context));
582 ApplyAndCheckFreshIds(transformation2, context.get(),
583 &transformation_context);
584 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
585 kConsoleMessageConsumer));
586
587 // %27 is available at the end of the entry block of %26 (it is a function
588 // parameter).
589 ASSERT_TRUE(TransformationMergeFunctionReturns(26, 120, 121, 122, 27, {{}})
590 .IsApplicable(context.get(), transformation_context));
591
592 // %1000 cannot be found in the module, but there is a suitable id available
593 // at the end of the entry block (%27).
594 auto transformation3 =
595 TransformationMergeFunctionReturns(26, 120, 121, 122, 1000, {{}});
596 ASSERT_TRUE(
597 transformation3.IsApplicable(context.get(), transformation_context));
598 ApplyAndCheckFreshIds(transformation3, context.get(),
599 &transformation_context);
600 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
601 kConsoleMessageConsumer));
602
603 // %35 is available at the end of the entry block of %33 (it is in the entry
604 // block).
605 ASSERT_TRUE(TransformationMergeFunctionReturns(26, 130, 131, 132, 27, {{}})
606 .IsApplicable(context.get(), transformation_context));
607
608 // %1000 cannot be found in the module, but there is a suitable id available
609 // at the end of the entry block (%35).
610 auto transformation4 =
611 TransformationMergeFunctionReturns(33, 130, 131, 132, 1000, {{}});
612 ASSERT_TRUE(
613 transformation4.IsApplicable(context.get(), transformation_context));
614 ApplyAndCheckFreshIds(transformation4, context.get(),
615 &transformation_context);
616 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
617 kConsoleMessageConsumer));
618
619 std::string after_transformations = R"(
620 OpCapability Shader
621 %1 = OpExtInstImport "GLSL.std.450"
622 OpMemoryModel Logical GLSL450
623 OpEntryPoint Fragment %2 "main"
624 OpExecutionMode %2 OriginUpperLeft
625 OpSource ESSL 310
626 %3 = OpTypeVoid
627 %4 = OpTypeFunction %3
628 %5 = OpTypeInt 32 1
629 %6 = OpTypeFunction %5
630 %7 = OpTypeFloat 32
631 %8 = OpTypeFunction %7 %7
632 %9 = OpTypeFunction %7
633 %10 = OpTypeBool
634 %11 = OpConstantTrue %10
635 %40 = OpConstantFalse %10
636 %12 = OpConstant %5 1
637 %2 = OpFunction %3 None %4
638 %13 = OpLabel
639 OpReturn
640 OpFunctionEnd
641 %14 = OpFunction %3 None %4
642 %15 = OpLabel
643 OpBranch %100
644 %100 = OpLabel
645 OpLoopMerge %101 %100 None
646 OpBranchConditional %11 %16 %100
647 %16 = OpLabel
648 OpSelectionMerge %17 None
649 OpBranchConditional %11 %18 %17
650 %18 = OpLabel
651 OpBranch %101
652 %17 = OpLabel
653 OpBranch %101
654 %101 = OpLabel
655 OpReturn
656 OpFunctionEnd
657 %19 = OpFunction %5 None %6
658 %20 = OpLabel
659 OpBranch %110
660 %110 = OpLabel
661 OpLoopMerge %111 %110 None
662 OpBranchConditional %11 %21 %110
663 %21 = OpLabel
664 OpSelectionMerge %22 None
665 OpBranchConditional %11 %23 %24
666 %23 = OpLabel
667 OpBranch %111
668 %24 = OpLabel
669 %25 = OpIAdd %5 %12 %12
670 OpBranch %111
671 %22 = OpLabel
672 OpUnreachable
673 %111 = OpLabel
674 %112 = OpPhi %5 %12 %23 %25 %24
675 OpReturnValue %112
676 OpFunctionEnd
677 %26 = OpFunction %7 None %8
678 %27 = OpFunctionParameter %7
679 %28 = OpLabel
680 OpBranch %120
681 %120 = OpLabel
682 OpLoopMerge %121 %120 None
683 OpBranchConditional %11 %29 %120
684 %29 = OpLabel
685 OpSelectionMerge %30 None
686 OpBranchConditional %11 %31 %30
687 %31 = OpLabel
688 %32 = OpFAdd %7 %27 %27
689 OpBranch %121
690 %30 = OpLabel
691 OpBranch %121
692 %121 = OpLabel
693 %122 = OpPhi %7 %27 %30 %32 %31
694 OpReturnValue %122
695 OpFunctionEnd
696 %33 = OpFunction %7 None %9
697 %34 = OpLabel
698 %35 = OpConvertSToF %7 %12
699 OpBranch %130
700 %130 = OpLabel
701 OpLoopMerge %131 %130 None
702 OpBranchConditional %11 %36 %130
703 %36 = OpLabel
704 OpSelectionMerge %37 None
705 OpBranchConditional %11 %38 %37
706 %38 = OpLabel
707 %39 = OpFAdd %7 %35 %35
708 OpBranch %131
709 %37 = OpLabel
710 OpBranch %131
711 %131 = OpLabel
712 %132 = OpPhi %7 %35 %37 %39 %38
713 OpReturnValue %132
714 OpFunctionEnd
715 )";
716
717 ASSERT_TRUE(IsEqual(env, after_transformations, context.get()));
718 }
719
TEST(TransformationMergeFunctionReturnsTest,NestedLoops)720 TEST(TransformationMergeFunctionReturnsTest, NestedLoops) {
721 std::string shader = R"(
722 OpCapability Shader
723 %1 = OpExtInstImport "GLSL.std.450"
724 OpMemoryModel Logical GLSL450
725 OpEntryPoint Fragment %2 "main"
726 OpExecutionMode %2 OriginUpperLeft
727 OpSource ESSL 310
728 %3 = OpTypeVoid
729 %4 = OpTypeFunction %3
730 %5 = OpTypeInt 32 1
731 %6 = OpTypeFunction %5
732 %7 = OpTypeBool
733 %8 = OpConstantTrue %7
734 %9 = OpConstantFalse %7
735 %10 = OpConstant %5 2
736 %11 = OpConstant %5 1
737 %12 = OpConstant %5 3
738 %2 = OpFunction %3 None %4
739 %13 = OpLabel
740 OpReturn
741 OpFunctionEnd
742 %14 = OpFunction %5 None %6
743 %15 = OpLabel
744 OpBranch %16
745 %16 = OpLabel
746 OpLoopMerge %17 %16 None
747 OpBranchConditional %8 %18 %16
748 %18 = OpLabel
749 OpLoopMerge %19 %20 None
750 OpBranchConditional %8 %19 %21
751 %19 = OpLabel
752 OpBranch %17
753 %21 = OpLabel
754 OpReturnValue %12
755 %17 = OpLabel
756 OpBranch %22
757 %20 = OpLabel
758 OpBranch %18
759 %22 = OpLabel
760 OpLoopMerge %23 %24 None
761 OpBranch %25
762 %25 = OpLabel
763 OpBranchConditional %8 %26 %23
764 %26 = OpLabel
765 OpSelectionMerge %27 None
766 OpBranchConditional %9 %28 %27
767 %28 = OpLabel
768 OpBranch %29
769 %29 = OpLabel
770 OpLoopMerge %30 %29 None
771 OpBranchConditional %8 %30 %29
772 %30 = OpLabel
773 OpLoopMerge %31 %32 None
774 OpBranch %33
775 %33 = OpLabel
776 OpBranchConditional %9 %34 %31
777 %34 = OpLabel
778 OpReturnValue %10
779 %32 = OpLabel
780 OpBranch %30
781 %31 = OpLabel
782 %35 = OpPhi %5 %11 %33
783 %36 = OpPhi %5 %10 %33
784 OpBranch %37
785 %37 = OpLabel
786 OpReturnValue %35
787 %27 = OpLabel
788 OpBranch %24
789 %24 = OpLabel
790 OpBranch %22
791 %23 = OpLabel
792 OpBranch %38
793 %38 = OpLabel
794 OpReturnValue %12
795 OpFunctionEnd
796 )";
797
798 const auto env = SPV_ENV_UNIVERSAL_1_5;
799 const auto consumer = nullptr;
800 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
801 spvtools::ValidatorOptions validator_options;
802 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
803 kConsoleMessageConsumer));
804 TransformationContext transformation_context(
805 MakeUnique<FactManager>(context.get()), validator_options);
806
807 auto transformation = TransformationMergeFunctionReturns(
808 14, 100, 101, 102, 11,
809 {{MakeReturnMergingInfo(19, 103, 104, {{}}),
810 MakeReturnMergingInfo(17, 105, 106, {{}}),
811 MakeReturnMergingInfo(31, 107, 108, {{{35, 10}, {36, 12}}}),
812 MakeReturnMergingInfo(23, 109, 110, {})}});
813 ASSERT_TRUE(
814 transformation.IsApplicable(context.get(), transformation_context));
815 ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
816 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
817 kConsoleMessageConsumer));
818
819 std::string after_transformation = R"(
820 OpCapability Shader
821 %1 = OpExtInstImport "GLSL.std.450"
822 OpMemoryModel Logical GLSL450
823 OpEntryPoint Fragment %2 "main"
824 OpExecutionMode %2 OriginUpperLeft
825 OpSource ESSL 310
826 %3 = OpTypeVoid
827 %4 = OpTypeFunction %3
828 %5 = OpTypeInt 32 1
829 %6 = OpTypeFunction %5
830 %7 = OpTypeBool
831 %8 = OpConstantTrue %7
832 %9 = OpConstantFalse %7
833 %10 = OpConstant %5 2
834 %11 = OpConstant %5 1
835 %12 = OpConstant %5 3
836 %2 = OpFunction %3 None %4
837 %13 = OpLabel
838 OpReturn
839 OpFunctionEnd
840 %14 = OpFunction %5 None %6
841 %15 = OpLabel
842 OpBranch %100
843 %100 = OpLabel
844 OpLoopMerge %101 %100 None
845 OpBranchConditional %8 %16 %100
846 %16 = OpLabel
847 OpLoopMerge %17 %16 None
848 OpBranchConditional %8 %18 %16
849 %18 = OpLabel
850 OpLoopMerge %19 %20 None
851 OpBranchConditional %8 %19 %21
852 %19 = OpLabel
853 %103 = OpPhi %7 %8 %21 %9 %18
854 %104 = OpPhi %5 %12 %21 %11 %18
855 OpBranch %17
856 %21 = OpLabel
857 OpBranch %19
858 %17 = OpLabel
859 %105 = OpPhi %7 %103 %19
860 %106 = OpPhi %5 %104 %19
861 OpBranchConditional %105 %101 %22
862 %20 = OpLabel
863 OpBranch %18
864 %22 = OpLabel
865 OpLoopMerge %23 %24 None
866 OpBranch %25
867 %25 = OpLabel
868 OpBranchConditional %8 %26 %23
869 %26 = OpLabel
870 OpSelectionMerge %27 None
871 OpBranchConditional %9 %28 %27
872 %28 = OpLabel
873 OpBranch %29
874 %29 = OpLabel
875 OpLoopMerge %30 %29 None
876 OpBranchConditional %8 %30 %29
877 %30 = OpLabel
878 OpLoopMerge %31 %32 None
879 OpBranch %33
880 %33 = OpLabel
881 OpBranchConditional %9 %34 %31
882 %34 = OpLabel
883 OpBranch %31
884 %32 = OpLabel
885 OpBranch %30
886 %31 = OpLabel
887 %107 = OpPhi %7 %8 %34 %9 %33
888 %108 = OpPhi %5 %10 %34 %11 %33
889 %35 = OpPhi %5 %11 %33 %10 %34
890 %36 = OpPhi %5 %10 %33 %12 %34
891 OpBranchConditional %107 %23 %37
892 %37 = OpLabel
893 OpBranch %23
894 %27 = OpLabel
895 OpBranch %24
896 %24 = OpLabel
897 OpBranch %22
898 %23 = OpLabel
899 %109 = OpPhi %7 %107 %31 %8 %37 %9 %25
900 %110 = OpPhi %5 %108 %31 %35 %37 %11 %25
901 OpBranchConditional %109 %101 %38
902 %38 = OpLabel
903 OpBranch %101
904 %101 = OpLabel
905 %102 = OpPhi %5 %106 %17 %110 %23 %12 %38
906 OpReturnValue %102
907 OpFunctionEnd
908 )";
909
910 ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
911 }
912
TEST(TransformationMergeFunctionReturnsTest,OverflowIds)913 TEST(TransformationMergeFunctionReturnsTest, OverflowIds) {
914 std::string shader = R"(
915 OpCapability Shader
916 %1 = OpExtInstImport "GLSL.std.450"
917 OpMemoryModel Logical GLSL450
918 OpEntryPoint Fragment %2 "main"
919 OpExecutionMode %2 OriginUpperLeft
920 OpSource ESSL 310
921 %3 = OpTypeVoid
922 %4 = OpTypeFunction %3
923 %5 = OpTypeInt 32 1
924 %6 = OpTypeFunction %5
925 %7 = OpTypeBool
926 %8 = OpConstantTrue %7
927 %9 = OpConstantFalse %7
928 %10 = OpConstant %5 1
929 %2 = OpFunction %3 None %4
930 %11 = OpLabel
931 OpReturn
932 OpFunctionEnd
933 %12 = OpFunction %5 None %6
934 %13 = OpLabel
935 OpBranch %14
936 %14 = OpLabel
937 %15 = OpIAdd %5 %10 %10
938 OpLoopMerge %16 %17 None
939 OpBranch %18
940 %18 = OpLabel
941 OpBranchConditional %8 %19 %16
942 %19 = OpLabel
943 OpSelectionMerge %20 None
944 OpBranchConditional %9 %21 %20
945 %21 = OpLabel
946 OpReturnValue %10
947 %20 = OpLabel
948 OpBranch %17
949 %17 = OpLabel
950 OpBranchConditional %8 %14 %16
951 %16 = OpLabel
952 %22 = OpPhi %5 %15 %17 %10 %18
953 OpBranch %23
954 %23 = OpLabel
955 OpReturnValue %22
956 OpFunctionEnd
957 %24 = OpFunction %3 None %4
958 %25 = OpLabel
959 OpBranch %26
960 %26 = OpLabel
961 OpLoopMerge %27 %28 None
962 OpBranch %29
963 %29 = OpLabel
964 OpBranchConditional %8 %30 %27
965 %30 = OpLabel
966 OpSelectionMerge %31 None
967 OpBranchConditional %9 %32 %31
968 %32 = OpLabel
969 OpReturn
970 %31 = OpLabel
971 OpBranch %28
972 %28 = OpLabel
973 OpBranch %26
974 %27 = OpLabel
975 %33 = OpPhi %5 %10 %29
976 OpBranch %34
977 %34 = OpLabel
978 OpReturn
979 OpFunctionEnd
980 )";
981
982 const auto env = SPV_ENV_UNIVERSAL_1_5;
983 const auto consumer = nullptr;
984 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
985 spvtools::ValidatorOptions validator_options;
986 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
987 kConsoleMessageConsumer));
988 TransformationContext transformation_context(
989 MakeUnique<FactManager>(context.get()), validator_options);
990
991 auto overflow_ids_unique_ptr = MakeUnique<CounterOverflowIdSource>(1000);
992 auto overflow_ids_ptr = overflow_ids_unique_ptr.get();
993 TransformationContext transformation_context_with_overflow_ids(
994 MakeUnique<FactManager>(context.get()), validator_options,
995 std::move(overflow_ids_unique_ptr));
996
997 // No mapping from merge block %16 to fresh ids is given, so overflow ids are
998 // needed.
999 auto transformation1 =
1000 TransformationMergeFunctionReturns(12, 100, 101, 102, 10, {{}});
1001
1002 #ifndef NDEBUG
1003 ASSERT_DEATH(
1004 transformation1.IsApplicable(context.get(), transformation_context),
1005 "Bad attempt to query whether overflow ids are available.");
1006 #endif
1007
1008 ASSERT_TRUE(transformation1.IsApplicable(
1009 context.get(), transformation_context_with_overflow_ids));
1010 ApplyAndCheckFreshIds(transformation1, context.get(),
1011 &transformation_context_with_overflow_ids,
1012 overflow_ids_ptr->GetIssuedOverflowIds());
1013 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1014 kConsoleMessageConsumer));
1015
1016 // No mapping from merge block %27 to fresh ids is given, so overflow ids are
1017 // needed.
1018 auto transformation2 =
1019 TransformationMergeFunctionReturns(24, 110, 111, 0, 0, {{}});
1020
1021 #ifndef NDEBUG
1022 ASSERT_DEATH(
1023 transformation2.IsApplicable(context.get(), transformation_context),
1024 "Bad attempt to query whether overflow ids are available.");
1025 #endif
1026
1027 ASSERT_TRUE(transformation2.IsApplicable(
1028 context.get(), transformation_context_with_overflow_ids));
1029 ApplyAndCheckFreshIds(transformation2, context.get(),
1030 &transformation_context_with_overflow_ids, {1002});
1031 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1032 kConsoleMessageConsumer));
1033
1034 std::string after_transformations = R"(
1035 OpCapability Shader
1036 %1 = OpExtInstImport "GLSL.std.450"
1037 OpMemoryModel Logical GLSL450
1038 OpEntryPoint Fragment %2 "main"
1039 OpExecutionMode %2 OriginUpperLeft
1040 OpSource ESSL 310
1041 %3 = OpTypeVoid
1042 %4 = OpTypeFunction %3
1043 %5 = OpTypeInt 32 1
1044 %6 = OpTypeFunction %5
1045 %7 = OpTypeBool
1046 %8 = OpConstantTrue %7
1047 %9 = OpConstantFalse %7
1048 %10 = OpConstant %5 1
1049 %2 = OpFunction %3 None %4
1050 %11 = OpLabel
1051 OpReturn
1052 OpFunctionEnd
1053 %12 = OpFunction %5 None %6
1054 %13 = OpLabel
1055 OpBranch %100
1056 %100 = OpLabel
1057 OpLoopMerge %101 %100 None
1058 OpBranchConditional %8 %14 %100
1059 %14 = OpLabel
1060 %15 = OpIAdd %5 %10 %10
1061 OpLoopMerge %16 %17 None
1062 OpBranch %18
1063 %18 = OpLabel
1064 OpBranchConditional %8 %19 %16
1065 %19 = OpLabel
1066 OpSelectionMerge %20 None
1067 OpBranchConditional %9 %21 %20
1068 %21 = OpLabel
1069 OpBranch %16
1070 %20 = OpLabel
1071 OpBranch %17
1072 %17 = OpLabel
1073 OpBranchConditional %8 %14 %16
1074 %16 = OpLabel
1075 %1000 = OpPhi %7 %8 %21 %9 %17 %9 %18
1076 %1001 = OpPhi %5 %10 %21 %10 %17 %10 %18
1077 %22 = OpPhi %5 %15 %17 %10 %18 %10 %21
1078 OpBranchConditional %1000 %101 %23
1079 %23 = OpLabel
1080 OpBranch %101
1081 %101 = OpLabel
1082 %102 = OpPhi %5 %1001 %16 %22 %23
1083 OpReturnValue %102
1084 OpFunctionEnd
1085 %24 = OpFunction %3 None %4
1086 %25 = OpLabel
1087 OpBranch %110
1088 %110 = OpLabel
1089 OpLoopMerge %111 %110 None
1090 OpBranchConditional %8 %26 %110
1091 %26 = OpLabel
1092 OpLoopMerge %27 %28 None
1093 OpBranch %29
1094 %29 = OpLabel
1095 OpBranchConditional %8 %30 %27
1096 %30 = OpLabel
1097 OpSelectionMerge %31 None
1098 OpBranchConditional %9 %32 %31
1099 %32 = OpLabel
1100 OpBranch %27
1101 %31 = OpLabel
1102 OpBranch %28
1103 %28 = OpLabel
1104 OpBranch %26
1105 %27 = OpLabel
1106 %1002 = OpPhi %7 %8 %32 %9 %29
1107 %33 = OpPhi %5 %10 %29 %10 %32
1108 OpBranchConditional %1002 %111 %34
1109 %34 = OpLabel
1110 OpBranch %111
1111 %111 = OpLabel
1112 OpReturn
1113 OpFunctionEnd
1114 )";
1115
1116 ASSERT_TRUE(IsEqual(env, after_transformations, context.get()));
1117 }
1118
TEST(TransformationMergeFunctionReturnsTest,MissingIdsForOpPhi)1119 TEST(TransformationMergeFunctionReturnsTest, MissingIdsForOpPhi) {
1120 std::string shader = R"(
1121 OpCapability Shader
1122 %1 = OpExtInstImport "GLSL.std.450"
1123 OpMemoryModel Logical GLSL450
1124 OpEntryPoint Fragment %2 "main"
1125 OpExecutionMode %2 OriginUpperLeft
1126 OpSource ESSL 310
1127 %3 = OpTypeVoid
1128 %4 = OpTypeFunction %3
1129 %5 = OpTypeBool
1130 %6 = OpConstantTrue %5
1131 %7 = OpConstantFalse %5
1132 %8 = OpTypeInt 32 1
1133 %9 = OpTypeFunction %3 %8
1134 %10 = OpTypeFloat 32
1135 %2 = OpFunction %3 None %4
1136 %11 = OpLabel
1137 OpReturn
1138 OpFunctionEnd
1139 %12 = OpFunction %3 None %9
1140 %13 = OpFunctionParameter %8
1141 %14 = OpLabel
1142 %15 = OpConvertSToF %10 %13
1143 OpBranch %16
1144 %16 = OpLabel
1145 OpLoopMerge %17 %18 None
1146 OpBranch %19
1147 %19 = OpLabel
1148 OpBranchConditional %6 %20 %17
1149 %20 = OpLabel
1150 OpSelectionMerge %21 None
1151 OpBranchConditional %7 %22 %21
1152 %22 = OpLabel
1153 OpReturn
1154 %21 = OpLabel
1155 OpBranch %18
1156 %18 = OpLabel
1157 OpBranch %16
1158 %17 = OpLabel
1159 %23 = OpPhi %8 %13 %19
1160 %24 = OpPhi %10 %15 %19
1161 %25 = OpPhi %5 %6 %19
1162 OpBranch %26
1163 %26 = OpLabel
1164 OpReturn
1165 OpFunctionEnd
1166 )";
1167
1168 const auto env = SPV_ENV_UNIVERSAL_1_5;
1169 const auto consumer = nullptr;
1170 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1171 spvtools::ValidatorOptions validator_options;
1172 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1173 kConsoleMessageConsumer));
1174 TransformationContext transformation_context(
1175 MakeUnique<FactManager>(context.get()), validator_options);
1176
1177 // This test checks whether the transformation is able to find suitable ids
1178 // to use in existing OpPhi instructions if they are not provided in the
1179 // corresponding mapping.
1180
1181 auto transformation = TransformationMergeFunctionReturns(
1182 12, 101, 102, 0, 0,
1183 {{MakeReturnMergingInfo(17, 103, 0, {{{25, 7}, {35, 8}}})}});
1184 ASSERT_TRUE(
1185 transformation.IsApplicable(context.get(), transformation_context));
1186 ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
1187 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1188 kConsoleMessageConsumer));
1189
1190 std::string after_transformation = R"(
1191 OpCapability Shader
1192 %1 = OpExtInstImport "GLSL.std.450"
1193 OpMemoryModel Logical GLSL450
1194 OpEntryPoint Fragment %2 "main"
1195 OpExecutionMode %2 OriginUpperLeft
1196 OpSource ESSL 310
1197 %3 = OpTypeVoid
1198 %4 = OpTypeFunction %3
1199 %5 = OpTypeBool
1200 %6 = OpConstantTrue %5
1201 %7 = OpConstantFalse %5
1202 %8 = OpTypeInt 32 1
1203 %9 = OpTypeFunction %3 %8
1204 %10 = OpTypeFloat 32
1205 %2 = OpFunction %3 None %4
1206 %11 = OpLabel
1207 OpReturn
1208 OpFunctionEnd
1209 %12 = OpFunction %3 None %9
1210 %13 = OpFunctionParameter %8
1211 %14 = OpLabel
1212 %15 = OpConvertSToF %10 %13
1213 OpBranch %101
1214 %101 = OpLabel
1215 OpLoopMerge %102 %101 None
1216 OpBranchConditional %6 %16 %101
1217 %16 = OpLabel
1218 OpLoopMerge %17 %18 None
1219 OpBranch %19
1220 %19 = OpLabel
1221 OpBranchConditional %6 %20 %17
1222 %20 = OpLabel
1223 OpSelectionMerge %21 None
1224 OpBranchConditional %7 %22 %21
1225 %22 = OpLabel
1226 OpBranch %17
1227 %21 = OpLabel
1228 OpBranch %18
1229 %18 = OpLabel
1230 OpBranch %16
1231 %17 = OpLabel
1232 %103 = OpPhi %5 %6 %22 %7 %19
1233 %23 = OpPhi %8 %13 %19 %13 %22
1234 %24 = OpPhi %10 %15 %19 %15 %22
1235 %25 = OpPhi %5 %6 %19 %7 %22
1236 OpBranchConditional %103 %102 %26
1237 %26 = OpLabel
1238 OpBranch %102
1239 %102 = OpLabel
1240 OpReturn
1241 OpFunctionEnd
1242 )";
1243
1244 ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
1245 }
1246
TEST(TransformationMergeFunctionReturnsTest,RespectDominanceRules1)1247 TEST(TransformationMergeFunctionReturnsTest, RespectDominanceRules1) {
1248 // An id defined in a loop is used in the corresponding merge block. After the
1249 // transformation, the id will not dominate the merge block anymore. This is
1250 // only OK if the use is inside an OpPhi instruction. (Note that there is also
1251 // another condition for this transformation that forbids non-OpPhi
1252 // instructions in relevant merge blocks, but that case is also considered
1253 // here for completeness).
1254
1255 std::string shader = R"(
1256 OpCapability Shader
1257 %1 = OpExtInstImport "GLSL.std.450"
1258 OpMemoryModel Logical GLSL450
1259 OpEntryPoint Fragment %2 "main"
1260 OpExecutionMode %2 OriginUpperLeft
1261 OpSource ESSL 310
1262 %3 = OpTypeVoid
1263 %4 = OpTypeFunction %3
1264 %5 = OpTypeBool
1265 %6 = OpConstantTrue %5
1266 %7 = OpConstantFalse %5
1267 %2 = OpFunction %3 None %4
1268 %8 = OpLabel
1269 OpBranch %9
1270 %9 = OpLabel
1271 OpLoopMerge %10 %11 None
1272 OpBranch %12
1273 %12 = OpLabel
1274 OpSelectionMerge %13 None
1275 OpBranchConditional %7 %13 %14
1276 %14 = OpLabel
1277 OpReturn
1278 %13 = OpLabel
1279 %15 = OpCopyObject %5 %7
1280 OpBranch %11
1281 %11 = OpLabel
1282 OpBranchConditional %7 %9 %10
1283 %10 = OpLabel
1284 %16 = OpCopyObject %5 %15
1285 OpBranch %17
1286 %17 = OpLabel
1287 OpReturn
1288 OpFunctionEnd
1289 %18 = OpFunction %3 None %4
1290 %19 = OpLabel
1291 OpBranch %20
1292 %20 = OpLabel
1293 OpLoopMerge %21 %22 None
1294 OpBranch %23
1295 %23 = OpLabel
1296 OpSelectionMerge %24 None
1297 OpBranchConditional %7 %24 %25
1298 %25 = OpLabel
1299 OpReturn
1300 %24 = OpLabel
1301 %26 = OpCopyObject %5 %7
1302 OpBranch %22
1303 %22 = OpLabel
1304 OpBranchConditional %7 %20 %21
1305 %21 = OpLabel
1306 %27 = OpPhi %5 %26 %22
1307 OpBranch %28
1308 %28 = OpLabel
1309 OpReturn
1310 OpFunctionEnd
1311 )";
1312
1313 const auto env = SPV_ENV_UNIVERSAL_1_5;
1314 const auto consumer = nullptr;
1315 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1316 spvtools::ValidatorOptions validator_options;
1317 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1318 kConsoleMessageConsumer));
1319 TransformationContext transformation_context(
1320 MakeUnique<FactManager>(context.get()), validator_options);
1321
1322 // In function %2, the definition of id %15 will not dominate its use in
1323 // instruction %16 (inside merge block %10) after a new branch from return
1324 // block %14 is added.
1325 ASSERT_FALSE(
1326 TransformationMergeFunctionReturns(
1327 2, 100, 101, 0, 0, {{MakeReturnMergingInfo(10, 102, 103, {{}})}})
1328 .IsApplicable(context.get(), transformation_context));
1329
1330 // In function %18, The definition of id %26 will still dominate its use in
1331 // instruction %27 (inside merge block %21), because %27 is an OpPhi
1332 // instruction.
1333 auto transformation = TransformationMergeFunctionReturns(
1334 18, 100, 101, 0, 0, {{MakeReturnMergingInfo(21, 102, 103, {{}})}});
1335 ASSERT_TRUE(
1336 transformation.IsApplicable(context.get(), transformation_context));
1337 ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
1338 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1339 kConsoleMessageConsumer));
1340
1341 std::string after_transformation = R"(
1342 OpCapability Shader
1343 %1 = OpExtInstImport "GLSL.std.450"
1344 OpMemoryModel Logical GLSL450
1345 OpEntryPoint Fragment %2 "main"
1346 OpExecutionMode %2 OriginUpperLeft
1347 OpSource ESSL 310
1348 %3 = OpTypeVoid
1349 %4 = OpTypeFunction %3
1350 %5 = OpTypeBool
1351 %6 = OpConstantTrue %5
1352 %7 = OpConstantFalse %5
1353 %2 = OpFunction %3 None %4
1354 %8 = OpLabel
1355 OpBranch %9
1356 %9 = OpLabel
1357 OpLoopMerge %10 %11 None
1358 OpBranch %12
1359 %12 = OpLabel
1360 OpSelectionMerge %13 None
1361 OpBranchConditional %7 %13 %14
1362 %14 = OpLabel
1363 OpReturn
1364 %13 = OpLabel
1365 %15 = OpCopyObject %5 %7
1366 OpBranch %11
1367 %11 = OpLabel
1368 OpBranchConditional %7 %9 %10
1369 %10 = OpLabel
1370 %16 = OpCopyObject %5 %15
1371 OpBranch %17
1372 %17 = OpLabel
1373 OpReturn
1374 OpFunctionEnd
1375 %18 = OpFunction %3 None %4
1376 %19 = OpLabel
1377 OpBranch %100
1378 %100 = OpLabel
1379 OpLoopMerge %101 %100 None
1380 OpBranchConditional %6 %20 %100
1381 %20 = OpLabel
1382 OpLoopMerge %21 %22 None
1383 OpBranch %23
1384 %23 = OpLabel
1385 OpSelectionMerge %24 None
1386 OpBranchConditional %7 %24 %25
1387 %25 = OpLabel
1388 OpBranch %21
1389 %24 = OpLabel
1390 %26 = OpCopyObject %5 %7
1391 OpBranch %22
1392 %22 = OpLabel
1393 OpBranchConditional %7 %20 %21
1394 %21 = OpLabel
1395 %102 = OpPhi %5 %6 %25 %7 %22
1396 %27 = OpPhi %5 %26 %22 %6 %25
1397 OpBranchConditional %102 %101 %28
1398 %28 = OpLabel
1399 OpBranch %101
1400 %101 = OpLabel
1401 OpReturn
1402 OpFunctionEnd
1403 )";
1404
1405 ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
1406 }
1407
TEST(TransformationMergeFunctionReturnsTest,RespectDominanceRules2)1408 TEST(TransformationMergeFunctionReturnsTest, RespectDominanceRules2) {
1409 // An id defined in a loop is used after the corresponding merge block. After
1410 // the transformation, the id will not dominate its use anymore, regardless of
1411 // the kind of instruction in which it is used.
1412
1413 std::string shader = R"(
1414 OpCapability Shader
1415 %1 = OpExtInstImport "GLSL.std.450"
1416 OpMemoryModel Logical GLSL450
1417 OpEntryPoint Fragment %2 "main"
1418 OpExecutionMode %2 OriginUpperLeft
1419 OpSource ESSL 310
1420 %3 = OpTypeVoid
1421 %4 = OpTypeFunction %3
1422 %5 = OpTypeBool
1423 %6 = OpConstantTrue %5
1424 %7 = OpConstantFalse %5
1425 %2 = OpFunction %3 None %4
1426 %8 = OpLabel
1427 OpBranch %9
1428 %9 = OpLabel
1429 OpLoopMerge %10 %11 None
1430 OpBranch %12
1431 %12 = OpLabel
1432 OpSelectionMerge %13 None
1433 OpBranchConditional %7 %13 %14
1434 %14 = OpLabel
1435 OpReturn
1436 %13 = OpLabel
1437 %15 = OpCopyObject %5 %7
1438 OpBranch %11
1439 %11 = OpLabel
1440 OpBranchConditional %7 %9 %10
1441 %10 = OpLabel
1442 OpBranch %16
1443 %16 = OpLabel
1444 %17 = OpCopyObject %5 %15
1445 OpReturn
1446 OpFunctionEnd
1447 %18 = OpFunction %3 None %4
1448 %19 = OpLabel
1449 OpBranch %20
1450 %20 = OpLabel
1451 OpLoopMerge %21 %22 None
1452 OpBranch %23
1453 %23 = OpLabel
1454 OpSelectionMerge %24 None
1455 OpBranchConditional %7 %24 %25
1456 %25 = OpLabel
1457 OpReturn
1458 %24 = OpLabel
1459 %26 = OpCopyObject %5 %7
1460 OpBranch %22
1461 %22 = OpLabel
1462 OpBranchConditional %7 %20 %21
1463 %21 = OpLabel
1464 OpBranch %27
1465 %27 = OpLabel
1466 %28 = OpPhi %5 %26 %21
1467 OpReturn
1468 OpFunctionEnd
1469 )";
1470
1471 const auto env = SPV_ENV_UNIVERSAL_1_5;
1472 const auto consumer = nullptr;
1473 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1474 spvtools::ValidatorOptions validator_options;
1475 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1476 kConsoleMessageConsumer));
1477 TransformationContext transformation_context(
1478 MakeUnique<FactManager>(context.get()), validator_options);
1479
1480 // In function %2, the definition of id %15 will not dominate its use in
1481 // instruction %17 (inside block %16) after a new branch from return
1482 // block %14 to merge block %10 is added.
1483 ASSERT_FALSE(
1484 TransformationMergeFunctionReturns(
1485 2, 100, 101, 0, 0, {{MakeReturnMergingInfo(10, 102, 103, {{}})}})
1486 .IsApplicable(context.get(), transformation_context));
1487
1488 // In function %18, the definition of id %26 will not dominate its use in
1489 // instruction %28 (inside block %27) after a new branch from return
1490 // block %25 to merge block %21 is added.
1491 ASSERT_FALSE(TransformationMergeFunctionReturns(
1492 2, 100, 101, 0, 0,
1493 {{MakeReturnMergingInfo(10, 102, 0, {{}}),
1494 MakeReturnMergingInfo(21, 103, 0, {{}})}})
1495 .IsApplicable(context.get(), transformation_context));
1496 }
1497
TEST(TransformationMergeFunctionReturnsTest,RespectDominanceRules3)1498 TEST(TransformationMergeFunctionReturnsTest, RespectDominanceRules3) {
1499 // An id defined in a loop is used inside the loop.
1500 // Changes to the predecessors of the merge block do not affect the validity
1501 // of the uses of such id.
1502
1503 std::string shader = R"(
1504 OpCapability Shader
1505 %1 = OpExtInstImport "GLSL.std.450"
1506 OpMemoryModel Logical GLSL450
1507 OpEntryPoint Fragment %2 "main"
1508 OpExecutionMode %2 OriginUpperLeft
1509 OpSource ESSL 310
1510 %3 = OpTypeVoid
1511 %4 = OpTypeFunction %3
1512 %5 = OpTypeBool
1513 %6 = OpConstantTrue %5
1514 %7 = OpConstantFalse %5
1515 %2 = OpFunction %3 None %4
1516 %8 = OpLabel
1517 OpBranch %9
1518 %9 = OpLabel
1519 OpLoopMerge %10 %11 None
1520 OpBranch %12
1521 %12 = OpLabel
1522 OpSelectionMerge %13 None
1523 OpBranchConditional %7 %13 %14
1524 %14 = OpLabel
1525 OpReturn
1526 %13 = OpLabel
1527 %15 = OpCopyObject %5 %7
1528 OpSelectionMerge %16 None
1529 OpBranchConditional %7 %16 %17
1530 %17 = OpLabel
1531 %18 = OpPhi %5 %15 %13
1532 %19 = OpCopyObject %5 %15
1533 OpBranch %16
1534 %16 = OpLabel
1535 OpBranch %11
1536 %11 = OpLabel
1537 OpBranchConditional %7 %9 %10
1538 %10 = OpLabel
1539 OpBranch %20
1540 %20 = OpLabel
1541 OpReturn
1542 OpFunctionEnd
1543 )";
1544
1545 const auto env = SPV_ENV_UNIVERSAL_1_5;
1546 const auto consumer = nullptr;
1547 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1548 spvtools::ValidatorOptions validator_options;
1549 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1550 kConsoleMessageConsumer));
1551 TransformationContext transformation_context(
1552 MakeUnique<FactManager>(context.get()), validator_options);
1553
1554 // In function %2, the definition of id %15 will still dominate its use in
1555 // instructions %18 and %19 after the transformation is applied, because the
1556 // fact that the id definition dominates the uses does not depend on it
1557 // dominating the merge block.
1558 auto transformation = TransformationMergeFunctionReturns(
1559 2, 100, 101, 0, 0, {{MakeReturnMergingInfo(10, 102, 103, {{}})}});
1560 ASSERT_TRUE(
1561 transformation.IsApplicable(context.get(), transformation_context));
1562 ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
1563 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1564 kConsoleMessageConsumer));
1565
1566 std::string after_transformation = R"(
1567 OpCapability Shader
1568 %1 = OpExtInstImport "GLSL.std.450"
1569 OpMemoryModel Logical GLSL450
1570 OpEntryPoint Fragment %2 "main"
1571 OpExecutionMode %2 OriginUpperLeft
1572 OpSource ESSL 310
1573 %3 = OpTypeVoid
1574 %4 = OpTypeFunction %3
1575 %5 = OpTypeBool
1576 %6 = OpConstantTrue %5
1577 %7 = OpConstantFalse %5
1578 %2 = OpFunction %3 None %4
1579 %8 = OpLabel
1580 OpBranch %100
1581 %100 = OpLabel
1582 OpLoopMerge %101 %100 None
1583 OpBranchConditional %6 %9 %100
1584 %9 = OpLabel
1585 OpLoopMerge %10 %11 None
1586 OpBranch %12
1587 %12 = OpLabel
1588 OpSelectionMerge %13 None
1589 OpBranchConditional %7 %13 %14
1590 %14 = OpLabel
1591 OpBranch %10
1592 %13 = OpLabel
1593 %15 = OpCopyObject %5 %7
1594 OpSelectionMerge %16 None
1595 OpBranchConditional %7 %16 %17
1596 %17 = OpLabel
1597 %18 = OpPhi %5 %15 %13
1598 %19 = OpCopyObject %5 %15
1599 OpBranch %16
1600 %16 = OpLabel
1601 OpBranch %11
1602 %11 = OpLabel
1603 OpBranchConditional %7 %9 %10
1604 %10 = OpLabel
1605 %102 = OpPhi %5 %6 %14 %7 %11
1606 OpBranchConditional %102 %101 %20
1607 %20 = OpLabel
1608 OpBranch %101
1609 %101 = OpLabel
1610 OpReturn
1611 OpFunctionEnd
1612 )";
1613
1614 ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
1615 }
1616
TEST(TransformationMergeFunctionReturnsTest,RespectDominanceRules4)1617 TEST(TransformationMergeFunctionReturnsTest, RespectDominanceRules4) {
1618 // An id defined in a loop, which contain 2 return statements, is used after
1619 // the loop. We can only apply the transformation if the id dominates all of
1620 // the return blocks.
1621
1622 std::string shader = R"(
1623 OpCapability Shader
1624 %1 = OpExtInstImport "GLSL.std.450"
1625 OpMemoryModel Logical GLSL450
1626 OpEntryPoint Fragment %2 "main"
1627 OpExecutionMode %2 OriginUpperLeft
1628 OpSource ESSL 310
1629 %3 = OpTypeVoid
1630 %4 = OpTypeFunction %3
1631 %5 = OpTypeBool
1632 %6 = OpConstantTrue %5
1633 %7 = OpConstantFalse %5
1634 %2 = OpFunction %3 None %4
1635 %8 = OpLabel
1636 OpBranch %9
1637 %9 = OpLabel
1638 OpLoopMerge %10 %11 None
1639 OpBranch %12
1640 %12 = OpLabel
1641 %13 = OpCopyObject %5 %7
1642 OpSelectionMerge %14 None
1643 OpBranchConditional %7 %14 %15
1644 %15 = OpLabel
1645 OpReturn
1646 %14 = OpLabel
1647 OpSelectionMerge %16 None
1648 OpBranchConditional %7 %16 %17
1649 %17 = OpLabel
1650 OpReturn
1651 %16 = OpLabel
1652 OpBranch %11
1653 %11 = OpLabel
1654 OpBranchConditional %7 %9 %10
1655 %10 = OpLabel
1656 OpBranch %18
1657 %18 = OpLabel
1658 %19 = OpCopyObject %5 %13
1659 OpReturn
1660 OpFunctionEnd
1661 %20 = OpFunction %3 None %4
1662 %21 = OpLabel
1663 OpBranch %22
1664 %22 = OpLabel
1665 OpLoopMerge %23 %24 None
1666 OpBranch %25
1667 %25 = OpLabel
1668 OpSelectionMerge %26 None
1669 OpBranchConditional %7 %26 %27
1670 %27 = OpLabel
1671 OpReturn
1672 %26 = OpLabel
1673 %28 = OpCopyObject %5 %7
1674 OpSelectionMerge %29 None
1675 OpBranchConditional %7 %29 %30
1676 %30 = OpLabel
1677 OpReturn
1678 %29 = OpLabel
1679 OpBranch %24
1680 %24 = OpLabel
1681 OpBranchConditional %7 %22 %23
1682 %23 = OpLabel
1683 OpBranch %31
1684 %31 = OpLabel
1685 %32 = OpCopyObject %5 %28
1686 OpReturn
1687 OpFunctionEnd
1688 )";
1689
1690 const auto env = SPV_ENV_UNIVERSAL_1_5;
1691 const auto consumer = nullptr;
1692 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1693 spvtools::ValidatorOptions validator_options;
1694 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1695 kConsoleMessageConsumer));
1696 TransformationContext transformation_context(
1697 MakeUnique<FactManager>(context.get()), validator_options);
1698
1699 // In function %2, the definition of id %13 will still dominate its use in
1700 // instruction %19 after the transformation is applied, because %13 dominates
1701 // all of the return blocks.
1702 auto transformation = TransformationMergeFunctionReturns(
1703 2, 100, 101, 0, 0, {{MakeReturnMergingInfo(10, 102, 103, {{}})}});
1704 ASSERT_TRUE(
1705 transformation.IsApplicable(context.get(), transformation_context));
1706 ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
1707 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1708 kConsoleMessageConsumer));
1709
1710 // In function %20, the definition of id %28 will not dominate its use in
1711 // instruction %32 after the transformation is applied, because %28 dominates
1712 // only one of the return blocks.
1713 ASSERT_FALSE(
1714 TransformationMergeFunctionReturns(
1715 20, 100, 101, 0, 0, {{MakeReturnMergingInfo(23, 102, 103, {{}})}})
1716 .IsApplicable(context.get(), transformation_context));
1717
1718 std::string after_transformation = R"(
1719 OpCapability Shader
1720 %1 = OpExtInstImport "GLSL.std.450"
1721 OpMemoryModel Logical GLSL450
1722 OpEntryPoint Fragment %2 "main"
1723 OpExecutionMode %2 OriginUpperLeft
1724 OpSource ESSL 310
1725 %3 = OpTypeVoid
1726 %4 = OpTypeFunction %3
1727 %5 = OpTypeBool
1728 %6 = OpConstantTrue %5
1729 %7 = OpConstantFalse %5
1730 %2 = OpFunction %3 None %4
1731 %8 = OpLabel
1732 OpBranch %100
1733 %100 = OpLabel
1734 OpLoopMerge %101 %100 None
1735 OpBranchConditional %6 %9 %100
1736 %9 = OpLabel
1737 OpLoopMerge %10 %11 None
1738 OpBranch %12
1739 %12 = OpLabel
1740 %13 = OpCopyObject %5 %7
1741 OpSelectionMerge %14 None
1742 OpBranchConditional %7 %14 %15
1743 %15 = OpLabel
1744 OpBranch %10
1745 %14 = OpLabel
1746 OpSelectionMerge %16 None
1747 OpBranchConditional %7 %16 %17
1748 %17 = OpLabel
1749 OpBranch %10
1750 %16 = OpLabel
1751 OpBranch %11
1752 %11 = OpLabel
1753 OpBranchConditional %7 %9 %10
1754 %10 = OpLabel
1755 %102 = OpPhi %5 %6 %15 %6 %17 %7 %11
1756 OpBranchConditional %102 %101 %18
1757 %18 = OpLabel
1758 %19 = OpCopyObject %5 %13
1759 OpBranch %101
1760 %101 = OpLabel
1761 OpReturn
1762 OpFunctionEnd
1763 %20 = OpFunction %3 None %4
1764 %21 = OpLabel
1765 OpBranch %22
1766 %22 = OpLabel
1767 OpLoopMerge %23 %24 None
1768 OpBranch %25
1769 %25 = OpLabel
1770 OpSelectionMerge %26 None
1771 OpBranchConditional %7 %26 %27
1772 %27 = OpLabel
1773 OpReturn
1774 %26 = OpLabel
1775 %28 = OpCopyObject %5 %7
1776 OpSelectionMerge %29 None
1777 OpBranchConditional %7 %29 %30
1778 %30 = OpLabel
1779 OpReturn
1780 %29 = OpLabel
1781 OpBranch %24
1782 %24 = OpLabel
1783 OpBranchConditional %7 %22 %23
1784 %23 = OpLabel
1785 OpBranch %31
1786 %31 = OpLabel
1787 %32 = OpCopyObject %5 %28
1788 OpReturn
1789 OpFunctionEnd
1790 )";
1791
1792 ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
1793 }
1794
TEST(TransformationMergeFunctionReturnsTest,OpPhiAfterFirstBlock)1795 TEST(TransformationMergeFunctionReturnsTest, OpPhiAfterFirstBlock) {
1796 std::string shader = R"(
1797 OpCapability Shader
1798 %1 = OpExtInstImport "GLSL.std.450"
1799 OpMemoryModel Logical GLSL450
1800 OpEntryPoint Fragment %2 "main"
1801 OpExecutionMode %2 OriginUpperLeft
1802 OpSource ESSL 310
1803 %3 = OpTypeVoid
1804 %4 = OpTypeFunction %3
1805 %5 = OpTypeBool
1806 %6 = OpConstantTrue %5
1807 %7 = OpConstantFalse %5
1808 %2 = OpFunction %3 None %4
1809 %8 = OpLabel
1810 OpBranch %9
1811 %9 = OpLabel
1812 %10 = OpPhi %5 %6 %8
1813 OpSelectionMerge %11 None
1814 OpBranchConditional %6 %12 %11
1815 %12 = OpLabel
1816 OpReturn
1817 %11 = OpLabel
1818 OpReturn
1819 OpFunctionEnd
1820 )";
1821
1822 const auto env = SPV_ENV_UNIVERSAL_1_5;
1823 const auto consumer = nullptr;
1824 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1825 spvtools::ValidatorOptions validator_options;
1826 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1827 kConsoleMessageConsumer));
1828
1829 TransformationContext transformation_context(
1830 MakeUnique<FactManager>(context.get()), validator_options);
1831
1832 auto transformation =
1833 TransformationMergeFunctionReturns(2, 100, 101, 0, 0, {});
1834 ASSERT_TRUE(
1835 transformation.IsApplicable(context.get(), transformation_context));
1836 ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
1837 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1838 kConsoleMessageConsumer));
1839
1840 // Ensure that all input operands of OpBranchConditional instructions have
1841 // the right operand type.
1842 context->module()->ForEachInst([](opt::Instruction* inst) {
1843 if (inst->opcode() == SpvOpBranchConditional) {
1844 ASSERT_EQ(inst->GetInOperand(0).type, SPV_OPERAND_TYPE_ID);
1845 ASSERT_EQ(inst->GetInOperand(1).type, SPV_OPERAND_TYPE_ID);
1846 ASSERT_EQ(inst->GetInOperand(2).type, SPV_OPERAND_TYPE_ID);
1847 }
1848 });
1849
1850 std::string after_transformation = R"(
1851 OpCapability Shader
1852 %1 = OpExtInstImport "GLSL.std.450"
1853 OpMemoryModel Logical GLSL450
1854 OpEntryPoint Fragment %2 "main"
1855 OpExecutionMode %2 OriginUpperLeft
1856 OpSource ESSL 310
1857 %3 = OpTypeVoid
1858 %4 = OpTypeFunction %3
1859 %5 = OpTypeBool
1860 %6 = OpConstantTrue %5
1861 %7 = OpConstantFalse %5
1862 %2 = OpFunction %3 None %4
1863 %8 = OpLabel
1864 OpBranch %100
1865 %100 = OpLabel
1866 OpLoopMerge %101 %100 None
1867 OpBranchConditional %6 %9 %100
1868 %9 = OpLabel
1869 %10 = OpPhi %5 %6 %100
1870 OpSelectionMerge %11 None
1871 OpBranchConditional %6 %12 %11
1872 %12 = OpLabel
1873 OpBranch %101
1874 %11 = OpLabel
1875 OpBranch %101
1876 %101 = OpLabel
1877 OpReturn
1878 OpFunctionEnd
1879 )";
1880
1881 ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
1882 }
1883
1884 } // namespace
1885 } // namespace fuzz
1886 } // namespace spvtools