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