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_replace_boolean_constant_with_constant_binary.h"
16 
17 #include "source/fuzz/fuzzer_util.h"
18 #include "source/fuzz/id_use_descriptor.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(TransformationReplaceBooleanConstantWithConstantBinaryTest,BasicReplacements)26 TEST(TransformationReplaceBooleanConstantWithConstantBinaryTest,
27      BasicReplacements) {
28   // The test came from the following pseudo-GLSL, where int64 and uint64 denote
29   // 64-bit integer types (they were replaced with int and uint during
30   // translation to SPIR-V, and the generated SPIR-V has been doctored to
31   // accommodate them).
32   //
33   // #version 450
34   //
35   // void main() {
36   //   double d1, d2;
37   //   d1 = 1.0;
38   //   d2 = 2.0;
39   //   float f1, f2;
40   //   f1 = 4.0;
41   //   f2 = 8.0;
42   //   int i1, i2;
43   //   i1 = 100;
44   //   i2 = 200;
45   //
46   //   uint u1, u2;
47   //   u1 = 300u;
48   //   u2 = 400u;
49   //
50   //   int64 i64_1, i64_2;
51   //   i64_1 = 500;
52   //   i64_2 = 600;
53   //
54   //   uint64 u64_1, u64_2;
55   //   u64_1 = 700u;
56   //   u64_2 = 800u;
57   //
58   //   bool b, c, d, e;
59   //   b = true;
60   //   c = false;
61   //   d = true || c;
62   //   c = c && false;
63   // }
64   std::string shader = R"(
65                OpCapability Shader
66                OpCapability Float64
67                OpCapability Int64
68           %1 = OpExtInstImport "GLSL.std.450"
69                OpMemoryModel Logical GLSL450
70                OpEntryPoint Fragment %4 "main"
71                OpExecutionMode %4 OriginUpperLeft
72                OpSource GLSL 450
73                OpName %4 "main"
74                OpName %8 "d1"
75                OpName %10 "d2"
76                OpName %14 "f1"
77                OpName %16 "f2"
78                OpName %20 "i1"
79                OpName %22 "i2"
80                OpName %26 "u1"
81                OpName %28 "u2"
82                OpName %30 "i64_1"
83                OpName %32 "i64_2"
84                OpName %34 "u64_1"
85                OpName %36 "u64_2"
86                OpName %40 "b"
87                OpName %42 "c"
88                OpName %44 "d"
89           %2 = OpTypeVoid
90           %3 = OpTypeFunction %2
91           %6 = OpTypeFloat 64
92           %7 = OpTypePointer Function %6
93           %9 = OpConstant %6 1
94          %11 = OpConstant %6 2
95          %12 = OpTypeFloat 32
96          %13 = OpTypePointer Function %12
97          %15 = OpConstant %12 4
98          %17 = OpConstant %12 8
99          %18 = OpTypeInt 32 1
100          %60 = OpTypeInt 64 1
101          %61 = OpTypePointer Function %60
102          %19 = OpTypePointer Function %18
103          %21 = OpConstant %18 -100
104          %23 = OpConstant %18 200
105          %24 = OpTypeInt 32 0
106          %62 = OpTypeInt 64 0
107          %63 = OpTypePointer Function %62
108          %25 = OpTypePointer Function %24
109          %27 = OpConstant %24 300
110          %29 = OpConstant %24 400
111          %31 = OpConstant %60 -600
112          %33 = OpConstant %60 -500
113          %35 = OpConstant %62 700
114          %37 = OpConstant %62 800
115          %38 = OpTypeBool
116          %39 = OpTypePointer Function %38
117          %41 = OpConstantTrue %38
118          %43 = OpConstantFalse %38
119           %4 = OpFunction %2 None %3
120           %5 = OpLabel
121           %8 = OpVariable %7 Function
122          %10 = OpVariable %7 Function
123          %14 = OpVariable %13 Function
124          %16 = OpVariable %13 Function
125          %20 = OpVariable %19 Function
126          %22 = OpVariable %19 Function
127          %26 = OpVariable %25 Function
128          %28 = OpVariable %25 Function
129          %30 = OpVariable %61 Function
130          %32 = OpVariable %61 Function
131          %34 = OpVariable %63 Function
132          %36 = OpVariable %63 Function
133          %40 = OpVariable %39 Function
134          %42 = OpVariable %39 Function
135          %44 = OpVariable %39 Function
136                OpStore %8 %9
137                OpStore %10 %11
138                OpStore %14 %15
139                OpStore %16 %17
140                OpStore %20 %21
141                OpStore %22 %23
142                OpStore %26 %27
143                OpStore %28 %29
144                OpStore %30 %31
145                OpStore %32 %33
146                OpStore %34 %35
147                OpStore %36 %37
148                OpStore %40 %41
149                OpStore %42 %43
150          %45 = OpLoad %38 %42
151          %46 = OpLogicalOr %38 %41 %45
152                OpStore %44 %46
153          %47 = OpLoad %38 %42
154          %48 = OpLogicalAnd %38 %47 %43
155                OpStore %42 %48
156                OpReturn
157                OpFunctionEnd
158   )";
159 
160   const auto env = SPV_ENV_UNIVERSAL_1_3;
161   const auto consumer = nullptr;
162   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
163   ASSERT_TRUE(IsValid(env, context.get()));
164 
165   FactManager fact_manager;
166 
167   std::vector<protobufs::IdUseDescriptor> uses_of_true = {
168       MakeIdUseDescriptor(41, MakeInstructionDescriptor(44, SpvOpStore, 12), 1),
169       MakeIdUseDescriptor(41, MakeInstructionDescriptor(46, SpvOpLogicalOr, 0),
170                           0)};
171 
172   std::vector<protobufs::IdUseDescriptor> uses_of_false = {
173       MakeIdUseDescriptor(43, MakeInstructionDescriptor(44, SpvOpStore, 13), 1),
174       MakeIdUseDescriptor(43, MakeInstructionDescriptor(48, SpvOpLogicalAnd, 0),
175                           1)};
176 
177   const uint32_t fresh_id = 100;
178 
179   std::vector<SpvOp> fp_gt_opcodes = {
180       SpvOpFOrdGreaterThan, SpvOpFOrdGreaterThanEqual, SpvOpFUnordGreaterThan,
181       SpvOpFUnordGreaterThanEqual};
182 
183   std::vector<SpvOp> fp_lt_opcodes = {SpvOpFOrdLessThan, SpvOpFOrdLessThanEqual,
184                                       SpvOpFUnordLessThan,
185                                       SpvOpFUnordLessThanEqual};
186 
187   std::vector<SpvOp> int_gt_opcodes = {SpvOpSGreaterThan,
188                                        SpvOpSGreaterThanEqual};
189 
190   std::vector<SpvOp> int_lt_opcodes = {SpvOpSLessThan, SpvOpSLessThanEqual};
191 
192   std::vector<SpvOp> uint_gt_opcodes = {SpvOpUGreaterThan,
193                                         SpvOpUGreaterThanEqual};
194 
195   std::vector<SpvOp> uint_lt_opcodes = {SpvOpULessThan, SpvOpULessThanEqual};
196 
197 #define CHECK_OPERATOR(USE_DESCRIPTOR, LHS_ID, RHS_ID, OPCODE, FRESH_ID) \
198   ASSERT_TRUE(TransformationReplaceBooleanConstantWithConstantBinary(    \
199                   USE_DESCRIPTOR, LHS_ID, RHS_ID, OPCODE, FRESH_ID)      \
200                   .IsApplicable(context.get(), fact_manager));           \
201   ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary(   \
202                    USE_DESCRIPTOR, RHS_ID, LHS_ID, OPCODE, FRESH_ID)     \
203                    .IsApplicable(context.get(), fact_manager));
204 
205 #define CHECK_TRANSFORMATION_APPLICABILITY(GT_OPCODES, LT_OPCODES, SMALL_ID, \
206                                            LARGE_ID)                         \
207   for (auto gt_opcode : GT_OPCODES) {                                        \
208     for (auto& true_use : uses_of_true) {                                    \
209       CHECK_OPERATOR(true_use, LARGE_ID, SMALL_ID, gt_opcode, fresh_id);     \
210     }                                                                        \
211     for (auto& false_use : uses_of_false) {                                  \
212       CHECK_OPERATOR(false_use, SMALL_ID, LARGE_ID, gt_opcode, fresh_id);    \
213     }                                                                        \
214   }                                                                          \
215   for (auto lt_opcode : LT_OPCODES) {                                        \
216     for (auto& true_use : uses_of_true) {                                    \
217       CHECK_OPERATOR(true_use, SMALL_ID, LARGE_ID, lt_opcode, fresh_id);     \
218     }                                                                        \
219     for (auto& false_use : uses_of_false) {                                  \
220       CHECK_OPERATOR(false_use, LARGE_ID, SMALL_ID, lt_opcode, fresh_id);    \
221     }                                                                        \
222   }
223 
224   // Float
225   { CHECK_TRANSFORMATION_APPLICABILITY(fp_gt_opcodes, fp_lt_opcodes, 15, 17); }
226 
227   // Double
228   { CHECK_TRANSFORMATION_APPLICABILITY(fp_gt_opcodes, fp_lt_opcodes, 9, 11); }
229 
230   // Int32
231   {
232     CHECK_TRANSFORMATION_APPLICABILITY(int_gt_opcodes, int_lt_opcodes, 21, 23);
233   }
234 
235   // Int64
236   {
237     CHECK_TRANSFORMATION_APPLICABILITY(int_gt_opcodes, int_lt_opcodes, 31, 33);
238   }
239 
240   // Uint32
241   {
242     CHECK_TRANSFORMATION_APPLICABILITY(uint_gt_opcodes, uint_lt_opcodes, 27,
243                                        29);
244   }
245 
246   // Uint64
247   {
248     CHECK_TRANSFORMATION_APPLICABILITY(uint_gt_opcodes, uint_lt_opcodes, 35,
249                                        37);
250   }
251 
252   // Target id is not fresh
253   ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary(
254                    uses_of_true[0], 15, 17, SpvOpFOrdLessThan, 15)
255                    .IsApplicable(context.get(), fact_manager));
256 
257   // LHS id does not exist
258   ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary(
259                    uses_of_true[0], 300, 17, SpvOpFOrdLessThan, 200)
260                    .IsApplicable(context.get(), fact_manager));
261 
262   // RHS id does not exist
263   ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary(
264                    uses_of_true[0], 15, 300, SpvOpFOrdLessThan, 200)
265                    .IsApplicable(context.get(), fact_manager));
266 
267   // LHS and RHS ids do not match type
268   ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary(
269                    uses_of_true[0], 11, 17, SpvOpFOrdLessThan, 200)
270                    .IsApplicable(context.get(), fact_manager));
271 
272   // Opcode not appropriate
273   ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary(
274                    uses_of_true[0], 15, 17, SpvOpFDiv, 200)
275                    .IsApplicable(context.get(), fact_manager));
276 
277   auto replace_true_with_double_comparison =
278       TransformationReplaceBooleanConstantWithConstantBinary(
279           uses_of_true[0], 11, 9, SpvOpFUnordGreaterThan, 100);
280   auto replace_true_with_uint32_comparison =
281       TransformationReplaceBooleanConstantWithConstantBinary(
282           uses_of_true[1], 27, 29, SpvOpULessThanEqual, 101);
283   auto replace_false_with_float_comparison =
284       TransformationReplaceBooleanConstantWithConstantBinary(
285           uses_of_false[0], 17, 15, SpvOpFOrdLessThan, 102);
286   auto replace_false_with_sint64_comparison =
287       TransformationReplaceBooleanConstantWithConstantBinary(
288           uses_of_false[1], 33, 31, SpvOpSLessThan, 103);
289 
290   ASSERT_TRUE(replace_true_with_double_comparison.IsApplicable(context.get(),
291                                                                fact_manager));
292   replace_true_with_double_comparison.Apply(context.get(), &fact_manager);
293   ASSERT_TRUE(IsValid(env, context.get()));
294   ASSERT_TRUE(replace_true_with_uint32_comparison.IsApplicable(context.get(),
295                                                                fact_manager));
296   replace_true_with_uint32_comparison.Apply(context.get(), &fact_manager);
297   ASSERT_TRUE(IsValid(env, context.get()));
298   ASSERT_TRUE(replace_false_with_float_comparison.IsApplicable(context.get(),
299                                                                fact_manager));
300   replace_false_with_float_comparison.Apply(context.get(), &fact_manager);
301   ASSERT_TRUE(IsValid(env, context.get()));
302   ASSERT_TRUE(replace_false_with_sint64_comparison.IsApplicable(context.get(),
303                                                                 fact_manager));
304   replace_false_with_sint64_comparison.Apply(context.get(), &fact_manager);
305   ASSERT_TRUE(IsValid(env, context.get()));
306 
307   std::string after = R"(
308                OpCapability Shader
309                OpCapability Float64
310                OpCapability Int64
311           %1 = OpExtInstImport "GLSL.std.450"
312                OpMemoryModel Logical GLSL450
313                OpEntryPoint Fragment %4 "main"
314                OpExecutionMode %4 OriginUpperLeft
315                OpSource GLSL 450
316                OpName %4 "main"
317                OpName %8 "d1"
318                OpName %10 "d2"
319                OpName %14 "f1"
320                OpName %16 "f2"
321                OpName %20 "i1"
322                OpName %22 "i2"
323                OpName %26 "u1"
324                OpName %28 "u2"
325                OpName %30 "i64_1"
326                OpName %32 "i64_2"
327                OpName %34 "u64_1"
328                OpName %36 "u64_2"
329                OpName %40 "b"
330                OpName %42 "c"
331                OpName %44 "d"
332           %2 = OpTypeVoid
333           %3 = OpTypeFunction %2
334           %6 = OpTypeFloat 64
335           %7 = OpTypePointer Function %6
336           %9 = OpConstant %6 1
337          %11 = OpConstant %6 2
338          %12 = OpTypeFloat 32
339          %13 = OpTypePointer Function %12
340          %15 = OpConstant %12 4
341          %17 = OpConstant %12 8
342          %18 = OpTypeInt 32 1
343          %60 = OpTypeInt 64 1
344          %61 = OpTypePointer Function %60
345          %19 = OpTypePointer Function %18
346          %21 = OpConstant %18 -100
347          %23 = OpConstant %18 200
348          %24 = OpTypeInt 32 0
349          %62 = OpTypeInt 64 0
350          %63 = OpTypePointer Function %62
351          %25 = OpTypePointer Function %24
352          %27 = OpConstant %24 300
353          %29 = OpConstant %24 400
354          %31 = OpConstant %60 -600
355          %33 = OpConstant %60 -500
356          %35 = OpConstant %62 700
357          %37 = OpConstant %62 800
358          %38 = OpTypeBool
359          %39 = OpTypePointer Function %38
360          %41 = OpConstantTrue %38
361          %43 = OpConstantFalse %38
362           %4 = OpFunction %2 None %3
363           %5 = OpLabel
364           %8 = OpVariable %7 Function
365          %10 = OpVariable %7 Function
366          %14 = OpVariable %13 Function
367          %16 = OpVariable %13 Function
368          %20 = OpVariable %19 Function
369          %22 = OpVariable %19 Function
370          %26 = OpVariable %25 Function
371          %28 = OpVariable %25 Function
372          %30 = OpVariable %61 Function
373          %32 = OpVariable %61 Function
374          %34 = OpVariable %63 Function
375          %36 = OpVariable %63 Function
376          %40 = OpVariable %39 Function
377          %42 = OpVariable %39 Function
378          %44 = OpVariable %39 Function
379                OpStore %8 %9
380                OpStore %10 %11
381                OpStore %14 %15
382                OpStore %16 %17
383                OpStore %20 %21
384                OpStore %22 %23
385                OpStore %26 %27
386                OpStore %28 %29
387                OpStore %30 %31
388                OpStore %32 %33
389                OpStore %34 %35
390                OpStore %36 %37
391         %100 = OpFUnordGreaterThan %38 %11 %9
392                OpStore %40 %100
393         %102 = OpFOrdLessThan %38 %17 %15
394                OpStore %42 %102
395          %45 = OpLoad %38 %42
396         %101 = OpULessThanEqual %38 %27 %29
397          %46 = OpLogicalOr %38 %101 %45
398                OpStore %44 %46
399          %47 = OpLoad %38 %42
400         %103 = OpSLessThan %38 %33 %31
401          %48 = OpLogicalAnd %38 %47 %103
402                OpStore %42 %48
403                OpReturn
404                OpFunctionEnd
405   )";
406   ASSERT_TRUE(IsEqual(env, after, context.get()));
407 
408   if (std::numeric_limits<double>::has_quiet_NaN) {
409     double quiet_nan_double = std::numeric_limits<double>::quiet_NaN();
410     uint32_t words[2];
411     memcpy(words, &quiet_nan_double, sizeof(double));
412     opt::Instruction::OperandList operands = {
413         {SPV_OPERAND_TYPE_LITERAL_INTEGER, {words[0]}},
414         {SPV_OPERAND_TYPE_LITERAL_INTEGER, {words[1]}}};
415     context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
416         context.get(), SpvOpConstant, 6, 200, operands));
417     fuzzerutil::UpdateModuleIdBound(context.get(), 200);
418     ASSERT_TRUE(IsValid(env, context.get()));
419     // The transformation is not applicable because %200 is NaN.
420     ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary(
421                      uses_of_true[0], 11, 200, SpvOpFOrdLessThan, 300)
422                      .IsApplicable(context.get(), fact_manager));
423   }
424   if (std::numeric_limits<double>::has_infinity) {
425     double positive_infinity_double = std::numeric_limits<double>::infinity();
426     uint32_t words[2];
427     memcpy(words, &positive_infinity_double, sizeof(double));
428     opt::Instruction::OperandList operands = {
429         {SPV_OPERAND_TYPE_LITERAL_INTEGER, {words[0]}},
430         {SPV_OPERAND_TYPE_LITERAL_INTEGER, {words[1]}}};
431     context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
432         context.get(), SpvOpConstant, 6, 201, operands));
433     fuzzerutil::UpdateModuleIdBound(context.get(), 201);
434     ASSERT_TRUE(IsValid(env, context.get()));
435     // Even though the double constant %11 is less than the infinity %201, the
436     // transformation is restricted to only apply to finite values.
437     ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary(
438                      uses_of_true[0], 11, 201, SpvOpFOrdLessThan, 300)
439                      .IsApplicable(context.get(), fact_manager));
440   }
441   if (std::numeric_limits<float>::has_infinity) {
442     float positive_infinity_float = std::numeric_limits<float>::infinity();
443     float negative_infinity_float = -1 * positive_infinity_float;
444     uint32_t words_positive_infinity[1];
445     uint32_t words_negative_infinity[1];
446     memcpy(words_positive_infinity, &positive_infinity_float, sizeof(float));
447     memcpy(words_negative_infinity, &negative_infinity_float, sizeof(float));
448     opt::Instruction::OperandList operands_positive_infinity = {
449         {SPV_OPERAND_TYPE_LITERAL_INTEGER, {words_positive_infinity[0]}}};
450     context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
451         context.get(), SpvOpConstant, 12, 202, operands_positive_infinity));
452     fuzzerutil::UpdateModuleIdBound(context.get(), 202);
453     opt::Instruction::OperandList operands = {
454         {SPV_OPERAND_TYPE_LITERAL_INTEGER, {words_negative_infinity[0]}}};
455     context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
456         context.get(), SpvOpConstant, 12, 203, operands));
457     fuzzerutil::UpdateModuleIdBound(context.get(), 203);
458     ASSERT_TRUE(IsValid(env, context.get()));
459     // Even though the negative infinity at %203 is less than the positive
460     // infinity %202, the transformation is restricted to only apply to finite
461     // values.
462     ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary(
463                      uses_of_true[0], 203, 202, SpvOpFOrdLessThan, 300)
464                      .IsApplicable(context.get(), fact_manager));
465   }
466 }
467 
TEST(TransformationReplaceBooleanConstantWithConstantBinaryTest,MergeInstructions)468 TEST(TransformationReplaceBooleanConstantWithConstantBinaryTest,
469      MergeInstructions) {
470   // The test came from the following GLSL:
471   //
472   // void main() {
473   //   int x = 1;
474   //   int y = 2;
475   //   if (true) {
476   //     x = 2;
477   //   }
478   //   while(false) {
479   //     y = 2;
480   //   }
481   // }
482 
483   std::string shader = R"(
484                OpCapability Shader
485           %1 = OpExtInstImport "GLSL.std.450"
486                OpMemoryModel Logical GLSL450
487                OpEntryPoint Fragment %4 "main"
488                OpExecutionMode %4 OriginUpperLeft
489                OpSource GLSL 450
490                OpName %4 "main"
491                OpName %8 "x"
492                OpName %10 "y"
493           %2 = OpTypeVoid
494           %3 = OpTypeFunction %2
495           %6 = OpTypeInt 32 1
496           %7 = OpTypePointer Function %6
497           %9 = OpConstant %6 1
498          %11 = OpConstant %6 2
499          %12 = OpTypeBool
500          %13 = OpConstantTrue %12
501          %21 = OpConstantFalse %12
502           %4 = OpFunction %2 None %3
503           %5 = OpLabel
504           %8 = OpVariable %7 Function
505          %10 = OpVariable %7 Function
506                OpStore %8 %9
507                OpStore %10 %11
508                OpSelectionMerge %15 None
509                OpBranchConditional %13 %14 %15
510          %14 = OpLabel
511                OpStore %8 %11
512                OpBranch %15
513          %15 = OpLabel
514                OpBranch %16
515          %16 = OpLabel
516                OpLoopMerge %18 %19 None
517                OpBranchConditional %21 %17 %18
518          %17 = OpLabel
519                OpStore %10 %11
520                OpBranch %19
521          %19 = OpLabel
522                OpBranch %16
523          %18 = OpLabel
524                OpReturn
525                OpFunctionEnd
526   )";
527 
528   const auto env = SPV_ENV_UNIVERSAL_1_3;
529   const auto consumer = nullptr;
530   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
531   ASSERT_TRUE(IsValid(env, context.get()));
532 
533   FactManager fact_manager;
534 
535   auto use_of_true_in_if = MakeIdUseDescriptor(
536       13, MakeInstructionDescriptor(10, SpvOpBranchConditional, 0), 0);
537   auto use_of_false_in_while = MakeIdUseDescriptor(
538       21, MakeInstructionDescriptor(16, SpvOpBranchConditional, 0), 0);
539 
540   auto replacement_1 = TransformationReplaceBooleanConstantWithConstantBinary(
541       use_of_true_in_if, 9, 11, SpvOpSLessThan, 100);
542   auto replacement_2 = TransformationReplaceBooleanConstantWithConstantBinary(
543       use_of_false_in_while, 9, 11, SpvOpSGreaterThanEqual, 101);
544 
545   ASSERT_TRUE(replacement_1.IsApplicable(context.get(), fact_manager));
546   replacement_1.Apply(context.get(), &fact_manager);
547   ASSERT_TRUE(IsValid(env, context.get()));
548 
549   ASSERT_TRUE(replacement_2.IsApplicable(context.get(), fact_manager));
550   replacement_2.Apply(context.get(), &fact_manager);
551   ASSERT_TRUE(IsValid(env, context.get()));
552 
553   std::string after = R"(
554                OpCapability Shader
555           %1 = OpExtInstImport "GLSL.std.450"
556                OpMemoryModel Logical GLSL450
557                OpEntryPoint Fragment %4 "main"
558                OpExecutionMode %4 OriginUpperLeft
559                OpSource GLSL 450
560                OpName %4 "main"
561                OpName %8 "x"
562                OpName %10 "y"
563           %2 = OpTypeVoid
564           %3 = OpTypeFunction %2
565           %6 = OpTypeInt 32 1
566           %7 = OpTypePointer Function %6
567           %9 = OpConstant %6 1
568          %11 = OpConstant %6 2
569          %12 = OpTypeBool
570          %13 = OpConstantTrue %12
571          %21 = OpConstantFalse %12
572           %4 = OpFunction %2 None %3
573           %5 = OpLabel
574           %8 = OpVariable %7 Function
575          %10 = OpVariable %7 Function
576                OpStore %8 %9
577                OpStore %10 %11
578         %100 = OpSLessThan %12 %9 %11
579                OpSelectionMerge %15 None
580                OpBranchConditional %100 %14 %15
581          %14 = OpLabel
582                OpStore %8 %11
583                OpBranch %15
584          %15 = OpLabel
585                OpBranch %16
586          %16 = OpLabel
587         %101 = OpSGreaterThanEqual %12 %9 %11
588                OpLoopMerge %18 %19 None
589                OpBranchConditional %101 %17 %18
590          %17 = OpLabel
591                OpStore %10 %11
592                OpBranch %19
593          %19 = OpLabel
594                OpBranch %16
595          %18 = OpLabel
596                OpReturn
597                OpFunctionEnd
598   )";
599 
600   ASSERT_TRUE(IsEqual(env, after, context.get()));
601 }
602 
TEST(TransformationReplaceBooleanConstantWithConstantBinaryTest,OpPhi)603 TEST(TransformationReplaceBooleanConstantWithConstantBinaryTest, OpPhi) {
604   // Hand-written SPIR-V to check applicability of the transformation on an
605   // OpPhi argument.
606 
607   std::string shader = R"(
608                OpCapability Shader
609           %1 = OpExtInstImport "GLSL.std.450"
610                OpMemoryModel Logical GLSL450
611                OpEntryPoint Fragment %4 "main"
612                OpExecutionMode %4 OriginUpperLeft
613                OpSource ESSL 310
614                OpName %4 "main"
615           %2 = OpTypeVoid
616           %3 = OpTypeFunction %2
617           %6 = OpTypeBool
618           %7 = OpTypePointer Function %6
619           %9 = OpConstantTrue %6
620          %16 = OpConstantFalse %6
621          %10 = OpTypeInt 32 1
622          %11 = OpTypePointer Function %10
623          %13 = OpConstant %10 0
624          %15 = OpConstant %10 1
625           %4 = OpFunction %2 None %3
626           %5 = OpLabel
627                OpSelectionMerge %20 None
628                OpBranchConditional %9 %21 %22
629          %21 = OpLabel
630                OpBranch %20
631          %22 = OpLabel
632                OpBranch %20
633          %20 = OpLabel
634          %23 = OpPhi %6 %9 %21 %16 %22
635                OpReturn
636                OpFunctionEnd
637   )";
638 
639   const auto env = SPV_ENV_UNIVERSAL_1_3;
640   const auto consumer = nullptr;
641   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
642   ASSERT_TRUE(IsValid(env, context.get()));
643 
644   FactManager fact_manager;
645 
646   auto replacement = TransformationReplaceBooleanConstantWithConstantBinary(
647       MakeIdUseDescriptor(9, MakeInstructionDescriptor(23, SpvOpPhi, 0), 0), 13,
648       15, SpvOpSLessThan, 100);
649 
650   ASSERT_FALSE(replacement.IsApplicable(context.get(), fact_manager));
651 }
652 
653 }  // namespace
654 }  // namespace fuzz
655 }  // namespace spvtools
656