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