1 // Copyright (c) 2020 Vasyl Teliman
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "source/fuzz/transformation_add_synonym.h"
16
17 #include "gtest/gtest.h"
18 #include "source/fuzz/fuzzer_util.h"
19 #include "source/fuzz/instruction_descriptor.h"
20 #include "test/fuzz/fuzz_test_util.h"
21
22 namespace spvtools {
23 namespace fuzz {
24 namespace {
25
TEST(TransformationAddSynonymTest,NotApplicable)26 TEST(TransformationAddSynonymTest, NotApplicable) {
27 std::string shader = R"(
28 OpCapability Shader
29 %1 = OpExtInstImport "GLSL.std.450"
30 OpMemoryModel Logical GLSL450
31 OpEntryPoint Fragment %4 "main"
32 OpExecutionMode %4 OriginUpperLeft
33 OpSource ESSL 310
34 OpDecorate %8 RelaxedPrecision
35 OpDecorate %22 RelaxedPrecision
36 %2 = OpTypeVoid
37 %3 = OpTypeFunction %2
38 %6 = OpTypeInt 32 1
39 %7 = OpTypePointer Function %6
40 %9 = OpConstant %6 3
41 %10 = OpTypeFloat 32
42 %11 = OpTypePointer Function %10
43 %13 = OpConstant %10 4.5
44 %14 = OpTypeVector %10 2
45 %15 = OpTypePointer Function %14
46 %17 = OpConstant %10 3
47 %18 = OpConstant %10 4
48 %19 = OpConstantComposite %14 %17 %18
49 %20 = OpTypeVector %6 2
50 %21 = OpTypePointer Function %20
51 %23 = OpConstant %6 4
52 %24 = OpConstantComposite %20 %9 %23
53 %26 = OpConstantNull %6
54 %4 = OpFunction %2 None %3
55 %5 = OpLabel
56 %8 = OpVariable %7 Function
57 %12 = OpVariable %11 Function
58 %16 = OpVariable %15 Function
59 %22 = OpVariable %21 Function
60 OpStore %8 %9
61 OpStore %12 %13
62 OpStore %16 %19
63 OpStore %22 %24
64 %25 = OpUndef %6
65 %27 = OpLoad %6 %8
66 OpReturn
67 OpFunctionEnd
68 )";
69
70 const auto env = SPV_ENV_UNIVERSAL_1_3;
71 const auto consumer = nullptr;
72 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
73 spvtools::ValidatorOptions validator_options;
74 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
75 kConsoleMessageConsumer));
76 TransformationContext transformation_context(
77 MakeUnique<FactManager>(context.get()), validator_options);
78 transformation_context.GetFactManager()->AddFactIdIsIrrelevant(24);
79
80 auto insert_before = MakeInstructionDescriptor(22, SpvOpReturn, 0);
81
82 #ifndef NDEBUG
83 ASSERT_DEATH(
84 TransformationAddSynonym(
85 9, static_cast<protobufs::TransformationAddSynonym::SynonymType>(-1),
86 40, insert_before)
87 .IsApplicable(context.get(), transformation_context),
88 "Synonym type is invalid");
89 #endif
90
91 // These tests should succeed regardless of the synonym type.
92 for (int i = 0;
93 i < protobufs::TransformationAddSynonym::SynonymType_descriptor()
94 ->value_count();
95 ++i) {
96 const auto* synonym_value =
97 protobufs::TransformationAddSynonym::SynonymType_descriptor()->value(i);
98 ASSERT_TRUE(protobufs::TransformationAddSynonym::SynonymType_IsValid(
99 synonym_value->number()));
100 auto synonym_type =
101 static_cast<protobufs::TransformationAddSynonym::SynonymType>(
102 synonym_value->number());
103
104 // |synonym_fresh_id| is not fresh.
105 ASSERT_FALSE(TransformationAddSynonym(9, synonym_type, 9, insert_before)
106 .IsApplicable(context.get(), transformation_context));
107
108 // |result_id| is invalid.
109 ASSERT_FALSE(TransformationAddSynonym(40, synonym_type, 40, insert_before)
110 .IsApplicable(context.get(), transformation_context));
111
112 // Instruction with |result_id| has no type id.
113 ASSERT_FALSE(TransformationAddSynonym(5, synonym_type, 40, insert_before)
114 .IsApplicable(context.get(), transformation_context));
115
116 // Instruction with |result_id| is an OpUndef.
117 ASSERT_FALSE(TransformationAddSynonym(25, synonym_type, 40, insert_before)
118 .IsApplicable(context.get(), transformation_context));
119
120 // Instruction with |result_id| is an OpConstantNull.
121 ASSERT_FALSE(TransformationAddSynonym(26, synonym_type, 40, insert_before)
122 .IsApplicable(context.get(), transformation_context));
123
124 // |result_id| is irrelevant.
125 ASSERT_FALSE(TransformationAddSynonym(24, synonym_type, 40, insert_before)
126 .IsApplicable(context.get(), transformation_context));
127
128 // |insert_before| is invalid.
129 ASSERT_FALSE(
130 TransformationAddSynonym(9, synonym_type, 40,
131 MakeInstructionDescriptor(25, SpvOpStore, 0))
132 .IsApplicable(context.get(), transformation_context));
133
134 // Can't insert before |insert_before|.
135 ASSERT_FALSE(
136 TransformationAddSynonym(9, synonym_type, 40,
137 MakeInstructionDescriptor(5, SpvOpLabel, 0))
138 .IsApplicable(context.get(), transformation_context));
139 ASSERT_FALSE(TransformationAddSynonym(
140 9, synonym_type, 40,
141 MakeInstructionDescriptor(22, SpvOpVariable, 0))
142 .IsApplicable(context.get(), transformation_context));
143 ASSERT_FALSE(TransformationAddSynonym(
144 9, synonym_type, 40,
145 MakeInstructionDescriptor(25, SpvOpFunctionEnd, 0))
146 .IsApplicable(context.get(), transformation_context));
147
148 // Domination rules are not satisfied.
149 ASSERT_FALSE(
150 TransformationAddSynonym(27, synonym_type, 40,
151 MakeInstructionDescriptor(27, SpvOpLoad, 0))
152 .IsApplicable(context.get(), transformation_context));
153 ASSERT_FALSE(
154 TransformationAddSynonym(27, synonym_type, 40,
155 MakeInstructionDescriptor(22, SpvOpStore, 1))
156 .IsApplicable(context.get(), transformation_context));
157 }
158 }
159
TEST(TransformationAddSynonymTest,AddZeroSubZeroMulOne)160 TEST(TransformationAddSynonymTest, AddZeroSubZeroMulOne) {
161 std::string shader = R"(
162 OpCapability Shader
163 %1 = OpExtInstImport "GLSL.std.450"
164 OpMemoryModel Logical GLSL450
165 OpEntryPoint Fragment %4 "main"
166 OpExecutionMode %4 OriginUpperLeft
167 OpSource ESSL 310
168 %2 = OpTypeVoid
169 %3 = OpTypeFunction %2
170 %6 = OpTypeInt 32 1
171 %7 = OpConstant %6 0
172 %8 = OpConstant %6 1
173 %9 = OpConstant %6 34
174 %10 = OpTypeInt 32 0
175 %13 = OpConstant %10 34
176 %14 = OpTypeFloat 32
177 %15 = OpConstant %14 0
178 %16 = OpConstant %14 1
179 %17 = OpConstant %14 34
180 %18 = OpTypeVector %14 2
181 %19 = OpConstantComposite %18 %15 %15
182 %20 = OpConstantComposite %18 %16 %16
183 %21 = OpConstant %14 3
184 %22 = OpConstant %14 4
185 %23 = OpConstantComposite %18 %21 %22
186 %24 = OpTypeVector %6 2
187 %25 = OpConstantComposite %24 %7 %7
188 %26 = OpConstantComposite %24 %8 %8
189 %27 = OpConstant %6 3
190 %28 = OpConstant %6 4
191 %29 = OpConstantComposite %24 %27 %28
192 %30 = OpTypeVector %10 2
193 %33 = OpConstant %10 3
194 %34 = OpConstant %10 4
195 %35 = OpConstantComposite %30 %33 %34
196 %36 = OpTypeBool
197 %37 = OpTypeVector %36 2
198 %38 = OpConstantTrue %36
199 %39 = OpConstantComposite %37 %38 %38
200 %4 = OpFunction %2 None %3
201 %5 = OpLabel
202 OpReturn
203 OpFunctionEnd
204 )";
205
206 const auto env = SPV_ENV_UNIVERSAL_1_3;
207 const auto consumer = nullptr;
208 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
209 spvtools::ValidatorOptions validator_options;
210 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
211 kConsoleMessageConsumer));
212 TransformationContext transformation_context(
213 MakeUnique<FactManager>(context.get()), validator_options);
214 auto insert_before = MakeInstructionDescriptor(5, SpvOpReturn, 0);
215
216 uint32_t fresh_id = 50;
217 for (auto synonym_type : {protobufs::TransformationAddSynonym::ADD_ZERO,
218 protobufs::TransformationAddSynonym::SUB_ZERO,
219 protobufs::TransformationAddSynonym::MUL_ONE}) {
220 ASSERT_TRUE(
221 TransformationAddSynonym::IsAdditionalConstantRequired(synonym_type));
222
223 // Can't create a synonym of a scalar or a vector of a wrong (in this case -
224 // boolean) type.
225 ASSERT_FALSE(
226 TransformationAddSynonym(38, synonym_type, fresh_id, insert_before)
227 .IsApplicable(context.get(), transformation_context));
228 ASSERT_FALSE(
229 TransformationAddSynonym(39, synonym_type, fresh_id, insert_before)
230 .IsApplicable(context.get(), transformation_context));
231
232 // Required constant is not present in the module.
233 ASSERT_FALSE(
234 TransformationAddSynonym(13, synonym_type, fresh_id, insert_before)
235 .IsApplicable(context.get(), transformation_context));
236 ASSERT_FALSE(
237 TransformationAddSynonym(35, synonym_type, fresh_id, insert_before)
238 .IsApplicable(context.get(), transformation_context));
239
240 for (auto result_id : {9, 17, 23, 29}) {
241 TransformationAddSynonym transformation(result_id, synonym_type, fresh_id,
242 insert_before);
243 ASSERT_TRUE(
244 transformation.IsApplicable(context.get(), transformation_context));
245 ApplyAndCheckFreshIds(transformation, context.get(),
246 &transformation_context);
247 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
248 MakeDataDescriptor(result_id, {}), MakeDataDescriptor(fresh_id, {})));
249 ++fresh_id;
250 }
251 }
252
253 std::string expected_shader = R"(
254 OpCapability Shader
255 %1 = OpExtInstImport "GLSL.std.450"
256 OpMemoryModel Logical GLSL450
257 OpEntryPoint Fragment %4 "main"
258 OpExecutionMode %4 OriginUpperLeft
259 OpSource ESSL 310
260 %2 = OpTypeVoid
261 %3 = OpTypeFunction %2
262 %6 = OpTypeInt 32 1
263 %7 = OpConstant %6 0
264 %8 = OpConstant %6 1
265 %9 = OpConstant %6 34
266 %10 = OpTypeInt 32 0
267 %13 = OpConstant %10 34
268 %14 = OpTypeFloat 32
269 %15 = OpConstant %14 0
270 %16 = OpConstant %14 1
271 %17 = OpConstant %14 34
272 %18 = OpTypeVector %14 2
273 %19 = OpConstantComposite %18 %15 %15
274 %20 = OpConstantComposite %18 %16 %16
275 %21 = OpConstant %14 3
276 %22 = OpConstant %14 4
277 %23 = OpConstantComposite %18 %21 %22
278 %24 = OpTypeVector %6 2
279 %25 = OpConstantComposite %24 %7 %7
280 %26 = OpConstantComposite %24 %8 %8
281 %27 = OpConstant %6 3
282 %28 = OpConstant %6 4
283 %29 = OpConstantComposite %24 %27 %28
284 %30 = OpTypeVector %10 2
285 %33 = OpConstant %10 3
286 %34 = OpConstant %10 4
287 %35 = OpConstantComposite %30 %33 %34
288 %36 = OpTypeBool
289 %37 = OpTypeVector %36 2
290 %38 = OpConstantTrue %36
291 %39 = OpConstantComposite %37 %38 %38
292 %4 = OpFunction %2 None %3
293 %5 = OpLabel
294 %50 = OpIAdd %6 %9 %7
295 %51 = OpFAdd %14 %17 %15
296 %52 = OpFAdd %18 %23 %19
297 %53 = OpIAdd %24 %29 %25
298 %54 = OpISub %6 %9 %7
299 %55 = OpFSub %14 %17 %15
300 %56 = OpFSub %18 %23 %19
301 %57 = OpISub %24 %29 %25
302 %58 = OpIMul %6 %9 %8
303 %59 = OpFMul %14 %17 %16
304 %60 = OpFMul %18 %23 %20
305 %61 = OpIMul %24 %29 %26
306 OpReturn
307 OpFunctionEnd
308 )";
309
310 ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
311 }
312
TEST(TransformationAddSynonymTest,LogicalAndLogicalOr)313 TEST(TransformationAddSynonymTest, LogicalAndLogicalOr) {
314 std::string shader = R"(
315 OpCapability Shader
316 %1 = OpExtInstImport "GLSL.std.450"
317 OpMemoryModel Logical GLSL450
318 OpEntryPoint Fragment %4 "main"
319 OpExecutionMode %4 OriginUpperLeft
320 OpSource ESSL 310
321 %2 = OpTypeVoid
322 %3 = OpTypeFunction %2
323 %6 = OpTypeBool
324 %7 = OpConstantFalse %6
325 %9 = OpConstantTrue %6
326 %10 = OpTypeVector %6 2
327 %11 = OpConstantComposite %10 %7 %9
328 %12 = OpConstantComposite %10 %7 %7
329 %13 = OpConstantComposite %10 %9 %9
330 %14 = OpTypeFloat 32
331 %17 = OpConstant %14 35
332 %18 = OpTypeVector %14 2
333 %21 = OpConstant %14 3
334 %22 = OpConstant %14 4
335 %23 = OpConstantComposite %18 %21 %22
336 %4 = OpFunction %2 None %3
337 %5 = OpLabel
338 OpReturn
339 OpFunctionEnd
340 )";
341
342 const auto env = SPV_ENV_UNIVERSAL_1_3;
343 const auto consumer = nullptr;
344 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
345 spvtools::ValidatorOptions validator_options;
346 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
347 kConsoleMessageConsumer));
348 TransformationContext transformation_context(
349 MakeUnique<FactManager>(context.get()), validator_options);
350 auto insert_before = MakeInstructionDescriptor(5, SpvOpReturn, 0);
351
352 uint32_t fresh_id = 50;
353 for (auto synonym_type : {protobufs::TransformationAddSynonym::LOGICAL_AND,
354 protobufs::TransformationAddSynonym::LOGICAL_OR}) {
355 ASSERT_TRUE(
356 TransformationAddSynonym::IsAdditionalConstantRequired(synonym_type));
357
358 // Can't create a synonym of a scalar or a vector of a wrong (in this case -
359 // float) type.
360 ASSERT_FALSE(
361 TransformationAddSynonym(17, synonym_type, fresh_id, insert_before)
362 .IsApplicable(context.get(), transformation_context));
363 ASSERT_FALSE(
364 TransformationAddSynonym(23, synonym_type, fresh_id, insert_before)
365 .IsApplicable(context.get(), transformation_context));
366
367 for (auto result_id : {9, 11}) {
368 TransformationAddSynonym transformation(result_id, synonym_type, fresh_id,
369 insert_before);
370 ASSERT_TRUE(
371 transformation.IsApplicable(context.get(), transformation_context));
372 ApplyAndCheckFreshIds(transformation, context.get(),
373 &transformation_context);
374 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
375 MakeDataDescriptor(result_id, {}), MakeDataDescriptor(fresh_id, {})));
376 ++fresh_id;
377 }
378 }
379
380 std::string expected_shader = R"(
381 OpCapability Shader
382 %1 = OpExtInstImport "GLSL.std.450"
383 OpMemoryModel Logical GLSL450
384 OpEntryPoint Fragment %4 "main"
385 OpExecutionMode %4 OriginUpperLeft
386 OpSource ESSL 310
387 %2 = OpTypeVoid
388 %3 = OpTypeFunction %2
389 %6 = OpTypeBool
390 %7 = OpConstantFalse %6
391 %9 = OpConstantTrue %6
392 %10 = OpTypeVector %6 2
393 %11 = OpConstantComposite %10 %7 %9
394 %12 = OpConstantComposite %10 %7 %7
395 %13 = OpConstantComposite %10 %9 %9
396 %14 = OpTypeFloat 32
397 %17 = OpConstant %14 35
398 %18 = OpTypeVector %14 2
399 %21 = OpConstant %14 3
400 %22 = OpConstant %14 4
401 %23 = OpConstantComposite %18 %21 %22
402 %4 = OpFunction %2 None %3
403 %5 = OpLabel
404 %50 = OpLogicalAnd %6 %9 %9
405 %51 = OpLogicalAnd %10 %11 %13
406 %52 = OpLogicalOr %6 %9 %7
407 %53 = OpLogicalOr %10 %11 %12
408 OpReturn
409 OpFunctionEnd
410 )";
411
412 ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
413 }
414
TEST(TransformationAddSynonymTest,LogicalAndConstantIsNotPresent)415 TEST(TransformationAddSynonymTest, LogicalAndConstantIsNotPresent) {
416 std::string shader = R"(
417 OpCapability Shader
418 %1 = OpExtInstImport "GLSL.std.450"
419 OpMemoryModel Logical GLSL450
420 OpEntryPoint Fragment %4 "main"
421 OpExecutionMode %4 OriginUpperLeft
422 OpSource ESSL 310
423 %2 = OpTypeVoid
424 %3 = OpTypeFunction %2
425 %6 = OpTypeBool
426 %7 = OpConstantFalse %6
427 %10 = OpTypeVector %6 2
428 %12 = OpConstantComposite %10 %7 %7
429 %4 = OpFunction %2 None %3
430 %5 = OpLabel
431 OpReturn
432 OpFunctionEnd
433 )";
434
435 const auto env = SPV_ENV_UNIVERSAL_1_3;
436 const auto consumer = nullptr;
437 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
438 spvtools::ValidatorOptions validator_options;
439 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
440 kConsoleMessageConsumer));
441 TransformationContext transformation_context(
442 MakeUnique<FactManager>(context.get()), validator_options);
443 auto insert_before = MakeInstructionDescriptor(5, SpvOpReturn, 0);
444 const auto synonym_type = protobufs::TransformationAddSynonym::LOGICAL_AND;
445
446 // Required constant is not present in the module.
447 ASSERT_FALSE(TransformationAddSynonym(7, synonym_type, 50, insert_before)
448 .IsApplicable(context.get(), transformation_context));
449 ASSERT_FALSE(TransformationAddSynonym(12, synonym_type, 50, insert_before)
450 .IsApplicable(context.get(), transformation_context));
451 }
452
TEST(TransformationAddSynonymTest,LogicalOrConstantIsNotPresent)453 TEST(TransformationAddSynonymTest, LogicalOrConstantIsNotPresent) {
454 std::string shader = R"(
455 OpCapability Shader
456 %1 = OpExtInstImport "GLSL.std.450"
457 OpMemoryModel Logical GLSL450
458 OpEntryPoint Fragment %4 "main"
459 OpExecutionMode %4 OriginUpperLeft
460 OpSource ESSL 310
461 %2 = OpTypeVoid
462 %3 = OpTypeFunction %2
463 %6 = OpTypeBool
464 %7 = OpConstantTrue %6
465 %10 = OpTypeVector %6 2
466 %12 = OpConstantComposite %10 %7 %7
467 %4 = OpFunction %2 None %3
468 %5 = OpLabel
469 OpReturn
470 OpFunctionEnd
471 )";
472
473 const auto env = SPV_ENV_UNIVERSAL_1_3;
474 const auto consumer = nullptr;
475 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
476 spvtools::ValidatorOptions validator_options;
477 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
478 kConsoleMessageConsumer));
479 TransformationContext transformation_context(
480 MakeUnique<FactManager>(context.get()), validator_options);
481 auto insert_before = MakeInstructionDescriptor(5, SpvOpReturn, 0);
482 const auto synonym_type = protobufs::TransformationAddSynonym::LOGICAL_OR;
483
484 // Required constant is not present in the module.
485 ASSERT_FALSE(TransformationAddSynonym(7, synonym_type, 50, insert_before)
486 .IsApplicable(context.get(), transformation_context));
487 ASSERT_FALSE(TransformationAddSynonym(12, synonym_type, 50, insert_before)
488 .IsApplicable(context.get(), transformation_context));
489 }
490
TEST(TransformationAddSynonymTest,CopyObject)491 TEST(TransformationAddSynonymTest, CopyObject) {
492 std::string shader = R"(
493 OpCapability Shader
494 %1 = OpExtInstImport "GLSL.std.450"
495 OpMemoryModel Logical GLSL450
496 OpEntryPoint Fragment %4 "main"
497 OpExecutionMode %4 OriginUpperLeft
498 OpSource ESSL 310
499 OpDecorate %8 RelaxedPrecision
500 %2 = OpTypeVoid
501 %3 = OpTypeFunction %2
502 %6 = OpTypeInt 32 1
503 %7 = OpTypePointer Function %6
504 %9 = OpConstant %6 4
505 %10 = OpTypeFloat 32
506 %11 = OpTypePointer Function %10
507 %13 = OpConstant %10 4
508 %14 = OpTypeVector %10 2
509 %15 = OpTypePointer Function %14
510 %17 = OpConstant %10 3.4000001
511 %18 = OpConstantComposite %14 %17 %17
512 %19 = OpTypeBool
513 %20 = OpTypeStruct %19
514 %21 = OpTypePointer Function %20
515 %23 = OpConstantTrue %19
516 %24 = OpConstantComposite %20 %23
517 %4 = OpFunction %2 None %3
518 %5 = OpLabel
519 %8 = OpVariable %7 Function
520 %12 = OpVariable %11 Function
521 %16 = OpVariable %15 Function
522 %22 = OpVariable %21 Function
523 OpStore %8 %9
524 OpStore %12 %13
525 OpStore %16 %18
526 OpStore %22 %24
527 OpReturn
528 OpFunctionEnd
529 )";
530
531 const auto env = SPV_ENV_UNIVERSAL_1_3;
532 const auto consumer = nullptr;
533 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
534 spvtools::ValidatorOptions validator_options;
535 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
536 kConsoleMessageConsumer));
537 TransformationContext transformation_context(
538 MakeUnique<FactManager>(context.get()), validator_options);
539 auto insert_before = MakeInstructionDescriptor(5, SpvOpReturn, 0);
540 const auto synonym_type = protobufs::TransformationAddSynonym::COPY_OBJECT;
541
542 ASSERT_FALSE(
543 TransformationAddSynonym::IsAdditionalConstantRequired(synonym_type));
544
545 uint32_t fresh_id = 50;
546 for (auto result_id : {9, 13, 17, 18, 23, 24, 22}) {
547 TransformationAddSynonym transformation(result_id, synonym_type, fresh_id,
548 insert_before);
549 ASSERT_TRUE(
550 transformation.IsApplicable(context.get(), transformation_context));
551 ApplyAndCheckFreshIds(transformation, context.get(),
552 &transformation_context);
553 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
554 MakeDataDescriptor(result_id, {}), MakeDataDescriptor(fresh_id, {})));
555 ++fresh_id;
556 }
557
558 std::string expected_shader = R"(
559 OpCapability Shader
560 %1 = OpExtInstImport "GLSL.std.450"
561 OpMemoryModel Logical GLSL450
562 OpEntryPoint Fragment %4 "main"
563 OpExecutionMode %4 OriginUpperLeft
564 OpSource ESSL 310
565 OpDecorate %8 RelaxedPrecision
566 %2 = OpTypeVoid
567 %3 = OpTypeFunction %2
568 %6 = OpTypeInt 32 1
569 %7 = OpTypePointer Function %6
570 %9 = OpConstant %6 4
571 %10 = OpTypeFloat 32
572 %11 = OpTypePointer Function %10
573 %13 = OpConstant %10 4
574 %14 = OpTypeVector %10 2
575 %15 = OpTypePointer Function %14
576 %17 = OpConstant %10 3.4000001
577 %18 = OpConstantComposite %14 %17 %17
578 %19 = OpTypeBool
579 %20 = OpTypeStruct %19
580 %21 = OpTypePointer Function %20
581 %23 = OpConstantTrue %19
582 %24 = OpConstantComposite %20 %23
583 %4 = OpFunction %2 None %3
584 %5 = OpLabel
585 %8 = OpVariable %7 Function
586 %12 = OpVariable %11 Function
587 %16 = OpVariable %15 Function
588 %22 = OpVariable %21 Function
589 OpStore %8 %9
590 OpStore %12 %13
591 OpStore %16 %18
592 OpStore %22 %24
593 %50 = OpCopyObject %6 %9
594 %51 = OpCopyObject %10 %13
595 %52 = OpCopyObject %10 %17
596 %53 = OpCopyObject %14 %18
597 %54 = OpCopyObject %19 %23
598 %55 = OpCopyObject %20 %24
599 %56 = OpCopyObject %21 %22
600 OpReturn
601 OpFunctionEnd
602 )";
603
604 ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
605 }
606
TEST(TransformationAddSynonymTest,CopyBooleanConstants)607 TEST(TransformationAddSynonymTest, CopyBooleanConstants) {
608 std::string shader = R"(
609 OpCapability Shader
610 %1 = OpExtInstImport "GLSL.std.450"
611 OpMemoryModel Logical GLSL450
612 OpEntryPoint Fragment %4 "main"
613 OpExecutionMode %4 OriginUpperLeft
614 OpSource ESSL 310
615 OpName %4 "main"
616 %2 = OpTypeVoid
617 %6 = OpTypeBool
618 %7 = OpConstantTrue %6
619 %8 = OpConstantFalse %6
620 %3 = OpTypeFunction %2
621 %4 = OpFunction %2 None %3
622 %5 = OpLabel
623 OpReturn
624 OpFunctionEnd
625 )";
626
627 const auto env = SPV_ENV_UNIVERSAL_1_3;
628 const auto consumer = nullptr;
629 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
630 spvtools::ValidatorOptions validator_options;
631 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
632 kConsoleMessageConsumer));
633 TransformationContext transformation_context(
634 MakeUnique<FactManager>(context.get()), validator_options);
635 ASSERT_EQ(0, transformation_context.GetFactManager()
636 ->GetIdsForWhichSynonymsAreKnown()
637 .size());
638
639 {
640 TransformationAddSynonym copy_true(
641 7, protobufs::TransformationAddSynonym::COPY_OBJECT, 100,
642 MakeInstructionDescriptor(5, SpvOpReturn, 0));
643 ASSERT_TRUE(copy_true.IsApplicable(context.get(), transformation_context));
644 ApplyAndCheckFreshIds(copy_true, context.get(), &transformation_context);
645
646 std::vector<uint32_t> ids_for_which_synonyms_are_known =
647 transformation_context.GetFactManager()
648 ->GetIdsForWhichSynonymsAreKnown();
649 ASSERT_EQ(2, ids_for_which_synonyms_are_known.size());
650 ASSERT_TRUE(std::find(ids_for_which_synonyms_are_known.begin(),
651 ids_for_which_synonyms_are_known.end(),
652 7) != ids_for_which_synonyms_are_known.end());
653 ASSERT_EQ(
654 2, transformation_context.GetFactManager()->GetSynonymsForId(7).size());
655 protobufs::DataDescriptor descriptor_100 = MakeDataDescriptor(100, {});
656 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
657 MakeDataDescriptor(7, {}), descriptor_100));
658 }
659
660 {
661 TransformationAddSynonym copy_false(
662 8, protobufs::TransformationAddSynonym::COPY_OBJECT, 101,
663 MakeInstructionDescriptor(100, SpvOpReturn, 0));
664 ASSERT_TRUE(copy_false.IsApplicable(context.get(), transformation_context));
665 ApplyAndCheckFreshIds(copy_false, context.get(), &transformation_context);
666 std::vector<uint32_t> ids_for_which_synonyms_are_known =
667 transformation_context.GetFactManager()
668 ->GetIdsForWhichSynonymsAreKnown();
669 ASSERT_EQ(4, ids_for_which_synonyms_are_known.size());
670 ASSERT_TRUE(std::find(ids_for_which_synonyms_are_known.begin(),
671 ids_for_which_synonyms_are_known.end(),
672 8) != ids_for_which_synonyms_are_known.end());
673 ASSERT_EQ(
674 2, transformation_context.GetFactManager()->GetSynonymsForId(8).size());
675 protobufs::DataDescriptor descriptor_101 = MakeDataDescriptor(101, {});
676 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
677 MakeDataDescriptor(8, {}), descriptor_101));
678 }
679
680 {
681 TransformationAddSynonym copy_false_again(
682 101, protobufs::TransformationAddSynonym::COPY_OBJECT, 102,
683 MakeInstructionDescriptor(5, SpvOpReturn, 0));
684 ASSERT_TRUE(
685 copy_false_again.IsApplicable(context.get(), transformation_context));
686 ApplyAndCheckFreshIds(copy_false_again, context.get(),
687 &transformation_context);
688 std::vector<uint32_t> ids_for_which_synonyms_are_known =
689 transformation_context.GetFactManager()
690 ->GetIdsForWhichSynonymsAreKnown();
691 ASSERT_EQ(5, ids_for_which_synonyms_are_known.size());
692 ASSERT_TRUE(std::find(ids_for_which_synonyms_are_known.begin(),
693 ids_for_which_synonyms_are_known.end(),
694 101) != ids_for_which_synonyms_are_known.end());
695 ASSERT_EQ(
696 3,
697 transformation_context.GetFactManager()->GetSynonymsForId(101).size());
698 protobufs::DataDescriptor descriptor_102 = MakeDataDescriptor(102, {});
699 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
700 MakeDataDescriptor(101, {}), descriptor_102));
701 }
702
703 {
704 TransformationAddSynonym copy_true_again(
705 7, protobufs::TransformationAddSynonym::COPY_OBJECT, 103,
706 MakeInstructionDescriptor(102, SpvOpReturn, 0));
707 ASSERT_TRUE(
708 copy_true_again.IsApplicable(context.get(), transformation_context));
709 ApplyAndCheckFreshIds(copy_true_again, context.get(),
710 &transformation_context);
711 std::vector<uint32_t> ids_for_which_synonyms_are_known =
712 transformation_context.GetFactManager()
713 ->GetIdsForWhichSynonymsAreKnown();
714 ASSERT_EQ(6, ids_for_which_synonyms_are_known.size());
715 ASSERT_TRUE(std::find(ids_for_which_synonyms_are_known.begin(),
716 ids_for_which_synonyms_are_known.end(),
717 7) != ids_for_which_synonyms_are_known.end());
718 ASSERT_EQ(
719 3, transformation_context.GetFactManager()->GetSynonymsForId(7).size());
720 protobufs::DataDescriptor descriptor_103 = MakeDataDescriptor(103, {});
721 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
722 MakeDataDescriptor(7, {}), descriptor_103));
723 }
724
725 std::string after_transformation = 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 %2 = OpTypeVoid
734 %6 = OpTypeBool
735 %7 = OpConstantTrue %6
736 %8 = OpConstantFalse %6
737 %3 = OpTypeFunction %2
738 %4 = OpFunction %2 None %3
739 %5 = OpLabel
740 %100 = OpCopyObject %6 %7
741 %101 = OpCopyObject %6 %8
742 %102 = OpCopyObject %6 %101
743 %103 = OpCopyObject %6 %7
744 OpReturn
745 OpFunctionEnd
746 )";
747
748 ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
749 }
750
TEST(TransformationAddSynonymTest,CheckIllegalCases)751 TEST(TransformationAddSynonymTest, CheckIllegalCases) {
752 // The following SPIR-V comes from this GLSL, pushed through spirv-opt
753 // and then doctored a bit.
754 //
755 // #version 310 es
756 //
757 // precision highp float;
758 //
759 // struct S {
760 // int a;
761 // float b;
762 // };
763 //
764 // layout(set = 0, binding = 2) uniform block {
765 // S s;
766 // lowp float f;
767 // int ii;
768 // } ubuf;
769 //
770 // layout(location = 0) out vec4 color;
771 //
772 // void main() {
773 // float c = 0.0;
774 // lowp float d = 0.0;
775 // S localS = ubuf.s;
776 // for (int i = 0; i < ubuf.s.a; i++) {
777 // switch (ubuf.ii) {
778 // case 0:
779 // c += 0.1;
780 // d += 0.2;
781 // case 1:
782 // c += 0.1;
783 // if (c > d) {
784 // d += 0.2;
785 // } else {
786 // d += c;
787 // }
788 // break;
789 // default:
790 // i += 1;
791 // localS.b += d;
792 // }
793 // }
794 // color = vec4(c, d, localS.b, 1.0);
795 // }
796
797 std::string shader = R"(
798 OpCapability Shader
799 %1 = OpExtInstImport "GLSL.std.450"
800 OpMemoryModel Logical GLSL450
801 OpEntryPoint Fragment %4 "main" %80
802 OpExecutionMode %4 OriginUpperLeft
803 OpSource ESSL 310
804 OpName %4 "main"
805 OpName %12 "S"
806 OpMemberName %12 0 "a"
807 OpMemberName %12 1 "b"
808 OpName %15 "S"
809 OpMemberName %15 0 "a"
810 OpMemberName %15 1 "b"
811 OpName %16 "block"
812 OpMemberName %16 0 "s"
813 OpMemberName %16 1 "f"
814 OpMemberName %16 2 "ii"
815 OpName %18 "ubuf"
816 OpName %80 "color"
817 OpMemberDecorate %12 0 RelaxedPrecision
818 OpMemberDecorate %15 0 RelaxedPrecision
819 OpMemberDecorate %15 0 Offset 0
820 OpMemberDecorate %15 1 Offset 4
821 OpMemberDecorate %16 0 Offset 0
822 OpMemberDecorate %16 1 RelaxedPrecision
823 OpMemberDecorate %16 1 Offset 16
824 OpMemberDecorate %16 2 RelaxedPrecision
825 OpMemberDecorate %16 2 Offset 20
826 OpDecorate %16 Block
827 OpDecorate %18 DescriptorSet 0
828 OpDecorate %18 Binding 2
829 OpDecorate %38 RelaxedPrecision
830 OpDecorate %43 RelaxedPrecision
831 OpDecorate %53 RelaxedPrecision
832 OpDecorate %62 RelaxedPrecision
833 OpDecorate %69 RelaxedPrecision
834 OpDecorate %77 RelaxedPrecision
835 OpDecorate %80 Location 0
836 OpDecorate %101 RelaxedPrecision
837 OpDecorate %102 RelaxedPrecision
838 OpDecorate %96 RelaxedPrecision
839 OpDecorate %108 RelaxedPrecision
840 OpDecorate %107 RelaxedPrecision
841 OpDecorate %98 RelaxedPrecision
842 %2 = OpTypeVoid
843 %3 = OpTypeFunction %2
844 %6 = OpTypeFloat 32
845 %9 = OpConstant %6 0
846 %11 = OpTypeInt 32 1
847 %12 = OpTypeStruct %11 %6
848 %15 = OpTypeStruct %11 %6
849 %16 = OpTypeStruct %15 %6 %11
850 %17 = OpTypePointer Uniform %16
851 %18 = OpVariable %17 Uniform
852 %19 = OpConstant %11 0
853 %20 = OpTypePointer Uniform %15
854 %27 = OpConstant %11 1
855 %36 = OpTypePointer Uniform %11
856 %39 = OpTypeBool
857 %41 = OpConstant %11 2
858 %48 = OpConstant %6 0.100000001
859 %51 = OpConstant %6 0.200000003
860 %78 = OpTypeVector %6 4
861 %79 = OpTypePointer Output %78
862 %80 = OpVariable %79 Output
863 %85 = OpConstant %6 1
864 %95 = OpUndef %12
865 %112 = OpTypePointer Uniform %6
866 %113 = OpTypeInt 32 0
867 %114 = OpConstant %113 1
868 %179 = OpTypePointer Function %39
869 %4 = OpFunction %2 None %3
870 %5 = OpLabel
871 %180 = OpVariable %179 Function
872 %181 = OpVariable %179 Function
873 %182 = OpVariable %179 Function
874 %21 = OpAccessChain %20 %18 %19
875 %115 = OpAccessChain %112 %21 %114
876 %116 = OpLoad %6 %115
877 %90 = OpCompositeInsert %12 %116 %95 1
878 OpBranch %30
879 %30 = OpLabel
880 %99 = OpPhi %12 %90 %5 %109 %47
881 %98 = OpPhi %6 %9 %5 %107 %47
882 %97 = OpPhi %6 %9 %5 %105 %47
883 %96 = OpPhi %11 %19 %5 %77 %47
884 %37 = OpAccessChain %36 %18 %19 %19
885 %38 = OpLoad %11 %37
886 %40 = OpSLessThan %39 %96 %38
887 OpLoopMerge %32 %47 None
888 OpBranchConditional %40 %31 %32
889 %31 = OpLabel
890 %42 = OpAccessChain %36 %18 %41
891 %43 = OpLoad %11 %42
892 OpSelectionMerge %45 None
893 OpSwitch %43 %46 0 %44 1 %45
894 %46 = OpLabel
895 %69 = OpIAdd %11 %96 %27
896 %72 = OpCompositeExtract %6 %99 1
897 %73 = OpFAdd %6 %72 %98
898 %93 = OpCompositeInsert %12 %73 %99 1
899 OpBranch %47
900 %44 = OpLabel
901 %50 = OpFAdd %6 %97 %48
902 %53 = OpFAdd %6 %98 %51
903 OpBranch %45
904 %45 = OpLabel
905 %101 = OpPhi %6 %98 %31 %53 %44
906 %100 = OpPhi %6 %97 %31 %50 %44
907 %55 = OpFAdd %6 %100 %48
908 %58 = OpFOrdGreaterThan %39 %55 %101
909 OpSelectionMerge %60 None
910 OpBranchConditional %58 %59 %63
911 %59 = OpLabel
912 %62 = OpFAdd %6 %101 %51
913 OpBranch %60
914 %63 = OpLabel
915 %66 = OpFAdd %6 %101 %55
916 OpBranch %60
917 %60 = OpLabel
918 %108 = OpPhi %6 %62 %59 %66 %63
919 OpBranch %47
920 %47 = OpLabel
921 %109 = OpPhi %12 %93 %46 %99 %60
922 %107 = OpPhi %6 %98 %46 %108 %60
923 %105 = OpPhi %6 %97 %46 %55 %60
924 %102 = OpPhi %11 %69 %46 %96 %60
925 %77 = OpIAdd %11 %102 %27
926 OpBranch %30
927 %32 = OpLabel
928 %84 = OpCompositeExtract %6 %99 1
929 %86 = OpCompositeConstruct %78 %97 %98 %84 %85
930 OpStore %80 %86
931 OpReturn
932 OpFunctionEnd
933 )";
934
935 const auto env = SPV_ENV_UNIVERSAL_1_3;
936 const auto consumer = nullptr;
937 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
938 spvtools::ValidatorOptions validator_options;
939 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
940 kConsoleMessageConsumer));
941 TransformationContext transformation_context(
942 MakeUnique<FactManager>(context.get()), validator_options);
943 // Inapplicable because %18 is decorated.
944 ASSERT_FALSE(TransformationAddSynonym(
945 18, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
946 MakeInstructionDescriptor(21, SpvOpAccessChain, 0))
947 .IsApplicable(context.get(), transformation_context));
948
949 // Inapplicable because %77 is decorated.
950 ASSERT_FALSE(TransformationAddSynonym(
951 77, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
952 MakeInstructionDescriptor(77, SpvOpBranch, 0))
953 .IsApplicable(context.get(), transformation_context));
954
955 // Inapplicable because %80 is decorated.
956 ASSERT_FALSE(TransformationAddSynonym(
957 80, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
958 MakeInstructionDescriptor(77, SpvOpIAdd, 0))
959 .IsApplicable(context.get(), transformation_context));
960
961 // Inapplicable because %84 is not available at the requested point
962 ASSERT_FALSE(TransformationAddSynonym(
963 84, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
964 MakeInstructionDescriptor(32, SpvOpCompositeExtract, 0))
965 .IsApplicable(context.get(), transformation_context));
966
967 // Fine because %84 is available at the requested point
968 ASSERT_TRUE(TransformationAddSynonym(
969 84, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
970 MakeInstructionDescriptor(32, SpvOpCompositeConstruct, 0))
971 .IsApplicable(context.get(), transformation_context));
972
973 // Inapplicable because id %9 is already in use
974 ASSERT_FALSE(TransformationAddSynonym(
975 84, protobufs::TransformationAddSynonym::COPY_OBJECT, 9,
976 MakeInstructionDescriptor(32, SpvOpCompositeConstruct, 0))
977 .IsApplicable(context.get(), transformation_context));
978
979 // Inapplicable because the requested point does not exist
980 ASSERT_FALSE(TransformationAddSynonym(
981 84, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
982 MakeInstructionDescriptor(86, SpvOpReturn, 2))
983 .IsApplicable(context.get(), transformation_context));
984
985 // Inapplicable because %9 is not in a function
986 ASSERT_FALSE(TransformationAddSynonym(
987 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
988 MakeInstructionDescriptor(9, SpvOpTypeInt, 0))
989 .IsApplicable(context.get(), transformation_context));
990
991 // Inapplicable because the insert point is right before, or inside, a chunk
992 // of OpPhis
993 ASSERT_FALSE(TransformationAddSynonym(
994 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
995 MakeInstructionDescriptor(30, SpvOpPhi, 0))
996 .IsApplicable(context.get(), transformation_context));
997 ASSERT_FALSE(TransformationAddSynonym(
998 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
999 MakeInstructionDescriptor(99, SpvOpPhi, 1))
1000 .IsApplicable(context.get(), transformation_context));
1001
1002 // OK, because the insert point is just after a chunk of OpPhis.
1003 ASSERT_TRUE(TransformationAddSynonym(
1004 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
1005 MakeInstructionDescriptor(96, SpvOpAccessChain, 0))
1006 .IsApplicable(context.get(), transformation_context));
1007
1008 // Inapplicable because the insert point is right after an OpSelectionMerge
1009 ASSERT_FALSE(TransformationAddSynonym(
1010 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
1011 MakeInstructionDescriptor(58, SpvOpBranchConditional, 0))
1012 .IsApplicable(context.get(), transformation_context));
1013
1014 // OK, because the insert point is right before the OpSelectionMerge
1015 ASSERT_TRUE(TransformationAddSynonym(
1016 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
1017 MakeInstructionDescriptor(58, SpvOpSelectionMerge, 0))
1018 .IsApplicable(context.get(), transformation_context));
1019
1020 // Inapplicable because the insert point is right after an OpSelectionMerge
1021 ASSERT_FALSE(TransformationAddSynonym(
1022 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
1023 MakeInstructionDescriptor(43, SpvOpSwitch, 0))
1024 .IsApplicable(context.get(), transformation_context));
1025
1026 // OK, because the insert point is right before the OpSelectionMerge
1027 ASSERT_TRUE(TransformationAddSynonym(
1028 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
1029 MakeInstructionDescriptor(43, SpvOpSelectionMerge, 0))
1030 .IsApplicable(context.get(), transformation_context));
1031
1032 // Inapplicable because the insert point is right after an OpLoopMerge
1033 ASSERT_FALSE(TransformationAddSynonym(
1034 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
1035 MakeInstructionDescriptor(40, SpvOpBranchConditional, 0))
1036 .IsApplicable(context.get(), transformation_context));
1037
1038 // OK, because the insert point is right before the OpLoopMerge
1039 ASSERT_TRUE(TransformationAddSynonym(
1040 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
1041 MakeInstructionDescriptor(40, SpvOpLoopMerge, 0))
1042 .IsApplicable(context.get(), transformation_context));
1043
1044 // Inapplicable because id %300 does not exist
1045 ASSERT_FALSE(TransformationAddSynonym(
1046 300, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
1047 MakeInstructionDescriptor(40, SpvOpLoopMerge, 0))
1048 .IsApplicable(context.get(), transformation_context));
1049
1050 // Inapplicable because the following instruction is OpVariable
1051 ASSERT_FALSE(TransformationAddSynonym(
1052 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
1053 MakeInstructionDescriptor(180, SpvOpVariable, 0))
1054 .IsApplicable(context.get(), transformation_context));
1055 ASSERT_FALSE(TransformationAddSynonym(
1056 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
1057 MakeInstructionDescriptor(181, SpvOpVariable, 0))
1058 .IsApplicable(context.get(), transformation_context));
1059 ASSERT_FALSE(TransformationAddSynonym(
1060 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
1061 MakeInstructionDescriptor(182, SpvOpVariable, 0))
1062 .IsApplicable(context.get(), transformation_context));
1063
1064 // OK, because this is just past the group of OpVariable instructions.
1065 ASSERT_TRUE(TransformationAddSynonym(
1066 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
1067 MakeInstructionDescriptor(182, SpvOpAccessChain, 0))
1068 .IsApplicable(context.get(), transformation_context));
1069 }
1070
TEST(TransformationAddSynonymTest,MiscellaneousCopies)1071 TEST(TransformationAddSynonymTest, MiscellaneousCopies) {
1072 // The following SPIR-V comes from this GLSL:
1073 //
1074 // #version 310 es
1075 //
1076 // precision highp float;
1077 //
1078 // float g;
1079 //
1080 // vec4 h;
1081 //
1082 // void main() {
1083 // int a;
1084 // int b;
1085 // b = int(g);
1086 // h.x = float(a);
1087 // }
1088
1089 std::string shader = R"(
1090 OpCapability Shader
1091 %1 = OpExtInstImport "GLSL.std.450"
1092 OpMemoryModel Logical GLSL450
1093 OpEntryPoint Fragment %4 "main"
1094 OpExecutionMode %4 OriginUpperLeft
1095 OpSource ESSL 310
1096 OpName %4 "main"
1097 OpName %8 "b"
1098 OpName %11 "g"
1099 OpName %16 "h"
1100 OpName %17 "a"
1101 %2 = OpTypeVoid
1102 %3 = OpTypeFunction %2
1103 %6 = OpTypeInt 32 1
1104 %7 = OpTypePointer Function %6
1105 %9 = OpTypeFloat 32
1106 %10 = OpTypePointer Private %9
1107 %11 = OpVariable %10 Private
1108 %14 = OpTypeVector %9 4
1109 %15 = OpTypePointer Private %14
1110 %16 = OpVariable %15 Private
1111 %20 = OpTypeInt 32 0
1112 %21 = OpConstant %20 0
1113 %4 = OpFunction %2 None %3
1114 %5 = OpLabel
1115 %8 = OpVariable %7 Function
1116 %17 = OpVariable %7 Function
1117 %12 = OpLoad %9 %11
1118 %13 = OpConvertFToS %6 %12
1119 OpStore %8 %13
1120 %18 = OpLoad %6 %17
1121 %19 = OpConvertSToF %9 %18
1122 %22 = OpAccessChain %10 %16 %21
1123 OpStore %22 %19
1124 OpReturn
1125 OpFunctionEnd
1126 )";
1127
1128 const auto env = SPV_ENV_UNIVERSAL_1_3;
1129 const auto consumer = nullptr;
1130 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1131 spvtools::ValidatorOptions validator_options;
1132 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1133 kConsoleMessageConsumer));
1134 TransformationContext transformation_context(
1135 MakeUnique<FactManager>(context.get()), validator_options);
1136 std::vector<TransformationAddSynonym> transformations = {
1137 TransformationAddSynonym(
1138 19, protobufs::TransformationAddSynonym::COPY_OBJECT, 100,
1139 MakeInstructionDescriptor(22, SpvOpStore, 0)),
1140 TransformationAddSynonym(
1141 22, protobufs::TransformationAddSynonym::COPY_OBJECT, 101,
1142 MakeInstructionDescriptor(22, SpvOpCopyObject, 0)),
1143 TransformationAddSynonym(
1144 12, protobufs::TransformationAddSynonym::COPY_OBJECT, 102,
1145 MakeInstructionDescriptor(22, SpvOpCopyObject, 0)),
1146 TransformationAddSynonym(
1147 11, protobufs::TransformationAddSynonym::COPY_OBJECT, 103,
1148 MakeInstructionDescriptor(22, SpvOpCopyObject, 0)),
1149 TransformationAddSynonym(
1150 16, protobufs::TransformationAddSynonym::COPY_OBJECT, 104,
1151 MakeInstructionDescriptor(22, SpvOpCopyObject, 0)),
1152 TransformationAddSynonym(
1153 8, protobufs::TransformationAddSynonym::COPY_OBJECT, 105,
1154 MakeInstructionDescriptor(22, SpvOpCopyObject, 0)),
1155 TransformationAddSynonym(
1156 17, protobufs::TransformationAddSynonym::COPY_OBJECT, 106,
1157 MakeInstructionDescriptor(22, SpvOpCopyObject, 0))};
1158
1159 for (auto& transformation : transformations) {
1160 ASSERT_TRUE(
1161 transformation.IsApplicable(context.get(), transformation_context));
1162 ApplyAndCheckFreshIds(transformation, context.get(),
1163 &transformation_context);
1164 }
1165
1166 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1167 kConsoleMessageConsumer));
1168
1169 std::string after_transformation = R"(
1170 OpCapability Shader
1171 %1 = OpExtInstImport "GLSL.std.450"
1172 OpMemoryModel Logical GLSL450
1173 OpEntryPoint Fragment %4 "main"
1174 OpExecutionMode %4 OriginUpperLeft
1175 OpSource ESSL 310
1176 OpName %4 "main"
1177 OpName %8 "b"
1178 OpName %11 "g"
1179 OpName %16 "h"
1180 OpName %17 "a"
1181 %2 = OpTypeVoid
1182 %3 = OpTypeFunction %2
1183 %6 = OpTypeInt 32 1
1184 %7 = OpTypePointer Function %6
1185 %9 = OpTypeFloat 32
1186 %10 = OpTypePointer Private %9
1187 %11 = OpVariable %10 Private
1188 %14 = OpTypeVector %9 4
1189 %15 = OpTypePointer Private %14
1190 %16 = OpVariable %15 Private
1191 %20 = OpTypeInt 32 0
1192 %21 = OpConstant %20 0
1193 %4 = OpFunction %2 None %3
1194 %5 = OpLabel
1195 %8 = OpVariable %7 Function
1196 %17 = OpVariable %7 Function
1197 %12 = OpLoad %9 %11
1198 %13 = OpConvertFToS %6 %12
1199 OpStore %8 %13
1200 %18 = OpLoad %6 %17
1201 %19 = OpConvertSToF %9 %18
1202 %22 = OpAccessChain %10 %16 %21
1203 %106 = OpCopyObject %7 %17
1204 %105 = OpCopyObject %7 %8
1205 %104 = OpCopyObject %15 %16
1206 %103 = OpCopyObject %10 %11
1207 %102 = OpCopyObject %9 %12
1208 %101 = OpCopyObject %10 %22
1209 %100 = OpCopyObject %9 %19
1210 OpStore %22 %19
1211 OpReturn
1212 OpFunctionEnd
1213 )";
1214
1215 ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
1216 }
1217
TEST(TransformationAddSynonymTest,DoNotCopyNullOrUndefPointers)1218 TEST(TransformationAddSynonymTest, DoNotCopyNullOrUndefPointers) {
1219 std::string shader = R"(
1220 OpCapability Shader
1221 %1 = OpExtInstImport "GLSL.std.450"
1222 OpMemoryModel Logical GLSL450
1223 OpEntryPoint Fragment %4 "main"
1224 OpExecutionMode %4 OriginUpperLeft
1225 OpSource ESSL 310
1226 %2 = OpTypeVoid
1227 %3 = OpTypeFunction %2
1228 %6 = OpTypeInt 32 1
1229 %7 = OpTypePointer Function %6
1230 %8 = OpConstantNull %7
1231 %9 = OpUndef %7
1232 %4 = OpFunction %2 None %3
1233 %5 = OpLabel
1234 OpReturn
1235 OpFunctionEnd
1236 )";
1237
1238 const auto env = SPV_ENV_UNIVERSAL_1_3;
1239 const auto consumer = nullptr;
1240 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1241 spvtools::ValidatorOptions validator_options;
1242 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1243 kConsoleMessageConsumer));
1244 TransformationContext transformation_context(
1245 MakeUnique<FactManager>(context.get()), validator_options);
1246 // Illegal to copy null.
1247 ASSERT_FALSE(TransformationAddSynonym(
1248 8, protobufs::TransformationAddSynonym::COPY_OBJECT, 100,
1249 MakeInstructionDescriptor(5, SpvOpReturn, 0))
1250 .IsApplicable(context.get(), transformation_context));
1251
1252 // Illegal to copy an OpUndef of pointer type.
1253 ASSERT_FALSE(TransformationAddSynonym(
1254 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 100,
1255 MakeInstructionDescriptor(5, SpvOpReturn, 0))
1256 .IsApplicable(context.get(), transformation_context));
1257 }
1258
TEST(TransformationAddSynonymTest,PropagateIrrelevantPointeeFact)1259 TEST(TransformationAddSynonymTest, PropagateIrrelevantPointeeFact) {
1260 // Checks that if a pointer is known to have an irrelevant value, the same
1261 // holds after the pointer is copied.
1262
1263 std::string shader = R"(
1264 OpCapability Shader
1265 %1 = OpExtInstImport "GLSL.std.450"
1266 OpMemoryModel Logical GLSL450
1267 OpEntryPoint Fragment %4 "main"
1268 OpExecutionMode %4 OriginUpperLeft
1269 OpSource ESSL 310
1270 %2 = OpTypeVoid
1271 %3 = OpTypeFunction %2
1272 %6 = OpTypeInt 32 1
1273 %7 = OpTypePointer Function %6
1274 %4 = OpFunction %2 None %3
1275 %5 = OpLabel
1276 %8 = OpVariable %7 Function
1277 %9 = OpVariable %7 Function
1278 OpReturn
1279 OpFunctionEnd
1280 )";
1281
1282 const auto env = SPV_ENV_UNIVERSAL_1_3;
1283 const auto consumer = nullptr;
1284 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1285 spvtools::ValidatorOptions validator_options;
1286 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1287 kConsoleMessageConsumer));
1288 TransformationContext transformation_context(
1289 MakeUnique<FactManager>(context.get()), validator_options);
1290 transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(8);
1291
1292 TransformationAddSynonym transformation1(
1293 8, protobufs::TransformationAddSynonym::COPY_OBJECT, 100,
1294 MakeInstructionDescriptor(9, SpvOpReturn, 0));
1295 TransformationAddSynonym transformation2(
1296 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 101,
1297 MakeInstructionDescriptor(9, SpvOpReturn, 0));
1298 TransformationAddSynonym transformation3(
1299 100, protobufs::TransformationAddSynonym::COPY_OBJECT, 102,
1300 MakeInstructionDescriptor(9, SpvOpReturn, 0));
1301
1302 ASSERT_TRUE(
1303 transformation1.IsApplicable(context.get(), transformation_context));
1304 ApplyAndCheckFreshIds(transformation1, context.get(),
1305 &transformation_context);
1306 ASSERT_TRUE(
1307 transformation2.IsApplicable(context.get(), transformation_context));
1308 ApplyAndCheckFreshIds(transformation2, context.get(),
1309 &transformation_context);
1310 ASSERT_TRUE(
1311 transformation3.IsApplicable(context.get(), transformation_context));
1312 ApplyAndCheckFreshIds(transformation3, context.get(),
1313 &transformation_context);
1314
1315 ASSERT_TRUE(
1316 transformation_context.GetFactManager()->PointeeValueIsIrrelevant(8));
1317 ASSERT_TRUE(
1318 transformation_context.GetFactManager()->PointeeValueIsIrrelevant(100));
1319 ASSERT_TRUE(
1320 transformation_context.GetFactManager()->PointeeValueIsIrrelevant(102));
1321 ASSERT_FALSE(
1322 transformation_context.GetFactManager()->PointeeValueIsIrrelevant(9));
1323 ASSERT_FALSE(
1324 transformation_context.GetFactManager()->PointeeValueIsIrrelevant(101));
1325 }
1326
TEST(TransformationAddSynonymTest,DoNotCopyOpSampledImage)1327 TEST(TransformationAddSynonymTest, DoNotCopyOpSampledImage) {
1328 // This checks that we do not try to copy the result id of an OpSampledImage
1329 // instruction.
1330 std::string shader = R"(
1331 OpCapability Shader
1332 OpCapability SampledBuffer
1333 OpCapability ImageBuffer
1334 %1 = OpExtInstImport "GLSL.std.450"
1335 OpMemoryModel Logical GLSL450
1336 OpEntryPoint Fragment %2 "main" %40 %41
1337 OpExecutionMode %2 OriginUpperLeft
1338 OpSource GLSL 450
1339 OpDecorate %40 DescriptorSet 0
1340 OpDecorate %40 Binding 69
1341 OpDecorate %41 DescriptorSet 0
1342 OpDecorate %41 Binding 1
1343 %54 = OpTypeFloat 32
1344 %76 = OpTypeVector %54 4
1345 %55 = OpConstant %54 0
1346 %56 = OpTypeVector %54 3
1347 %94 = OpTypeVector %54 2
1348 %112 = OpConstantComposite %94 %55 %55
1349 %57 = OpConstantComposite %56 %55 %55 %55
1350 %15 = OpTypeImage %54 2D 2 0 0 1 Unknown
1351 %114 = OpTypePointer UniformConstant %15
1352 %38 = OpTypeSampler
1353 %125 = OpTypePointer UniformConstant %38
1354 %132 = OpTypeVoid
1355 %133 = OpTypeFunction %132
1356 %45 = OpTypeSampledImage %15
1357 %40 = OpVariable %114 UniformConstant
1358 %41 = OpVariable %125 UniformConstant
1359 %2 = OpFunction %132 None %133
1360 %164 = OpLabel
1361 %184 = OpLoad %15 %40
1362 %213 = OpLoad %38 %41
1363 %216 = OpSampledImage %45 %184 %213
1364 %217 = OpImageSampleImplicitLod %76 %216 %112 Bias %55
1365 OpReturn
1366 OpFunctionEnd
1367 )";
1368
1369 const auto env = SPV_ENV_UNIVERSAL_1_3;
1370 const auto consumer = nullptr;
1371 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1372
1373 spvtools::ValidatorOptions validator_options;
1374 TransformationContext transformation_context(
1375 MakeUnique<FactManager>(context.get()), validator_options);
1376 ASSERT_FALSE(
1377 TransformationAddSynonym(
1378 216, protobufs::TransformationAddSynonym::COPY_OBJECT, 500,
1379 MakeInstructionDescriptor(217, SpvOpImageSampleImplicitLod, 0))
1380 .IsApplicable(context.get(), transformation_context));
1381 }
1382
TEST(TransformationAddSynonymTest,DoNotCopyVoidRunctionResult)1383 TEST(TransformationAddSynonymTest, DoNotCopyVoidRunctionResult) {
1384 // This checks that we do not try to copy the result of a void function.
1385 std::string shader = R"(
1386 OpCapability Shader
1387 %1 = OpExtInstImport "GLSL.std.450"
1388 OpMemoryModel Logical GLSL450
1389 OpEntryPoint Fragment %4 "main"
1390 OpExecutionMode %4 OriginUpperLeft
1391 OpSource ESSL 320
1392 OpName %4 "main"
1393 OpName %6 "foo("
1394 %2 = OpTypeVoid
1395 %3 = OpTypeFunction %2
1396 %4 = OpFunction %2 None %3
1397 %5 = OpLabel
1398 %8 = OpFunctionCall %2 %6
1399 OpReturn
1400 OpFunctionEnd
1401 %6 = OpFunction %2 None %3
1402 %7 = OpLabel
1403 OpReturn
1404 OpFunctionEnd
1405 )";
1406
1407 const auto env = SPV_ENV_UNIVERSAL_1_3;
1408 const auto consumer = nullptr;
1409 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1410
1411 spvtools::ValidatorOptions validator_options;
1412 TransformationContext transformation_context(
1413 MakeUnique<FactManager>(context.get()), validator_options);
1414 ASSERT_FALSE(TransformationAddSynonym(
1415 8, protobufs::TransformationAddSynonym::COPY_OBJECT, 500,
1416 MakeInstructionDescriptor(8, SpvOpReturn, 0))
1417 .IsApplicable(context.get(), transformation_context));
1418 }
1419
TEST(TransformationAddSynonymTest,HandlesDeadBlocks)1420 TEST(TransformationAddSynonymTest, HandlesDeadBlocks) {
1421 std::string shader = R"(
1422 OpCapability Shader
1423 %1 = OpExtInstImport "GLSL.std.450"
1424 OpMemoryModel Logical GLSL450
1425 OpEntryPoint Fragment %4 "main"
1426 OpExecutionMode %4 OriginUpperLeft
1427 OpSource ESSL 320
1428 %2 = OpTypeVoid
1429 %3 = OpTypeFunction %2
1430 %6 = OpTypeBool
1431 %7 = OpConstantTrue %6
1432 %11 = OpTypePointer Function %6
1433 %4 = OpFunction %2 None %3
1434 %5 = OpLabel
1435 %12 = OpVariable %11 Function
1436 OpSelectionMerge %10 None
1437 OpBranchConditional %7 %8 %9
1438 %8 = OpLabel
1439 OpBranch %10
1440 %9 = OpLabel
1441 OpBranch %10
1442 %10 = OpLabel
1443 OpReturn
1444 OpFunctionEnd
1445 )";
1446
1447 const auto env = SPV_ENV_UNIVERSAL_1_3;
1448 const auto consumer = nullptr;
1449 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1450
1451 spvtools::ValidatorOptions validator_options;
1452 TransformationContext transformation_context(
1453 MakeUnique<FactManager>(context.get()), validator_options);
1454
1455 transformation_context.GetFactManager()->AddFactBlockIsDead(9);
1456
1457 auto insert_before = MakeInstructionDescriptor(9, SpvOpBranch, 0);
1458
1459 ASSERT_FALSE(TransformationAddSynonym(
1460 7, protobufs::TransformationAddSynonym::COPY_OBJECT, 100,
1461 insert_before)
1462 .IsApplicable(context.get(), transformation_context));
1463
1464 ASSERT_FALSE(TransformationAddSynonym(
1465 12, protobufs::TransformationAddSynonym::COPY_OBJECT, 100,
1466 insert_before)
1467 .IsApplicable(context.get(), transformation_context));
1468 }
1469
1470 } // namespace
1471 } // namespace fuzz
1472 } // namespace spvtools
1473