1// Copyright (c) 2020 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#include "source/fuzz/transformation_composite_insert.h"
16
17#include "gtest/gtest.h"
18#include "source/fuzz/data_descriptor.h"
19#include "source/fuzz/fuzzer_util.h"
20#include "source/fuzz/instruction_descriptor.h"
21#include "test/fuzz/fuzz_test_util.h"
22
23namespace spvtools {
24namespace fuzz {
25namespace {
26
27TEST(TransformationCompositeInsertTest, NotApplicableScenarios) {
28  // This test handles cases where IsApplicable() returns false.
29
30  std::string shader = R"(
31               OpCapability Shader
32          %1 = OpExtInstImport "GLSL.std.450"
33               OpMemoryModel Logical GLSL450
34               OpEntryPoint Fragment %4 "main"
35               OpExecutionMode %4 OriginUpperLeft
36               OpSource ESSL 310
37               OpName %4 "main"
38               OpName %8 "i1"
39               OpName %10 "i2"
40               OpName %12 "base"
41               OpMemberName %12 0 "a1"
42               OpMemberName %12 1 "a2"
43               OpName %14 "b"
44               OpName %18 "level_1"
45               OpMemberName %18 0 "b1"
46               OpMemberName %18 1 "b2"
47               OpName %20 "l1"
48               OpName %24 "level_2"
49               OpMemberName %24 0 "c1"
50               OpMemberName %24 1 "c2"
51               OpName %26 "l2"
52          %2 = OpTypeVoid
53          %3 = OpTypeFunction %2
54          %6 = OpTypeInt 32 1
55          %7 = OpTypePointer Function %6
56          %9 = OpConstant %6 1
57         %11 = OpConstant %6 2
58         %12 = OpTypeStruct %6 %6
59         %13 = OpTypePointer Function %12
60         %18 = OpTypeStruct %12 %12
61         %19 = OpTypePointer Function %18
62         %24 = OpTypeStruct %18 %18
63         %25 = OpTypePointer Function %24
64         %30 = OpTypeBool
65         %31 = OpConstantTrue %30
66          %4 = OpFunction %2 None %3
67          %5 = OpLabel
68          %8 = OpVariable %7 Function
69         %10 = OpVariable %7 Function
70         %14 = OpVariable %13 Function
71         %20 = OpVariable %19 Function
72         %26 = OpVariable %25 Function
73               OpStore %8 %9
74               OpStore %10 %11
75         %15 = OpLoad %6 %8
76         %16 = OpLoad %6 %10
77         %17 = OpCompositeConstruct %12 %15 %16
78               OpStore %14 %17
79         %21 = OpLoad %12 %14
80         %22 = OpLoad %12 %14
81         %23 = OpCompositeConstruct %18 %21 %22
82               OpStore %20 %23
83         %27 = OpLoad %18 %20
84         %28 = OpLoad %18 %20
85         %29 = OpCompositeConstruct %24 %27 %28
86               OpStore %26 %29
87               OpSelectionMerge %33 None
88               OpBranchConditional %31 %32 %33
89         %32 = OpLabel
90               OpBranch %33
91         %33 = OpLabel
92               OpReturn
93               OpFunctionEnd
94    )";
95
96  const auto env = SPV_ENV_UNIVERSAL_1_4;
97  const auto consumer = nullptr;
98  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
99  spvtools::ValidatorOptions validator_options;
100  ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
101                                               kConsoleMessageConsumer));
102  TransformationContext transformation_context(
103      MakeUnique<FactManager>(context.get()), validator_options);
104  // Bad: |fresh_id| is not fresh.
105  auto transformation_bad_1 = TransformationCompositeInsert(
106      MakeInstructionDescriptor(29, spv::Op::OpStore, 0), 20, 29, 11,
107      {1, 0, 0});
108  ASSERT_FALSE(
109      transformation_bad_1.IsApplicable(context.get(), transformation_context));
110
111  // Bad: |composite_id| does not refer to a existing instruction.
112  auto transformation_bad_2 = TransformationCompositeInsert(
113      MakeInstructionDescriptor(29, spv::Op::OpStore, 0), 50, 40, 11,
114      {1, 0, 0});
115  ASSERT_FALSE(
116      transformation_bad_2.IsApplicable(context.get(), transformation_context));
117
118  // Bad: |composite_id| does not refer to a composite value.
119  auto transformation_bad_3 = TransformationCompositeInsert(
120      MakeInstructionDescriptor(29, spv::Op::OpStore, 0), 50, 9, 11, {1, 0, 0});
121  ASSERT_FALSE(
122      transformation_bad_3.IsApplicable(context.get(), transformation_context));
123
124  // Bad: |object_id| does not refer to a defined instruction.
125  auto transformation_bad_4 = TransformationCompositeInsert(
126      MakeInstructionDescriptor(29, spv::Op::OpStore, 0), 50, 29, 40,
127      {1, 0, 0});
128  ASSERT_FALSE(
129      transformation_bad_4.IsApplicable(context.get(), transformation_context));
130
131  // Bad: |object_id| cannot refer to a pointer.
132  auto transformation_bad_5 = TransformationCompositeInsert(
133      MakeInstructionDescriptor(29, spv::Op::OpStore, 0), 50, 29, 8, {1, 0, 0});
134  ASSERT_FALSE(
135      transformation_bad_5.IsApplicable(context.get(), transformation_context));
136
137  // Bad: |index| is not a correct index.
138  auto transformation_bad_6 = TransformationCompositeInsert(
139      MakeInstructionDescriptor(29, spv::Op::OpStore, 0), 50, 29, 11,
140      {2, 0, 0});
141  ASSERT_FALSE(
142      transformation_bad_6.IsApplicable(context.get(), transformation_context));
143
144  // Bad: Type id of the object to be inserted and the type id of the
145  // component at |index| are not the same.
146  auto transformation_bad_7 = TransformationCompositeInsert(
147      MakeInstructionDescriptor(29, spv::Op::OpStore, 0), 50, 29, 11, {1, 0});
148  ASSERT_FALSE(
149      transformation_bad_7.IsApplicable(context.get(), transformation_context));
150
151  // Bad: |instruction_to_insert_before| does not refer to a defined
152  // instruction.
153  auto transformation_bad_8 = TransformationCompositeInsert(
154      MakeInstructionDescriptor(29, spv::Op::OpIMul, 0), 50, 29, 11, {1, 0, 0});
155  ASSERT_FALSE(
156      transformation_bad_8.IsApplicable(context.get(), transformation_context));
157
158  // Bad: OpCompositeInsert cannot be inserted before OpBranchConditional with
159  // OpSelectionMerge above it.
160  auto transformation_bad_9 = TransformationCompositeInsert(
161      MakeInstructionDescriptor(29, spv::Op::OpBranchConditional, 0), 50, 29,
162      11, {1, 0, 0});
163  ASSERT_FALSE(
164      transformation_bad_9.IsApplicable(context.get(), transformation_context));
165
166  // Bad: |composite_id| does not have a type_id.
167  auto transformation_bad_10 = TransformationCompositeInsert(
168      MakeInstructionDescriptor(29, spv::Op::OpStore, 0), 50, 1, 11, {1, 0, 0});
169  ASSERT_FALSE(transformation_bad_10.IsApplicable(context.get(),
170                                                  transformation_context));
171}
172
173TEST(TransformationCompositeInsertTest, EmptyCompositeScenarios) {
174  // This test handles cases where either the composite is empty or the
175  // composite contains an empty composite.
176
177  std::string shader = R"(
178               OpCapability Shader
179          %1 = OpExtInstImport "GLSL.std.450"
180               OpMemoryModel Logical GLSL450
181               OpEntryPoint Fragment %4 "main"
182               OpExecutionMode %4 OriginUpperLeft
183               OpSource ESSL 310
184               OpName %4 "main"
185               OpName %8 "i1"
186               OpName %10 "i2"
187               OpName %12 "base"
188               OpMemberName %12 0 "a1"
189               OpMemberName %12 1 "a2"
190               OpName %14 "b"
191          %2 = OpTypeVoid
192         %60 = OpTypeStruct
193          %3 = OpTypeFunction %2
194          %6 = OpTypeInt 32 1
195          %7 = OpTypePointer Function %6
196          %9 = OpConstant %6 1
197         %11 = OpConstant %6 2
198         %61 = OpConstantComposite %60
199         %62 = OpConstantComposite %60
200         %12 = OpTypeStruct %6 %6
201         %63 = OpTypeStruct %6 %60
202         %13 = OpTypePointer Function %12
203          %4 = OpFunction %2 None %3
204          %5 = OpLabel
205          %8 = OpVariable %7 Function
206         %10 = OpVariable %7 Function
207         %14 = OpVariable %13 Function
208               OpStore %8 %9
209               OpStore %10 %11
210         %15 = OpLoad %6 %8
211         %16 = OpLoad %6 %10
212         %17 = OpCompositeConstruct %12 %15 %16
213         %64 = OpCompositeConstruct %63 %15 %61
214               OpStore %14 %17
215               OpReturn
216               OpFunctionEnd
217    )";
218
219  const auto env = SPV_ENV_UNIVERSAL_1_4;
220  const auto consumer = nullptr;
221  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
222  spvtools::ValidatorOptions validator_options;
223  ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
224                                               kConsoleMessageConsumer));
225  TransformationContext transformation_context(
226      MakeUnique<FactManager>(context.get()), validator_options);
227  // Bad: The composite with |composite_id| cannot be empty.
228  auto transformation_bad_1 = TransformationCompositeInsert(
229      MakeInstructionDescriptor(64, spv::Op::OpStore, 0), 50, 61, 62, {1});
230  ASSERT_FALSE(
231      transformation_bad_1.IsApplicable(context.get(), transformation_context));
232
233  // Good: It is possible to insert into a composite an element which is an
234  // empty composite.
235  auto transformation_good_1 = TransformationCompositeInsert(
236      MakeInstructionDescriptor(64, spv::Op::OpStore, 0), 50, 64, 62, {1});
237  ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(),
238                                                 transformation_context));
239  ApplyAndCheckFreshIds(transformation_good_1, context.get(),
240                        &transformation_context);
241  ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
242                                               kConsoleMessageConsumer));
243
244  std::string after_transformations = R"(
245               OpCapability Shader
246          %1 = OpExtInstImport "GLSL.std.450"
247               OpMemoryModel Logical GLSL450
248               OpEntryPoint Fragment %4 "main"
249               OpExecutionMode %4 OriginUpperLeft
250               OpSource ESSL 310
251               OpName %4 "main"
252               OpName %8 "i1"
253               OpName %10 "i2"
254               OpName %12 "base"
255               OpMemberName %12 0 "a1"
256               OpMemberName %12 1 "a2"
257               OpName %14 "b"
258          %2 = OpTypeVoid
259         %60 = OpTypeStruct
260          %3 = OpTypeFunction %2
261          %6 = OpTypeInt 32 1
262          %7 = OpTypePointer Function %6
263          %9 = OpConstant %6 1
264         %11 = OpConstant %6 2
265         %61 = OpConstantComposite %60
266         %62 = OpConstantComposite %60
267         %12 = OpTypeStruct %6 %6
268         %63 = OpTypeStruct %6 %60
269         %13 = OpTypePointer Function %12
270          %4 = OpFunction %2 None %3
271          %5 = OpLabel
272          %8 = OpVariable %7 Function
273         %10 = OpVariable %7 Function
274         %14 = OpVariable %13 Function
275               OpStore %8 %9
276               OpStore %10 %11
277         %15 = OpLoad %6 %8
278         %16 = OpLoad %6 %10
279         %17 = OpCompositeConstruct %12 %15 %16
280         %64 = OpCompositeConstruct %63 %15 %61
281         %50 = OpCompositeInsert %63 %62 %64 1
282               OpStore %14 %17
283               OpReturn
284               OpFunctionEnd
285  )";
286  ASSERT_TRUE(IsEqual(env, after_transformations, context.get()));
287}
288
289TEST(TransformationCompositeInsertTest, IrrelevantCompositeNoSynonyms) {
290  // This test handles cases where either |composite| is irrelevant.
291  // The transformation shouldn't create any synonyms.
292  // The member composite has a different number of elements than the parent
293  // composite.
294
295  std::string shader = R"(
296               OpCapability Shader
297          %1 = OpExtInstImport "GLSL.std.450"
298               OpMemoryModel Logical GLSL450
299               OpEntryPoint Fragment %4 "main"
300               OpExecutionMode %4 OriginUpperLeft
301               OpSource ESSL 310
302               OpName %4 "main"
303               OpName %8 "i1"
304               OpName %10 "i2"
305               OpName %12 "base"
306               OpMemberName %12 0 "a1"
307               OpMemberName %12 1 "a2"
308               OpName %14 "b"
309               OpName %18 "level_1"
310               OpMemberName %18 0 "b1"
311               OpMemberName %18 1 "b2"
312               OpMemberName %18 2 "b3"
313               OpName %20 "l1"
314               OpName %25 "level_2"
315               OpMemberName %25 0 "c1"
316               OpMemberName %25 1 "c2"
317               OpName %27 "l2"
318          %2 = OpTypeVoid
319          %3 = OpTypeFunction %2
320          %6 = OpTypeInt 32 1
321          %7 = OpTypePointer Function %6
322          %9 = OpConstant %6 1
323         %11 = OpConstant %6 2
324         %12 = OpTypeStruct %6 %6
325         %13 = OpTypePointer Function %12
326         %18 = OpTypeStruct %12 %12 %12
327         %19 = OpTypePointer Function %18
328         %25 = OpTypeStruct %18 %18
329         %26 = OpTypePointer Function %25
330         %31 = OpTypeBool
331         %32 = OpConstantTrue %31
332          %4 = OpFunction %2 None %3
333          %5 = OpLabel
334          %8 = OpVariable %7 Function
335         %10 = OpVariable %7 Function
336         %14 = OpVariable %13 Function
337         %20 = OpVariable %19 Function
338         %27 = OpVariable %26 Function
339               OpStore %8 %9
340               OpStore %10 %11
341         %15 = OpLoad %6 %8
342         %16 = OpLoad %6 %10
343         %17 = OpCompositeConstruct %12 %15 %16
344               OpStore %14 %17
345         %21 = OpLoad %12 %14
346         %22 = OpLoad %12 %14
347         %23 = OpLoad %12 %14
348         %24 = OpCompositeConstruct %18 %21 %22 %23
349               OpStore %20 %24
350         %28 = OpLoad %18 %20
351         %29 = OpLoad %18 %20
352         %30 = OpCompositeConstruct %25 %28 %29
353               OpStore %27 %30
354               OpSelectionMerge %34 None
355               OpBranchConditional %32 %33 %34
356         %33 = OpLabel
357               OpBranch %34
358         %34 = OpLabel
359               OpReturn
360               OpFunctionEnd
361    )";
362
363  const auto env = SPV_ENV_UNIVERSAL_1_4;
364  const auto consumer = nullptr;
365  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
366  spvtools::ValidatorOptions validator_options;
367  ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
368                                               kConsoleMessageConsumer));
369  TransformationContext transformation_context(
370      MakeUnique<FactManager>(context.get()), validator_options);
371  // Add fact that the composite is irrelevant.
372  transformation_context.GetFactManager()->AddFactIdIsIrrelevant(30);
373
374  auto transformation_good_1 = TransformationCompositeInsert(
375      MakeInstructionDescriptor(30, spv::Op::OpStore, 0), 50, 30, 11,
376      {1, 0, 0});
377  ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(),
378                                                 transformation_context));
379  ApplyAndCheckFreshIds(transformation_good_1, context.get(),
380                        &transformation_context);
381  ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
382                                               kConsoleMessageConsumer));
383
384  // No synonyms that involve the original object - %30 - should have been
385  // added.
386  ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
387      MakeDataDescriptor(30, {0}), MakeDataDescriptor(50, {0})));
388  ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
389      MakeDataDescriptor(30, {1, 1}), MakeDataDescriptor(50, {1, 1})));
390  ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
391      MakeDataDescriptor(30, {1, 2}), MakeDataDescriptor(50, {1, 2})));
392  ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
393      MakeDataDescriptor(30, {1, 0, 1}), MakeDataDescriptor(50, {1, 0, 1})));
394  // We *should* have a synonym between %11 and the component of %50 into which
395  // it has been inserted.
396  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
397      MakeDataDescriptor(50, {1, 0, 0}), MakeDataDescriptor(11, {})));
398}
399
400TEST(TransformationCompositeInsertTest, IrrelevantObjectNoSynonyms) {
401  std::string shader = R"(
402               OpCapability Shader
403          %1 = OpExtInstImport "GLSL.std.450"
404               OpMemoryModel Logical GLSL450
405               OpEntryPoint Fragment %4 "main"
406               OpExecutionMode %4 OriginUpperLeft
407               OpSource ESSL 310
408               OpName %4 "main"
409               OpName %8 "i1"
410               OpName %10 "i2"
411               OpName %12 "base"
412               OpMemberName %12 0 "a1"
413               OpMemberName %12 1 "a2"
414               OpName %14 "b"
415               OpName %18 "level_1"
416               OpMemberName %18 0 "b1"
417               OpMemberName %18 1 "b2"
418               OpMemberName %18 2 "b3"
419               OpName %20 "l1"
420               OpName %25 "level_2"
421               OpMemberName %25 0 "c1"
422               OpMemberName %25 1 "c2"
423               OpName %27 "l2"
424          %2 = OpTypeVoid
425          %3 = OpTypeFunction %2
426          %6 = OpTypeInt 32 1
427          %7 = OpTypePointer Function %6
428          %9 = OpConstant %6 1
429         %11 = OpConstant %6 2
430         %12 = OpTypeStruct %6 %6
431         %13 = OpTypePointer Function %12
432         %18 = OpTypeStruct %12 %12 %12
433         %19 = OpTypePointer Function %18
434         %25 = OpTypeStruct %18 %18
435         %26 = OpTypePointer Function %25
436         %31 = OpTypeBool
437         %32 = OpConstantTrue %31
438          %4 = OpFunction %2 None %3
439          %5 = OpLabel
440          %8 = OpVariable %7 Function
441         %10 = OpVariable %7 Function
442         %14 = OpVariable %13 Function
443         %20 = OpVariable %19 Function
444         %27 = OpVariable %26 Function
445               OpStore %8 %9
446               OpStore %10 %11
447         %15 = OpLoad %6 %8
448         %16 = OpLoad %6 %10
449         %17 = OpCompositeConstruct %12 %15 %16
450               OpStore %14 %17
451         %21 = OpLoad %12 %14
452         %22 = OpLoad %12 %14
453         %23 = OpLoad %12 %14
454         %24 = OpCompositeConstruct %18 %21 %22 %23
455               OpStore %20 %24
456         %28 = OpLoad %18 %20
457         %29 = OpLoad %18 %20
458         %30 = OpCompositeConstruct %25 %28 %29
459               OpStore %27 %30
460               OpSelectionMerge %34 None
461               OpBranchConditional %32 %33 %34
462         %33 = OpLabel
463               OpBranch %34
464         %34 = OpLabel
465               OpReturn
466               OpFunctionEnd
467    )";
468
469  const auto env = SPV_ENV_UNIVERSAL_1_4;
470  const auto consumer = nullptr;
471  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
472  spvtools::ValidatorOptions validator_options;
473  ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
474                                               kConsoleMessageConsumer));
475  TransformationContext transformation_context(
476      MakeUnique<FactManager>(context.get()), validator_options);
477  // Add fact that the object is irrelevant.
478  transformation_context.GetFactManager()->AddFactIdIsIrrelevant(11);
479
480  auto transformation_good_1 = TransformationCompositeInsert(
481      MakeInstructionDescriptor(30, spv::Op::OpStore, 0), 50, 30, 11,
482      {1, 0, 0});
483  ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(),
484                                                 transformation_context));
485  ApplyAndCheckFreshIds(transformation_good_1, context.get(),
486                        &transformation_context);
487  ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
488                                               kConsoleMessageConsumer));
489
490  // Since %30 and %50 are not irrelevant, they should be synonymous at all
491  // indices unaffected by the insertion.
492  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
493      MakeDataDescriptor(30, {0}), MakeDataDescriptor(50, {0})));
494  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
495      MakeDataDescriptor(30, {1, 1}), MakeDataDescriptor(50, {1, 1})));
496  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
497      MakeDataDescriptor(30, {1, 2}), MakeDataDescriptor(50, {1, 2})));
498  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
499      MakeDataDescriptor(30, {1, 0, 1}), MakeDataDescriptor(50, {1, 0, 1})));
500  // Since %11 is irrelevant it should not be synonymous with the component into
501  // which it has been inserted.
502  ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
503      MakeDataDescriptor(50, {1, 0, 0}), MakeDataDescriptor(11, {})));
504}
505
506TEST(TransformationCompositeInsertTest, ApplicableCreatedSynonyms) {
507  // This test handles cases where neither |composite| nor |object| is
508  // irrelevant. The transformation should create synonyms.
509  // The member composite has a different number of elements than the parent
510  // composite.
511
512  std::string shader = R"(
513               OpCapability Shader
514          %1 = OpExtInstImport "GLSL.std.450"
515               OpMemoryModel Logical GLSL450
516               OpEntryPoint Fragment %4 "main"
517               OpExecutionMode %4 OriginUpperLeft
518               OpSource ESSL 310
519               OpName %4 "main"
520               OpName %8 "i1"
521               OpName %10 "i2"
522               OpName %12 "base"
523               OpMemberName %12 0 "a1"
524               OpMemberName %12 1 "a2"
525               OpName %14 "b"
526               OpName %18 "level_1"
527               OpMemberName %18 0 "b1"
528               OpMemberName %18 1 "b2"
529               OpMemberName %18 2 "b3"
530               OpName %20 "l1"
531               OpName %25 "level_2"
532               OpMemberName %25 0 "c1"
533               OpMemberName %25 1 "c2"
534               OpName %27 "l2"
535          %2 = OpTypeVoid
536          %3 = OpTypeFunction %2
537          %6 = OpTypeInt 32 1
538          %7 = OpTypePointer Function %6
539          %9 = OpConstant %6 1
540         %11 = OpConstant %6 2
541         %12 = OpTypeStruct %6 %6
542         %13 = OpTypePointer Function %12
543         %18 = OpTypeStruct %12 %12 %12
544         %19 = OpTypePointer Function %18
545         %25 = OpTypeStruct %18 %18
546         %26 = OpTypePointer Function %25
547         %31 = OpTypeBool
548         %32 = OpConstantTrue %31
549          %4 = OpFunction %2 None %3
550          %5 = OpLabel
551          %8 = OpVariable %7 Function
552         %10 = OpVariable %7 Function
553         %14 = OpVariable %13 Function
554         %20 = OpVariable %19 Function
555         %27 = OpVariable %26 Function
556               OpStore %8 %9
557               OpStore %10 %11
558         %15 = OpLoad %6 %8
559         %16 = OpLoad %6 %10
560         %17 = OpCompositeConstruct %12 %15 %16
561               OpStore %14 %17
562         %21 = OpLoad %12 %14
563         %22 = OpLoad %12 %14
564         %23 = OpLoad %12 %14
565         %24 = OpCompositeConstruct %18 %21 %22 %23
566               OpStore %20 %24
567         %28 = OpLoad %18 %20
568         %29 = OpLoad %18 %20
569         %30 = OpCompositeConstruct %25 %28 %29
570               OpStore %27 %30
571               OpSelectionMerge %34 None
572               OpBranchConditional %32 %33 %34
573         %33 = OpLabel
574               OpBranch %34
575         %34 = OpLabel
576               OpReturn
577               OpFunctionEnd
578    )";
579
580  const auto env = SPV_ENV_UNIVERSAL_1_4;
581  const auto consumer = nullptr;
582  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
583  spvtools::ValidatorOptions validator_options;
584  ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
585                                               kConsoleMessageConsumer));
586  TransformationContext transformation_context(
587      MakeUnique<FactManager>(context.get()), validator_options);
588  auto transformation_good_1 = TransformationCompositeInsert(
589      MakeInstructionDescriptor(30, spv::Op::OpStore, 0), 50, 30, 11,
590      {1, 0, 0});
591  ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(),
592                                                 transformation_context));
593  ApplyAndCheckFreshIds(transformation_good_1, context.get(),
594                        &transformation_context);
595  ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
596                                               kConsoleMessageConsumer));
597
598  // These synonyms should have been added.
599  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
600      MakeDataDescriptor(30, {0}), MakeDataDescriptor(50, {0})));
601  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
602      MakeDataDescriptor(30, {1, 1}), MakeDataDescriptor(50, {1, 1})));
603  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
604      MakeDataDescriptor(30, {1, 2}), MakeDataDescriptor(50, {1, 2})));
605  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
606      MakeDataDescriptor(30, {1, 0, 1}), MakeDataDescriptor(50, {1, 0, 1})));
607  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
608      MakeDataDescriptor(50, {1, 0, 0}), MakeDataDescriptor(11, {})));
609
610  // These synonyms should not have been added.
611  ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
612      MakeDataDescriptor(30, {1}), MakeDataDescriptor(50, {1})));
613  ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
614      MakeDataDescriptor(30, {1, 0}), MakeDataDescriptor(50, {1, 0})));
615  ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
616      MakeDataDescriptor(30, {1, 0, 0}), MakeDataDescriptor(50, {1, 0, 0})));
617
618  auto transformation_good_2 = TransformationCompositeInsert(
619      MakeInstructionDescriptor(50, spv::Op::OpStore, 0), 51, 50, 11,
620      {0, 1, 1});
621  ASSERT_TRUE(transformation_good_2.IsApplicable(context.get(),
622                                                 transformation_context));
623  ApplyAndCheckFreshIds(transformation_good_2, context.get(),
624                        &transformation_context);
625  ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
626                                               kConsoleMessageConsumer));
627
628  // These synonyms should have been added.
629  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
630      MakeDataDescriptor(50, {1}), MakeDataDescriptor(51, {1})));
631  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
632      MakeDataDescriptor(50, {0, 0}), MakeDataDescriptor(51, {0, 0})));
633  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
634      MakeDataDescriptor(50, {0, 2}), MakeDataDescriptor(51, {0, 2})));
635  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
636      MakeDataDescriptor(50, {0, 1, 0}), MakeDataDescriptor(51, {0, 1, 0})));
637  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
638      MakeDataDescriptor(51, {0, 1, 1}), MakeDataDescriptor(11, {})));
639
640  // These synonyms should not have been added.
641  ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
642      MakeDataDescriptor(50, {0}), MakeDataDescriptor(51, {0})));
643  ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
644      MakeDataDescriptor(50, {0, 1}), MakeDataDescriptor(51, {0, 1})));
645  ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
646      MakeDataDescriptor(50, {0, 1, 1}), MakeDataDescriptor(51, {0, 1, 1})));
647
648  std::string after_transformations = R"(
649               OpCapability Shader
650          %1 = OpExtInstImport "GLSL.std.450"
651               OpMemoryModel Logical GLSL450
652               OpEntryPoint Fragment %4 "main"
653               OpExecutionMode %4 OriginUpperLeft
654               OpSource ESSL 310
655               OpName %4 "main"
656               OpName %8 "i1"
657               OpName %10 "i2"
658               OpName %12 "base"
659               OpMemberName %12 0 "a1"
660               OpMemberName %12 1 "a2"
661               OpName %14 "b"
662               OpName %18 "level_1"
663               OpMemberName %18 0 "b1"
664               OpMemberName %18 1 "b2"
665               OpMemberName %18 2 "b3"
666               OpName %20 "l1"
667               OpName %25 "level_2"
668               OpMemberName %25 0 "c1"
669               OpMemberName %25 1 "c2"
670               OpName %27 "l2"
671          %2 = OpTypeVoid
672          %3 = OpTypeFunction %2
673          %6 = OpTypeInt 32 1
674          %7 = OpTypePointer Function %6
675          %9 = OpConstant %6 1
676         %11 = OpConstant %6 2
677         %12 = OpTypeStruct %6 %6
678         %13 = OpTypePointer Function %12
679         %18 = OpTypeStruct %12 %12 %12
680         %19 = OpTypePointer Function %18
681         %25 = OpTypeStruct %18 %18
682         %26 = OpTypePointer Function %25
683         %31 = OpTypeBool
684         %32 = OpConstantTrue %31
685          %4 = OpFunction %2 None %3
686          %5 = OpLabel
687          %8 = OpVariable %7 Function
688         %10 = OpVariable %7 Function
689         %14 = OpVariable %13 Function
690         %20 = OpVariable %19 Function
691         %27 = OpVariable %26 Function
692               OpStore %8 %9
693               OpStore %10 %11
694         %15 = OpLoad %6 %8
695         %16 = OpLoad %6 %10
696         %17 = OpCompositeConstruct %12 %15 %16
697               OpStore %14 %17
698         %21 = OpLoad %12 %14
699         %22 = OpLoad %12 %14
700         %23 = OpLoad %12 %14
701         %24 = OpCompositeConstruct %18 %21 %22 %23
702               OpStore %20 %24
703         %28 = OpLoad %18 %20
704         %29 = OpLoad %18 %20
705         %30 = OpCompositeConstruct %25 %28 %29
706         %50 = OpCompositeInsert %25 %11 %30 1 0 0
707         %51 = OpCompositeInsert %25 %11 %50 0 1 1
708               OpStore %27 %30
709               OpSelectionMerge %34 None
710               OpBranchConditional %32 %33 %34
711         %33 = OpLabel
712               OpBranch %34
713         %34 = OpLabel
714               OpReturn
715               OpFunctionEnd
716    )";
717
718  ASSERT_TRUE(IsEqual(env, after_transformations, context.get()));
719}
720
721TEST(TransformationCompositeInsertTest, IdNotAvailableScenarios) {
722  // This test handles cases where either the composite or the object is not
723  // available before the |instruction_to_insert_before|.
724
725  std::string shader = 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               OpName %8 "i1"
734               OpName %10 "i2"
735               OpName %12 "base"
736               OpMemberName %12 0 "a1"
737               OpMemberName %12 1 "a2"
738               OpName %14 "b1"
739               OpName %18 "b2"
740               OpName %22 "lvl1"
741               OpMemberName %22 0 "b1"
742               OpMemberName %22 1 "b2"
743               OpName %24 "l1"
744               OpName %28 "i3"
745          %2 = OpTypeVoid
746          %3 = OpTypeFunction %2
747          %6 = OpTypeInt 32 1
748          %7 = OpTypePointer Function %6
749          %9 = OpConstant %6 1
750         %11 = OpConstant %6 2
751         %12 = OpTypeStruct %6 %6
752         %13 = OpTypePointer Function %12
753         %22 = OpTypeStruct %12 %12
754         %23 = OpTypePointer Function %22
755          %4 = OpFunction %2 None %3
756          %5 = OpLabel
757          %8 = OpVariable %7 Function
758         %10 = OpVariable %7 Function
759         %14 = OpVariable %13 Function
760         %18 = OpVariable %13 Function
761         %24 = OpVariable %23 Function
762         %28 = OpVariable %7 Function
763               OpStore %8 %9
764               OpStore %10 %11
765         %15 = OpLoad %6 %8
766         %16 = OpLoad %6 %10
767         %17 = OpCompositeConstruct %12 %15 %16
768               OpStore %14 %17
769         %19 = OpLoad %6 %10
770         %20 = OpLoad %6 %8
771         %21 = OpCompositeConstruct %12 %19 %20
772               OpStore %18 %21
773         %25 = OpLoad %12 %14
774         %26 = OpLoad %12 %18
775         %27 = OpCompositeConstruct %22 %25 %26
776               OpStore %24 %27
777         %29 = OpLoad %6 %8
778         %30 = OpLoad %6 %10
779         %31 = OpIMul %6 %29 %30
780               OpStore %28 %31
781         %60 = OpCompositeConstruct %12 %20 %19
782         %61 = OpCompositeConstruct %22 %26 %25
783               OpReturn
784               OpFunctionEnd
785    )";
786
787  const auto env = SPV_ENV_UNIVERSAL_1_4;
788  const auto consumer = nullptr;
789  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
790  spvtools::ValidatorOptions validator_options;
791  ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
792                                               kConsoleMessageConsumer));
793  TransformationContext transformation_context(
794      MakeUnique<FactManager>(context.get()), validator_options);
795  // Bad: The object with |object_id| is not available at
796  // |instruction_to_insert_before|.
797  auto transformation_bad_1 = TransformationCompositeInsert(
798      MakeInstructionDescriptor(31, spv::Op::OpIMul, 0), 50, 27, 60, {1});
799  ASSERT_FALSE(
800      transformation_bad_1.IsApplicable(context.get(), transformation_context));
801
802  // Bad: The composite with |composite_id| is not available at
803  // |instruction_to_insert_before|.
804  auto transformation_bad_2 = TransformationCompositeInsert(
805      MakeInstructionDescriptor(31, spv::Op::OpIMul, 0), 50, 61, 21, {1});
806  ASSERT_FALSE(
807      transformation_bad_2.IsApplicable(context.get(), transformation_context));
808
809  // Bad: The |instruction_to_insert_before| is the composite itself and is
810  // available.
811  auto transformation_bad_3 = TransformationCompositeInsert(
812      MakeInstructionDescriptor(61, spv::Op::OpCompositeConstruct, 0), 50, 61,
813      21, {1});
814  ASSERT_FALSE(
815      transformation_bad_3.IsApplicable(context.get(), transformation_context));
816
817  // Bad: The |instruction_to_insert_before| is the object itself and is not
818  // available.
819  auto transformation_bad_4 = TransformationCompositeInsert(
820      MakeInstructionDescriptor(60, spv::Op::OpCompositeConstruct, 0), 50, 27,
821      60, {1});
822  ASSERT_FALSE(
823      transformation_bad_4.IsApplicable(context.get(), transformation_context));
824}
825
826TEST(TransformationCompositeInsertTest, CompositeInsertionWithIrrelevantIds) {
827  // This checks that we do *not* get data synonym facts when we do composite
828  // insertion using irrelevant ids or in dead blocks.
829
830  std::string shader = R"(
831               OpCapability Shader
832          %1 = OpExtInstImport "GLSL.std.450"
833               OpMemoryModel Logical GLSL450
834               OpEntryPoint Fragment %12 "main"
835               OpExecutionMode %12 OriginUpperLeft
836               OpSource ESSL 310
837          %2 = OpTypeVoid
838          %3 = OpTypeFunction %2
839          %6 = OpTypeInt 32 1
840          %7 = OpTypeVector %6 2
841          %8 = OpConstant %6 0
842          %9 = OpConstantComposite %7 %8 %8
843         %10 = OpTypeBool
844         %11 = OpConstantFalse %10
845         %16 = OpConstant %6 0
846         %17 = OpConstant %6 1
847         %18 = OpConstantComposite %7 %8 %8
848         %12 = OpFunction %2 None %3
849         %13 = OpLabel
850               OpSelectionMerge %15 None
851               OpBranchConditional %11 %14 %15
852         %14 = OpLabel
853               OpBranch %15
854         %15 = OpLabel
855               OpReturn
856               OpFunctionEnd
857  )";
858
859  const auto env = SPV_ENV_UNIVERSAL_1_3;
860  const auto consumer = nullptr;
861  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
862  spvtools::ValidatorOptions validator_options;
863  ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
864                                               kConsoleMessageConsumer));
865  TransformationContext transformation_context(
866      MakeUnique<FactManager>(context.get()), validator_options);
867
868  transformation_context.GetFactManager()->AddFactBlockIsDead(14);
869  transformation_context.GetFactManager()->AddFactIdIsIrrelevant(16);
870  transformation_context.GetFactManager()->AddFactIdIsIrrelevant(18);
871
872  // Leads to synonyms - nothing is irrelevant.
873  auto transformation1 = TransformationCompositeInsert(
874      MakeInstructionDescriptor(13, spv::Op::OpSelectionMerge, 0), 100, 9, 17,
875      {0});
876  ASSERT_TRUE(
877      transformation1.IsApplicable(context.get(), transformation_context));
878  ApplyAndCheckFreshIds(transformation1, context.get(),
879                        &transformation_context);
880  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
881      MakeDataDescriptor(100, {0}), MakeDataDescriptor(17, {})));
882  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
883      MakeDataDescriptor(100, {1}), MakeDataDescriptor(9, {1})));
884
885  // Because %16 is irrelevant, we don't get a synonym with the component to
886  // which it has been inserted (but we do for the other component).
887  auto transformation2 = TransformationCompositeInsert(
888      MakeInstructionDescriptor(13, spv::Op::OpSelectionMerge, 0), 101, 9, 16,
889      {0});
890  ASSERT_TRUE(
891      transformation2.IsApplicable(context.get(), transformation_context));
892  ApplyAndCheckFreshIds(transformation2, context.get(),
893                        &transformation_context);
894  ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
895      MakeDataDescriptor(101, {0}), MakeDataDescriptor(16, {})));
896  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
897      MakeDataDescriptor(101, {1}), MakeDataDescriptor(9, {1})));
898
899  // Because %18 is irrelevant we only get a synonym for the component into
900  // which insertion has taken place.
901  auto transformation3 = TransformationCompositeInsert(
902      MakeInstructionDescriptor(13, spv::Op::OpSelectionMerge, 0), 102, 18, 17,
903      {0});
904  ASSERT_TRUE(
905      transformation3.IsApplicable(context.get(), transformation_context));
906  ApplyAndCheckFreshIds(transformation3, context.get(),
907                        &transformation_context);
908  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
909      MakeDataDescriptor(102, {0}), MakeDataDescriptor(17, {})));
910  ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
911      MakeDataDescriptor(102, {1}), MakeDataDescriptor(18, {1})));
912
913  // Does not lead to synonyms as block %14 is dead.
914  auto transformation4 = TransformationCompositeInsert(
915      MakeInstructionDescriptor(14, spv::Op::OpBranch, 0), 103, 9, 17, {0});
916  ASSERT_TRUE(
917      transformation4.IsApplicable(context.get(), transformation_context));
918  ApplyAndCheckFreshIds(transformation4, context.get(),
919                        &transformation_context);
920  ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
921      MakeDataDescriptor(103, {0}), MakeDataDescriptor(17, {})));
922  ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
923      MakeDataDescriptor(103, {1}), MakeDataDescriptor(9, {1})));
924
925  ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
926                                               kConsoleMessageConsumer));
927}
928
929}  // namespace
930}  // namespace fuzz
931}  // namespace spvtools
932