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/replayer.h"
16
17 #include "gtest/gtest.h"
18 #include "source/fuzz/data_descriptor.h"
19 #include "source/fuzz/fuzzer_util.h"
20 #include "source/fuzz/instruction_descriptor.h"
21 #include "source/fuzz/transformation_add_constant_scalar.h"
22 #include "source/fuzz/transformation_add_global_variable.h"
23 #include "source/fuzz/transformation_add_parameter.h"
24 #include "source/fuzz/transformation_add_synonym.h"
25 #include "source/fuzz/transformation_flatten_conditional_branch.h"
26 #include "source/fuzz/transformation_split_block.h"
27 #include "test/fuzz/fuzz_test_util.h"
28
29 namespace spvtools {
30 namespace fuzz {
31 namespace {
32
TEST(ReplayerTest,PartialReplay)33 TEST(ReplayerTest, PartialReplay) {
34 const std::string kTestShader = R"(
35 OpCapability Shader
36 %1 = OpExtInstImport "GLSL.std.450"
37 OpMemoryModel Logical GLSL450
38 OpEntryPoint Fragment %4 "main"
39 OpExecutionMode %4 OriginUpperLeft
40 OpSource ESSL 310
41 OpName %4 "main"
42 OpName %8 "g"
43 OpName %11 "x"
44 %2 = OpTypeVoid
45 %3 = OpTypeFunction %2
46 %6 = OpTypeInt 32 1
47 %7 = OpTypePointer Private %6
48 %8 = OpVariable %7 Private
49 %9 = OpConstant %6 10
50 %10 = OpTypePointer Function %6
51 %4 = OpFunction %2 None %3
52 %5 = OpLabel
53 %11 = OpVariable %10 Function
54 OpStore %8 %9
55 %12 = OpLoad %6 %8
56 OpStore %11 %12
57 %13 = OpLoad %6 %8
58 OpStore %11 %13
59 %14 = OpLoad %6 %8
60 OpStore %11 %14
61 %15 = OpLoad %6 %8
62 OpStore %11 %15
63 %16 = OpLoad %6 %8
64 OpStore %11 %16
65 %17 = OpLoad %6 %8
66 OpStore %11 %17
67 %18 = OpLoad %6 %8
68 OpStore %11 %18
69 %19 = OpLoad %6 %8
70 OpStore %11 %19
71 %20 = OpLoad %6 %8
72 OpStore %11 %20
73 %21 = OpLoad %6 %8
74 OpStore %11 %21
75 %22 = OpLoad %6 %8
76 OpStore %11 %22
77 OpReturn
78 OpFunctionEnd
79 )";
80
81 const auto env = SPV_ENV_UNIVERSAL_1_3;
82 spvtools::ValidatorOptions validator_options;
83
84 std::vector<uint32_t> binary_in;
85 SpirvTools t(env);
86 t.SetMessageConsumer(kConsoleMessageConsumer);
87 ASSERT_TRUE(t.Assemble(kTestShader, &binary_in, kFuzzAssembleOption));
88 ASSERT_TRUE(t.Validate(binary_in));
89
90 protobufs::TransformationSequence transformations;
91 for (uint32_t id = 12; id <= 22; id++) {
92 *transformations.add_transformation() =
93 TransformationSplitBlock(MakeInstructionDescriptor(id, SpvOpLoad, 0),
94 id + 100)
95 .ToMessage();
96 }
97
98 {
99 // Full replay
100 protobufs::FactSequence empty_facts;
101 auto replayer_result =
102 Replayer(env, kConsoleMessageConsumer, binary_in, empty_facts,
103 transformations, 11, true, validator_options)
104 .Run();
105 // Replay should succeed.
106 ASSERT_EQ(Replayer::ReplayerResultStatus::kComplete,
107 replayer_result.status);
108 // All transformations should be applied.
109 ASSERT_TRUE(google::protobuf::util::MessageDifferencer::Equals(
110 transformations, replayer_result.applied_transformations));
111
112 const std::string kFullySplitShader = R"(
113 OpCapability Shader
114 %1 = OpExtInstImport "GLSL.std.450"
115 OpMemoryModel Logical GLSL450
116 OpEntryPoint Fragment %4 "main"
117 OpExecutionMode %4 OriginUpperLeft
118 OpSource ESSL 310
119 OpName %4 "main"
120 OpName %8 "g"
121 OpName %11 "x"
122 %2 = OpTypeVoid
123 %3 = OpTypeFunction %2
124 %6 = OpTypeInt 32 1
125 %7 = OpTypePointer Private %6
126 %8 = OpVariable %7 Private
127 %9 = OpConstant %6 10
128 %10 = OpTypePointer Function %6
129 %4 = OpFunction %2 None %3
130 %5 = OpLabel
131 %11 = OpVariable %10 Function
132 OpStore %8 %9
133 OpBranch %112
134 %112 = OpLabel
135 %12 = OpLoad %6 %8
136 OpStore %11 %12
137 OpBranch %113
138 %113 = OpLabel
139 %13 = OpLoad %6 %8
140 OpStore %11 %13
141 OpBranch %114
142 %114 = OpLabel
143 %14 = OpLoad %6 %8
144 OpStore %11 %14
145 OpBranch %115
146 %115 = OpLabel
147 %15 = OpLoad %6 %8
148 OpStore %11 %15
149 OpBranch %116
150 %116 = OpLabel
151 %16 = OpLoad %6 %8
152 OpStore %11 %16
153 OpBranch %117
154 %117 = OpLabel
155 %17 = OpLoad %6 %8
156 OpStore %11 %17
157 OpBranch %118
158 %118 = OpLabel
159 %18 = OpLoad %6 %8
160 OpStore %11 %18
161 OpBranch %119
162 %119 = OpLabel
163 %19 = OpLoad %6 %8
164 OpStore %11 %19
165 OpBranch %120
166 %120 = OpLabel
167 %20 = OpLoad %6 %8
168 OpStore %11 %20
169 OpBranch %121
170 %121 = OpLabel
171 %21 = OpLoad %6 %8
172 OpStore %11 %21
173 OpBranch %122
174 %122 = OpLabel
175 %22 = OpLoad %6 %8
176 OpStore %11 %22
177 OpReturn
178 OpFunctionEnd
179 )";
180 ASSERT_TRUE(IsEqual(env, kFullySplitShader,
181 replayer_result.transformed_module.get()));
182 }
183
184 {
185 // Half replay
186 protobufs::FactSequence empty_facts;
187 auto replayer_result =
188 Replayer(env, kConsoleMessageConsumer, binary_in, empty_facts,
189 transformations, 5, true, validator_options)
190 .Run();
191 // Replay should succeed.
192 ASSERT_EQ(Replayer::ReplayerResultStatus::kComplete,
193 replayer_result.status);
194 // The first 5 transformations should be applied
195 ASSERT_EQ(5, replayer_result.applied_transformations.transformation_size());
196 for (uint32_t i = 0; i < 5; i++) {
197 ASSERT_TRUE(google::protobuf::util::MessageDifferencer::Equals(
198 transformations.transformation(i),
199 replayer_result.applied_transformations.transformation(i)));
200 }
201
202 const std::string kHalfSplitShader = R"(
203 OpCapability Shader
204 %1 = OpExtInstImport "GLSL.std.450"
205 OpMemoryModel Logical GLSL450
206 OpEntryPoint Fragment %4 "main"
207 OpExecutionMode %4 OriginUpperLeft
208 OpSource ESSL 310
209 OpName %4 "main"
210 OpName %8 "g"
211 OpName %11 "x"
212 %2 = OpTypeVoid
213 %3 = OpTypeFunction %2
214 %6 = OpTypeInt 32 1
215 %7 = OpTypePointer Private %6
216 %8 = OpVariable %7 Private
217 %9 = OpConstant %6 10
218 %10 = OpTypePointer Function %6
219 %4 = OpFunction %2 None %3
220 %5 = OpLabel
221 %11 = OpVariable %10 Function
222 OpStore %8 %9
223 OpBranch %112
224 %112 = OpLabel
225 %12 = OpLoad %6 %8
226 OpStore %11 %12
227 OpBranch %113
228 %113 = OpLabel
229 %13 = OpLoad %6 %8
230 OpStore %11 %13
231 OpBranch %114
232 %114 = OpLabel
233 %14 = OpLoad %6 %8
234 OpStore %11 %14
235 OpBranch %115
236 %115 = OpLabel
237 %15 = OpLoad %6 %8
238 OpStore %11 %15
239 OpBranch %116
240 %116 = OpLabel
241 %16 = OpLoad %6 %8
242 OpStore %11 %16
243 %17 = OpLoad %6 %8
244 OpStore %11 %17
245 %18 = OpLoad %6 %8
246 OpStore %11 %18
247 %19 = OpLoad %6 %8
248 OpStore %11 %19
249 %20 = OpLoad %6 %8
250 OpStore %11 %20
251 %21 = OpLoad %6 %8
252 OpStore %11 %21
253 %22 = OpLoad %6 %8
254 OpStore %11 %22
255 OpReturn
256 OpFunctionEnd
257 )";
258 ASSERT_TRUE(IsEqual(env, kHalfSplitShader,
259 replayer_result.transformed_module.get()));
260 }
261
262 {
263 // Empty replay
264 protobufs::FactSequence empty_facts;
265 auto replayer_result =
266 Replayer(env, kConsoleMessageConsumer, binary_in, empty_facts,
267 transformations, 0, true, validator_options)
268 .Run();
269 // Replay should succeed.
270 ASSERT_EQ(Replayer::ReplayerResultStatus::kComplete,
271 replayer_result.status);
272 // No transformations should be applied
273 ASSERT_EQ(0, replayer_result.applied_transformations.transformation_size());
274 ASSERT_TRUE(
275 IsEqual(env, kTestShader, replayer_result.transformed_module.get()));
276 }
277
278 {
279 // Invalid replay: too many transformations
280 protobufs::FactSequence empty_facts;
281 // The number of transformations requested to be applied exceeds the number
282 // of transformations
283 auto replayer_result =
284 Replayer(env, kConsoleMessageConsumer, binary_in, empty_facts,
285 transformations, 12, true, validator_options)
286 .Run();
287
288 // Replay should not succeed.
289 ASSERT_EQ(Replayer::ReplayerResultStatus::kTooManyTransformationsRequested,
290 replayer_result.status);
291 // No transformations should be applied
292 ASSERT_EQ(0, replayer_result.applied_transformations.transformation_size());
293 // The output binary should be empty
294 ASSERT_EQ(nullptr, replayer_result.transformed_module);
295 }
296 }
297
TEST(ReplayerTest,CheckFactsAfterReplay)298 TEST(ReplayerTest, CheckFactsAfterReplay) {
299 const std::string kTestShader = R"(
300 OpCapability Shader
301 %1 = OpExtInstImport "GLSL.std.450"
302 OpMemoryModel Logical GLSL450
303 OpEntryPoint Fragment %4 "main"
304 OpExecutionMode %4 OriginUpperLeft
305 OpSource ESSL 320
306 %2 = OpTypeVoid
307 %3 = OpTypeFunction %2
308 %8 = OpTypeInt 32 1
309 %9 = OpTypePointer Function %8
310 %50 = OpTypePointer Private %8
311 %11 = OpConstant %8 1
312 %4 = OpFunction %2 None %3
313 %5 = OpLabel
314 %10 = OpVariable %9 Function
315 OpStore %10 %11
316 %12 = OpFunctionCall %2 %6
317 OpReturn
318 OpFunctionEnd
319 %6 = OpFunction %2 None %3
320 %7 = OpLabel
321 OpReturn
322 OpFunctionEnd
323 )";
324
325 const auto env = SPV_ENV_UNIVERSAL_1_3;
326 spvtools::ValidatorOptions validator_options;
327
328 std::vector<uint32_t> binary_in;
329 SpirvTools t(env);
330 t.SetMessageConsumer(kConsoleMessageConsumer);
331 ASSERT_TRUE(t.Assemble(kTestShader, &binary_in, kFuzzAssembleOption));
332 ASSERT_TRUE(t.Validate(binary_in));
333
334 protobufs::TransformationSequence transformations;
335 *transformations.add_transformation() =
336 TransformationAddConstantScalar(100, 8, {42}, true).ToMessage();
337 *transformations.add_transformation() =
338 TransformationAddGlobalVariable(101, 50, SpvStorageClassPrivate, 100,
339 true)
340 .ToMessage();
341 *transformations.add_transformation() =
342 TransformationAddParameter(6, 102, 8, {{12, 100}}, 103).ToMessage();
343 *transformations.add_transformation() =
344 TransformationAddSynonym(
345 11,
346 protobufs::TransformationAddSynonym::SynonymType::
347 TransformationAddSynonym_SynonymType_COPY_OBJECT,
348 104, MakeInstructionDescriptor(12, SpvOpFunctionCall, 0))
349 .ToMessage();
350
351 // Full replay
352 protobufs::FactSequence empty_facts;
353 auto replayer_result =
354 Replayer(env, kConsoleMessageConsumer, binary_in, empty_facts,
355 transformations, transformations.transformation_size(), true,
356 validator_options)
357 .Run();
358 // Replay should succeed.
359 ASSERT_EQ(Replayer::ReplayerResultStatus::kComplete, replayer_result.status);
360 // All transformations should be applied.
361 ASSERT_TRUE(google::protobuf::util::MessageDifferencer::Equals(
362 transformations, replayer_result.applied_transformations));
363
364 const std::string kExpected = R"(
365 OpCapability Shader
366 %1 = OpExtInstImport "GLSL.std.450"
367 OpMemoryModel Logical GLSL450
368 OpEntryPoint Fragment %4 "main"
369 OpExecutionMode %4 OriginUpperLeft
370 OpSource ESSL 320
371 %2 = OpTypeVoid
372 %3 = OpTypeFunction %2
373 %8 = OpTypeInt 32 1
374 %9 = OpTypePointer Function %8
375 %50 = OpTypePointer Private %8
376 %11 = OpConstant %8 1
377 %100 = OpConstant %8 42
378 %101 = OpVariable %50 Private %100
379 %103 = OpTypeFunction %2 %8
380 %4 = OpFunction %2 None %3
381 %5 = OpLabel
382 %10 = OpVariable %9 Function
383 OpStore %10 %11
384 %104 = OpCopyObject %8 %11
385 %12 = OpFunctionCall %2 %6 %100
386 OpReturn
387 OpFunctionEnd
388 %6 = OpFunction %2 None %103
389 %102 = OpFunctionParameter %8
390 %7 = OpLabel
391 OpReturn
392 OpFunctionEnd
393 )";
394 ASSERT_TRUE(
395 IsEqual(env, kExpected, replayer_result.transformed_module.get()));
396
397 ASSERT_TRUE(
398 replayer_result.transformation_context->GetFactManager()->IdIsIrrelevant(
399 100));
400 ASSERT_TRUE(replayer_result.transformation_context->GetFactManager()
401 ->PointeeValueIsIrrelevant(101));
402 ASSERT_TRUE(
403 replayer_result.transformation_context->GetFactManager()->IdIsIrrelevant(
404 102));
405 ASSERT_TRUE(
406 replayer_result.transformation_context->GetFactManager()->IsSynonymous(
407 MakeDataDescriptor(11, {}), MakeDataDescriptor(104, {})));
408 }
409
TEST(ReplayerTest,ReplayWithOverflowIds)410 TEST(ReplayerTest, ReplayWithOverflowIds) {
411 const std::string kTestShader = R"(
412 OpCapability Shader
413 %1 = OpExtInstImport "GLSL.std.450"
414 OpMemoryModel Logical GLSL450
415 OpEntryPoint Fragment %4 "main"
416 OpExecutionMode %4 OriginUpperLeft
417 OpSource ESSL 320
418 %2 = OpTypeVoid
419 %3 = OpTypeFunction %2
420 %6 = OpTypeInt 32 1
421 %7 = OpTypePointer Function %6
422 %50 = OpTypePointer Private %6
423 %9 = OpConstant %6 2
424 %11 = OpConstant %6 0
425 %12 = OpTypeBool
426 %17 = OpConstant %6 1
427 %4 = OpFunction %2 None %3
428 %5 = OpLabel
429 %8 = OpVariable %7 Function
430 OpStore %8 %9
431 %10 = OpLoad %6 %8
432 %13 = OpSGreaterThan %12 %10 %11
433 OpSelectionMerge %15 None
434 OpBranchConditional %13 %14 %15
435 %14 = OpLabel
436 %16 = OpLoad %6 %8
437 %18 = OpIAdd %6 %16 %17
438 OpStore %8 %18
439 OpBranch %15
440 %15 = OpLabel
441 OpReturn
442 OpFunctionEnd
443 )";
444
445 const auto env = SPV_ENV_UNIVERSAL_1_3;
446 spvtools::ValidatorOptions validator_options;
447
448 std::vector<uint32_t> binary_in;
449 SpirvTools t(env);
450 t.SetMessageConsumer(kConsoleMessageConsumer);
451 ASSERT_TRUE(t.Assemble(kTestShader, &binary_in, kFuzzAssembleOption));
452 ASSERT_TRUE(t.Validate(binary_in));
453
454 protobufs::TransformationSequence transformations;
455 *transformations.add_transformation() =
456 TransformationFlattenConditionalBranch(5, true, 0, 0, 0, {}).ToMessage();
457 *transformations.add_transformation() =
458 TransformationAddGlobalVariable(101, 50, SpvStorageClassPrivate, 11, true)
459 .ToMessage();
460
461 protobufs::FactSequence empty_facts;
462 auto replayer_result =
463 Replayer(env, kConsoleMessageConsumer, binary_in, empty_facts,
464 transformations, transformations.transformation_size(), true,
465 validator_options)
466 .Run();
467 // Replay should succeed.
468 ASSERT_EQ(Replayer::ReplayerResultStatus::kComplete, replayer_result.status);
469 // All transformations should be applied.
470 ASSERT_EQ(2, replayer_result.applied_transformations.transformation_size());
471 }
472
473 } // namespace
474 } // namespace fuzz
475 } // namespace spvtools
476