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