1 // Copyright (c) 2020 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "source/fuzz/transformation_add_opphi_synonym.h"
16
17 #include "gtest/gtest.h"
18 #include "source/fuzz/fuzzer_util.h"
19 #include "test/fuzz/fuzz_test_util.h"
20
21 namespace spvtools {
22 namespace fuzz {
23 namespace {
24
MakeSynonymFact(uint32_t first,uint32_t second)25 protobufs::Fact MakeSynonymFact(uint32_t first, uint32_t second) {
26 protobufs::FactDataSynonym data_synonym_fact;
27 *data_synonym_fact.mutable_data1() = MakeDataDescriptor(first, {});
28 *data_synonym_fact.mutable_data2() = MakeDataDescriptor(second, {});
29 protobufs::Fact result;
30 *result.mutable_data_synonym_fact() = data_synonym_fact;
31 return result;
32 }
33
34 // Adds synonym facts to the fact manager.
SetUpIdSynonyms(FactManager * fact_manager)35 void SetUpIdSynonyms(FactManager* fact_manager) {
36 fact_manager->MaybeAddFact(MakeSynonymFact(11, 9));
37 fact_manager->MaybeAddFact(MakeSynonymFact(13, 9));
38 fact_manager->MaybeAddFact(MakeSynonymFact(14, 9));
39 fact_manager->MaybeAddFact(MakeSynonymFact(19, 9));
40 fact_manager->MaybeAddFact(MakeSynonymFact(20, 9));
41 fact_manager->MaybeAddFact(MakeSynonymFact(10, 21));
42 }
43
TEST(TransformationAddOpPhiSynonymTest,Inapplicable)44 TEST(TransformationAddOpPhiSynonymTest, Inapplicable) {
45 std::string shader = R"(
46 OpCapability Shader
47 %1 = OpExtInstImport "GLSL.std.450"
48 OpMemoryModel Logical GLSL450
49 OpEntryPoint Fragment %2 "main"
50 OpExecutionMode %2 OriginUpperLeft
51 OpSource ESSL 310
52 OpName %2 "main"
53 %3 = OpTypeVoid
54 %4 = OpTypeFunction %3
55 %5 = OpTypeBool
56 %6 = OpConstantTrue %5
57 %7 = OpTypeInt 32 1
58 %8 = OpTypeInt 32 0
59 %22 = OpTypePointer Function %7
60 %9 = OpConstant %7 1
61 %10 = OpConstant %7 2
62 %11 = OpConstant %8 1
63 %2 = OpFunction %3 None %4
64 %12 = OpLabel
65 %23 = OpVariable %22 Function
66 %13 = OpCopyObject %7 %9
67 %14 = OpCopyObject %8 %11
68 OpBranch %15
69 %15 = OpLabel
70 OpSelectionMerge %16 None
71 OpBranchConditional %6 %17 %18
72 %17 = OpLabel
73 %19 = OpCopyObject %7 %13
74 %20 = OpCopyObject %8 %14
75 %21 = OpCopyObject %7 %10
76 OpBranch %16
77 %18 = OpLabel
78 %24 = OpCopyObject %22 %23
79 OpBranch %16
80 %16 = OpLabel
81 OpReturn
82 OpFunctionEnd
83 )";
84
85 const auto env = SPV_ENV_UNIVERSAL_1_5;
86 const auto consumer = nullptr;
87 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
88 spvtools::ValidatorOptions validator_options;
89 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
90 kConsoleMessageConsumer));
91 TransformationContext transformation_context(
92 MakeUnique<FactManager>(context.get()), validator_options);
93 SetUpIdSynonyms(transformation_context.GetFactManager());
94 transformation_context.GetFactManager()->MaybeAddFact(
95 MakeSynonymFact(23, 24));
96
97 // %13 is not a block label.
98 ASSERT_FALSE(TransformationAddOpPhiSynonym(13, {{}}, 100)
99 .IsApplicable(context.get(), transformation_context));
100
101 // Block %12 does not have a predecessor.
102 ASSERT_FALSE(TransformationAddOpPhiSynonym(12, {{}}, 100)
103 .IsApplicable(context.get(), transformation_context));
104
105 // Not all predecessors of %16 (%17 and %18) are considered in the map.
106 ASSERT_FALSE(TransformationAddOpPhiSynonym(16, {{{17, 19}}}, 100)
107 .IsApplicable(context.get(), transformation_context));
108
109 // %30 does not exist in the module.
110 ASSERT_FALSE(TransformationAddOpPhiSynonym(16, {{{30, 19}}}, 100)
111 .IsApplicable(context.get(), transformation_context));
112
113 // %20 is not a block label.
114 ASSERT_FALSE(TransformationAddOpPhiSynonym(16, {{{20, 19}}}, 100)
115 .IsApplicable(context.get(), transformation_context));
116
117 // %15 is not the id of one of the predecessors of the block.
118 ASSERT_FALSE(TransformationAddOpPhiSynonym(16, {{{15, 19}}}, 100)
119 .IsApplicable(context.get(), transformation_context));
120
121 // %30 does not exist in the module.
122 ASSERT_FALSE(TransformationAddOpPhiSynonym(16, {{{17, 30}, {18, 13}}}, 100)
123 .IsApplicable(context.get(), transformation_context));
124
125 // %19 and %10 are not synonymous.
126 ASSERT_FALSE(TransformationAddOpPhiSynonym(16, {{{17, 19}, {18, 10}}}, 100)
127 .IsApplicable(context.get(), transformation_context));
128
129 // %19 and %14 do not have the same type.
130 ASSERT_FALSE(TransformationAddOpPhiSynonym(16, {{{17, 19}, {18, 14}}}, 100)
131 .IsApplicable(context.get(), transformation_context));
132
133 // %19 is not available at the end of %18.
134 ASSERT_FALSE(TransformationAddOpPhiSynonym(16, {{{17, 9}, {18, 19}}}, 100)
135 .IsApplicable(context.get(), transformation_context));
136
137 // %21 is not a fresh id.
138 ASSERT_FALSE(TransformationAddOpPhiSynonym(16, {{{17, 9}, {18, 9}}}, 21)
139 .IsApplicable(context.get(), transformation_context));
140
141 // %23 and %24 have pointer id.
142 ASSERT_FALSE(TransformationAddOpPhiSynonym(16, {{{17, 23}, {18, 24}}}, 100)
143 .IsApplicable(context.get(), transformation_context));
144 }
145
TEST(TransformationAddOpPhiSynonymTest,Apply)146 TEST(TransformationAddOpPhiSynonymTest, Apply) {
147 std::string shader = R"(
148 OpCapability Shader
149 %1 = OpExtInstImport "GLSL.std.450"
150 OpMemoryModel Logical GLSL450
151 OpEntryPoint Fragment %2 "main"
152 OpExecutionMode %2 OriginUpperLeft
153 OpSource ESSL 310
154 OpName %2 "main"
155 %3 = OpTypeVoid
156 %4 = OpTypeFunction %3
157 %5 = OpTypeBool
158 %6 = OpConstantTrue %5
159 %7 = OpTypeInt 32 1
160 %8 = OpTypeInt 32 0
161 %9 = OpConstant %7 1
162 %10 = OpConstant %7 2
163 %11 = OpConstant %8 1
164 %2 = OpFunction %3 None %4
165 %12 = OpLabel
166 %13 = OpCopyObject %7 %9
167 %14 = OpCopyObject %8 %11
168 OpBranch %15
169 %15 = OpLabel
170 OpSelectionMerge %16 None
171 OpBranchConditional %6 %17 %18
172 %17 = OpLabel
173 %19 = OpCopyObject %7 %13
174 %20 = OpCopyObject %8 %14
175 %21 = OpCopyObject %7 %10
176 OpBranch %16
177 %18 = OpLabel
178 OpBranch %16
179 %16 = OpLabel
180 OpBranch %22
181 %22 = OpLabel
182 OpLoopMerge %23 %24 None
183 OpBranchConditional %6 %25 %23
184 %25 = OpLabel
185 OpSelectionMerge %26 None
186 OpBranchConditional %6 %27 %26
187 %27 = OpLabel
188 %28 = OpCopyObject %7 %13
189 OpBranch %23
190 %26 = OpLabel
191 OpSelectionMerge %29 None
192 OpBranchConditional %6 %29 %24
193 %29 = OpLabel
194 %30 = OpCopyObject %7 %13
195 OpBranch %23
196 %24 = OpLabel
197 OpBranch %22
198 %23 = OpLabel
199 OpSelectionMerge %31 None
200 OpBranchConditional %6 %31 %31
201 %31 = OpLabel
202 OpReturn
203 OpFunctionEnd
204 )";
205
206 const auto env = SPV_ENV_UNIVERSAL_1_5;
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 SetUpIdSynonyms(transformation_context.GetFactManager());
215
216 // Add some further synonym facts.
217 transformation_context.GetFactManager()->MaybeAddFact(MakeSynonymFact(28, 9));
218 transformation_context.GetFactManager()->MaybeAddFact(MakeSynonymFact(30, 9));
219
220 auto transformation1 = TransformationAddOpPhiSynonym(17, {{{15, 13}}}, 100);
221 ASSERT_TRUE(
222 transformation1.IsApplicable(context.get(), transformation_context));
223 ApplyAndCheckFreshIds(transformation1, context.get(),
224 &transformation_context);
225 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
226 MakeDataDescriptor(100, {}), MakeDataDescriptor(9, {})));
227
228 auto transformation2 =
229 TransformationAddOpPhiSynonym(16, {{{17, 19}, {18, 13}}}, 101);
230 ASSERT_TRUE(
231 transformation2.IsApplicable(context.get(), transformation_context));
232 ApplyAndCheckFreshIds(transformation2, context.get(),
233 &transformation_context);
234 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
235 MakeDataDescriptor(101, {}), MakeDataDescriptor(9, {})));
236
237 auto transformation3 =
238 TransformationAddOpPhiSynonym(23, {{{22, 13}, {27, 28}, {29, 30}}}, 102);
239 ASSERT_TRUE(
240 transformation3.IsApplicable(context.get(), transformation_context));
241 ApplyAndCheckFreshIds(transformation3, context.get(),
242 &transformation_context);
243 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
244 MakeDataDescriptor(102, {}), MakeDataDescriptor(9, {})));
245
246 auto transformation4 = TransformationAddOpPhiSynonym(31, {{{23, 13}}}, 103);
247 ASSERT_TRUE(
248 transformation4.IsApplicable(context.get(), transformation_context));
249 ApplyAndCheckFreshIds(transformation4, context.get(),
250 &transformation_context);
251 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
252 MakeDataDescriptor(103, {}), MakeDataDescriptor(9, {})));
253
254 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
255 kConsoleMessageConsumer));
256
257 std::string after_transformations = R"(
258 OpCapability Shader
259 %1 = OpExtInstImport "GLSL.std.450"
260 OpMemoryModel Logical GLSL450
261 OpEntryPoint Fragment %2 "main"
262 OpExecutionMode %2 OriginUpperLeft
263 OpSource ESSL 310
264 OpName %2 "main"
265 %3 = OpTypeVoid
266 %4 = OpTypeFunction %3
267 %5 = OpTypeBool
268 %6 = OpConstantTrue %5
269 %7 = OpTypeInt 32 1
270 %8 = OpTypeInt 32 0
271 %9 = OpConstant %7 1
272 %10 = OpConstant %7 2
273 %11 = OpConstant %8 1
274 %2 = OpFunction %3 None %4
275 %12 = OpLabel
276 %13 = OpCopyObject %7 %9
277 %14 = OpCopyObject %8 %11
278 OpBranch %15
279 %15 = OpLabel
280 OpSelectionMerge %16 None
281 OpBranchConditional %6 %17 %18
282 %17 = OpLabel
283 %100 = OpPhi %7 %13 %15
284 %19 = OpCopyObject %7 %13
285 %20 = OpCopyObject %8 %14
286 %21 = OpCopyObject %7 %10
287 OpBranch %16
288 %18 = OpLabel
289 OpBranch %16
290 %16 = OpLabel
291 %101 = OpPhi %7 %19 %17 %13 %18
292 OpBranch %22
293 %22 = OpLabel
294 OpLoopMerge %23 %24 None
295 OpBranchConditional %6 %25 %23
296 %25 = OpLabel
297 OpSelectionMerge %26 None
298 OpBranchConditional %6 %27 %26
299 %27 = OpLabel
300 %28 = OpCopyObject %7 %13
301 OpBranch %23
302 %26 = OpLabel
303 OpSelectionMerge %29 None
304 OpBranchConditional %6 %29 %24
305 %29 = OpLabel
306 %30 = OpCopyObject %7 %13
307 OpBranch %23
308 %24 = OpLabel
309 OpBranch %22
310 %23 = OpLabel
311 %102 = OpPhi %7 %13 %22 %28 %27 %30 %29
312 OpSelectionMerge %31 None
313 OpBranchConditional %6 %31 %31
314 %31 = OpLabel
315 %103 = OpPhi %7 %13 %23
316 OpReturn
317 OpFunctionEnd
318 )";
319
320 ASSERT_TRUE(IsEqual(env, after_transformations, context.get()));
321 }
322
TEST(TransformationAddOpPhiSynonymTest,VariablePointers)323 TEST(TransformationAddOpPhiSynonymTest, VariablePointers) {
324 std::string shader = R"(
325 OpCapability Shader
326 OpCapability VariablePointers
327 %1 = OpExtInstImport "GLSL.std.450"
328 OpMemoryModel Logical GLSL450
329 OpEntryPoint Fragment %2 "main" %3
330 OpExecutionMode %2 OriginUpperLeft
331 OpSource ESSL 310
332 %4 = OpTypeVoid
333 %5 = OpTypeFunction %4
334 %6 = OpTypeBool
335 %7 = OpConstantTrue %6
336 %8 = OpTypeInt 32 1
337 %9 = OpTypePointer Function %8
338 %10 = OpTypePointer Workgroup %8
339 %3 = OpVariable %10 Workgroup
340 %2 = OpFunction %4 None %5
341 %11 = OpLabel
342 %12 = OpVariable %9 Function
343 OpSelectionMerge %13 None
344 OpBranchConditional %7 %14 %13
345 %14 = OpLabel
346 %15 = OpCopyObject %10 %3
347 %16 = OpCopyObject %9 %12
348 OpBranch %13
349 %13 = OpLabel
350 OpReturn
351 OpFunctionEnd
352 )";
353
354 const auto env = SPV_ENV_UNIVERSAL_1_5;
355 const auto consumer = nullptr;
356 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
357 spvtools::ValidatorOptions validator_options;
358 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
359 kConsoleMessageConsumer));
360 TransformationContext transformation_context(
361 MakeUnique<FactManager>(context.get()), validator_options);
362 // Declare synonyms
363 transformation_context.GetFactManager()->MaybeAddFact(MakeSynonymFact(3, 15));
364 transformation_context.GetFactManager()->MaybeAddFact(
365 MakeSynonymFact(12, 16));
366
367 // Remove the VariablePointers capability.
368 context.get()->get_feature_mgr()->RemoveCapability(
369 SpvCapabilityVariablePointers);
370
371 // The VariablePointers capability is required to add an OpPhi instruction of
372 // pointer type.
373 ASSERT_FALSE(TransformationAddOpPhiSynonym(13, {{{11, 3}, {14, 15}}}, 100)
374 .IsApplicable(context.get(), transformation_context));
375
376 // Add the VariablePointers capability back.
377 context.get()->get_feature_mgr()->AddCapability(
378 SpvCapabilityVariablePointers);
379
380 // If the ids have pointer type, the storage class must be Workgroup or
381 // StorageBuffer, but it is Function in this case.
382 ASSERT_FALSE(TransformationAddOpPhiSynonym(13, {{{11, 12}, {14, 16}}}, 100)
383 .IsApplicable(context.get(), transformation_context));
384
385 auto transformation =
386 TransformationAddOpPhiSynonym(13, {{{11, 3}, {14, 15}}}, 100);
387 ASSERT_TRUE(
388 transformation.IsApplicable(context.get(), transformation_context));
389 ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
390
391 std::string after_transformation = R"(
392 OpCapability Shader
393 OpCapability VariablePointers
394 %1 = OpExtInstImport "GLSL.std.450"
395 OpMemoryModel Logical GLSL450
396 OpEntryPoint Fragment %2 "main" %3
397 OpExecutionMode %2 OriginUpperLeft
398 OpSource ESSL 310
399 %4 = OpTypeVoid
400 %5 = OpTypeFunction %4
401 %6 = OpTypeBool
402 %7 = OpConstantTrue %6
403 %8 = OpTypeInt 32 1
404 %9 = OpTypePointer Function %8
405 %10 = OpTypePointer Workgroup %8
406 %3 = OpVariable %10 Workgroup
407 %2 = OpFunction %4 None %5
408 %11 = OpLabel
409 %12 = OpVariable %9 Function
410 OpSelectionMerge %13 None
411 OpBranchConditional %7 %14 %13
412 %14 = OpLabel
413 %15 = OpCopyObject %10 %3
414 %16 = OpCopyObject %9 %12
415 OpBranch %13
416 %13 = OpLabel
417 %100 = OpPhi %10 %3 %11 %15 %14
418 OpReturn
419 OpFunctionEnd
420 )";
421
422 ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
423 }
424
TEST(TransformationAddOpPhiSynonymTest,DeadBlock)425 TEST(TransformationAddOpPhiSynonymTest, DeadBlock) {
426 std::string shader = R"(
427 OpCapability Shader
428 %1 = OpExtInstImport "GLSL.std.450"
429 OpMemoryModel Logical GLSL450
430 OpEntryPoint Fragment %4 "main"
431 OpExecutionMode %4 OriginUpperLeft
432 OpSource ESSL 320
433 %2 = OpTypeVoid
434 %3 = OpTypeFunction %2
435 %6 = OpTypeInt 32 1
436 %7 = OpTypePointer Function %6
437 %9 = OpConstant %6 2
438 %10 = OpTypeBool
439 %11 = OpConstantFalse %10
440 %15 = OpConstant %6 0
441 %50 = OpConstant %6 0
442 %4 = OpFunction %2 None %3
443 %5 = OpLabel
444 %8 = OpVariable %7 Function
445 OpStore %8 %9
446 OpSelectionMerge %13 None
447 OpBranchConditional %11 %12 %13
448 %12 = OpLabel
449 %14 = OpLoad %6 %8
450 %16 = OpIEqual %10 %14 %15
451 OpSelectionMerge %18 None
452 OpBranchConditional %16 %17 %40
453 %17 = OpLabel
454 OpBranch %18
455 %40 = OpLabel
456 OpBranch %18
457 %18 = OpLabel
458 OpBranch %13
459 %13 = OpLabel
460 OpReturn
461 OpFunctionEnd
462 )";
463
464 const auto env = SPV_ENV_UNIVERSAL_1_5;
465 const auto consumer = nullptr;
466 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
467 spvtools::ValidatorOptions validator_options;
468 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
469 kConsoleMessageConsumer));
470 TransformationContext transformation_context(
471 MakeUnique<FactManager>(context.get()), validator_options);
472 // Dead blocks
473 transformation_context.GetFactManager()->AddFactBlockIsDead(12);
474 transformation_context.GetFactManager()->AddFactBlockIsDead(17);
475 transformation_context.GetFactManager()->AddFactBlockIsDead(18);
476
477 // Declare synonym
478 ASSERT_TRUE(transformation_context.GetFactManager()->MaybeAddFact(
479 MakeSynonymFact(15, 50)));
480
481 // Bad because the block 18 is dead.
482 ASSERT_FALSE(TransformationAddOpPhiSynonym(18, {{{17, 15}, {40, 50}}}, 100)
483 .IsApplicable(context.get(), transformation_context));
484 }
485
486 } // namespace
487 } // namespace fuzz
488 } // namespace spvtools
489