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