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