• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2020 Vasyl Teliman
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_add_synonym.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(TransformationAddSynonymTest,NotApplicable)26 TEST(TransformationAddSynonymTest, NotApplicable) {
27   std::string shader = R"(
28                OpCapability Shader
29           %1 = OpExtInstImport "GLSL.std.450"
30                OpMemoryModel Logical GLSL450
31                OpEntryPoint Fragment %4 "main"
32                OpExecutionMode %4 OriginUpperLeft
33                OpSource ESSL 310
34                OpDecorate %8 RelaxedPrecision
35                OpDecorate %22 RelaxedPrecision
36           %2 = OpTypeVoid
37           %3 = OpTypeFunction %2
38           %6 = OpTypeInt 32 1
39           %7 = OpTypePointer Function %6
40           %9 = OpConstant %6 3
41          %10 = OpTypeFloat 32
42          %11 = OpTypePointer Function %10
43          %13 = OpConstant %10 4.5
44          %14 = OpTypeVector %10 2
45          %15 = OpTypePointer Function %14
46          %17 = OpConstant %10 3
47          %18 = OpConstant %10 4
48          %19 = OpConstantComposite %14 %17 %18
49          %20 = OpTypeVector %6 2
50          %21 = OpTypePointer Function %20
51          %23 = OpConstant %6 4
52          %24 = OpConstantComposite %20 %9 %23
53          %26 = OpConstantNull %6
54           %4 = OpFunction %2 None %3
55           %5 = OpLabel
56           %8 = OpVariable %7 Function
57          %12 = OpVariable %11 Function
58          %16 = OpVariable %15 Function
59          %22 = OpVariable %21 Function
60                OpStore %8 %9
61                OpStore %12 %13
62                OpStore %16 %19
63                OpStore %22 %24
64          %25 = OpUndef %6
65          %27 = OpLoad %6 %8
66                OpReturn
67                OpFunctionEnd
68   )";
69 
70   const auto env = SPV_ENV_UNIVERSAL_1_3;
71   const auto consumer = nullptr;
72   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
73   spvtools::ValidatorOptions validator_options;
74   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
75                                                kConsoleMessageConsumer));
76   TransformationContext transformation_context(
77       MakeUnique<FactManager>(context.get()), validator_options);
78   transformation_context.GetFactManager()->AddFactIdIsIrrelevant(24);
79 
80   auto insert_before = MakeInstructionDescriptor(22, spv::Op::OpReturn, 0);
81 
82 #ifndef NDEBUG
83   ASSERT_DEATH(
84       TransformationAddSynonym(
85           9, static_cast<protobufs::TransformationAddSynonym::SynonymType>(-1),
86           40, insert_before)
87           .IsApplicable(context.get(), transformation_context),
88       "Synonym type is invalid");
89 #endif
90 
91   // These tests should succeed regardless of the synonym type.
92   for (int i = 0;
93        i < protobufs::TransformationAddSynonym::SynonymType_descriptor()
94                ->value_count();
95        ++i) {
96     const auto* synonym_value =
97         protobufs::TransformationAddSynonym::SynonymType_descriptor()->value(i);
98     ASSERT_TRUE(protobufs::TransformationAddSynonym::SynonymType_IsValid(
99         synonym_value->number()));
100     auto synonym_type =
101         static_cast<protobufs::TransformationAddSynonym::SynonymType>(
102             synonym_value->number());
103 
104     // |synonym_fresh_id| is not fresh.
105     ASSERT_FALSE(TransformationAddSynonym(9, synonym_type, 9, insert_before)
106                      .IsApplicable(context.get(), transformation_context));
107 
108     // |result_id| is invalid.
109     ASSERT_FALSE(TransformationAddSynonym(40, synonym_type, 40, insert_before)
110                      .IsApplicable(context.get(), transformation_context));
111 
112     // Instruction with |result_id| has no type id.
113     ASSERT_FALSE(TransformationAddSynonym(5, synonym_type, 40, insert_before)
114                      .IsApplicable(context.get(), transformation_context));
115 
116     // Instruction with |result_id| is an OpUndef.
117     ASSERT_FALSE(TransformationAddSynonym(25, synonym_type, 40, insert_before)
118                      .IsApplicable(context.get(), transformation_context));
119 
120     // Instruction with |result_id| is an OpConstantNull.
121     ASSERT_FALSE(TransformationAddSynonym(26, synonym_type, 40, insert_before)
122                      .IsApplicable(context.get(), transformation_context));
123 
124     // |result_id| is irrelevant.
125     ASSERT_FALSE(TransformationAddSynonym(24, synonym_type, 40, insert_before)
126                      .IsApplicable(context.get(), transformation_context));
127 
128     // |insert_before| is invalid.
129     ASSERT_FALSE(TransformationAddSynonym(
130                      9, synonym_type, 40,
131                      MakeInstructionDescriptor(25, spv::Op::OpStore, 0))
132                      .IsApplicable(context.get(), transformation_context));
133 
134     // Can't insert before |insert_before|.
135     ASSERT_FALSE(TransformationAddSynonym(
136                      9, synonym_type, 40,
137                      MakeInstructionDescriptor(5, spv::Op::OpLabel, 0))
138                      .IsApplicable(context.get(), transformation_context));
139     ASSERT_FALSE(TransformationAddSynonym(
140                      9, synonym_type, 40,
141                      MakeInstructionDescriptor(22, spv::Op::OpVariable, 0))
142                      .IsApplicable(context.get(), transformation_context));
143     ASSERT_FALSE(TransformationAddSynonym(
144                      9, synonym_type, 40,
145                      MakeInstructionDescriptor(25, spv::Op::OpFunctionEnd, 0))
146                      .IsApplicable(context.get(), transformation_context));
147 
148     // Domination rules are not satisfied.
149     ASSERT_FALSE(TransformationAddSynonym(
150                      27, synonym_type, 40,
151                      MakeInstructionDescriptor(27, spv::Op::OpLoad, 0))
152                      .IsApplicable(context.get(), transformation_context));
153     ASSERT_FALSE(TransformationAddSynonym(
154                      27, synonym_type, 40,
155                      MakeInstructionDescriptor(22, spv::Op::OpStore, 1))
156                      .IsApplicable(context.get(), transformation_context));
157   }
158 }
159 
TEST(TransformationAddSynonymTest,AddZeroSubZeroMulOne)160 TEST(TransformationAddSynonymTest, AddZeroSubZeroMulOne) {
161   std::string shader = R"(
162                OpCapability Shader
163           %1 = OpExtInstImport "GLSL.std.450"
164                OpMemoryModel Logical GLSL450
165                OpEntryPoint Fragment %4 "main"
166                OpExecutionMode %4 OriginUpperLeft
167                OpSource ESSL 310
168           %2 = OpTypeVoid
169           %3 = OpTypeFunction %2
170           %6 = OpTypeInt 32 1
171           %7 = OpConstant %6 0
172           %8 = OpConstant %6 1
173           %9 = OpConstant %6 34
174          %10 = OpTypeInt 32 0
175          %13 = OpConstant %10 34
176          %14 = OpTypeFloat 32
177          %15 = OpConstant %14 0
178          %16 = OpConstant %14 1
179          %17 = OpConstant %14 34
180          %18 = OpTypeVector %14 2
181          %19 = OpConstantComposite %18 %15 %15
182          %20 = OpConstantComposite %18 %16 %16
183          %21 = OpConstant %14 3
184          %22 = OpConstant %14 4
185          %23 = OpConstantComposite %18 %21 %22
186          %24 = OpTypeVector %6 2
187          %25 = OpConstantComposite %24 %7 %7
188          %26 = OpConstantComposite %24 %8 %8
189          %27 = OpConstant %6 3
190          %28 = OpConstant %6 4
191          %29 = OpConstantComposite %24 %27 %28
192          %30 = OpTypeVector %10 2
193          %33 = OpConstant %10 3
194          %34 = OpConstant %10 4
195          %35 = OpConstantComposite %30 %33 %34
196          %36 = OpTypeBool
197          %37 = OpTypeVector %36 2
198          %38 = OpConstantTrue %36
199          %39 = OpConstantComposite %37 %38 %38
200          %40 = OpConstant %6 37
201           %4 = OpFunction %2 None %3
202           %5 = OpLabel
203                OpReturn
204                OpFunctionEnd
205   )";
206 
207   const auto env = SPV_ENV_UNIVERSAL_1_3;
208   const auto consumer = nullptr;
209   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
210   spvtools::ValidatorOptions validator_options;
211   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
212                                                kConsoleMessageConsumer));
213   TransformationContext transformation_context(
214       MakeUnique<FactManager>(context.get()), validator_options);
215   auto insert_before = MakeInstructionDescriptor(5, spv::Op::OpReturn, 0);
216 
217   uint32_t fresh_id = 50;
218   for (auto synonym_type : {protobufs::TransformationAddSynonym::ADD_ZERO,
219                             protobufs::TransformationAddSynonym::SUB_ZERO,
220                             protobufs::TransformationAddSynonym::MUL_ONE}) {
221     ASSERT_TRUE(
222         TransformationAddSynonym::IsAdditionalConstantRequired(synonym_type));
223 
224     // Can't create a synonym of a scalar or a vector of a wrong (in this case -
225     // boolean) type.
226     ASSERT_FALSE(
227         TransformationAddSynonym(38, synonym_type, fresh_id, insert_before)
228             .IsApplicable(context.get(), transformation_context));
229     ASSERT_FALSE(
230         TransformationAddSynonym(39, synonym_type, fresh_id, insert_before)
231             .IsApplicable(context.get(), transformation_context));
232 
233     // Required constant is not present in the module.
234     ASSERT_FALSE(
235         TransformationAddSynonym(13, synonym_type, fresh_id, insert_before)
236             .IsApplicable(context.get(), transformation_context));
237     ASSERT_FALSE(
238         TransformationAddSynonym(35, synonym_type, fresh_id, insert_before)
239             .IsApplicable(context.get(), transformation_context));
240 
241     for (auto result_id : {9, 17, 23, 29}) {
242       TransformationAddSynonym transformation(result_id, synonym_type, fresh_id,
243                                               insert_before);
244       ASSERT_TRUE(
245           transformation.IsApplicable(context.get(), transformation_context));
246       ApplyAndCheckFreshIds(transformation, context.get(),
247                             &transformation_context);
248       ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
249           MakeDataDescriptor(result_id, {}), MakeDataDescriptor(fresh_id, {})));
250       ++fresh_id;
251     }
252   }
253   {
254     TransformationAddSynonym transformation(
255         40, protobufs::TransformationAddSynonym::BITWISE_OR, fresh_id,
256         insert_before);
257     ASSERT_TRUE(
258         transformation.IsApplicable(context.get(), transformation_context));
259     ApplyAndCheckFreshIds(transformation, context.get(),
260                           &transformation_context);
261     ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
262         MakeDataDescriptor(40, {}), MakeDataDescriptor(fresh_id, {})));
263     ++fresh_id;
264   }
265   {
266     TransformationAddSynonym transformation(
267         40, protobufs::TransformationAddSynonym::BITWISE_XOR, fresh_id,
268         insert_before);
269     ASSERT_TRUE(
270         transformation.IsApplicable(context.get(), transformation_context));
271     ApplyAndCheckFreshIds(transformation, context.get(),
272                           &transformation_context);
273     ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
274         MakeDataDescriptor(40, {}), MakeDataDescriptor(fresh_id, {})));
275   }
276 
277   std::string expected_shader = R"(
278                OpCapability Shader
279           %1 = OpExtInstImport "GLSL.std.450"
280                OpMemoryModel Logical GLSL450
281                OpEntryPoint Fragment %4 "main"
282                OpExecutionMode %4 OriginUpperLeft
283                OpSource ESSL 310
284           %2 = OpTypeVoid
285           %3 = OpTypeFunction %2
286           %6 = OpTypeInt 32 1
287           %7 = OpConstant %6 0
288           %8 = OpConstant %6 1
289           %9 = OpConstant %6 34
290          %10 = OpTypeInt 32 0
291          %13 = OpConstant %10 34
292          %14 = OpTypeFloat 32
293          %15 = OpConstant %14 0
294          %16 = OpConstant %14 1
295          %17 = OpConstant %14 34
296          %18 = OpTypeVector %14 2
297          %19 = OpConstantComposite %18 %15 %15
298          %20 = OpConstantComposite %18 %16 %16
299          %21 = OpConstant %14 3
300          %22 = OpConstant %14 4
301          %23 = OpConstantComposite %18 %21 %22
302          %24 = OpTypeVector %6 2
303          %25 = OpConstantComposite %24 %7 %7
304          %26 = OpConstantComposite %24 %8 %8
305          %27 = OpConstant %6 3
306          %28 = OpConstant %6 4
307          %29 = OpConstantComposite %24 %27 %28
308          %30 = OpTypeVector %10 2
309          %33 = OpConstant %10 3
310          %34 = OpConstant %10 4
311          %35 = OpConstantComposite %30 %33 %34
312          %36 = OpTypeBool
313          %37 = OpTypeVector %36 2
314          %38 = OpConstantTrue %36
315          %39 = OpConstantComposite %37 %38 %38
316          %40 = OpConstant %6 37
317           %4 = OpFunction %2 None %3
318           %5 = OpLabel
319          %50 = OpIAdd %6 %9 %7
320          %51 = OpFAdd %14 %17 %15
321          %52 = OpFAdd %18 %23 %19
322          %53 = OpIAdd %24 %29 %25
323          %54 = OpISub %6 %9 %7
324          %55 = OpFSub %14 %17 %15
325          %56 = OpFSub %18 %23 %19
326          %57 = OpISub %24 %29 %25
327          %58 = OpIMul %6 %9 %8
328          %59 = OpFMul %14 %17 %16
329          %60 = OpFMul %18 %23 %20
330          %61 = OpIMul %24 %29 %26
331          %62 = OpBitwiseOr %6 %40 %7
332          %63 = OpBitwiseXor %6 %40 %7
333                OpReturn
334                OpFunctionEnd
335   )";
336 
337   ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
338 }
339 
TEST(TransformationAddSynonymTest,LogicalAndLogicalOr)340 TEST(TransformationAddSynonymTest, LogicalAndLogicalOr) {
341   std::string shader = R"(
342                OpCapability Shader
343           %1 = OpExtInstImport "GLSL.std.450"
344                OpMemoryModel Logical GLSL450
345                OpEntryPoint Fragment %4 "main"
346                OpExecutionMode %4 OriginUpperLeft
347                OpSource ESSL 310
348           %2 = OpTypeVoid
349           %3 = OpTypeFunction %2
350           %6 = OpTypeBool
351           %7 = OpConstantFalse %6
352           %9 = OpConstantTrue %6
353          %10 = OpTypeVector %6 2
354          %11 = OpConstantComposite %10 %7 %9
355          %12 = OpConstantComposite %10 %7 %7
356          %13 = OpConstantComposite %10 %9 %9
357          %14 = OpTypeFloat 32
358          %17 = OpConstant %14 35
359          %18 = OpTypeVector %14 2
360          %21 = OpConstant %14 3
361          %22 = OpConstant %14 4
362          %23 = OpConstantComposite %18 %21 %22
363           %4 = OpFunction %2 None %3
364           %5 = OpLabel
365                OpReturn
366                OpFunctionEnd
367   )";
368 
369   const auto env = SPV_ENV_UNIVERSAL_1_3;
370   const auto consumer = nullptr;
371   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
372   spvtools::ValidatorOptions validator_options;
373   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
374                                                kConsoleMessageConsumer));
375   TransformationContext transformation_context(
376       MakeUnique<FactManager>(context.get()), validator_options);
377   auto insert_before = MakeInstructionDescriptor(5, spv::Op::OpReturn, 0);
378 
379   uint32_t fresh_id = 50;
380   for (auto synonym_type : {protobufs::TransformationAddSynonym::LOGICAL_AND,
381                             protobufs::TransformationAddSynonym::LOGICAL_OR}) {
382     ASSERT_TRUE(
383         TransformationAddSynonym::IsAdditionalConstantRequired(synonym_type));
384 
385     // Can't create a synonym of a scalar or a vector of a wrong (in this case -
386     // float) type.
387     ASSERT_FALSE(
388         TransformationAddSynonym(17, synonym_type, fresh_id, insert_before)
389             .IsApplicable(context.get(), transformation_context));
390     ASSERT_FALSE(
391         TransformationAddSynonym(23, synonym_type, fresh_id, insert_before)
392             .IsApplicable(context.get(), transformation_context));
393 
394     for (auto result_id : {9, 11}) {
395       TransformationAddSynonym transformation(result_id, synonym_type, fresh_id,
396                                               insert_before);
397       ASSERT_TRUE(
398           transformation.IsApplicable(context.get(), transformation_context));
399       ApplyAndCheckFreshIds(transformation, context.get(),
400                             &transformation_context);
401       ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
402           MakeDataDescriptor(result_id, {}), MakeDataDescriptor(fresh_id, {})));
403       ++fresh_id;
404     }
405   }
406 
407   std::string expected_shader = R"(
408                OpCapability Shader
409           %1 = OpExtInstImport "GLSL.std.450"
410                OpMemoryModel Logical GLSL450
411                OpEntryPoint Fragment %4 "main"
412                OpExecutionMode %4 OriginUpperLeft
413                OpSource ESSL 310
414           %2 = OpTypeVoid
415           %3 = OpTypeFunction %2
416           %6 = OpTypeBool
417           %7 = OpConstantFalse %6
418           %9 = OpConstantTrue %6
419          %10 = OpTypeVector %6 2
420          %11 = OpConstantComposite %10 %7 %9
421          %12 = OpConstantComposite %10 %7 %7
422          %13 = OpConstantComposite %10 %9 %9
423          %14 = OpTypeFloat 32
424          %17 = OpConstant %14 35
425          %18 = OpTypeVector %14 2
426          %21 = OpConstant %14 3
427          %22 = OpConstant %14 4
428          %23 = OpConstantComposite %18 %21 %22
429           %4 = OpFunction %2 None %3
430           %5 = OpLabel
431          %50 = OpLogicalAnd %6 %9 %9
432          %51 = OpLogicalAnd %10 %11 %13
433          %52 = OpLogicalOr %6 %9 %7
434          %53 = OpLogicalOr %10 %11 %12
435                OpReturn
436                OpFunctionEnd
437   )";
438 
439   ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
440 }
441 
TEST(TransformationAddSynonymTest,LogicalAndConstantIsNotPresent)442 TEST(TransformationAddSynonymTest, LogicalAndConstantIsNotPresent) {
443   std::string shader = R"(
444                OpCapability Shader
445           %1 = OpExtInstImport "GLSL.std.450"
446                OpMemoryModel Logical GLSL450
447                OpEntryPoint Fragment %4 "main"
448                OpExecutionMode %4 OriginUpperLeft
449                OpSource ESSL 310
450           %2 = OpTypeVoid
451           %3 = OpTypeFunction %2
452           %6 = OpTypeBool
453           %7 = OpConstantFalse %6
454          %10 = OpTypeVector %6 2
455          %12 = OpConstantComposite %10 %7 %7
456           %4 = OpFunction %2 None %3
457           %5 = OpLabel
458                OpReturn
459                OpFunctionEnd
460   )";
461 
462   const auto env = SPV_ENV_UNIVERSAL_1_3;
463   const auto consumer = nullptr;
464   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
465   spvtools::ValidatorOptions validator_options;
466   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
467                                                kConsoleMessageConsumer));
468   TransformationContext transformation_context(
469       MakeUnique<FactManager>(context.get()), validator_options);
470   auto insert_before = MakeInstructionDescriptor(5, spv::Op::OpReturn, 0);
471   const auto synonym_type = protobufs::TransformationAddSynonym::LOGICAL_AND;
472 
473   // Required constant is not present in the module.
474   ASSERT_FALSE(TransformationAddSynonym(7, synonym_type, 50, insert_before)
475                    .IsApplicable(context.get(), transformation_context));
476   ASSERT_FALSE(TransformationAddSynonym(12, synonym_type, 50, insert_before)
477                    .IsApplicable(context.get(), transformation_context));
478 }
479 
TEST(TransformationAddSynonymTest,LogicalOrConstantIsNotPresent)480 TEST(TransformationAddSynonymTest, LogicalOrConstantIsNotPresent) {
481   std::string shader = R"(
482                OpCapability Shader
483           %1 = OpExtInstImport "GLSL.std.450"
484                OpMemoryModel Logical GLSL450
485                OpEntryPoint Fragment %4 "main"
486                OpExecutionMode %4 OriginUpperLeft
487                OpSource ESSL 310
488           %2 = OpTypeVoid
489           %3 = OpTypeFunction %2
490           %6 = OpTypeBool
491           %7 = OpConstantTrue %6
492          %10 = OpTypeVector %6 2
493          %12 = OpConstantComposite %10 %7 %7
494           %4 = OpFunction %2 None %3
495           %5 = OpLabel
496                OpReturn
497                OpFunctionEnd
498   )";
499 
500   const auto env = SPV_ENV_UNIVERSAL_1_3;
501   const auto consumer = nullptr;
502   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
503   spvtools::ValidatorOptions validator_options;
504   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
505                                                kConsoleMessageConsumer));
506   TransformationContext transformation_context(
507       MakeUnique<FactManager>(context.get()), validator_options);
508   auto insert_before = MakeInstructionDescriptor(5, spv::Op::OpReturn, 0);
509   const auto synonym_type = protobufs::TransformationAddSynonym::LOGICAL_OR;
510 
511   // Required constant is not present in the module.
512   ASSERT_FALSE(TransformationAddSynonym(7, synonym_type, 50, insert_before)
513                    .IsApplicable(context.get(), transformation_context));
514   ASSERT_FALSE(TransformationAddSynonym(12, synonym_type, 50, insert_before)
515                    .IsApplicable(context.get(), transformation_context));
516 }
517 
TEST(TransformationAddSynonymTest,CopyObject)518 TEST(TransformationAddSynonymTest, CopyObject) {
519   std::string shader = R"(
520                OpCapability Shader
521           %1 = OpExtInstImport "GLSL.std.450"
522                OpMemoryModel Logical GLSL450
523                OpEntryPoint Fragment %4 "main"
524                OpExecutionMode %4 OriginUpperLeft
525                OpSource ESSL 310
526                OpDecorate %8 RelaxedPrecision
527           %2 = OpTypeVoid
528           %3 = OpTypeFunction %2
529           %6 = OpTypeInt 32 1
530           %7 = OpTypePointer Function %6
531           %9 = OpConstant %6 4
532          %10 = OpTypeFloat 32
533          %11 = OpTypePointer Function %10
534          %13 = OpConstant %10 4
535          %14 = OpTypeVector %10 2
536          %15 = OpTypePointer Function %14
537          %17 = OpConstant %10 3.4000001
538          %18 = OpConstantComposite %14 %17 %17
539          %19 = OpTypeBool
540          %20 = OpTypeStruct %19
541          %21 = OpTypePointer Function %20
542          %23 = OpConstantTrue %19
543          %24 = OpConstantComposite %20 %23
544           %4 = OpFunction %2 None %3
545           %5 = OpLabel
546           %8 = OpVariable %7 Function
547          %12 = OpVariable %11 Function
548          %16 = OpVariable %15 Function
549          %22 = OpVariable %21 Function
550                OpStore %8 %9
551                OpStore %12 %13
552                OpStore %16 %18
553                OpStore %22 %24
554                OpReturn
555                OpFunctionEnd
556   )";
557 
558   const auto env = SPV_ENV_UNIVERSAL_1_3;
559   const auto consumer = nullptr;
560   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
561   spvtools::ValidatorOptions validator_options;
562   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
563                                                kConsoleMessageConsumer));
564   TransformationContext transformation_context(
565       MakeUnique<FactManager>(context.get()), validator_options);
566   auto insert_before = MakeInstructionDescriptor(5, spv::Op::OpReturn, 0);
567   const auto synonym_type = protobufs::TransformationAddSynonym::COPY_OBJECT;
568 
569   ASSERT_FALSE(
570       TransformationAddSynonym::IsAdditionalConstantRequired(synonym_type));
571 
572   uint32_t fresh_id = 50;
573   for (auto result_id : {9, 13, 17, 18, 23, 24, 22}) {
574     TransformationAddSynonym transformation(result_id, synonym_type, fresh_id,
575                                             insert_before);
576     ASSERT_TRUE(
577         transformation.IsApplicable(context.get(), transformation_context));
578     ApplyAndCheckFreshIds(transformation, context.get(),
579                           &transformation_context);
580     ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
581         MakeDataDescriptor(result_id, {}), MakeDataDescriptor(fresh_id, {})));
582     ++fresh_id;
583   }
584 
585   std::string expected_shader = R"(
586                OpCapability Shader
587           %1 = OpExtInstImport "GLSL.std.450"
588                OpMemoryModel Logical GLSL450
589                OpEntryPoint Fragment %4 "main"
590                OpExecutionMode %4 OriginUpperLeft
591                OpSource ESSL 310
592                OpDecorate %8 RelaxedPrecision
593           %2 = OpTypeVoid
594           %3 = OpTypeFunction %2
595           %6 = OpTypeInt 32 1
596           %7 = OpTypePointer Function %6
597           %9 = OpConstant %6 4
598          %10 = OpTypeFloat 32
599          %11 = OpTypePointer Function %10
600          %13 = OpConstant %10 4
601          %14 = OpTypeVector %10 2
602          %15 = OpTypePointer Function %14
603          %17 = OpConstant %10 3.4000001
604          %18 = OpConstantComposite %14 %17 %17
605          %19 = OpTypeBool
606          %20 = OpTypeStruct %19
607          %21 = OpTypePointer Function %20
608          %23 = OpConstantTrue %19
609          %24 = OpConstantComposite %20 %23
610           %4 = OpFunction %2 None %3
611           %5 = OpLabel
612           %8 = OpVariable %7 Function
613          %12 = OpVariable %11 Function
614          %16 = OpVariable %15 Function
615          %22 = OpVariable %21 Function
616                OpStore %8 %9
617                OpStore %12 %13
618                OpStore %16 %18
619                OpStore %22 %24
620          %50 = OpCopyObject %6 %9
621          %51 = OpCopyObject %10 %13
622          %52 = OpCopyObject %10 %17
623          %53 = OpCopyObject %14 %18
624          %54 = OpCopyObject %19 %23
625          %55 = OpCopyObject %20 %24
626          %56 = OpCopyObject %21 %22
627                OpReturn
628                OpFunctionEnd
629   )";
630 
631   ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
632 }
633 
TEST(TransformationAddSynonymTest,CopyBooleanConstants)634 TEST(TransformationAddSynonymTest, CopyBooleanConstants) {
635   std::string shader = R"(
636                OpCapability Shader
637           %1 = OpExtInstImport "GLSL.std.450"
638                OpMemoryModel Logical GLSL450
639                OpEntryPoint Fragment %4 "main"
640                OpExecutionMode %4 OriginUpperLeft
641                OpSource ESSL 310
642                OpName %4 "main"
643           %2 = OpTypeVoid
644           %6 = OpTypeBool
645           %7 = OpConstantTrue %6
646           %8 = OpConstantFalse %6
647           %3 = OpTypeFunction %2
648           %4 = OpFunction %2 None %3
649           %5 = OpLabel
650                OpReturn
651                OpFunctionEnd
652   )";
653 
654   const auto env = SPV_ENV_UNIVERSAL_1_3;
655   const auto consumer = nullptr;
656   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
657   spvtools::ValidatorOptions validator_options;
658   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
659                                                kConsoleMessageConsumer));
660   TransformationContext transformation_context(
661       MakeUnique<FactManager>(context.get()), validator_options);
662   ASSERT_EQ(0, transformation_context.GetFactManager()
663                    ->GetIdsForWhichSynonymsAreKnown()
664                    .size());
665 
666   {
667     TransformationAddSynonym copy_true(
668         7, protobufs::TransformationAddSynonym::COPY_OBJECT, 100,
669         MakeInstructionDescriptor(5, spv::Op::OpReturn, 0));
670     ASSERT_TRUE(copy_true.IsApplicable(context.get(), transformation_context));
671     ApplyAndCheckFreshIds(copy_true, context.get(), &transformation_context);
672 
673     std::vector<uint32_t> ids_for_which_synonyms_are_known =
674         transformation_context.GetFactManager()
675             ->GetIdsForWhichSynonymsAreKnown();
676     ASSERT_EQ(2, ids_for_which_synonyms_are_known.size());
677     ASSERT_TRUE(std::find(ids_for_which_synonyms_are_known.begin(),
678                           ids_for_which_synonyms_are_known.end(),
679                           7) != ids_for_which_synonyms_are_known.end());
680     ASSERT_EQ(
681         2, transformation_context.GetFactManager()->GetSynonymsForId(7).size());
682     protobufs::DataDescriptor descriptor_100 = MakeDataDescriptor(100, {});
683     ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
684         MakeDataDescriptor(7, {}), descriptor_100));
685   }
686 
687   {
688     TransformationAddSynonym copy_false(
689         8, protobufs::TransformationAddSynonym::COPY_OBJECT, 101,
690         MakeInstructionDescriptor(100, spv::Op::OpReturn, 0));
691     ASSERT_TRUE(copy_false.IsApplicable(context.get(), transformation_context));
692     ApplyAndCheckFreshIds(copy_false, context.get(), &transformation_context);
693     std::vector<uint32_t> ids_for_which_synonyms_are_known =
694         transformation_context.GetFactManager()
695             ->GetIdsForWhichSynonymsAreKnown();
696     ASSERT_EQ(4, ids_for_which_synonyms_are_known.size());
697     ASSERT_TRUE(std::find(ids_for_which_synonyms_are_known.begin(),
698                           ids_for_which_synonyms_are_known.end(),
699                           8) != ids_for_which_synonyms_are_known.end());
700     ASSERT_EQ(
701         2, transformation_context.GetFactManager()->GetSynonymsForId(8).size());
702     protobufs::DataDescriptor descriptor_101 = MakeDataDescriptor(101, {});
703     ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
704         MakeDataDescriptor(8, {}), descriptor_101));
705   }
706 
707   {
708     TransformationAddSynonym copy_false_again(
709         101, protobufs::TransformationAddSynonym::COPY_OBJECT, 102,
710         MakeInstructionDescriptor(5, spv::Op::OpReturn, 0));
711     ASSERT_TRUE(
712         copy_false_again.IsApplicable(context.get(), transformation_context));
713     ApplyAndCheckFreshIds(copy_false_again, context.get(),
714                           &transformation_context);
715     std::vector<uint32_t> ids_for_which_synonyms_are_known =
716         transformation_context.GetFactManager()
717             ->GetIdsForWhichSynonymsAreKnown();
718     ASSERT_EQ(5, ids_for_which_synonyms_are_known.size());
719     ASSERT_TRUE(std::find(ids_for_which_synonyms_are_known.begin(),
720                           ids_for_which_synonyms_are_known.end(),
721                           101) != ids_for_which_synonyms_are_known.end());
722     ASSERT_EQ(
723         3,
724         transformation_context.GetFactManager()->GetSynonymsForId(101).size());
725     protobufs::DataDescriptor descriptor_102 = MakeDataDescriptor(102, {});
726     ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
727         MakeDataDescriptor(101, {}), descriptor_102));
728   }
729 
730   {
731     TransformationAddSynonym copy_true_again(
732         7, protobufs::TransformationAddSynonym::COPY_OBJECT, 103,
733         MakeInstructionDescriptor(102, spv::Op::OpReturn, 0));
734     ASSERT_TRUE(
735         copy_true_again.IsApplicable(context.get(), transformation_context));
736     ApplyAndCheckFreshIds(copy_true_again, context.get(),
737                           &transformation_context);
738     std::vector<uint32_t> ids_for_which_synonyms_are_known =
739         transformation_context.GetFactManager()
740             ->GetIdsForWhichSynonymsAreKnown();
741     ASSERT_EQ(6, ids_for_which_synonyms_are_known.size());
742     ASSERT_TRUE(std::find(ids_for_which_synonyms_are_known.begin(),
743                           ids_for_which_synonyms_are_known.end(),
744                           7) != ids_for_which_synonyms_are_known.end());
745     ASSERT_EQ(
746         3, transformation_context.GetFactManager()->GetSynonymsForId(7).size());
747     protobufs::DataDescriptor descriptor_103 = MakeDataDescriptor(103, {});
748     ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
749         MakeDataDescriptor(7, {}), descriptor_103));
750   }
751 
752   std::string after_transformation = R"(
753                OpCapability Shader
754           %1 = OpExtInstImport "GLSL.std.450"
755                OpMemoryModel Logical GLSL450
756                OpEntryPoint Fragment %4 "main"
757                OpExecutionMode %4 OriginUpperLeft
758                OpSource ESSL 310
759                OpName %4 "main"
760           %2 = OpTypeVoid
761           %6 = OpTypeBool
762           %7 = OpConstantTrue %6
763           %8 = OpConstantFalse %6
764           %3 = OpTypeFunction %2
765           %4 = OpFunction %2 None %3
766           %5 = OpLabel
767         %100 = OpCopyObject %6 %7
768         %101 = OpCopyObject %6 %8
769         %102 = OpCopyObject %6 %101
770         %103 = OpCopyObject %6 %7
771                OpReturn
772                OpFunctionEnd
773   )";
774 
775   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
776 }
777 
TEST(TransformationAddSynonymTest,CheckIllegalCases)778 TEST(TransformationAddSynonymTest, CheckIllegalCases) {
779   // The following SPIR-V comes from this GLSL, pushed through spirv-opt
780   // and then doctored a bit.
781   //
782   // #version 310 es
783   //
784   // precision highp float;
785   //
786   // struct S {
787   //   int a;
788   //   float b;
789   // };
790   //
791   // layout(set = 0, binding = 2) uniform block {
792   //   S s;
793   //   lowp float f;
794   //   int ii;
795   // } ubuf;
796   //
797   // layout(location = 0) out vec4 color;
798   //
799   // void main() {
800   //   float c = 0.0;
801   //   lowp float d = 0.0;
802   //   S localS = ubuf.s;
803   //   for (int i = 0; i < ubuf.s.a; i++) {
804   //     switch (ubuf.ii) {
805   //       case 0:
806   //         c += 0.1;
807   //         d += 0.2;
808   //       case 1:
809   //         c += 0.1;
810   //         if (c > d) {
811   //           d += 0.2;
812   //         } else {
813   //           d += c;
814   //         }
815   //         break;
816   //       default:
817   //         i += 1;
818   //         localS.b += d;
819   //     }
820   //   }
821   //   color = vec4(c, d, localS.b, 1.0);
822   // }
823 
824   std::string shader = R"(
825                OpCapability Shader
826           %1 = OpExtInstImport "GLSL.std.450"
827                OpMemoryModel Logical GLSL450
828                OpEntryPoint Fragment %4 "main" %80
829                OpExecutionMode %4 OriginUpperLeft
830                OpSource ESSL 310
831                OpName %4 "main"
832                OpName %12 "S"
833                OpMemberName %12 0 "a"
834                OpMemberName %12 1 "b"
835                OpName %15 "S"
836                OpMemberName %15 0 "a"
837                OpMemberName %15 1 "b"
838                OpName %16 "block"
839                OpMemberName %16 0 "s"
840                OpMemberName %16 1 "f"
841                OpMemberName %16 2 "ii"
842                OpName %18 "ubuf"
843                OpName %80 "color"
844                OpMemberDecorate %12 0 RelaxedPrecision
845                OpMemberDecorate %15 0 RelaxedPrecision
846                OpMemberDecorate %15 0 Offset 0
847                OpMemberDecorate %15 1 Offset 4
848                OpMemberDecorate %16 0 Offset 0
849                OpMemberDecorate %16 1 RelaxedPrecision
850                OpMemberDecorate %16 1 Offset 16
851                OpMemberDecorate %16 2 RelaxedPrecision
852                OpMemberDecorate %16 2 Offset 20
853                OpDecorate %16 Block
854                OpDecorate %18 DescriptorSet 0
855                OpDecorate %18 Binding 2
856                OpDecorate %38 RelaxedPrecision
857                OpDecorate %43 RelaxedPrecision
858                OpDecorate %53 RelaxedPrecision
859                OpDecorate %62 RelaxedPrecision
860                OpDecorate %69 RelaxedPrecision
861                OpDecorate %77 RelaxedPrecision
862                OpDecorate %80 Location 0
863                OpDecorate %101 RelaxedPrecision
864                OpDecorate %102 RelaxedPrecision
865                OpDecorate %96 RelaxedPrecision
866                OpDecorate %108 RelaxedPrecision
867                OpDecorate %107 RelaxedPrecision
868                OpDecorate %98 RelaxedPrecision
869           %2 = OpTypeVoid
870           %3 = OpTypeFunction %2
871           %6 = OpTypeFloat 32
872           %9 = OpConstant %6 0
873          %11 = OpTypeInt 32 1
874          %12 = OpTypeStruct %11 %6
875          %15 = OpTypeStruct %11 %6
876          %16 = OpTypeStruct %15 %6 %11
877          %17 = OpTypePointer Uniform %16
878          %18 = OpVariable %17 Uniform
879          %19 = OpConstant %11 0
880          %20 = OpTypePointer Uniform %15
881          %27 = OpConstant %11 1
882          %36 = OpTypePointer Uniform %11
883          %39 = OpTypeBool
884          %41 = OpConstant %11 2
885          %48 = OpConstant %6 0.100000001
886          %51 = OpConstant %6 0.200000003
887          %78 = OpTypeVector %6 4
888          %79 = OpTypePointer Output %78
889          %80 = OpVariable %79 Output
890          %85 = OpConstant %6 1
891          %95 = OpUndef %12
892         %112 = OpTypePointer Uniform %6
893         %113 = OpTypeInt 32 0
894         %114 = OpConstant %113 1
895         %179 = OpTypePointer Function %39
896           %4 = OpFunction %2 None %3
897           %5 = OpLabel
898         %180 = OpVariable %179 Function
899         %181 = OpVariable %179 Function
900         %182 = OpVariable %179 Function
901          %21 = OpAccessChain %20 %18 %19
902         %115 = OpAccessChain %112 %21 %114
903         %116 = OpLoad %6 %115
904          %90 = OpCompositeInsert %12 %116 %95 1
905                OpBranch %30
906          %30 = OpLabel
907          %99 = OpPhi %12 %90 %5 %109 %47
908          %98 = OpPhi %6 %9 %5 %107 %47
909          %97 = OpPhi %6 %9 %5 %105 %47
910          %96 = OpPhi %11 %19 %5 %77 %47
911          %37 = OpAccessChain %36 %18 %19 %19
912          %38 = OpLoad %11 %37
913          %40 = OpSLessThan %39 %96 %38
914                OpLoopMerge %32 %47 None
915                OpBranchConditional %40 %31 %32
916          %31 = OpLabel
917          %42 = OpAccessChain %36 %18 %41
918          %43 = OpLoad %11 %42
919                OpSelectionMerge %45 None
920                OpSwitch %43 %46 0 %44 1 %45
921          %46 = OpLabel
922          %69 = OpIAdd %11 %96 %27
923          %72 = OpCompositeExtract %6 %99 1
924          %73 = OpFAdd %6 %72 %98
925          %93 = OpCompositeInsert %12 %73 %99 1
926                OpBranch %47
927          %44 = OpLabel
928          %50 = OpFAdd %6 %97 %48
929          %53 = OpFAdd %6 %98 %51
930                OpBranch %45
931          %45 = OpLabel
932         %101 = OpPhi %6 %98 %31 %53 %44
933         %100 = OpPhi %6 %97 %31 %50 %44
934          %55 = OpFAdd %6 %100 %48
935          %58 = OpFOrdGreaterThan %39 %55 %101
936                OpSelectionMerge %60 None
937                OpBranchConditional %58 %59 %63
938          %59 = OpLabel
939          %62 = OpFAdd %6 %101 %51
940                OpBranch %60
941          %63 = OpLabel
942          %66 = OpFAdd %6 %101 %55
943                OpBranch %60
944          %60 = OpLabel
945         %108 = OpPhi %6 %62 %59 %66 %63
946                OpBranch %47
947          %47 = OpLabel
948         %109 = OpPhi %12 %93 %46 %99 %60
949         %107 = OpPhi %6 %98 %46 %108 %60
950         %105 = OpPhi %6 %97 %46 %55 %60
951         %102 = OpPhi %11 %69 %46 %96 %60
952          %77 = OpIAdd %11 %102 %27
953                OpBranch %30
954          %32 = OpLabel
955          %84 = OpCompositeExtract %6 %99 1
956          %86 = OpCompositeConstruct %78 %97 %98 %84 %85
957                OpStore %80 %86
958                OpReturn
959                OpFunctionEnd
960   )";
961 
962   const auto env = SPV_ENV_UNIVERSAL_1_3;
963   const auto consumer = nullptr;
964   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
965   spvtools::ValidatorOptions validator_options;
966   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
967                                                kConsoleMessageConsumer));
968   TransformationContext transformation_context(
969       MakeUnique<FactManager>(context.get()), validator_options);
970   // Inapplicable because %18 is decorated.
971   ASSERT_FALSE(TransformationAddSynonym(
972                    18, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
973                    MakeInstructionDescriptor(21, spv::Op::OpAccessChain, 0))
974                    .IsApplicable(context.get(), transformation_context));
975 
976   // Inapplicable because %77 is decorated.
977   ASSERT_FALSE(TransformationAddSynonym(
978                    77, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
979                    MakeInstructionDescriptor(77, spv::Op::OpBranch, 0))
980                    .IsApplicable(context.get(), transformation_context));
981 
982   // Inapplicable because %80 is decorated.
983   ASSERT_FALSE(TransformationAddSynonym(
984                    80, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
985                    MakeInstructionDescriptor(77, spv::Op::OpIAdd, 0))
986                    .IsApplicable(context.get(), transformation_context));
987 
988   // Inapplicable because %84 is not available at the requested point
989   ASSERT_FALSE(
990       TransformationAddSynonym(
991           84, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
992           MakeInstructionDescriptor(32, spv::Op::OpCompositeExtract, 0))
993           .IsApplicable(context.get(), transformation_context));
994 
995   // Fine because %84 is available at the requested point
996   ASSERT_TRUE(
997       TransformationAddSynonym(
998           84, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
999           MakeInstructionDescriptor(32, spv::Op::OpCompositeConstruct, 0))
1000           .IsApplicable(context.get(), transformation_context));
1001 
1002   // Inapplicable because id %9 is already in use
1003   ASSERT_FALSE(
1004       TransformationAddSynonym(
1005           84, protobufs::TransformationAddSynonym::COPY_OBJECT, 9,
1006           MakeInstructionDescriptor(32, spv::Op::OpCompositeConstruct, 0))
1007           .IsApplicable(context.get(), transformation_context));
1008 
1009   // Inapplicable because the requested point does not exist
1010   ASSERT_FALSE(TransformationAddSynonym(
1011                    84, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
1012                    MakeInstructionDescriptor(86, spv::Op::OpReturn, 2))
1013                    .IsApplicable(context.get(), transformation_context));
1014 
1015   // Inapplicable because %9 is not in a function
1016   ASSERT_FALSE(TransformationAddSynonym(
1017                    9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
1018                    MakeInstructionDescriptor(9, spv::Op::OpTypeInt, 0))
1019                    .IsApplicable(context.get(), transformation_context));
1020 
1021   // Inapplicable because the insert point is right before, or inside, a chunk
1022   // of OpPhis
1023   ASSERT_FALSE(TransformationAddSynonym(
1024                    9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
1025                    MakeInstructionDescriptor(30, spv::Op::OpPhi, 0))
1026                    .IsApplicable(context.get(), transformation_context));
1027   ASSERT_FALSE(TransformationAddSynonym(
1028                    9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
1029                    MakeInstructionDescriptor(99, spv::Op::OpPhi, 1))
1030                    .IsApplicable(context.get(), transformation_context));
1031 
1032   // OK, because the insert point is just after a chunk of OpPhis.
1033   ASSERT_TRUE(TransformationAddSynonym(
1034                   9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
1035                   MakeInstructionDescriptor(96, spv::Op::OpAccessChain, 0))
1036                   .IsApplicable(context.get(), transformation_context));
1037 
1038   // Inapplicable because the insert point is right after an OpSelectionMerge
1039   ASSERT_FALSE(
1040       TransformationAddSynonym(
1041           9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
1042           MakeInstructionDescriptor(58, spv::Op::OpBranchConditional, 0))
1043           .IsApplicable(context.get(), transformation_context));
1044 
1045   // OK, because the insert point is right before the OpSelectionMerge
1046   ASSERT_TRUE(TransformationAddSynonym(
1047                   9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
1048                   MakeInstructionDescriptor(58, spv::Op::OpSelectionMerge, 0))
1049                   .IsApplicable(context.get(), transformation_context));
1050 
1051   // Inapplicable because the insert point is right after an OpSelectionMerge
1052   ASSERT_FALSE(TransformationAddSynonym(
1053                    9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
1054                    MakeInstructionDescriptor(43, spv::Op::OpSwitch, 0))
1055                    .IsApplicable(context.get(), transformation_context));
1056 
1057   // OK, because the insert point is right before the OpSelectionMerge
1058   ASSERT_TRUE(TransformationAddSynonym(
1059                   9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
1060                   MakeInstructionDescriptor(43, spv::Op::OpSelectionMerge, 0))
1061                   .IsApplicable(context.get(), transformation_context));
1062 
1063   // Inapplicable because the insert point is right after an OpLoopMerge
1064   ASSERT_FALSE(
1065       TransformationAddSynonym(
1066           9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
1067           MakeInstructionDescriptor(40, spv::Op::OpBranchConditional, 0))
1068           .IsApplicable(context.get(), transformation_context));
1069 
1070   // OK, because the insert point is right before the OpLoopMerge
1071   ASSERT_TRUE(TransformationAddSynonym(
1072                   9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
1073                   MakeInstructionDescriptor(40, spv::Op::OpLoopMerge, 0))
1074                   .IsApplicable(context.get(), transformation_context));
1075 
1076   // Inapplicable because id %300 does not exist
1077   ASSERT_FALSE(TransformationAddSynonym(
1078                    300, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
1079                    MakeInstructionDescriptor(40, spv::Op::OpLoopMerge, 0))
1080                    .IsApplicable(context.get(), transformation_context));
1081 
1082   // Inapplicable because the following instruction is OpVariable
1083   ASSERT_FALSE(TransformationAddSynonym(
1084                    9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
1085                    MakeInstructionDescriptor(180, spv::Op::OpVariable, 0))
1086                    .IsApplicable(context.get(), transformation_context));
1087   ASSERT_FALSE(TransformationAddSynonym(
1088                    9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
1089                    MakeInstructionDescriptor(181, spv::Op::OpVariable, 0))
1090                    .IsApplicable(context.get(), transformation_context));
1091   ASSERT_FALSE(TransformationAddSynonym(
1092                    9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
1093                    MakeInstructionDescriptor(182, spv::Op::OpVariable, 0))
1094                    .IsApplicable(context.get(), transformation_context));
1095 
1096   // OK, because this is just past the group of OpVariable instructions.
1097   ASSERT_TRUE(TransformationAddSynonym(
1098                   9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
1099                   MakeInstructionDescriptor(182, spv::Op::OpAccessChain, 0))
1100                   .IsApplicable(context.get(), transformation_context));
1101 }
1102 
TEST(TransformationAddSynonymTest,MiscellaneousCopies)1103 TEST(TransformationAddSynonymTest, MiscellaneousCopies) {
1104   // The following SPIR-V comes from this GLSL:
1105   //
1106   // #version 310 es
1107   //
1108   // precision highp float;
1109   //
1110   // float g;
1111   //
1112   // vec4 h;
1113   //
1114   // void main() {
1115   //   int a;
1116   //   int b;
1117   //   b = int(g);
1118   //   h.x = float(a);
1119   // }
1120 
1121   std::string shader = R"(
1122                OpCapability Shader
1123           %1 = OpExtInstImport "GLSL.std.450"
1124                OpMemoryModel Logical GLSL450
1125                OpEntryPoint Fragment %4 "main"
1126                OpExecutionMode %4 OriginUpperLeft
1127                OpSource ESSL 310
1128                OpName %4 "main"
1129                OpName %8 "b"
1130                OpName %11 "g"
1131                OpName %16 "h"
1132                OpName %17 "a"
1133           %2 = OpTypeVoid
1134           %3 = OpTypeFunction %2
1135           %6 = OpTypeInt 32 1
1136           %7 = OpTypePointer Function %6
1137           %9 = OpTypeFloat 32
1138          %10 = OpTypePointer Private %9
1139          %11 = OpVariable %10 Private
1140          %14 = OpTypeVector %9 4
1141          %15 = OpTypePointer Private %14
1142          %16 = OpVariable %15 Private
1143          %20 = OpTypeInt 32 0
1144          %21 = OpConstant %20 0
1145           %4 = OpFunction %2 None %3
1146           %5 = OpLabel
1147           %8 = OpVariable %7 Function
1148          %17 = OpVariable %7 Function
1149          %12 = OpLoad %9 %11
1150          %13 = OpConvertFToS %6 %12
1151                OpStore %8 %13
1152          %18 = OpLoad %6 %17
1153          %19 = OpConvertSToF %9 %18
1154          %22 = OpAccessChain %10 %16 %21
1155                OpStore %22 %19
1156                OpReturn
1157                OpFunctionEnd
1158   )";
1159 
1160   const auto env = SPV_ENV_UNIVERSAL_1_3;
1161   const auto consumer = nullptr;
1162   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1163   spvtools::ValidatorOptions validator_options;
1164   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1165                                                kConsoleMessageConsumer));
1166   TransformationContext transformation_context(
1167       MakeUnique<FactManager>(context.get()), validator_options);
1168   std::vector<TransformationAddSynonym> transformations = {
1169       TransformationAddSynonym(
1170           19, protobufs::TransformationAddSynonym::COPY_OBJECT, 100,
1171           MakeInstructionDescriptor(22, spv::Op::OpStore, 0)),
1172       TransformationAddSynonym(
1173           22, protobufs::TransformationAddSynonym::COPY_OBJECT, 101,
1174           MakeInstructionDescriptor(22, spv::Op::OpCopyObject, 0)),
1175       TransformationAddSynonym(
1176           12, protobufs::TransformationAddSynonym::COPY_OBJECT, 102,
1177           MakeInstructionDescriptor(22, spv::Op::OpCopyObject, 0)),
1178       TransformationAddSynonym(
1179           11, protobufs::TransformationAddSynonym::COPY_OBJECT, 103,
1180           MakeInstructionDescriptor(22, spv::Op::OpCopyObject, 0)),
1181       TransformationAddSynonym(
1182           16, protobufs::TransformationAddSynonym::COPY_OBJECT, 104,
1183           MakeInstructionDescriptor(22, spv::Op::OpCopyObject, 0)),
1184       TransformationAddSynonym(
1185           8, protobufs::TransformationAddSynonym::COPY_OBJECT, 105,
1186           MakeInstructionDescriptor(22, spv::Op::OpCopyObject, 0)),
1187       TransformationAddSynonym(
1188           17, protobufs::TransformationAddSynonym::COPY_OBJECT, 106,
1189           MakeInstructionDescriptor(22, spv::Op::OpCopyObject, 0))};
1190 
1191   for (auto& transformation : transformations) {
1192     ASSERT_TRUE(
1193         transformation.IsApplicable(context.get(), transformation_context));
1194     ApplyAndCheckFreshIds(transformation, context.get(),
1195                           &transformation_context);
1196   }
1197 
1198   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1199                                                kConsoleMessageConsumer));
1200 
1201   std::string after_transformation = R"(
1202                OpCapability Shader
1203           %1 = OpExtInstImport "GLSL.std.450"
1204                OpMemoryModel Logical GLSL450
1205                OpEntryPoint Fragment %4 "main"
1206                OpExecutionMode %4 OriginUpperLeft
1207                OpSource ESSL 310
1208                OpName %4 "main"
1209                OpName %8 "b"
1210                OpName %11 "g"
1211                OpName %16 "h"
1212                OpName %17 "a"
1213           %2 = OpTypeVoid
1214           %3 = OpTypeFunction %2
1215           %6 = OpTypeInt 32 1
1216           %7 = OpTypePointer Function %6
1217           %9 = OpTypeFloat 32
1218          %10 = OpTypePointer Private %9
1219          %11 = OpVariable %10 Private
1220          %14 = OpTypeVector %9 4
1221          %15 = OpTypePointer Private %14
1222          %16 = OpVariable %15 Private
1223          %20 = OpTypeInt 32 0
1224          %21 = OpConstant %20 0
1225           %4 = OpFunction %2 None %3
1226           %5 = OpLabel
1227           %8 = OpVariable %7 Function
1228          %17 = OpVariable %7 Function
1229          %12 = OpLoad %9 %11
1230          %13 = OpConvertFToS %6 %12
1231                OpStore %8 %13
1232          %18 = OpLoad %6 %17
1233          %19 = OpConvertSToF %9 %18
1234          %22 = OpAccessChain %10 %16 %21
1235         %106 = OpCopyObject %7 %17
1236         %105 = OpCopyObject %7 %8
1237         %104 = OpCopyObject %15 %16
1238         %103 = OpCopyObject %10 %11
1239         %102 = OpCopyObject %9 %12
1240         %101 = OpCopyObject %10 %22
1241         %100 = OpCopyObject %9 %19
1242                OpStore %22 %19
1243                OpReturn
1244                OpFunctionEnd
1245   )";
1246 
1247   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
1248 }
1249 
TEST(TransformationAddSynonymTest,DoNotCopyNullPointers)1250 TEST(TransformationAddSynonymTest, DoNotCopyNullPointers) {
1251   std::string shader = R"(
1252                OpCapability Shader
1253                OpCapability VariablePointers
1254           %1 = OpExtInstImport "GLSL.std.450"
1255                OpMemoryModel Logical GLSL450
1256                OpEntryPoint Fragment %4 "main"
1257                OpExecutionMode %4 OriginUpperLeft
1258                OpSource ESSL 310
1259           %2 = OpTypeVoid
1260           %3 = OpTypeFunction %2
1261           %6 = OpTypeInt 32 1
1262           %7 = OpTypePointer Function %6
1263           %8 = OpConstantNull %7
1264           %4 = OpFunction %2 None %3
1265           %5 = OpLabel
1266                OpReturn
1267                OpFunctionEnd
1268   )";
1269 
1270   const auto env = SPV_ENV_UNIVERSAL_1_3;
1271   const auto consumer = nullptr;
1272   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1273   spvtools::ValidatorOptions validator_options;
1274   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1275                                                kConsoleMessageConsumer));
1276   TransformationContext transformation_context(
1277       MakeUnique<FactManager>(context.get()), validator_options);
1278   // Illegal to copy null.
1279   ASSERT_FALSE(TransformationAddSynonym(
1280                    8, protobufs::TransformationAddSynonym::COPY_OBJECT, 100,
1281                    MakeInstructionDescriptor(5, spv::Op::OpReturn, 0))
1282                    .IsApplicable(context.get(), transformation_context));
1283 }
1284 
TEST(TransformationAddSynonymTest,PropagateIrrelevantPointeeFact)1285 TEST(TransformationAddSynonymTest, PropagateIrrelevantPointeeFact) {
1286   // Checks that if a pointer is known to have an irrelevant value, the same
1287   // holds after the pointer is copied.
1288 
1289   std::string shader = R"(
1290                OpCapability Shader
1291           %1 = OpExtInstImport "GLSL.std.450"
1292                OpMemoryModel Logical GLSL450
1293                OpEntryPoint Fragment %4 "main"
1294                OpExecutionMode %4 OriginUpperLeft
1295                OpSource ESSL 310
1296           %2 = OpTypeVoid
1297           %3 = OpTypeFunction %2
1298           %6 = OpTypeInt 32 1
1299           %7 = OpTypePointer Function %6
1300           %4 = OpFunction %2 None %3
1301           %5 = OpLabel
1302           %8 = OpVariable %7 Function
1303           %9 = OpVariable %7 Function
1304                OpReturn
1305                OpFunctionEnd
1306   )";
1307 
1308   const auto env = SPV_ENV_UNIVERSAL_1_3;
1309   const auto consumer = nullptr;
1310   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1311   spvtools::ValidatorOptions validator_options;
1312   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1313                                                kConsoleMessageConsumer));
1314   TransformationContext transformation_context(
1315       MakeUnique<FactManager>(context.get()), validator_options);
1316   transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(8);
1317 
1318   TransformationAddSynonym transformation1(
1319       8, protobufs::TransformationAddSynonym::COPY_OBJECT, 100,
1320       MakeInstructionDescriptor(9, spv::Op::OpReturn, 0));
1321   TransformationAddSynonym transformation2(
1322       9, protobufs::TransformationAddSynonym::COPY_OBJECT, 101,
1323       MakeInstructionDescriptor(9, spv::Op::OpReturn, 0));
1324   TransformationAddSynonym transformation3(
1325       100, protobufs::TransformationAddSynonym::COPY_OBJECT, 102,
1326       MakeInstructionDescriptor(9, spv::Op::OpReturn, 0));
1327 
1328   ASSERT_TRUE(
1329       transformation1.IsApplicable(context.get(), transformation_context));
1330   ApplyAndCheckFreshIds(transformation1, context.get(),
1331                         &transformation_context);
1332   ASSERT_TRUE(
1333       transformation2.IsApplicable(context.get(), transformation_context));
1334   ApplyAndCheckFreshIds(transformation2, context.get(),
1335                         &transformation_context);
1336   ASSERT_TRUE(
1337       transformation3.IsApplicable(context.get(), transformation_context));
1338   ApplyAndCheckFreshIds(transformation3, context.get(),
1339                         &transformation_context);
1340 
1341   ASSERT_TRUE(
1342       transformation_context.GetFactManager()->PointeeValueIsIrrelevant(8));
1343   ASSERT_TRUE(
1344       transformation_context.GetFactManager()->PointeeValueIsIrrelevant(100));
1345   ASSERT_TRUE(
1346       transformation_context.GetFactManager()->PointeeValueIsIrrelevant(102));
1347   ASSERT_FALSE(
1348       transformation_context.GetFactManager()->PointeeValueIsIrrelevant(9));
1349   ASSERT_FALSE(
1350       transformation_context.GetFactManager()->PointeeValueIsIrrelevant(101));
1351 }
1352 
TEST(TransformationAddSynonymTest,DoNotCopyOpSampledImage)1353 TEST(TransformationAddSynonymTest, DoNotCopyOpSampledImage) {
1354   // This checks that we do not try to copy the result id of an OpSampledImage
1355   // instruction.
1356   std::string shader = R"(
1357                OpCapability Shader
1358                OpCapability SampledBuffer
1359                OpCapability ImageBuffer
1360           %1 = OpExtInstImport "GLSL.std.450"
1361                OpMemoryModel Logical GLSL450
1362                OpEntryPoint Fragment %2 "main" %40 %41
1363                OpExecutionMode %2 OriginUpperLeft
1364                OpSource GLSL 450
1365                OpDecorate %40 DescriptorSet 0
1366                OpDecorate %40 Binding 69
1367                OpDecorate %41 DescriptorSet 0
1368                OpDecorate %41 Binding 1
1369          %54 = OpTypeFloat 32
1370          %76 = OpTypeVector %54 4
1371          %55 = OpConstant %54 0
1372          %56 = OpTypeVector %54 3
1373          %94 = OpTypeVector %54 2
1374         %112 = OpConstantComposite %94 %55 %55
1375          %57 = OpConstantComposite %56 %55 %55 %55
1376          %15 = OpTypeImage %54 2D 2 0 0 1 Unknown
1377         %114 = OpTypePointer UniformConstant %15
1378          %38 = OpTypeSampler
1379         %125 = OpTypePointer UniformConstant %38
1380         %132 = OpTypeVoid
1381         %133 = OpTypeFunction %132
1382          %45 = OpTypeSampledImage %15
1383          %40 = OpVariable %114 UniformConstant
1384          %41 = OpVariable %125 UniformConstant
1385           %2 = OpFunction %132 None %133
1386         %164 = OpLabel
1387         %184 = OpLoad %15 %40
1388         %213 = OpLoad %38 %41
1389         %216 = OpSampledImage %45 %184 %213
1390         %217 = OpImageSampleImplicitLod %76 %216 %112 Bias %55
1391                OpReturn
1392                OpFunctionEnd
1393   )";
1394 
1395   const auto env = SPV_ENV_UNIVERSAL_1_3;
1396   const auto consumer = nullptr;
1397   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1398 
1399   spvtools::ValidatorOptions validator_options;
1400   TransformationContext transformation_context(
1401       MakeUnique<FactManager>(context.get()), validator_options);
1402   ASSERT_FALSE(
1403       TransformationAddSynonym(
1404           216, protobufs::TransformationAddSynonym::COPY_OBJECT, 500,
1405           MakeInstructionDescriptor(217, spv::Op::OpImageSampleImplicitLod, 0))
1406           .IsApplicable(context.get(), transformation_context));
1407 }
1408 
TEST(TransformationAddSynonymTest,DoNotCopyVoidRunctionResult)1409 TEST(TransformationAddSynonymTest, DoNotCopyVoidRunctionResult) {
1410   // This checks that we do not try to copy the result of a void function.
1411   std::string shader = R"(
1412                OpCapability Shader
1413           %1 = OpExtInstImport "GLSL.std.450"
1414                OpMemoryModel Logical GLSL450
1415                OpEntryPoint Fragment %4 "main"
1416                OpExecutionMode %4 OriginUpperLeft
1417                OpSource ESSL 320
1418                OpName %4 "main"
1419                OpName %6 "foo("
1420           %2 = OpTypeVoid
1421           %3 = OpTypeFunction %2
1422           %4 = OpFunction %2 None %3
1423           %5 = OpLabel
1424           %8 = OpFunctionCall %2 %6
1425                OpReturn
1426                OpFunctionEnd
1427           %6 = OpFunction %2 None %3
1428           %7 = OpLabel
1429                OpReturn
1430                OpFunctionEnd
1431   )";
1432 
1433   const auto env = SPV_ENV_UNIVERSAL_1_3;
1434   const auto consumer = nullptr;
1435   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1436 
1437   spvtools::ValidatorOptions validator_options;
1438   TransformationContext transformation_context(
1439       MakeUnique<FactManager>(context.get()), validator_options);
1440   ASSERT_FALSE(TransformationAddSynonym(
1441                    8, protobufs::TransformationAddSynonym::COPY_OBJECT, 500,
1442                    MakeInstructionDescriptor(8, spv::Op::OpReturn, 0))
1443                    .IsApplicable(context.get(), transformation_context));
1444 }
1445 
TEST(TransformationAddSynonymTest,HandlesDeadBlocks)1446 TEST(TransformationAddSynonymTest, HandlesDeadBlocks) {
1447   std::string shader = R"(
1448                OpCapability Shader
1449           %1 = OpExtInstImport "GLSL.std.450"
1450                OpMemoryModel Logical GLSL450
1451                OpEntryPoint Fragment %4 "main"
1452                OpExecutionMode %4 OriginUpperLeft
1453                OpSource ESSL 320
1454           %2 = OpTypeVoid
1455           %3 = OpTypeFunction %2
1456           %6 = OpTypeBool
1457           %7 = OpConstantTrue %6
1458          %11 = OpTypePointer Function %6
1459           %4 = OpFunction %2 None %3
1460           %5 = OpLabel
1461          %12 = OpVariable %11 Function
1462                OpSelectionMerge %10 None
1463                OpBranchConditional %7 %8 %9
1464           %8 = OpLabel
1465                OpBranch %10
1466           %9 = OpLabel
1467                OpBranch %10
1468          %10 = OpLabel
1469                OpReturn
1470                OpFunctionEnd
1471   )";
1472 
1473   const auto env = SPV_ENV_UNIVERSAL_1_3;
1474   const auto consumer = nullptr;
1475   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1476 
1477   spvtools::ValidatorOptions validator_options;
1478   TransformationContext transformation_context(
1479       MakeUnique<FactManager>(context.get()), validator_options);
1480 
1481   transformation_context.GetFactManager()->AddFactBlockIsDead(9);
1482 
1483   auto insert_before = MakeInstructionDescriptor(9, spv::Op::OpBranch, 0);
1484 
1485   ASSERT_FALSE(TransformationAddSynonym(
1486                    7, protobufs::TransformationAddSynonym::COPY_OBJECT, 100,
1487                    insert_before)
1488                    .IsApplicable(context.get(), transformation_context));
1489 
1490   ASSERT_FALSE(TransformationAddSynonym(
1491                    12, protobufs::TransformationAddSynonym::COPY_OBJECT, 100,
1492                    insert_before)
1493                    .IsApplicable(context.get(), transformation_context));
1494 }
1495 
1496 }  // namespace
1497 }  // namespace fuzz
1498 }  // namespace spvtools
1499