1 // Copyright (c) 2020 André Perez Maselco
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_push_id_through_variable.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(TransformationPushIdThroughVariableTest, IsApplicable)26 TEST(TransformationPushIdThroughVariableTest, IsApplicable) {
27   std::string reference_shader = R"(
28                OpCapability Shader
29                OpCapability VariablePointers
30           %1 = OpExtInstImport "GLSL.std.450"
31                OpMemoryModel Logical GLSL450
32                OpEntryPoint Fragment %4 "main" %92 %52 %53
33                OpExecutionMode %4 OriginUpperLeft
34                OpSource ESSL 310
35                OpDecorate %92 BuiltIn FragCoord
36           %2 = OpTypeVoid
37           %3 = OpTypeFunction %2
38           %6 = OpTypeInt 32 1
39           %7 = OpTypeFloat 32
40           %8 = OpTypeStruct %6 %7
41           %9 = OpTypePointer Function %8
42          %10 = OpTypeFunction %6 %9
43          %14 = OpConstant %6 0
44          %15 = OpTypePointer Function %6
45          %51 = OpTypePointer Private %6
46          %21 = OpConstant %6 2
47          %23 = OpConstant %6 1
48          %24 = OpConstant %7 1
49          %25 = OpTypePointer Function %7
50          %50 = OpTypePointer Private %7
51          %34 = OpTypeBool
52          %35 = OpConstantFalse %34
53          %60 = OpConstantNull %50
54          %52 = OpVariable %50 Private
55          %53 = OpVariable %51 Private
56          %80 = OpConstantComposite %8 %21 %24
57          %90 = OpTypeVector %7 4
58          %91 = OpTypePointer Input %90
59          %92 = OpVariable %91 Input
60          %93 = OpConstantComposite %90 %24 %24 %24 %24
61           %4 = OpFunction %2 None %3
62           %5 = OpLabel
63          %20 = OpVariable %9 Function
64          %27 = OpVariable %9 Function
65          %22 = OpAccessChain %15 %20 %14
66          %44 = OpCopyObject %9 %20
67          %26 = OpAccessChain %25 %20 %23
68          %29 = OpFunctionCall %6 %12 %27
69          %30 = OpAccessChain %15 %20 %14
70          %45 = OpCopyObject %15 %30
71          %81 = OpCopyObject %9 %27
72          %33 = OpAccessChain %15 %20 %14
73                OpSelectionMerge %37 None
74                OpBranchConditional %35 %36 %37
75          %36 = OpLabel
76          %38 = OpAccessChain %15 %20 %14
77          %40 = OpAccessChain %15 %20 %14
78          %43 = OpAccessChain %15 %20 %14
79          %82 = OpCopyObject %9 %27
80                OpBranch %37
81          %37 = OpLabel
82                OpReturn
83                OpFunctionEnd
84          %12 = OpFunction %6 None %10
85          %11 = OpFunctionParameter %9
86          %13 = OpLabel
87          %46 = OpCopyObject %9 %11
88          %16 = OpAccessChain %15 %11 %14
89          %95 = OpCopyObject %8 %80
90                OpReturnValue %21
91         %100 = OpLabel
92                OpUnreachable
93                OpFunctionEnd
94   )";
95 
96   const auto env = SPV_ENV_UNIVERSAL_1_4;
97   const auto consumer = nullptr;
98   const auto context =
99       BuildModule(env, consumer, reference_shader, kFuzzAssembleOption);
100 
101   spvtools::ValidatorOptions validator_options;
102   TransformationContext transformation_context(
103       MakeUnique<FactManager>(context.get()), validator_options);
104   // Tests the reference shader validity.
105   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
106                                                kConsoleMessageConsumer));
107 
108   // Tests |value_synonym_id| and |variable_id| are fresh ids.
109   uint32_t value_id = 21;
110   uint32_t value_synonym_id = 62;
111   uint32_t variable_id = 63;
112   uint32_t initializer_id = 23;
113   uint32_t variable_storage_class = (uint32_t)spv::StorageClass::Private;
114   auto instruction_descriptor =
115       MakeInstructionDescriptor(95, spv::Op::OpReturnValue, 0);
116   auto transformation = TransformationPushIdThroughVariable(
117       value_id, value_synonym_id, variable_id, variable_storage_class,
118       initializer_id, instruction_descriptor);
119   ASSERT_TRUE(
120       transformation.IsApplicable(context.get(), transformation_context));
121 
122   // Tests |value_synonym_id| and |variable_id| are non-fresh ids.
123   value_id = 80;
124   value_synonym_id = 60;
125   variable_id = 61;
126   initializer_id = 80;
127   variable_storage_class = (uint32_t)spv::StorageClass::Function;
128   instruction_descriptor =
129       MakeInstructionDescriptor(38, spv::Op::OpAccessChain, 0);
130   transformation = TransformationPushIdThroughVariable(
131       value_id, value_synonym_id, variable_id, variable_storage_class,
132       initializer_id, instruction_descriptor);
133   ASSERT_FALSE(
134       transformation.IsApplicable(context.get(), transformation_context));
135 
136   // The instruction to insert before is not defined.
137   value_id = 80;
138   value_synonym_id = 62;
139   variable_id = 63;
140   initializer_id = 80;
141   variable_storage_class = (uint32_t)spv::StorageClass::Function;
142   instruction_descriptor =
143       MakeInstructionDescriptor(64, spv::Op::OpAccessChain, 0);
144   transformation = TransformationPushIdThroughVariable(
145       value_id, value_synonym_id, variable_id, variable_storage_class,
146       initializer_id, instruction_descriptor);
147   ASSERT_FALSE(
148       transformation.IsApplicable(context.get(), transformation_context));
149 
150   // Attempting to insert the store and load instructions
151   // before an OpVariable instruction.
152   value_id = 24;
153   value_synonym_id = 62;
154   variable_id = 63;
155   initializer_id = 24;
156   variable_storage_class = (uint32_t)spv::StorageClass::Function;
157   instruction_descriptor =
158       MakeInstructionDescriptor(27, spv::Op::OpVariable, 0);
159   transformation = TransformationPushIdThroughVariable(
160       value_id, value_synonym_id, variable_id, variable_storage_class,
161       initializer_id, instruction_descriptor);
162   ASSERT_FALSE(
163       transformation.IsApplicable(context.get(), transformation_context));
164 
165   // The block containing instruction descriptor must be reachable.
166   value_id = 80;
167   value_synonym_id = 62;
168   variable_id = 63;
169   initializer_id = 80;
170   variable_storage_class = (uint32_t)spv::StorageClass::Function;
171   instruction_descriptor =
172       MakeInstructionDescriptor(100, spv::Op::OpUnreachable, 0);
173   transformation = TransformationPushIdThroughVariable(
174       value_id, value_synonym_id, variable_id, variable_storage_class,
175       initializer_id, instruction_descriptor);
176   ASSERT_FALSE(
177       transformation.IsApplicable(context.get(), transformation_context));
178 
179   // Tests value instruction not available.
180   value_id = 64;
181   value_synonym_id = 62;
182   variable_id = 63;
183   initializer_id = 23;
184   variable_storage_class = (uint32_t)spv::StorageClass::Function;
185   instruction_descriptor =
186       MakeInstructionDescriptor(95, spv::Op::OpReturnValue, 0);
187   transformation = TransformationPushIdThroughVariable(
188       value_id, value_synonym_id, variable_id, variable_storage_class,
189       initializer_id, instruction_descriptor);
190   ASSERT_FALSE(
191       transformation.IsApplicable(context.get(), transformation_context));
192 
193   // Tests pointer type not available.
194   value_id = 80;
195   value_synonym_id = 62;
196   variable_id = 63;
197   initializer_id = 80;
198   variable_storage_class = (uint32_t)spv::StorageClass::Private;
199   instruction_descriptor =
200       MakeInstructionDescriptor(95, spv::Op::OpReturnValue, 0);
201   transformation = TransformationPushIdThroughVariable(
202       value_id, value_synonym_id, variable_id, variable_storage_class,
203       initializer_id, instruction_descriptor);
204   ASSERT_FALSE(
205       transformation.IsApplicable(context.get(), transformation_context));
206 
207   // Tests not a private nor function storage class.
208   value_id = 93;
209   value_synonym_id = 62;
210   variable_id = 63;
211   initializer_id = 93;
212   variable_storage_class = (uint32_t)spv::StorageClass::Input;
213   instruction_descriptor =
214       MakeInstructionDescriptor(95, spv::Op::OpReturnValue, 0);
215   transformation = TransformationPushIdThroughVariable(
216       value_id, value_synonym_id, variable_id, variable_storage_class,
217       initializer_id, instruction_descriptor);
218 #ifndef NDEBUG
219   ASSERT_DEATH(
220       transformation.IsApplicable(context.get(), transformation_context),
221       "The variable storage class must be private or function");
222 #endif
223 
224   // Tests value instruction not available before instruction.
225   value_id = 95;
226   value_synonym_id = 62;
227   variable_id = 63;
228   initializer_id = 80;
229   variable_storage_class = (uint32_t)spv::StorageClass::Function;
230   instruction_descriptor =
231       MakeInstructionDescriptor(40, spv::Op::OpAccessChain, 0);
232   transformation = TransformationPushIdThroughVariable(
233       value_id, value_synonym_id, variable_id, variable_storage_class,
234       initializer_id, instruction_descriptor);
235   ASSERT_FALSE(
236       transformation.IsApplicable(context.get(), transformation_context));
237 
238   // Variable initializer is not constant.
239   value_id = 95;
240   value_synonym_id = 62;
241   variable_id = 63;
242   initializer_id = 95;
243   variable_storage_class = (uint32_t)spv::StorageClass::Function;
244   instruction_descriptor =
245       MakeInstructionDescriptor(40, spv::Op::OpAccessChain, 0);
246   transformation = TransformationPushIdThroughVariable(
247       value_id, value_synonym_id, variable_id, variable_storage_class,
248       initializer_id, instruction_descriptor);
249   ASSERT_FALSE(
250       transformation.IsApplicable(context.get(), transformation_context));
251 
252   // Variable initializer has wrong type.
253   value_id = 95;
254   value_synonym_id = 62;
255   variable_id = 63;
256   initializer_id = 93;
257   variable_storage_class = (uint32_t)spv::StorageClass::Function;
258   instruction_descriptor =
259       MakeInstructionDescriptor(40, spv::Op::OpAccessChain, 0);
260   transformation = TransformationPushIdThroughVariable(
261       value_id, value_synonym_id, variable_id, variable_storage_class,
262       initializer_id, instruction_descriptor);
263   ASSERT_FALSE(
264       transformation.IsApplicable(context.get(), transformation_context));
265 }
266 
TEST(TransformationPushIdThroughVariableTest, Apply)267 TEST(TransformationPushIdThroughVariableTest, Apply) {
268   std::string reference_shader = R"(
269                OpCapability Shader
270                OpCapability VariablePointers
271           %1 = OpExtInstImport "GLSL.std.450"
272                OpMemoryModel Logical GLSL450
273                OpEntryPoint Fragment %4 "main" %92 %52 %53
274                OpExecutionMode %4 OriginUpperLeft
275                OpSource ESSL 310
276                OpDecorate %92 BuiltIn FragCoord
277           %2 = OpTypeVoid
278           %3 = OpTypeFunction %2
279           %6 = OpTypeInt 32 1
280           %7 = OpTypeFloat 32
281           %8 = OpTypeStruct %6 %7
282           %9 = OpTypePointer Function %8
283          %10 = OpTypeFunction %6 %9
284          %14 = OpConstant %6 0
285          %15 = OpTypePointer Function %6
286          %51 = OpTypePointer Private %6
287          %21 = OpConstant %6 2
288          %23 = OpConstant %6 1
289          %24 = OpConstant %7 1
290          %25 = OpTypePointer Function %7
291          %50 = OpTypePointer Private %7
292          %34 = OpTypeBool
293          %35 = OpConstantFalse %34
294          %60 = OpConstantNull %50
295          %52 = OpVariable %50 Private
296          %53 = OpVariable %51 Private
297          %80 = OpConstantComposite %8 %21 %24
298          %90 = OpTypeVector %7 4
299          %91 = OpTypePointer Input %90
300          %92 = OpVariable %91 Input
301          %93 = OpConstantComposite %90 %24 %24 %24 %24
302           %4 = OpFunction %2 None %3
303           %5 = OpLabel
304          %20 = OpVariable %9 Function
305          %27 = OpVariable %9 Function
306                OpStore %53 %21
307          %22 = OpAccessChain %15 %20 %14
308          %44 = OpCopyObject %9 %20
309          %26 = OpAccessChain %25 %20 %23
310          %29 = OpFunctionCall %6 %12 %27
311          %30 = OpAccessChain %15 %20 %14
312          %45 = OpCopyObject %15 %30
313          %81 = OpCopyObject %9 %27
314          %33 = OpAccessChain %15 %20 %14
315                OpSelectionMerge %37 None
316                OpBranchConditional %35 %36 %37
317          %36 = OpLabel
318          %38 = OpAccessChain %15 %20 %14
319          %40 = OpAccessChain %15 %20 %14
320          %43 = OpAccessChain %15 %20 %14
321          %82 = OpCopyObject %9 %27
322                OpBranch %37
323          %37 = OpLabel
324                OpReturn
325                OpFunctionEnd
326          %12 = OpFunction %6 None %10
327          %11 = OpFunctionParameter %9
328          %13 = OpLabel
329          %46 = OpCopyObject %9 %11
330          %16 = OpAccessChain %15 %11 %14
331          %95 = OpCopyObject %8 %80
332                OpReturnValue %21
333                OpFunctionEnd
334   )";
335 
336   const auto env = SPV_ENV_UNIVERSAL_1_4;
337   const auto consumer = nullptr;
338   const auto context =
339       BuildModule(env, consumer, reference_shader, kFuzzAssembleOption);
340 
341   spvtools::ValidatorOptions validator_options;
342   TransformationContext transformation_context(
343       MakeUnique<FactManager>(context.get()), validator_options);
344   uint32_t value_id = 80;
345   uint32_t value_synonym_id = 100;
346   uint32_t variable_id = 101;
347   uint32_t initializer_id = 80;
348   uint32_t variable_storage_class = (uint32_t)spv::StorageClass::Function;
349   auto instruction_descriptor =
350       MakeInstructionDescriptor(38, spv::Op::OpAccessChain, 0);
351   auto transformation = TransformationPushIdThroughVariable(
352       value_id, value_synonym_id, variable_id, variable_storage_class,
353       initializer_id, instruction_descriptor);
354   ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(value_synonym_id));
355   ASSERT_EQ(nullptr, context->get_instr_block(value_synonym_id));
356   ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(variable_id));
357   ASSERT_EQ(nullptr, context->get_instr_block(variable_id));
358   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
359   ASSERT_EQ(spv::Op::OpLoad,
360             context->get_def_use_mgr()->GetDef(value_synonym_id)->opcode());
361   ASSERT_EQ(36, context->get_instr_block(value_synonym_id)->id());
362   ASSERT_EQ(spv::Op::OpVariable,
363             context->get_def_use_mgr()->GetDef(variable_id)->opcode());
364   ASSERT_EQ(5, context->get_instr_block(variable_id)->id());
365   uint32_t variable_use_count = 0;
366   context->get_def_use_mgr()->ForEachUse(
367       variable_id,
368       [&variable_use_count](opt::Instruction* inst, uint32_t /*unused*/) {
369         ASSERT_TRUE(inst->opcode() == spv::Op::OpLoad ||
370                     inst->opcode() == spv::Op::OpStore);
371         variable_use_count++;
372       });
373   ASSERT_EQ(2, variable_use_count);
374 
375   value_id = 21;
376   value_synonym_id = 102;
377   variable_id = 103;
378   initializer_id = 21;
379   variable_storage_class = (uint32_t)spv::StorageClass::Function;
380   instruction_descriptor =
381       MakeInstructionDescriptor(38, spv::Op::OpAccessChain, 0);
382   transformation = TransformationPushIdThroughVariable(
383       value_id, value_synonym_id, variable_id, variable_storage_class,
384       initializer_id, instruction_descriptor);
385   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
386 
387   value_id = 95;
388   value_synonym_id = 104;
389   variable_id = 105;
390   initializer_id = 80;
391   variable_storage_class = (uint32_t)spv::StorageClass::Function;
392   instruction_descriptor =
393       MakeInstructionDescriptor(95, spv::Op::OpReturnValue, 0);
394   transformation = TransformationPushIdThroughVariable(
395       value_id, value_synonym_id, variable_id, variable_storage_class,
396       initializer_id, instruction_descriptor);
397   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
398 
399   value_id = 80;
400   value_synonym_id = 106;
401   variable_id = 107;
402   initializer_id = 80;
403   variable_storage_class = (uint32_t)spv::StorageClass::Function;
404   instruction_descriptor =
405       MakeInstructionDescriptor(95, spv::Op::OpReturnValue, 0);
406   transformation = TransformationPushIdThroughVariable(
407       value_id, value_synonym_id, variable_id, variable_storage_class,
408       initializer_id, instruction_descriptor);
409   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
410 
411   value_id = 21;
412   value_synonym_id = 108;
413   variable_id = 109;
414   initializer_id = 21;
415   variable_storage_class = (uint32_t)spv::StorageClass::Private;
416   instruction_descriptor =
417       MakeInstructionDescriptor(95, spv::Op::OpReturnValue, 0);
418   transformation = TransformationPushIdThroughVariable(
419       value_id, value_synonym_id, variable_id, variable_storage_class,
420       initializer_id, instruction_descriptor);
421   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
422 
423   value_id = 23;
424   value_synonym_id = 110;
425   variable_id = 111;
426   initializer_id = 21;
427   variable_storage_class = (uint32_t)spv::StorageClass::Private;
428   instruction_descriptor = MakeInstructionDescriptor(27, spv::Op::OpStore, 0);
429   transformation = TransformationPushIdThroughVariable(
430       value_id, value_synonym_id, variable_id, variable_storage_class,
431       initializer_id, instruction_descriptor);
432   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
433 
434   std::string variant_shader = R"(
435                OpCapability Shader
436                OpCapability VariablePointers
437           %1 = OpExtInstImport "GLSL.std.450"
438                OpMemoryModel Logical GLSL450
439                OpEntryPoint Fragment %4 "main" %92 %52 %53 %109 %111
440                OpExecutionMode %4 OriginUpperLeft
441                OpSource ESSL 310
442                OpDecorate %92 BuiltIn FragCoord
443           %2 = OpTypeVoid
444           %3 = OpTypeFunction %2
445           %6 = OpTypeInt 32 1
446           %7 = OpTypeFloat 32
447           %8 = OpTypeStruct %6 %7
448           %9 = OpTypePointer Function %8
449          %10 = OpTypeFunction %6 %9
450          %14 = OpConstant %6 0
451          %15 = OpTypePointer Function %6
452          %51 = OpTypePointer Private %6
453          %21 = OpConstant %6 2
454          %23 = OpConstant %6 1
455          %24 = OpConstant %7 1
456          %25 = OpTypePointer Function %7
457          %50 = OpTypePointer Private %7
458          %34 = OpTypeBool
459          %35 = OpConstantFalse %34
460          %60 = OpConstantNull %50
461          %52 = OpVariable %50 Private
462          %53 = OpVariable %51 Private
463          %80 = OpConstantComposite %8 %21 %24
464          %90 = OpTypeVector %7 4
465          %91 = OpTypePointer Input %90
466          %92 = OpVariable %91 Input
467          %93 = OpConstantComposite %90 %24 %24 %24 %24
468         %109 = OpVariable %51 Private %21
469         %111 = OpVariable %51 Private %21
470           %4 = OpFunction %2 None %3
471           %5 = OpLabel
472         %103 = OpVariable %15 Function %21
473         %101 = OpVariable %9 Function %80
474          %20 = OpVariable %9 Function
475          %27 = OpVariable %9 Function
476                OpStore %111 %23
477         %110 = OpLoad %6 %111
478                OpStore %53 %21
479          %22 = OpAccessChain %15 %20 %14
480          %44 = OpCopyObject %9 %20
481          %26 = OpAccessChain %25 %20 %23
482          %29 = OpFunctionCall %6 %12 %27
483          %30 = OpAccessChain %15 %20 %14
484          %45 = OpCopyObject %15 %30
485          %81 = OpCopyObject %9 %27
486          %33 = OpAccessChain %15 %20 %14
487                OpSelectionMerge %37 None
488                OpBranchConditional %35 %36 %37
489          %36 = OpLabel
490                OpStore %101 %80
491         %100 = OpLoad %8 %101
492                OpStore %103 %21
493         %102 = OpLoad %6 %103
494          %38 = OpAccessChain %15 %20 %14
495          %40 = OpAccessChain %15 %20 %14
496          %43 = OpAccessChain %15 %20 %14
497          %82 = OpCopyObject %9 %27
498                OpBranch %37
499          %37 = OpLabel
500                OpReturn
501                OpFunctionEnd
502          %12 = OpFunction %6 None %10
503          %11 = OpFunctionParameter %9
504          %13 = OpLabel
505         %107 = OpVariable %9 Function %80
506         %105 = OpVariable %9 Function %80
507          %46 = OpCopyObject %9 %11
508          %16 = OpAccessChain %15 %11 %14
509          %95 = OpCopyObject %8 %80
510                OpStore %105 %95
511         %104 = OpLoad %8 %105
512                OpStore %107 %80
513         %106 = OpLoad %8 %107
514                OpStore %109 %21
515         %108 = OpLoad %6 %109
516                OpReturnValue %21
517                OpFunctionEnd
518   )";
519 
520   ASSERT_TRUE(IsEqual(env, variant_shader, context.get()));
521   ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
522       MakeDataDescriptor(80, {}), MakeDataDescriptor(100, {})));
523   ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
524       MakeDataDescriptor(21, {}), MakeDataDescriptor(102, {})));
525   ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
526       MakeDataDescriptor(95, {}), MakeDataDescriptor(104, {})));
527   ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
528       MakeDataDescriptor(80, {}), MakeDataDescriptor(106, {})));
529   ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
530       MakeDataDescriptor(21, {}), MakeDataDescriptor(108, {})));
531 }
532 
TEST(TransformationPushIdThroughVariableTest, AddSynonymsForRelevantIds)533 TEST(TransformationPushIdThroughVariableTest, AddSynonymsForRelevantIds) {
534   std::string reference_shader = R"(
535                OpCapability Shader
536                OpCapability VariablePointers
537           %1 = OpExtInstImport "GLSL.std.450"
538                OpMemoryModel Logical GLSL450
539                OpEntryPoint Fragment %4 "main" %92 %52 %53
540                OpExecutionMode %4 OriginUpperLeft
541                OpSource ESSL 310
542                OpDecorate %92 BuiltIn FragCoord
543           %2 = OpTypeVoid
544           %3 = OpTypeFunction %2
545           %6 = OpTypeInt 32 1
546           %7 = OpTypeFloat 32
547           %8 = OpTypeStruct %6 %7
548           %9 = OpTypePointer Function %8
549          %10 = OpTypeFunction %6 %9
550          %14 = OpConstant %6 0
551          %15 = OpTypePointer Function %6
552          %51 = OpTypePointer Private %6
553          %21 = OpConstant %6 2
554          %23 = OpConstant %6 1
555          %24 = OpConstant %7 1
556          %25 = OpTypePointer Function %7
557          %50 = OpTypePointer Private %7
558          %34 = OpTypeBool
559          %35 = OpConstantFalse %34
560          %60 = OpConstantNull %50
561          %52 = OpVariable %50 Private
562          %53 = OpVariable %51 Private
563          %80 = OpConstantComposite %8 %21 %24
564          %90 = OpTypeVector %7 4
565          %91 = OpTypePointer Input %90
566          %92 = OpVariable %91 Input
567          %93 = OpConstantComposite %90 %24 %24 %24 %24
568           %4 = OpFunction %2 None %3
569           %5 = OpLabel
570          %20 = OpVariable %9 Function
571          %27 = OpVariable %9 Function
572          %22 = OpAccessChain %15 %20 %14
573          %44 = OpCopyObject %9 %20
574          %26 = OpAccessChain %25 %20 %23
575          %29 = OpFunctionCall %6 %12 %27
576          %30 = OpAccessChain %15 %20 %14
577          %45 = OpCopyObject %15 %30
578          %81 = OpCopyObject %9 %27
579          %33 = OpAccessChain %15 %20 %14
580                OpSelectionMerge %37 None
581                OpBranchConditional %35 %36 %37
582          %36 = OpLabel
583          %38 = OpAccessChain %15 %20 %14
584          %40 = OpAccessChain %15 %20 %14
585          %43 = OpAccessChain %15 %20 %14
586          %82 = OpCopyObject %9 %27
587                OpBranch %37
588          %37 = OpLabel
589                OpReturn
590                OpFunctionEnd
591          %12 = OpFunction %6 None %10
592          %11 = OpFunctionParameter %9
593          %13 = OpLabel
594          %46 = OpCopyObject %9 %11
595          %16 = OpAccessChain %15 %11 %14
596          %95 = OpCopyObject %8 %80
597                OpReturnValue %21
598         %100 = OpLabel
599                OpUnreachable
600                OpFunctionEnd
601   )";
602 
603   const auto env = SPV_ENV_UNIVERSAL_1_4;
604   const auto consumer = nullptr;
605   const auto context =
606       BuildModule(env, consumer, reference_shader, kFuzzAssembleOption);
607 
608   spvtools::ValidatorOptions validator_options;
609   TransformationContext transformation_context(
610       MakeUnique<FactManager>(context.get()), validator_options);
611   // Tests the reference shader validity.
612   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
613                                                kConsoleMessageConsumer));
614 
615   uint32_t value_id = 21;
616   uint32_t value_synonym_id = 62;
617   uint32_t variable_id = 63;
618   uint32_t initializer_id = 23;
619   uint32_t variable_storage_class = (uint32_t)spv::StorageClass::Private;
620   auto instruction_descriptor =
621       MakeInstructionDescriptor(95, spv::Op::OpReturnValue, 0);
622   auto transformation = TransformationPushIdThroughVariable(
623       value_id, value_synonym_id, variable_id, variable_storage_class,
624       initializer_id, instruction_descriptor);
625   ASSERT_TRUE(
626       transformation.IsApplicable(context.get(), transformation_context));
627   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
628   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
629                                                kConsoleMessageConsumer));
630   ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
631       MakeDataDescriptor(21, {}), MakeDataDescriptor(62, {})));
632 }
633 
TEST(TransformationPushIdThroughVariableTest, DontAddSynonymsForIrrelevantIds)634 TEST(TransformationPushIdThroughVariableTest, DontAddSynonymsForIrrelevantIds) {
635   std::string reference_shader = R"(
636                OpCapability Shader
637                OpCapability VariablePointers
638           %1 = OpExtInstImport "GLSL.std.450"
639                OpMemoryModel Logical GLSL450
640                OpEntryPoint Fragment %4 "main" %92 %52 %53
641                OpExecutionMode %4 OriginUpperLeft
642                OpSource ESSL 310
643                OpDecorate %92 BuiltIn FragCoord
644           %2 = OpTypeVoid
645           %3 = OpTypeFunction %2
646           %6 = OpTypeInt 32 1
647           %7 = OpTypeFloat 32
648           %8 = OpTypeStruct %6 %7
649           %9 = OpTypePointer Function %8
650          %10 = OpTypeFunction %6 %9
651          %14 = OpConstant %6 0
652          %15 = OpTypePointer Function %6
653          %51 = OpTypePointer Private %6
654          %21 = OpConstant %6 2
655          %23 = OpConstant %6 1
656          %24 = OpConstant %7 1
657          %25 = OpTypePointer Function %7
658          %50 = OpTypePointer Private %7
659          %34 = OpTypeBool
660          %35 = OpConstantFalse %34
661          %60 = OpConstantNull %50
662          %52 = OpVariable %50 Private
663          %53 = OpVariable %51 Private
664          %80 = OpConstantComposite %8 %21 %24
665          %90 = OpTypeVector %7 4
666          %91 = OpTypePointer Input %90
667          %92 = OpVariable %91 Input
668          %93 = OpConstantComposite %90 %24 %24 %24 %24
669           %4 = OpFunction %2 None %3
670           %5 = OpLabel
671          %20 = OpVariable %9 Function
672          %27 = OpVariable %9 Function
673          %22 = OpAccessChain %15 %20 %14
674          %44 = OpCopyObject %9 %20
675          %26 = OpAccessChain %25 %20 %23
676          %29 = OpFunctionCall %6 %12 %27
677          %30 = OpAccessChain %15 %20 %14
678          %45 = OpCopyObject %15 %30
679          %81 = OpCopyObject %9 %27
680          %33 = OpAccessChain %15 %20 %14
681                OpSelectionMerge %37 None
682                OpBranchConditional %35 %36 %37
683          %36 = OpLabel
684          %38 = OpAccessChain %15 %20 %14
685          %40 = OpAccessChain %15 %20 %14
686          %43 = OpAccessChain %15 %20 %14
687          %82 = OpCopyObject %9 %27
688                OpBranch %37
689          %37 = OpLabel
690                OpReturn
691                OpFunctionEnd
692          %12 = OpFunction %6 None %10
693          %11 = OpFunctionParameter %9
694          %13 = OpLabel
695          %46 = OpCopyObject %9 %11
696          %16 = OpAccessChain %15 %11 %14
697          %95 = OpCopyObject %8 %80
698                OpReturnValue %21
699         %100 = OpLabel
700                OpUnreachable
701                OpFunctionEnd
702   )";
703 
704   const auto env = SPV_ENV_UNIVERSAL_1_4;
705   const auto consumer = nullptr;
706   const auto context =
707       BuildModule(env, consumer, reference_shader, kFuzzAssembleOption);
708 
709   spvtools::ValidatorOptions validator_options;
710   TransformationContext transformation_context(
711       MakeUnique<FactManager>(context.get()), validator_options);
712   // Tests the reference shader validity.
713   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
714                                                kConsoleMessageConsumer));
715 
716   transformation_context.GetFactManager()->AddFactIdIsIrrelevant(21);
717 
718   uint32_t value_id = 21;
719   uint32_t value_synonym_id = 62;
720   uint32_t variable_id = 63;
721   uint32_t initializer_id = 23;
722   uint32_t variable_storage_class = (uint32_t)spv::StorageClass::Private;
723   auto instruction_descriptor =
724       MakeInstructionDescriptor(95, spv::Op::OpReturnValue, 0);
725   auto transformation = TransformationPushIdThroughVariable(
726       value_id, value_synonym_id, variable_id, variable_storage_class,
727       initializer_id, instruction_descriptor);
728   ASSERT_TRUE(
729       transformation.IsApplicable(context.get(), transformation_context));
730   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
731   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
732                                                kConsoleMessageConsumer));
733   ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
734       MakeDataDescriptor(21, {}), MakeDataDescriptor(62, {})));
735 }
736 
TEST(TransformationPushIdThroughVariableTest, DontAddSynonymsInDeadBlocks)737 TEST(TransformationPushIdThroughVariableTest, DontAddSynonymsInDeadBlocks) {
738   std::string reference_shader = R"(
739                OpCapability Shader
740           %1 = OpExtInstImport "GLSL.std.450"
741                OpMemoryModel Logical GLSL450
742                OpEntryPoint Fragment %4 "main"
743                OpExecutionMode %4 OriginUpperLeft
744                OpSource ESSL 320
745           %2 = OpTypeVoid
746           %3 = OpTypeFunction %2
747           %6 = OpTypeInt 32 1
748           %7 = OpTypeVector %6 2
749           %8 = OpTypePointer Function %7
750          %10 = OpConstant %6 0
751          %11 = OpConstant %6 1
752          %12 = OpConstantComposite %7 %10 %11
753          %13 = OpTypeBool
754          %50 = OpTypePointer Function %13
755          %14 = OpConstantFalse %13
756           %4 = OpFunction %2 None %3
757           %5 = OpLabel
758           %9 = OpVariable %8 Function
759                OpStore %9 %12
760                OpSelectionMerge %16 None
761                OpBranchConditional %14 %15 %16
762          %15 = OpLabel
763                OpBranch %16
764          %16 = OpLabel
765                OpReturn
766                OpFunctionEnd
767   )";
768 
769   const auto env = SPV_ENV_UNIVERSAL_1_4;
770   const auto consumer = nullptr;
771   const auto context =
772       BuildModule(env, consumer, reference_shader, kFuzzAssembleOption);
773 
774   spvtools::ValidatorOptions validator_options;
775   TransformationContext transformation_context(
776       MakeUnique<FactManager>(context.get()), validator_options);
777   // Tests the reference shader validity.
778   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
779                                                kConsoleMessageConsumer));
780 
781   transformation_context.GetFactManager()->AddFactBlockIsDead(15);
782   auto transformation = TransformationPushIdThroughVariable(
783       14, 100, 101, uint32_t(spv::StorageClass::Function), 14,
784       MakeInstructionDescriptor(15, spv::Op::OpBranch, 0));
785   ASSERT_TRUE(
786       transformation.IsApplicable(context.get(), transformation_context));
787   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
788   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
789                                                kConsoleMessageConsumer));
790   ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
791       MakeDataDescriptor(14, {}), MakeDataDescriptor(100, {})));
792 }
793 
794 }  // namespace
795 }  // namespace fuzz
796 }  // namespace spvtools
797