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
22namespace spvtools {
23namespace fuzz {
24namespace {
25
26TEST(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
160TEST(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
340TEST(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
442TEST(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
480TEST(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
518TEST(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
634TEST(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
778TEST(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
1103TEST(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
1250TEST(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
1285TEST(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
1353TEST(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
1409TEST(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
1446TEST(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