1 // Copyright (c) 2019 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_function.h"
16
17 #include "gtest/gtest.h"
18 #include "source/fuzz/fuzzer_util.h"
19 #include "source/fuzz/instruction_message.h"
20 #include "test/fuzz/fuzz_test_util.h"
21
22 namespace spvtools {
23 namespace fuzz {
24 namespace {
25
MakeAccessClampingInfo(uint32_t access_chain_id,const std::vector<std::pair<uint32_t,uint32_t>> & compare_and_select_ids)26 protobufs::AccessChainClampingInfo MakeAccessClampingInfo(
27 uint32_t access_chain_id,
28 const std::vector<std::pair<uint32_t, uint32_t>>& compare_and_select_ids) {
29 protobufs::AccessChainClampingInfo result;
30 result.set_access_chain_id(access_chain_id);
31 for (auto& compare_and_select_id : compare_and_select_ids) {
32 auto pair = result.add_compare_and_select_ids();
33 pair->set_first(compare_and_select_id.first);
34 pair->set_second(compare_and_select_id.second);
35 }
36 return result;
37 }
38
GetInstructionsForFunction(spv_target_env env,const MessageConsumer & consumer,const std::string & donor,uint32_t function_id)39 std::vector<protobufs::Instruction> GetInstructionsForFunction(
40 spv_target_env env, const MessageConsumer& consumer,
41 const std::string& donor, uint32_t function_id) {
42 std::vector<protobufs::Instruction> result;
43 const auto donor_context =
44 BuildModule(env, consumer, donor, kFuzzAssembleOption);
45 spvtools::ValidatorOptions validator_options;
46 assert(fuzzerutil::IsValidAndWellFormed(
47 donor_context.get(), validator_options, kConsoleMessageConsumer) &&
48 "The given donor must be valid.");
49 for (auto& function : *donor_context->module()) {
50 if (function.result_id() == function_id) {
51 function.ForEachInst([&result](opt::Instruction* inst) {
52 opt::Instruction::OperandList input_operands;
53 for (uint32_t i = 0; i < inst->NumInOperands(); i++) {
54 input_operands.push_back(inst->GetInOperand(i));
55 }
56 result.push_back(MakeInstructionMessage(inst->opcode(), inst->type_id(),
57 inst->result_id(),
58 input_operands));
59 });
60 break;
61 }
62 }
63 assert(!result.empty() && "The required function should have been found.");
64 return result;
65 }
66
67 // Returns true if and only if every pointer parameter and variable associated
68 // with |function_id| in |context| is known by |transformation_context| to be
69 // irrelevant, with the exception of |loop_limiter_id|, which must not be
70 // irrelevant. (It can be 0 if no loop limiter is expected, and 0 should not be
71 // deemed irrelevant).
AllVariablesAndParametersExceptLoopLimiterAreIrrelevant(opt::IRContext * context,const TransformationContext & transformation_context,uint32_t function_id,uint32_t loop_limiter_id)72 bool AllVariablesAndParametersExceptLoopLimiterAreIrrelevant(
73 opt::IRContext* context,
74 const TransformationContext& transformation_context, uint32_t function_id,
75 uint32_t loop_limiter_id) {
76 // Look at all the functions until the function of interest is found.
77 for (auto& function : *context->module()) {
78 if (function.result_id() != function_id) {
79 continue;
80 }
81 // Check that the parameters are all irrelevant.
82 bool found_non_irrelevant_parameter = false;
83 function.ForEachParam([context, &transformation_context,
84 &found_non_irrelevant_parameter](
85 opt::Instruction* inst) {
86 if (context->get_def_use_mgr()->GetDef(inst->type_id())->opcode() ==
87 SpvOpTypePointer &&
88 !transformation_context.GetFactManager()->PointeeValueIsIrrelevant(
89 inst->result_id())) {
90 found_non_irrelevant_parameter = true;
91 }
92 });
93 if (found_non_irrelevant_parameter) {
94 // A non-irrelevant parameter was found.
95 return false;
96 }
97 // Look through the instructions in the function's first block.
98 for (auto& inst : *function.begin()) {
99 if (inst.opcode() != SpvOpVariable) {
100 // We have found a non-variable instruction; this means we have gotten
101 // past all variables, so we are done.
102 return true;
103 }
104 // The variable should be irrelevant if and only if it is not the loop
105 // limiter.
106 if ((inst.result_id() == loop_limiter_id) ==
107 transformation_context.GetFactManager()->PointeeValueIsIrrelevant(
108 inst.result_id())) {
109 return false;
110 }
111 }
112 assert(false &&
113 "We should have processed all variables and returned by "
114 "this point.");
115 }
116 assert(false && "We should have found the function of interest.");
117 return true;
118 }
119
TEST(TransformationAddFunctionTest,BasicTest)120 TEST(TransformationAddFunctionTest, BasicTest) {
121 std::string shader = R"(
122 OpCapability Shader
123 %1 = OpExtInstImport "GLSL.std.450"
124 OpMemoryModel Logical GLSL450
125 OpEntryPoint Fragment %4 "main"
126 OpExecutionMode %4 OriginUpperLeft
127 OpSource ESSL 310
128 %2 = OpTypeVoid
129 %3 = OpTypeFunction %2
130 %6 = OpTypeInt 32 1
131 %7 = OpTypePointer Function %6
132 %8 = OpTypeFloat 32
133 %9 = OpTypePointer Function %8
134 %10 = OpTypeFunction %8 %7 %9
135 %18 = OpConstant %8 0
136 %20 = OpConstant %6 0
137 %28 = OpTypeBool
138 %37 = OpConstant %6 1
139 %42 = OpTypePointer Private %8
140 %43 = OpVariable %42 Private
141 %47 = OpConstant %8 1
142 %4 = OpFunction %2 None %3
143 %5 = OpLabel
144 OpReturn
145 OpFunctionEnd
146 )";
147
148 const auto env = SPV_ENV_UNIVERSAL_1_4;
149 const auto consumer = nullptr;
150 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
151 spvtools::ValidatorOptions validator_options;
152 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
153 kConsoleMessageConsumer));
154 TransformationContext transformation_context(
155 MakeUnique<FactManager>(context.get()), validator_options);
156 TransformationAddFunction transformation1(std::vector<protobufs::Instruction>(
157 {MakeInstructionMessage(
158 SpvOpFunction, 8, 13,
159 {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, {SpvFunctionControlMaskNone}},
160 {SPV_OPERAND_TYPE_ID, {10}}}),
161 MakeInstructionMessage(SpvOpFunctionParameter, 7, 11, {}),
162 MakeInstructionMessage(SpvOpFunctionParameter, 9, 12, {}),
163 MakeInstructionMessage(SpvOpLabel, 0, 14, {}),
164 MakeInstructionMessage(
165 SpvOpVariable, 9, 17,
166 {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}}),
167 MakeInstructionMessage(
168 SpvOpVariable, 7, 19,
169 {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}}),
170 MakeInstructionMessage(
171 SpvOpStore, 0, 0,
172 {{SPV_OPERAND_TYPE_ID, {17}}, {SPV_OPERAND_TYPE_ID, {18}}}),
173 MakeInstructionMessage(
174 SpvOpStore, 0, 0,
175 {{SPV_OPERAND_TYPE_ID, {19}}, {SPV_OPERAND_TYPE_ID, {20}}}),
176 MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {21}}}),
177 MakeInstructionMessage(SpvOpLabel, 0, 21, {}),
178 MakeInstructionMessage(
179 SpvOpLoopMerge, 0, 0,
180 {{SPV_OPERAND_TYPE_ID, {23}},
181 {SPV_OPERAND_TYPE_ID, {24}},
182 {SPV_OPERAND_TYPE_LOOP_CONTROL, {SpvLoopControlMaskNone}}}),
183 MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {25}}}),
184 MakeInstructionMessage(SpvOpLabel, 0, 25, {}),
185 MakeInstructionMessage(SpvOpLoad, 6, 26, {{SPV_OPERAND_TYPE_ID, {19}}}),
186 MakeInstructionMessage(SpvOpLoad, 6, 27, {{SPV_OPERAND_TYPE_ID, {11}}}),
187 MakeInstructionMessage(
188 SpvOpSLessThan, 28, 29,
189 {{SPV_OPERAND_TYPE_ID, {26}}, {SPV_OPERAND_TYPE_ID, {27}}}),
190 MakeInstructionMessage(SpvOpBranchConditional, 0, 0,
191 {{SPV_OPERAND_TYPE_ID, {29}},
192 {SPV_OPERAND_TYPE_ID, {22}},
193 {SPV_OPERAND_TYPE_ID, {23}}}),
194 MakeInstructionMessage(SpvOpLabel, 0, 22, {}),
195 MakeInstructionMessage(SpvOpLoad, 8, 30, {{SPV_OPERAND_TYPE_ID, {12}}}),
196 MakeInstructionMessage(SpvOpLoad, 6, 31, {{SPV_OPERAND_TYPE_ID, {19}}}),
197 MakeInstructionMessage(SpvOpConvertSToF, 8, 32,
198 {{SPV_OPERAND_TYPE_ID, {31}}}),
199 MakeInstructionMessage(
200 SpvOpFMul, 8, 33,
201 {{SPV_OPERAND_TYPE_ID, {30}}, {SPV_OPERAND_TYPE_ID, {32}}}),
202 MakeInstructionMessage(SpvOpLoad, 8, 34, {{SPV_OPERAND_TYPE_ID, {17}}}),
203 MakeInstructionMessage(
204 SpvOpFAdd, 8, 35,
205 {{SPV_OPERAND_TYPE_ID, {34}}, {SPV_OPERAND_TYPE_ID, {33}}}),
206 MakeInstructionMessage(
207 SpvOpStore, 0, 0,
208 {{SPV_OPERAND_TYPE_ID, {17}}, {SPV_OPERAND_TYPE_ID, {35}}}),
209 MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {24}}}),
210 MakeInstructionMessage(SpvOpLabel, 0, 24, {}),
211 MakeInstructionMessage(SpvOpLoad, 6, 36, {{SPV_OPERAND_TYPE_ID, {19}}}),
212 MakeInstructionMessage(
213 SpvOpIAdd, 6, 38,
214 {{SPV_OPERAND_TYPE_ID, {36}}, {SPV_OPERAND_TYPE_ID, {37}}}),
215 MakeInstructionMessage(
216 SpvOpStore, 0, 0,
217 {{SPV_OPERAND_TYPE_ID, {19}}, {SPV_OPERAND_TYPE_ID, {38}}}),
218 MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {21}}}),
219 MakeInstructionMessage(SpvOpLabel, 0, 23, {}),
220 MakeInstructionMessage(SpvOpLoad, 8, 39, {{SPV_OPERAND_TYPE_ID, {17}}}),
221 MakeInstructionMessage(SpvOpReturnValue, 0, 0,
222 {{SPV_OPERAND_TYPE_ID, {39}}}),
223 MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {})}));
224
225 ASSERT_TRUE(
226 transformation1.IsApplicable(context.get(), transformation_context));
227 ApplyAndCheckFreshIds(transformation1, context.get(),
228 &transformation_context);
229 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
230 kConsoleMessageConsumer));
231
232 std::string after_transformation1 = R"(
233 OpCapability Shader
234 %1 = OpExtInstImport "GLSL.std.450"
235 OpMemoryModel Logical GLSL450
236 OpEntryPoint Fragment %4 "main"
237 OpExecutionMode %4 OriginUpperLeft
238 OpSource ESSL 310
239 %2 = OpTypeVoid
240 %3 = OpTypeFunction %2
241 %6 = OpTypeInt 32 1
242 %7 = OpTypePointer Function %6
243 %8 = OpTypeFloat 32
244 %9 = OpTypePointer Function %8
245 %10 = OpTypeFunction %8 %7 %9
246 %18 = OpConstant %8 0
247 %20 = OpConstant %6 0
248 %28 = OpTypeBool
249 %37 = OpConstant %6 1
250 %42 = OpTypePointer Private %8
251 %43 = OpVariable %42 Private
252 %47 = OpConstant %8 1
253 %4 = OpFunction %2 None %3
254 %5 = OpLabel
255 OpReturn
256 OpFunctionEnd
257 %13 = OpFunction %8 None %10
258 %11 = OpFunctionParameter %7
259 %12 = OpFunctionParameter %9
260 %14 = OpLabel
261 %17 = OpVariable %9 Function
262 %19 = OpVariable %7 Function
263 OpStore %17 %18
264 OpStore %19 %20
265 OpBranch %21
266 %21 = OpLabel
267 OpLoopMerge %23 %24 None
268 OpBranch %25
269 %25 = OpLabel
270 %26 = OpLoad %6 %19
271 %27 = OpLoad %6 %11
272 %29 = OpSLessThan %28 %26 %27
273 OpBranchConditional %29 %22 %23
274 %22 = OpLabel
275 %30 = OpLoad %8 %12
276 %31 = OpLoad %6 %19
277 %32 = OpConvertSToF %8 %31
278 %33 = OpFMul %8 %30 %32
279 %34 = OpLoad %8 %17
280 %35 = OpFAdd %8 %34 %33
281 OpStore %17 %35
282 OpBranch %24
283 %24 = OpLabel
284 %36 = OpLoad %6 %19
285 %38 = OpIAdd %6 %36 %37
286 OpStore %19 %38
287 OpBranch %21
288 %23 = OpLabel
289 %39 = OpLoad %8 %17
290 OpReturnValue %39
291 OpFunctionEnd
292 )";
293 ASSERT_TRUE(IsEqual(env, after_transformation1, context.get()));
294 ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(14));
295 ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(21));
296 ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(22));
297 ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(23));
298 ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(24));
299 ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(25));
300
301 TransformationAddFunction transformation2(std::vector<protobufs::Instruction>(
302 {MakeInstructionMessage(
303 SpvOpFunction, 2, 15,
304 {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, {SpvFunctionControlMaskNone}},
305 {SPV_OPERAND_TYPE_ID, {3}}}),
306 MakeInstructionMessage(SpvOpLabel, 0, 16, {}),
307 MakeInstructionMessage(
308 SpvOpVariable, 7, 44,
309 {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}}),
310 MakeInstructionMessage(
311 SpvOpVariable, 9, 45,
312 {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}}),
313 MakeInstructionMessage(
314 SpvOpVariable, 7, 48,
315 {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}}),
316 MakeInstructionMessage(
317 SpvOpVariable, 9, 49,
318 {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}}),
319 MakeInstructionMessage(
320 SpvOpStore, 0, 0,
321 {{SPV_OPERAND_TYPE_ID, {44}}, {SPV_OPERAND_TYPE_ID, {20}}}),
322 MakeInstructionMessage(
323 SpvOpStore, 0, 0,
324 {{SPV_OPERAND_TYPE_ID, {45}}, {SPV_OPERAND_TYPE_ID, {18}}}),
325 MakeInstructionMessage(SpvOpFunctionCall, 8, 46,
326 {{SPV_OPERAND_TYPE_ID, {13}},
327 {SPV_OPERAND_TYPE_ID, {44}},
328 {SPV_OPERAND_TYPE_ID, {45}}}),
329 MakeInstructionMessage(
330 SpvOpStore, 0, 0,
331 {{SPV_OPERAND_TYPE_ID, {48}}, {SPV_OPERAND_TYPE_ID, {37}}}),
332 MakeInstructionMessage(
333 SpvOpStore, 0, 0,
334 {{SPV_OPERAND_TYPE_ID, {49}}, {SPV_OPERAND_TYPE_ID, {47}}}),
335 MakeInstructionMessage(SpvOpFunctionCall, 8, 50,
336 {{SPV_OPERAND_TYPE_ID, {13}},
337 {SPV_OPERAND_TYPE_ID, {48}},
338 {SPV_OPERAND_TYPE_ID, {49}}}),
339 MakeInstructionMessage(
340 SpvOpFAdd, 8, 51,
341 {{SPV_OPERAND_TYPE_ID, {46}}, {SPV_OPERAND_TYPE_ID, {50}}}),
342 MakeInstructionMessage(
343 SpvOpStore, 0, 0,
344 {{SPV_OPERAND_TYPE_ID, {43}}, {SPV_OPERAND_TYPE_ID, {51}}}),
345 MakeInstructionMessage(SpvOpReturn, 0, 0, {}),
346 MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {})}));
347
348 ASSERT_TRUE(
349 transformation2.IsApplicable(context.get(), transformation_context));
350 ApplyAndCheckFreshIds(transformation2, context.get(),
351 &transformation_context);
352 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
353 kConsoleMessageConsumer));
354
355 std::string after_transformation2 = R"(
356 OpCapability Shader
357 %1 = OpExtInstImport "GLSL.std.450"
358 OpMemoryModel Logical GLSL450
359 OpEntryPoint Fragment %4 "main"
360 OpExecutionMode %4 OriginUpperLeft
361 OpSource ESSL 310
362 %2 = OpTypeVoid
363 %3 = OpTypeFunction %2
364 %6 = OpTypeInt 32 1
365 %7 = OpTypePointer Function %6
366 %8 = OpTypeFloat 32
367 %9 = OpTypePointer Function %8
368 %10 = OpTypeFunction %8 %7 %9
369 %18 = OpConstant %8 0
370 %20 = OpConstant %6 0
371 %28 = OpTypeBool
372 %37 = OpConstant %6 1
373 %42 = OpTypePointer Private %8
374 %43 = OpVariable %42 Private
375 %47 = OpConstant %8 1
376 %4 = OpFunction %2 None %3
377 %5 = OpLabel
378 OpReturn
379 OpFunctionEnd
380 %13 = OpFunction %8 None %10
381 %11 = OpFunctionParameter %7
382 %12 = OpFunctionParameter %9
383 %14 = OpLabel
384 %17 = OpVariable %9 Function
385 %19 = OpVariable %7 Function
386 OpStore %17 %18
387 OpStore %19 %20
388 OpBranch %21
389 %21 = OpLabel
390 OpLoopMerge %23 %24 None
391 OpBranch %25
392 %25 = OpLabel
393 %26 = OpLoad %6 %19
394 %27 = OpLoad %6 %11
395 %29 = OpSLessThan %28 %26 %27
396 OpBranchConditional %29 %22 %23
397 %22 = OpLabel
398 %30 = OpLoad %8 %12
399 %31 = OpLoad %6 %19
400 %32 = OpConvertSToF %8 %31
401 %33 = OpFMul %8 %30 %32
402 %34 = OpLoad %8 %17
403 %35 = OpFAdd %8 %34 %33
404 OpStore %17 %35
405 OpBranch %24
406 %24 = OpLabel
407 %36 = OpLoad %6 %19
408 %38 = OpIAdd %6 %36 %37
409 OpStore %19 %38
410 OpBranch %21
411 %23 = OpLabel
412 %39 = OpLoad %8 %17
413 OpReturnValue %39
414 OpFunctionEnd
415 %15 = OpFunction %2 None %3
416 %16 = OpLabel
417 %44 = OpVariable %7 Function
418 %45 = OpVariable %9 Function
419 %48 = OpVariable %7 Function
420 %49 = OpVariable %9 Function
421 OpStore %44 %20
422 OpStore %45 %18
423 %46 = OpFunctionCall %8 %13 %44 %45
424 OpStore %48 %37
425 OpStore %49 %47
426 %50 = OpFunctionCall %8 %13 %48 %49
427 %51 = OpFAdd %8 %46 %50
428 OpStore %43 %51
429 OpReturn
430 OpFunctionEnd
431 )";
432 ASSERT_TRUE(IsEqual(env, after_transformation2, context.get()));
433 ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(16));
434 }
435
TEST(TransformationAddFunctionTest,InapplicableTransformations)436 TEST(TransformationAddFunctionTest, InapplicableTransformations) {
437 std::string shader = R"(
438 OpCapability Shader
439 %1 = OpExtInstImport "GLSL.std.450"
440 OpMemoryModel Logical GLSL450
441 OpEntryPoint Fragment %4 "main"
442 OpExecutionMode %4 OriginUpperLeft
443 OpSource ESSL 310
444 %2 = OpTypeVoid
445 %3 = OpTypeFunction %2
446 %6 = OpTypeInt 32 1
447 %7 = OpTypePointer Function %6
448 %8 = OpTypeFloat 32
449 %9 = OpTypePointer Function %8
450 %10 = OpTypeFunction %8 %7 %9
451 %18 = OpConstant %8 0
452 %20 = OpConstant %6 0
453 %28 = OpTypeBool
454 %37 = OpConstant %6 1
455 %42 = OpTypePointer Private %8
456 %43 = OpVariable %42 Private
457 %47 = OpConstant %8 1
458 %4 = OpFunction %2 None %3
459 %5 = OpLabel
460 OpReturn
461 OpFunctionEnd
462 %13 = OpFunction %8 None %10
463 %11 = OpFunctionParameter %7
464 %12 = OpFunctionParameter %9
465 %14 = OpLabel
466 %17 = OpVariable %9 Function
467 %19 = OpVariable %7 Function
468 OpStore %17 %18
469 OpStore %19 %20
470 OpBranch %21
471 %21 = OpLabel
472 OpLoopMerge %23 %24 None
473 OpBranch %25
474 %25 = OpLabel
475 %26 = OpLoad %6 %19
476 %27 = OpLoad %6 %11
477 %29 = OpSLessThan %28 %26 %27
478 OpBranchConditional %29 %22 %23
479 %22 = OpLabel
480 %30 = OpLoad %8 %12
481 %31 = OpLoad %6 %19
482 %32 = OpConvertSToF %8 %31
483 %33 = OpFMul %8 %30 %32
484 %34 = OpLoad %8 %17
485 %35 = OpFAdd %8 %34 %33
486 OpStore %17 %35
487 OpBranch %24
488 %24 = OpLabel
489 %36 = OpLoad %6 %19
490 %38 = OpIAdd %6 %36 %37
491 OpStore %19 %38
492 OpBranch %21
493 %23 = OpLabel
494 %39 = OpLoad %8 %17
495 OpReturnValue %39
496 OpFunctionEnd
497 )";
498
499 const auto env = SPV_ENV_UNIVERSAL_1_4;
500 const auto consumer = nullptr;
501 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
502 spvtools::ValidatorOptions validator_options;
503 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
504 kConsoleMessageConsumer));
505 TransformationContext transformation_context(
506 MakeUnique<FactManager>(context.get()), validator_options);
507 // No instructions
508 ASSERT_FALSE(
509 TransformationAddFunction(std::vector<protobufs::Instruction>({}))
510 .IsApplicable(context.get(), transformation_context));
511
512 // No function begin
513 ASSERT_FALSE(
514 TransformationAddFunction(
515 std::vector<protobufs::Instruction>(
516 {MakeInstructionMessage(SpvOpFunctionParameter, 7, 11, {}),
517 MakeInstructionMessage(SpvOpFunctionParameter, 9, 12, {}),
518 MakeInstructionMessage(SpvOpLabel, 0, 14, {})}))
519 .IsApplicable(context.get(), transformation_context));
520
521 // No OpLabel
522 ASSERT_FALSE(
523 TransformationAddFunction(
524 std::vector<protobufs::Instruction>(
525 {MakeInstructionMessage(SpvOpFunction, 8, 13,
526 {{SPV_OPERAND_TYPE_FUNCTION_CONTROL,
527 {SpvFunctionControlMaskNone}},
528 {SPV_OPERAND_TYPE_ID, {10}}}),
529 MakeInstructionMessage(SpvOpReturnValue, 0, 0,
530 {{SPV_OPERAND_TYPE_ID, {39}}}),
531 MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {})}))
532 .IsApplicable(context.get(), transformation_context));
533
534 // Abrupt end of instructions
535 ASSERT_FALSE(TransformationAddFunction(
536 std::vector<protobufs::Instruction>({MakeInstructionMessage(
537 SpvOpFunction, 8, 13,
538 {{SPV_OPERAND_TYPE_FUNCTION_CONTROL,
539 {SpvFunctionControlMaskNone}},
540 {SPV_OPERAND_TYPE_ID, {10}}})}))
541 .IsApplicable(context.get(), transformation_context));
542
543 // No function end
544 ASSERT_FALSE(
545 TransformationAddFunction(
546 std::vector<protobufs::Instruction>(
547 {MakeInstructionMessage(SpvOpFunction, 8, 13,
548 {{SPV_OPERAND_TYPE_FUNCTION_CONTROL,
549 {SpvFunctionControlMaskNone}},
550 {SPV_OPERAND_TYPE_ID, {10}}}),
551 MakeInstructionMessage(SpvOpLabel, 0, 14, {}),
552 MakeInstructionMessage(SpvOpReturnValue, 0, 0,
553 {{SPV_OPERAND_TYPE_ID, {39}}})}))
554 .IsApplicable(context.get(), transformation_context));
555 }
556
TEST(TransformationAddFunctionTest,LoopLimiters)557 TEST(TransformationAddFunctionTest, LoopLimiters) {
558 std::string 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 %2 = OpTypeVoid
566 %3 = OpTypeFunction %2
567 %6 = OpTypeInt 32 0
568 %7 = OpTypePointer Function %6
569 %8 = OpConstant %6 0
570 %9 = OpConstant %6 1
571 %10 = OpConstant %6 5
572 %11 = OpTypeBool
573 %12 = OpConstantTrue %11
574 %4 = OpFunction %2 None %3
575 %5 = OpLabel
576 OpReturn
577 OpFunctionEnd
578 )";
579
580 const auto env = SPV_ENV_UNIVERSAL_1_4;
581 const auto consumer = nullptr;
582
583 std::vector<protobufs::Instruction> instructions;
584 instructions.push_back(MakeInstructionMessage(
585 SpvOpFunction, 2, 30,
586 {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, {SpvFunctionControlMaskNone}},
587 {SPV_OPERAND_TYPE_TYPE_ID, {3}}}));
588 instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 31, {}));
589 instructions.push_back(
590 MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {20}}}));
591 instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 20, {}));
592 instructions.push_back(MakeInstructionMessage(
593 SpvOpLoopMerge, 0, 0,
594 {{SPV_OPERAND_TYPE_ID, {21}},
595 {SPV_OPERAND_TYPE_ID, {22}},
596 {SPV_OPERAND_TYPE_LOOP_CONTROL, {SpvLoopControlMaskNone}}}));
597 instructions.push_back(MakeInstructionMessage(SpvOpBranchConditional, 0, 0,
598 {{SPV_OPERAND_TYPE_ID, {12}},
599 {SPV_OPERAND_TYPE_ID, {23}},
600 {SPV_OPERAND_TYPE_ID, {21}}}));
601 instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 23, {}));
602 instructions.push_back(MakeInstructionMessage(
603 SpvOpLoopMerge, 0, 0,
604 {{SPV_OPERAND_TYPE_ID, {25}},
605 {SPV_OPERAND_TYPE_ID, {26}},
606 {SPV_OPERAND_TYPE_LOOP_CONTROL, {SpvLoopControlMaskNone}}}));
607 instructions.push_back(
608 MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {28}}}));
609 instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 28, {}));
610 instructions.push_back(MakeInstructionMessage(SpvOpBranchConditional, 0, 0,
611 {{SPV_OPERAND_TYPE_ID, {12}},
612 {SPV_OPERAND_TYPE_ID, {26}},
613 {SPV_OPERAND_TYPE_ID, {25}}}));
614 instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 26, {}));
615 instructions.push_back(
616 MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {23}}}));
617 instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 25, {}));
618 instructions.push_back(MakeInstructionMessage(
619 SpvOpLoopMerge, 0, 0,
620 {{SPV_OPERAND_TYPE_ID, {24}},
621 {SPV_OPERAND_TYPE_ID, {27}},
622 {SPV_OPERAND_TYPE_LOOP_CONTROL, {SpvLoopControlMaskNone}}}));
623 instructions.push_back(MakeInstructionMessage(SpvOpBranchConditional, 0, 0,
624 {{SPV_OPERAND_TYPE_ID, {12}},
625 {SPV_OPERAND_TYPE_ID, {24}},
626 {SPV_OPERAND_TYPE_ID, {27}}}));
627 instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 27, {}));
628 instructions.push_back(
629 MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {25}}}));
630 instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 24, {}));
631 instructions.push_back(
632 MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {22}}}));
633 instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 22, {}));
634 instructions.push_back(
635 MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {20}}}));
636 instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 21, {}));
637 instructions.push_back(MakeInstructionMessage(SpvOpReturn, 0, 0, {}));
638 instructions.push_back(MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {}));
639
640 spvtools::ValidatorOptions validator_options;
641
642 const auto context1 = BuildModule(env, consumer, shader, kFuzzAssembleOption);
643 const auto context2 = BuildModule(env, consumer, shader, kFuzzAssembleOption);
644 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
645 context1.get(), validator_options, kConsoleMessageConsumer));
646
647 TransformationContext transformation_context1(
648 MakeUnique<FactManager>(context1.get()), validator_options);
649 TransformationContext transformation_context2(
650 MakeUnique<FactManager>(context2.get()), validator_options);
651
652 TransformationAddFunction add_dead_function(instructions);
653 ASSERT_TRUE(
654 add_dead_function.IsApplicable(context1.get(), transformation_context1));
655 ApplyAndCheckFreshIds(add_dead_function, context1.get(),
656 &transformation_context1);
657 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
658 context1.get(), validator_options, kConsoleMessageConsumer));
659 // The added function should not be deemed livesafe.
660 ASSERT_FALSE(
661 transformation_context1.GetFactManager()->FunctionIsLivesafe(30));
662 // All variables/parameters in the function should be deemed irrelevant.
663 ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant(
664 context1.get(), transformation_context1, 30, 0));
665
666 std::string added_as_dead_code = R"(
667 OpCapability Shader
668 %1 = OpExtInstImport "GLSL.std.450"
669 OpMemoryModel Logical GLSL450
670 OpEntryPoint Fragment %4 "main"
671 OpExecutionMode %4 OriginUpperLeft
672 OpSource ESSL 310
673 %2 = OpTypeVoid
674 %3 = OpTypeFunction %2
675 %6 = OpTypeInt 32 0
676 %7 = OpTypePointer Function %6
677 %8 = OpConstant %6 0
678 %9 = OpConstant %6 1
679 %10 = OpConstant %6 5
680 %11 = OpTypeBool
681 %12 = OpConstantTrue %11
682 %4 = OpFunction %2 None %3
683 %5 = OpLabel
684 OpReturn
685 OpFunctionEnd
686 %30 = OpFunction %2 None %3
687 %31 = OpLabel
688 OpBranch %20
689 %20 = OpLabel
690 OpLoopMerge %21 %22 None
691 OpBranchConditional %12 %23 %21
692 %23 = OpLabel
693 OpLoopMerge %25 %26 None
694 OpBranch %28
695 %28 = OpLabel
696 OpBranchConditional %12 %26 %25
697 %26 = OpLabel
698 OpBranch %23
699 %25 = OpLabel
700 OpLoopMerge %24 %27 None
701 OpBranchConditional %12 %24 %27
702 %27 = OpLabel
703 OpBranch %25
704 %24 = OpLabel
705 OpBranch %22
706 %22 = OpLabel
707 OpBranch %20
708 %21 = OpLabel
709 OpReturn
710 OpFunctionEnd
711 )";
712 ASSERT_TRUE(IsEqual(env, added_as_dead_code, context1.get()));
713
714 protobufs::LoopLimiterInfo loop_limiter1;
715 loop_limiter1.set_loop_header_id(20);
716 loop_limiter1.set_load_id(101);
717 loop_limiter1.set_increment_id(102);
718 loop_limiter1.set_compare_id(103);
719 loop_limiter1.set_logical_op_id(104);
720
721 protobufs::LoopLimiterInfo loop_limiter2;
722 loop_limiter2.set_loop_header_id(23);
723 loop_limiter2.set_load_id(105);
724 loop_limiter2.set_increment_id(106);
725 loop_limiter2.set_compare_id(107);
726 loop_limiter2.set_logical_op_id(108);
727
728 protobufs::LoopLimiterInfo loop_limiter3;
729 loop_limiter3.set_loop_header_id(25);
730 loop_limiter3.set_load_id(109);
731 loop_limiter3.set_increment_id(110);
732 loop_limiter3.set_compare_id(111);
733 loop_limiter3.set_logical_op_id(112);
734
735 std::vector<protobufs::LoopLimiterInfo> loop_limiters = {
736 loop_limiter1, loop_limiter2, loop_limiter3};
737
738 TransformationAddFunction add_livesafe_function(instructions, 100, 10,
739 loop_limiters, 0, {});
740 ASSERT_TRUE(add_livesafe_function.IsApplicable(context2.get(),
741 transformation_context2));
742 ApplyAndCheckFreshIds(add_livesafe_function, context2.get(),
743 &transformation_context2);
744 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
745 context2.get(), validator_options, kConsoleMessageConsumer));
746 // The added function should indeed be deemed livesafe.
747 ASSERT_TRUE(transformation_context2.GetFactManager()->FunctionIsLivesafe(30));
748 // All variables/parameters in the function should be deemed irrelevant,
749 // except the loop limiter.
750 ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant(
751 context2.get(), transformation_context2, 30, 100));
752 std::string added_as_livesafe_code = 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 %2 = OpTypeVoid
760 %3 = OpTypeFunction %2
761 %6 = OpTypeInt 32 0
762 %7 = OpTypePointer Function %6
763 %8 = OpConstant %6 0
764 %9 = OpConstant %6 1
765 %10 = OpConstant %6 5
766 %11 = OpTypeBool
767 %12 = OpConstantTrue %11
768 %4 = OpFunction %2 None %3
769 %5 = OpLabel
770 OpReturn
771 OpFunctionEnd
772 %30 = OpFunction %2 None %3
773 %31 = OpLabel
774 %100 = OpVariable %7 Function %8
775 OpBranch %20
776 %20 = OpLabel
777 OpLoopMerge %21 %22 None
778 OpBranchConditional %12 %23 %21
779 %23 = OpLabel
780 OpLoopMerge %25 %26 None
781 OpBranch %28
782 %28 = OpLabel
783 OpBranchConditional %12 %26 %25
784 %26 = OpLabel
785 %105 = OpLoad %6 %100
786 %106 = OpIAdd %6 %105 %9
787 OpStore %100 %106
788 %107 = OpUGreaterThanEqual %11 %105 %10
789 OpBranchConditional %107 %25 %23
790 %25 = OpLabel
791 OpLoopMerge %24 %27 None
792 OpBranchConditional %12 %24 %27
793 %27 = OpLabel
794 %109 = OpLoad %6 %100
795 %110 = OpIAdd %6 %109 %9
796 OpStore %100 %110
797 %111 = OpUGreaterThanEqual %11 %109 %10
798 OpBranchConditional %111 %24 %25
799 %24 = OpLabel
800 OpBranch %22
801 %22 = OpLabel
802 %101 = OpLoad %6 %100
803 %102 = OpIAdd %6 %101 %9
804 OpStore %100 %102
805 %103 = OpUGreaterThanEqual %11 %101 %10
806 OpBranchConditional %103 %21 %20
807 %21 = OpLabel
808 OpReturn
809 OpFunctionEnd
810 )";
811 ASSERT_TRUE(IsEqual(env, added_as_livesafe_code, context2.get()));
812 }
813
TEST(TransformationAddFunctionTest,KillAndUnreachableInVoidFunction)814 TEST(TransformationAddFunctionTest, KillAndUnreachableInVoidFunction) {
815 std::string shader = R"(
816 OpCapability Shader
817 %1 = OpExtInstImport "GLSL.std.450"
818 OpMemoryModel Logical GLSL450
819 OpEntryPoint Fragment %4 "main"
820 OpExecutionMode %4 OriginUpperLeft
821 OpSource ESSL 310
822 %2 = OpTypeVoid
823 %3 = OpTypeFunction %2
824 %6 = OpTypeInt 32 1
825 %7 = OpTypePointer Function %6
826 %8 = OpTypeFunction %2 %7
827 %13 = OpConstant %6 2
828 %14 = OpTypeBool
829 %4 = OpFunction %2 None %3
830 %5 = OpLabel
831 OpReturn
832 OpFunctionEnd
833 )";
834
835 const auto env = SPV_ENV_UNIVERSAL_1_4;
836 const auto consumer = nullptr;
837
838 std::vector<protobufs::Instruction> instructions;
839
840 instructions.push_back(MakeInstructionMessage(
841 SpvOpFunction, 2, 10,
842 {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, {SpvFunctionControlMaskNone}},
843 {SPV_OPERAND_TYPE_TYPE_ID, {8}}}));
844 instructions.push_back(
845 MakeInstructionMessage(SpvOpFunctionParameter, 7, 9, {}));
846 instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 11, {}));
847 instructions.push_back(
848 MakeInstructionMessage(SpvOpLoad, 6, 12, {{SPV_OPERAND_TYPE_ID, {9}}}));
849 instructions.push_back(MakeInstructionMessage(
850 SpvOpIEqual, 14, 15,
851 {{SPV_OPERAND_TYPE_ID, {12}}, {SPV_OPERAND_TYPE_ID, {13}}}));
852 instructions.push_back(MakeInstructionMessage(
853 SpvOpSelectionMerge, 0, 0,
854 {{SPV_OPERAND_TYPE_ID, {17}},
855 {SPV_OPERAND_TYPE_SELECTION_CONTROL, {SpvSelectionControlMaskNone}}}));
856 instructions.push_back(MakeInstructionMessage(SpvOpBranchConditional, 0, 0,
857 {{SPV_OPERAND_TYPE_ID, {15}},
858 {SPV_OPERAND_TYPE_ID, {16}},
859 {SPV_OPERAND_TYPE_ID, {17}}}));
860 instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 16, {}));
861 instructions.push_back(MakeInstructionMessage(SpvOpUnreachable, 0, 0, {}));
862 instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 17, {}));
863 instructions.push_back(MakeInstructionMessage(SpvOpKill, 0, 0, {}));
864 instructions.push_back(MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {}));
865
866 spvtools::ValidatorOptions validator_options;
867
868 const auto context1 = BuildModule(env, consumer, shader, kFuzzAssembleOption);
869 const auto context2 = BuildModule(env, consumer, shader, kFuzzAssembleOption);
870 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
871 context1.get(), validator_options, kConsoleMessageConsumer));
872
873 TransformationContext transformation_context1(
874 MakeUnique<FactManager>(context1.get()), validator_options);
875 TransformationContext transformation_context2(
876 MakeUnique<FactManager>(context2.get()), validator_options);
877
878 TransformationAddFunction add_dead_function(instructions);
879 ASSERT_TRUE(
880 add_dead_function.IsApplicable(context1.get(), transformation_context1));
881 ApplyAndCheckFreshIds(add_dead_function, context1.get(),
882 &transformation_context1);
883 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
884 context1.get(), validator_options, kConsoleMessageConsumer));
885 // The added function should not be deemed livesafe.
886 ASSERT_FALSE(
887 transformation_context1.GetFactManager()->FunctionIsLivesafe(10));
888 // All variables/parameters in the function should be deemed irrelevant.
889 ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant(
890 context1.get(), transformation_context1, 10, 0));
891
892 std::string added_as_dead_code = R"(
893 OpCapability Shader
894 %1 = OpExtInstImport "GLSL.std.450"
895 OpMemoryModel Logical GLSL450
896 OpEntryPoint Fragment %4 "main"
897 OpExecutionMode %4 OriginUpperLeft
898 OpSource ESSL 310
899 %2 = OpTypeVoid
900 %3 = OpTypeFunction %2
901 %6 = OpTypeInt 32 1
902 %7 = OpTypePointer Function %6
903 %8 = OpTypeFunction %2 %7
904 %13 = OpConstant %6 2
905 %14 = OpTypeBool
906 %4 = OpFunction %2 None %3
907 %5 = OpLabel
908 OpReturn
909 OpFunctionEnd
910 %10 = OpFunction %2 None %8
911 %9 = OpFunctionParameter %7
912 %11 = OpLabel
913 %12 = OpLoad %6 %9
914 %15 = OpIEqual %14 %12 %13
915 OpSelectionMerge %17 None
916 OpBranchConditional %15 %16 %17
917 %16 = OpLabel
918 OpUnreachable
919 %17 = OpLabel
920 OpKill
921 OpFunctionEnd
922 )";
923 ASSERT_TRUE(IsEqual(env, added_as_dead_code, context1.get()));
924
925 TransformationAddFunction add_livesafe_function(instructions, 0, 0, {}, 0,
926 {});
927 ASSERT_TRUE(add_livesafe_function.IsApplicable(context2.get(),
928 transformation_context2));
929 ApplyAndCheckFreshIds(add_livesafe_function, context2.get(),
930 &transformation_context2);
931 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
932 context2.get(), validator_options, kConsoleMessageConsumer));
933 // The added function should indeed be deemed livesafe.
934 ASSERT_TRUE(transformation_context2.GetFactManager()->FunctionIsLivesafe(10));
935 // All variables/parameters in the function should be deemed irrelevant.
936 ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant(
937 context2.get(), transformation_context2, 10, 0));
938 std::string added_as_livesafe_code = R"(
939 OpCapability Shader
940 %1 = OpExtInstImport "GLSL.std.450"
941 OpMemoryModel Logical GLSL450
942 OpEntryPoint Fragment %4 "main"
943 OpExecutionMode %4 OriginUpperLeft
944 OpSource ESSL 310
945 %2 = OpTypeVoid
946 %3 = OpTypeFunction %2
947 %6 = OpTypeInt 32 1
948 %7 = OpTypePointer Function %6
949 %8 = OpTypeFunction %2 %7
950 %13 = OpConstant %6 2
951 %14 = OpTypeBool
952 %4 = OpFunction %2 None %3
953 %5 = OpLabel
954 OpReturn
955 OpFunctionEnd
956 %10 = OpFunction %2 None %8
957 %9 = OpFunctionParameter %7
958 %11 = OpLabel
959 %12 = OpLoad %6 %9
960 %15 = OpIEqual %14 %12 %13
961 OpSelectionMerge %17 None
962 OpBranchConditional %15 %16 %17
963 %16 = OpLabel
964 OpReturn
965 %17 = OpLabel
966 OpReturn
967 OpFunctionEnd
968 )";
969 ASSERT_TRUE(IsEqual(env, added_as_livesafe_code, context2.get()));
970 }
971
TEST(TransformationAddFunctionTest,KillAndUnreachableInNonVoidFunction)972 TEST(TransformationAddFunctionTest, KillAndUnreachableInNonVoidFunction) {
973 std::string shader = R"(
974 OpCapability Shader
975 %1 = OpExtInstImport "GLSL.std.450"
976 OpMemoryModel Logical GLSL450
977 OpEntryPoint Fragment %4 "main"
978 OpExecutionMode %4 OriginUpperLeft
979 OpSource ESSL 310
980 %2 = OpTypeVoid
981 %3 = OpTypeFunction %2
982 %6 = OpTypeInt 32 1
983 %7 = OpTypePointer Function %6
984 %8 = OpTypeFunction %2 %7
985 %50 = OpTypeFunction %6 %7
986 %13 = OpConstant %6 2
987 %14 = OpTypeBool
988 %4 = OpFunction %2 None %3
989 %5 = OpLabel
990 OpReturn
991 OpFunctionEnd
992 )";
993
994 const auto env = SPV_ENV_UNIVERSAL_1_4;
995 const auto consumer = nullptr;
996
997 std::vector<protobufs::Instruction> instructions;
998
999 instructions.push_back(MakeInstructionMessage(
1000 SpvOpFunction, 6, 10,
1001 {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, {SpvFunctionControlMaskNone}},
1002 {SPV_OPERAND_TYPE_TYPE_ID, {50}}}));
1003 instructions.push_back(
1004 MakeInstructionMessage(SpvOpFunctionParameter, 7, 9, {}));
1005 instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 11, {}));
1006 instructions.push_back(
1007 MakeInstructionMessage(SpvOpLoad, 6, 12, {{SPV_OPERAND_TYPE_ID, {9}}}));
1008 instructions.push_back(MakeInstructionMessage(
1009 SpvOpIEqual, 14, 15,
1010 {{SPV_OPERAND_TYPE_ID, {12}}, {SPV_OPERAND_TYPE_ID, {13}}}));
1011 instructions.push_back(MakeInstructionMessage(
1012 SpvOpSelectionMerge, 0, 0,
1013 {{SPV_OPERAND_TYPE_ID, {17}},
1014 {SPV_OPERAND_TYPE_SELECTION_CONTROL, {SpvSelectionControlMaskNone}}}));
1015 instructions.push_back(MakeInstructionMessage(SpvOpBranchConditional, 0, 0,
1016 {{SPV_OPERAND_TYPE_ID, {15}},
1017 {SPV_OPERAND_TYPE_ID, {16}},
1018 {SPV_OPERAND_TYPE_ID, {17}}}));
1019 instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 16, {}));
1020 instructions.push_back(MakeInstructionMessage(SpvOpUnreachable, 0, 0, {}));
1021 instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 17, {}));
1022 instructions.push_back(MakeInstructionMessage(SpvOpKill, 0, 0, {}));
1023 instructions.push_back(MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {}));
1024
1025 spvtools::ValidatorOptions validator_options;
1026
1027 const auto context1 = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1028 const auto context2 = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1029 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
1030 context1.get(), validator_options, kConsoleMessageConsumer));
1031
1032 TransformationContext transformation_context1(
1033 MakeUnique<FactManager>(context1.get()), validator_options);
1034 TransformationContext transformation_context2(
1035 MakeUnique<FactManager>(context2.get()), validator_options);
1036
1037 TransformationAddFunction add_dead_function(instructions);
1038 ASSERT_TRUE(
1039 add_dead_function.IsApplicable(context1.get(), transformation_context1));
1040 ApplyAndCheckFreshIds(add_dead_function, context1.get(),
1041 &transformation_context1);
1042 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
1043 context1.get(), validator_options, kConsoleMessageConsumer));
1044 // The added function should not be deemed livesafe.
1045 ASSERT_FALSE(
1046 transformation_context1.GetFactManager()->FunctionIsLivesafe(10));
1047 // All variables/parameters in the function should be deemed irrelevant.
1048 ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant(
1049 context1.get(), transformation_context1, 10, 0));
1050
1051 std::string added_as_dead_code = R"(
1052 OpCapability Shader
1053 %1 = OpExtInstImport "GLSL.std.450"
1054 OpMemoryModel Logical GLSL450
1055 OpEntryPoint Fragment %4 "main"
1056 OpExecutionMode %4 OriginUpperLeft
1057 OpSource ESSL 310
1058 %2 = OpTypeVoid
1059 %3 = OpTypeFunction %2
1060 %6 = OpTypeInt 32 1
1061 %7 = OpTypePointer Function %6
1062 %8 = OpTypeFunction %2 %7
1063 %50 = OpTypeFunction %6 %7
1064 %13 = OpConstant %6 2
1065 %14 = OpTypeBool
1066 %4 = OpFunction %2 None %3
1067 %5 = OpLabel
1068 OpReturn
1069 OpFunctionEnd
1070 %10 = OpFunction %6 None %50
1071 %9 = OpFunctionParameter %7
1072 %11 = OpLabel
1073 %12 = OpLoad %6 %9
1074 %15 = OpIEqual %14 %12 %13
1075 OpSelectionMerge %17 None
1076 OpBranchConditional %15 %16 %17
1077 %16 = OpLabel
1078 OpUnreachable
1079 %17 = OpLabel
1080 OpKill
1081 OpFunctionEnd
1082 )";
1083 ASSERT_TRUE(IsEqual(env, added_as_dead_code, context1.get()));
1084
1085 TransformationAddFunction add_livesafe_function(instructions, 0, 0, {}, 13,
1086 {});
1087 ASSERT_TRUE(add_livesafe_function.IsApplicable(context2.get(),
1088 transformation_context2));
1089 ApplyAndCheckFreshIds(add_livesafe_function, context2.get(),
1090 &transformation_context2);
1091 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
1092 context2.get(), validator_options, kConsoleMessageConsumer));
1093 // The added function should indeed be deemed livesafe.
1094 ASSERT_TRUE(transformation_context2.GetFactManager()->FunctionIsLivesafe(10));
1095 // All variables/parameters in the function should be deemed irrelevant.
1096 ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant(
1097 context2.get(), transformation_context2, 10, 0));
1098 std::string added_as_livesafe_code = R"(
1099 OpCapability Shader
1100 %1 = OpExtInstImport "GLSL.std.450"
1101 OpMemoryModel Logical GLSL450
1102 OpEntryPoint Fragment %4 "main"
1103 OpExecutionMode %4 OriginUpperLeft
1104 OpSource ESSL 310
1105 %2 = OpTypeVoid
1106 %3 = OpTypeFunction %2
1107 %6 = OpTypeInt 32 1
1108 %7 = OpTypePointer Function %6
1109 %8 = OpTypeFunction %2 %7
1110 %50 = OpTypeFunction %6 %7
1111 %13 = OpConstant %6 2
1112 %14 = OpTypeBool
1113 %4 = OpFunction %2 None %3
1114 %5 = OpLabel
1115 OpReturn
1116 OpFunctionEnd
1117 %10 = OpFunction %6 None %50
1118 %9 = OpFunctionParameter %7
1119 %11 = OpLabel
1120 %12 = OpLoad %6 %9
1121 %15 = OpIEqual %14 %12 %13
1122 OpSelectionMerge %17 None
1123 OpBranchConditional %15 %16 %17
1124 %16 = OpLabel
1125 OpReturnValue %13
1126 %17 = OpLabel
1127 OpReturnValue %13
1128 OpFunctionEnd
1129 )";
1130 ASSERT_TRUE(IsEqual(env, added_as_livesafe_code, context2.get()));
1131 }
1132
TEST(TransformationAddFunctionTest,ClampedAccessChains)1133 TEST(TransformationAddFunctionTest, ClampedAccessChains) {
1134 std::string shader = R"(
1135 OpCapability Shader
1136 %1 = OpExtInstImport "GLSL.std.450"
1137 OpMemoryModel Logical GLSL450
1138 OpEntryPoint Fragment %4 "main"
1139 OpExecutionMode %4 OriginUpperLeft
1140 OpSource ESSL 310
1141 %2 = OpTypeVoid
1142 %100 = OpTypeBool
1143 %3 = OpTypeFunction %2
1144 %6 = OpTypeInt 32 1
1145 %7 = OpTypePointer Function %6
1146 %15 = OpTypeInt 32 0
1147 %102 = OpTypePointer Function %15
1148 %8 = OpTypeFunction %2 %7 %102 %7
1149 %16 = OpConstant %15 5
1150 %17 = OpTypeArray %6 %16
1151 %18 = OpTypeArray %17 %16
1152 %19 = OpTypePointer Private %18
1153 %20 = OpVariable %19 Private
1154 %21 = OpConstant %6 0
1155 %23 = OpTypePointer Private %6
1156 %26 = OpTypePointer Function %17
1157 %29 = OpTypePointer Private %17
1158 %33 = OpConstant %6 4
1159 %200 = OpConstant %15 4
1160 %35 = OpConstant %15 10
1161 %36 = OpTypeArray %6 %35
1162 %37 = OpTypePointer Private %36
1163 %38 = OpVariable %37 Private
1164 %54 = OpTypeFloat 32
1165 %55 = OpTypeVector %54 4
1166 %56 = OpTypePointer Private %55
1167 %57 = OpVariable %56 Private
1168 %59 = OpTypeVector %54 3
1169 %60 = OpTypeMatrix %59 2
1170 %61 = OpTypePointer Private %60
1171 %62 = OpVariable %61 Private
1172 %64 = OpTypePointer Private %54
1173 %69 = OpConstant %54 2
1174 %71 = OpConstant %6 1
1175 %72 = OpConstant %6 2
1176 %201 = OpConstant %15 2
1177 %73 = OpConstant %6 3
1178 %202 = OpConstant %15 3
1179 %203 = OpConstant %6 1
1180 %204 = OpConstant %6 9
1181 %4 = OpFunction %2 None %3
1182 %5 = OpLabel
1183 OpReturn
1184 OpFunctionEnd
1185 )";
1186
1187 const auto env = SPV_ENV_UNIVERSAL_1_4;
1188 const auto consumer = nullptr;
1189
1190 std::vector<protobufs::Instruction> instructions;
1191
1192 instructions.push_back(MakeInstructionMessage(
1193 SpvOpFunction, 2, 12,
1194 {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, {SpvFunctionControlMaskNone}},
1195 {SPV_OPERAND_TYPE_TYPE_ID, {8}}}));
1196 instructions.push_back(
1197 MakeInstructionMessage(SpvOpFunctionParameter, 7, 9, {}));
1198 instructions.push_back(
1199 MakeInstructionMessage(SpvOpFunctionParameter, 102, 10, {}));
1200 instructions.push_back(
1201 MakeInstructionMessage(SpvOpFunctionParameter, 7, 11, {}));
1202 instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 13, {}));
1203
1204 instructions.push_back(MakeInstructionMessage(
1205 SpvOpVariable, 7, 14,
1206 {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}}));
1207 instructions.push_back(MakeInstructionMessage(
1208 SpvOpVariable, 26, 27,
1209 {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}}));
1210 instructions.push_back(
1211 MakeInstructionMessage(SpvOpLoad, 6, 22, {{SPV_OPERAND_TYPE_ID, {11}}}));
1212 instructions.push_back(MakeInstructionMessage(SpvOpAccessChain, 23, 24,
1213 {{SPV_OPERAND_TYPE_ID, {20}},
1214 {SPV_OPERAND_TYPE_ID, {21}},
1215 {SPV_OPERAND_TYPE_ID, {22}}}));
1216 instructions.push_back(
1217 MakeInstructionMessage(SpvOpLoad, 6, 25, {{SPV_OPERAND_TYPE_ID, {24}}}));
1218 instructions.push_back(MakeInstructionMessage(
1219 SpvOpStore, 0, 0,
1220 {{SPV_OPERAND_TYPE_ID, {14}}, {SPV_OPERAND_TYPE_ID, {25}}}));
1221 instructions.push_back(
1222 MakeInstructionMessage(SpvOpLoad, 15, 28, {{SPV_OPERAND_TYPE_ID, {10}}}));
1223 instructions.push_back(MakeInstructionMessage(
1224 SpvOpAccessChain, 29, 30,
1225 {{SPV_OPERAND_TYPE_ID, {20}}, {SPV_OPERAND_TYPE_ID, {28}}}));
1226 instructions.push_back(
1227 MakeInstructionMessage(SpvOpLoad, 17, 31, {{SPV_OPERAND_TYPE_ID, {30}}}));
1228 instructions.push_back(MakeInstructionMessage(
1229 SpvOpStore, 0, 0,
1230 {{SPV_OPERAND_TYPE_ID, {27}}, {SPV_OPERAND_TYPE_ID, {31}}}));
1231 instructions.push_back(
1232 MakeInstructionMessage(SpvOpLoad, 6, 32, {{SPV_OPERAND_TYPE_ID, {9}}}));
1233 instructions.push_back(MakeInstructionMessage(
1234 SpvOpInBoundsAccessChain, 7, 34,
1235 {{SPV_OPERAND_TYPE_ID, {27}}, {SPV_OPERAND_TYPE_ID, {32}}}));
1236 instructions.push_back(MakeInstructionMessage(
1237 SpvOpStore, 0, 0,
1238 {{SPV_OPERAND_TYPE_ID, {34}}, {SPV_OPERAND_TYPE_ID, {33}}}));
1239 instructions.push_back(
1240 MakeInstructionMessage(SpvOpLoad, 6, 39, {{SPV_OPERAND_TYPE_ID, {9}}}));
1241 instructions.push_back(MakeInstructionMessage(
1242 SpvOpAccessChain, 23, 40,
1243 {{SPV_OPERAND_TYPE_ID, {38}}, {SPV_OPERAND_TYPE_ID, {33}}}));
1244 instructions.push_back(
1245 MakeInstructionMessage(SpvOpLoad, 6, 41, {{SPV_OPERAND_TYPE_ID, {40}}}));
1246 instructions.push_back(MakeInstructionMessage(
1247 SpvOpInBoundsAccessChain, 23, 42,
1248 {{SPV_OPERAND_TYPE_ID, {38}}, {SPV_OPERAND_TYPE_ID, {39}}}));
1249 instructions.push_back(MakeInstructionMessage(
1250 SpvOpStore, 0, 0,
1251 {{SPV_OPERAND_TYPE_ID, {42}}, {SPV_OPERAND_TYPE_ID, {41}}}));
1252 instructions.push_back(
1253 MakeInstructionMessage(SpvOpLoad, 15, 43, {{SPV_OPERAND_TYPE_ID, {10}}}));
1254 instructions.push_back(
1255 MakeInstructionMessage(SpvOpLoad, 6, 44, {{SPV_OPERAND_TYPE_ID, {11}}}));
1256 instructions.push_back(
1257 MakeInstructionMessage(SpvOpLoad, 6, 45, {{SPV_OPERAND_TYPE_ID, {9}}}));
1258 instructions.push_back(
1259 MakeInstructionMessage(SpvOpLoad, 15, 46, {{SPV_OPERAND_TYPE_ID, {10}}}));
1260 instructions.push_back(MakeInstructionMessage(
1261 SpvOpIAdd, 6, 47,
1262 {{SPV_OPERAND_TYPE_ID, {45}}, {SPV_OPERAND_TYPE_ID, {46}}}));
1263 instructions.push_back(MakeInstructionMessage(
1264 SpvOpAccessChain, 23, 48,
1265 {{SPV_OPERAND_TYPE_ID, {38}}, {SPV_OPERAND_TYPE_ID, {47}}}));
1266 instructions.push_back(
1267 MakeInstructionMessage(SpvOpLoad, 6, 49, {{SPV_OPERAND_TYPE_ID, {48}}}));
1268 instructions.push_back(MakeInstructionMessage(SpvOpInBoundsAccessChain, 23,
1269 50,
1270 {{SPV_OPERAND_TYPE_ID, {20}},
1271 {SPV_OPERAND_TYPE_ID, {43}},
1272 {SPV_OPERAND_TYPE_ID, {44}}}));
1273 instructions.push_back(
1274 MakeInstructionMessage(SpvOpLoad, 6, 51, {{SPV_OPERAND_TYPE_ID, {50}}}));
1275 instructions.push_back(MakeInstructionMessage(
1276 SpvOpIAdd, 6, 52,
1277 {{SPV_OPERAND_TYPE_ID, {51}}, {SPV_OPERAND_TYPE_ID, {49}}}));
1278 instructions.push_back(MakeInstructionMessage(SpvOpAccessChain, 23, 53,
1279 {{SPV_OPERAND_TYPE_ID, {20}},
1280 {SPV_OPERAND_TYPE_ID, {43}},
1281 {SPV_OPERAND_TYPE_ID, {44}}}));
1282 instructions.push_back(MakeInstructionMessage(
1283 SpvOpStore, 0, 0,
1284 {{SPV_OPERAND_TYPE_ID, {53}}, {SPV_OPERAND_TYPE_ID, {52}}}));
1285 instructions.push_back(
1286 MakeInstructionMessage(SpvOpLoad, 15, 58, {{SPV_OPERAND_TYPE_ID, {10}}}));
1287 instructions.push_back(
1288 MakeInstructionMessage(SpvOpLoad, 6, 63, {{SPV_OPERAND_TYPE_ID, {11}}}));
1289 instructions.push_back(MakeInstructionMessage(SpvOpAccessChain, 64, 65,
1290 {{SPV_OPERAND_TYPE_ID, {62}},
1291 {SPV_OPERAND_TYPE_ID, {21}},
1292 {SPV_OPERAND_TYPE_ID, {63}}}));
1293 instructions.push_back(MakeInstructionMessage(SpvOpAccessChain, 64, 101,
1294 {{SPV_OPERAND_TYPE_ID, {62}},
1295 {SPV_OPERAND_TYPE_ID, {45}},
1296 {SPV_OPERAND_TYPE_ID, {46}}}));
1297 instructions.push_back(
1298 MakeInstructionMessage(SpvOpLoad, 54, 66, {{SPV_OPERAND_TYPE_ID, {65}}}));
1299 instructions.push_back(MakeInstructionMessage(
1300 SpvOpAccessChain, 64, 67,
1301 {{SPV_OPERAND_TYPE_ID, {57}}, {SPV_OPERAND_TYPE_ID, {58}}}));
1302 instructions.push_back(MakeInstructionMessage(
1303 SpvOpStore, 0, 0,
1304 {{SPV_OPERAND_TYPE_ID, {67}}, {SPV_OPERAND_TYPE_ID, {66}}}));
1305 instructions.push_back(
1306 MakeInstructionMessage(SpvOpLoad, 6, 68, {{SPV_OPERAND_TYPE_ID, {9}}}));
1307 instructions.push_back(MakeInstructionMessage(
1308 SpvOpInBoundsAccessChain, 64, 70,
1309 {{SPV_OPERAND_TYPE_ID, {57}}, {SPV_OPERAND_TYPE_ID, {68}}}));
1310 instructions.push_back(MakeInstructionMessage(
1311 SpvOpStore, 0, 0,
1312 {{SPV_OPERAND_TYPE_ID, {70}}, {SPV_OPERAND_TYPE_ID, {69}}}));
1313 instructions.push_back(MakeInstructionMessage(SpvOpReturn, 0, 0, {}));
1314 instructions.push_back(MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {}));
1315
1316 spvtools::ValidatorOptions validator_options;
1317
1318 const auto context1 = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1319 const auto context2 = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1320 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
1321 context1.get(), validator_options, kConsoleMessageConsumer));
1322
1323 TransformationContext transformation_context1(
1324 MakeUnique<FactManager>(context1.get()), validator_options);
1325 TransformationContext transformation_context2(
1326 MakeUnique<FactManager>(context2.get()), validator_options);
1327
1328 TransformationAddFunction add_dead_function(instructions);
1329 ASSERT_TRUE(
1330 add_dead_function.IsApplicable(context1.get(), transformation_context1));
1331 ApplyAndCheckFreshIds(add_dead_function, context1.get(),
1332 &transformation_context1);
1333 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
1334 context1.get(), validator_options, kConsoleMessageConsumer));
1335 // The function should not be deemed livesafe
1336 ASSERT_FALSE(
1337 transformation_context1.GetFactManager()->FunctionIsLivesafe(12));
1338 // All variables/parameters in the function should be deemed irrelevant.
1339 ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant(
1340 context1.get(), transformation_context1, 12, 0));
1341
1342 std::string added_as_dead_code = R"(
1343 OpCapability Shader
1344 %1 = OpExtInstImport "GLSL.std.450"
1345 OpMemoryModel Logical GLSL450
1346 OpEntryPoint Fragment %4 "main"
1347 OpExecutionMode %4 OriginUpperLeft
1348 OpSource ESSL 310
1349 %2 = OpTypeVoid
1350 %100 = OpTypeBool
1351 %3 = OpTypeFunction %2
1352 %6 = OpTypeInt 32 1
1353 %7 = OpTypePointer Function %6
1354 %15 = OpTypeInt 32 0
1355 %102 = OpTypePointer Function %15
1356 %8 = OpTypeFunction %2 %7 %102 %7
1357 %16 = OpConstant %15 5
1358 %17 = OpTypeArray %6 %16
1359 %18 = OpTypeArray %17 %16
1360 %19 = OpTypePointer Private %18
1361 %20 = OpVariable %19 Private
1362 %21 = OpConstant %6 0
1363 %23 = OpTypePointer Private %6
1364 %26 = OpTypePointer Function %17
1365 %29 = OpTypePointer Private %17
1366 %33 = OpConstant %6 4
1367 %200 = OpConstant %15 4
1368 %35 = OpConstant %15 10
1369 %36 = OpTypeArray %6 %35
1370 %37 = OpTypePointer Private %36
1371 %38 = OpVariable %37 Private
1372 %54 = OpTypeFloat 32
1373 %55 = OpTypeVector %54 4
1374 %56 = OpTypePointer Private %55
1375 %57 = OpVariable %56 Private
1376 %59 = OpTypeVector %54 3
1377 %60 = OpTypeMatrix %59 2
1378 %61 = OpTypePointer Private %60
1379 %62 = OpVariable %61 Private
1380 %64 = OpTypePointer Private %54
1381 %69 = OpConstant %54 2
1382 %71 = OpConstant %6 1
1383 %72 = OpConstant %6 2
1384 %201 = OpConstant %15 2
1385 %73 = OpConstant %6 3
1386 %202 = OpConstant %15 3
1387 %203 = OpConstant %6 1
1388 %204 = OpConstant %6 9
1389 %4 = OpFunction %2 None %3
1390 %5 = OpLabel
1391 OpReturn
1392 OpFunctionEnd
1393 %12 = OpFunction %2 None %8
1394 %9 = OpFunctionParameter %7
1395 %10 = OpFunctionParameter %102
1396 %11 = OpFunctionParameter %7
1397 %13 = OpLabel
1398 %14 = OpVariable %7 Function
1399 %27 = OpVariable %26 Function
1400 %22 = OpLoad %6 %11
1401 %24 = OpAccessChain %23 %20 %21 %22
1402 %25 = OpLoad %6 %24
1403 OpStore %14 %25
1404 %28 = OpLoad %15 %10
1405 %30 = OpAccessChain %29 %20 %28
1406 %31 = OpLoad %17 %30
1407 OpStore %27 %31
1408 %32 = OpLoad %6 %9
1409 %34 = OpInBoundsAccessChain %7 %27 %32
1410 OpStore %34 %33
1411 %39 = OpLoad %6 %9
1412 %40 = OpAccessChain %23 %38 %33
1413 %41 = OpLoad %6 %40
1414 %42 = OpInBoundsAccessChain %23 %38 %39
1415 OpStore %42 %41
1416 %43 = OpLoad %15 %10
1417 %44 = OpLoad %6 %11
1418 %45 = OpLoad %6 %9
1419 %46 = OpLoad %15 %10
1420 %47 = OpIAdd %6 %45 %46
1421 %48 = OpAccessChain %23 %38 %47
1422 %49 = OpLoad %6 %48
1423 %50 = OpInBoundsAccessChain %23 %20 %43 %44
1424 %51 = OpLoad %6 %50
1425 %52 = OpIAdd %6 %51 %49
1426 %53 = OpAccessChain %23 %20 %43 %44
1427 OpStore %53 %52
1428 %58 = OpLoad %15 %10
1429 %63 = OpLoad %6 %11
1430 %65 = OpAccessChain %64 %62 %21 %63
1431 %101 = OpAccessChain %64 %62 %45 %46
1432 %66 = OpLoad %54 %65
1433 %67 = OpAccessChain %64 %57 %58
1434 OpStore %67 %66
1435 %68 = OpLoad %6 %9
1436 %70 = OpInBoundsAccessChain %64 %57 %68
1437 OpStore %70 %69
1438 OpReturn
1439 OpFunctionEnd
1440 )";
1441 ASSERT_TRUE(IsEqual(env, added_as_dead_code, context1.get()));
1442
1443 std::vector<protobufs::AccessChainClampingInfo> access_chain_clamping_info;
1444 access_chain_clamping_info.push_back(
1445 MakeAccessClampingInfo(24, {{1001, 2001}, {1002, 2002}}));
1446 access_chain_clamping_info.push_back(
1447 MakeAccessClampingInfo(30, {{1003, 2003}}));
1448 access_chain_clamping_info.push_back(
1449 MakeAccessClampingInfo(34, {{1004, 2004}}));
1450 access_chain_clamping_info.push_back(
1451 MakeAccessClampingInfo(40, {{1005, 2005}}));
1452 access_chain_clamping_info.push_back(
1453 MakeAccessClampingInfo(42, {{1006, 2006}}));
1454 access_chain_clamping_info.push_back(
1455 MakeAccessClampingInfo(48, {{1007, 2007}}));
1456 access_chain_clamping_info.push_back(
1457 MakeAccessClampingInfo(50, {{1008, 2008}, {1009, 2009}}));
1458 access_chain_clamping_info.push_back(
1459 MakeAccessClampingInfo(53, {{1010, 2010}, {1011, 2011}}));
1460 access_chain_clamping_info.push_back(
1461 MakeAccessClampingInfo(65, {{1012, 2012}, {1013, 2013}}));
1462 access_chain_clamping_info.push_back(
1463 MakeAccessClampingInfo(101, {{1014, 2014}, {1015, 2015}}));
1464 access_chain_clamping_info.push_back(
1465 MakeAccessClampingInfo(67, {{1016, 2016}}));
1466 access_chain_clamping_info.push_back(
1467 MakeAccessClampingInfo(70, {{1017, 2017}}));
1468
1469 TransformationAddFunction add_livesafe_function(instructions, 0, 0, {}, 13,
1470 access_chain_clamping_info);
1471 ASSERT_TRUE(add_livesafe_function.IsApplicable(context2.get(),
1472 transformation_context2));
1473 ApplyAndCheckFreshIds(add_livesafe_function, context2.get(),
1474 &transformation_context2);
1475 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
1476 context2.get(), validator_options, kConsoleMessageConsumer));
1477 // The function should be deemed livesafe
1478 ASSERT_TRUE(transformation_context2.GetFactManager()->FunctionIsLivesafe(12));
1479 // All variables/parameters in the function should be deemed irrelevant.
1480 ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant(
1481 context2.get(), transformation_context2, 12, 0));
1482 std::string added_as_livesafe_code = R"(
1483 OpCapability Shader
1484 %1 = OpExtInstImport "GLSL.std.450"
1485 OpMemoryModel Logical GLSL450
1486 OpEntryPoint Fragment %4 "main"
1487 OpExecutionMode %4 OriginUpperLeft
1488 OpSource ESSL 310
1489 %2 = OpTypeVoid
1490 %100 = OpTypeBool
1491 %3 = OpTypeFunction %2
1492 %6 = OpTypeInt 32 1
1493 %7 = OpTypePointer Function %6
1494 %15 = OpTypeInt 32 0
1495 %102 = OpTypePointer Function %15
1496 %8 = OpTypeFunction %2 %7 %102 %7
1497 %16 = OpConstant %15 5
1498 %17 = OpTypeArray %6 %16
1499 %18 = OpTypeArray %17 %16
1500 %19 = OpTypePointer Private %18
1501 %20 = OpVariable %19 Private
1502 %21 = OpConstant %6 0
1503 %23 = OpTypePointer Private %6
1504 %26 = OpTypePointer Function %17
1505 %29 = OpTypePointer Private %17
1506 %33 = OpConstant %6 4
1507 %200 = OpConstant %15 4
1508 %35 = OpConstant %15 10
1509 %36 = OpTypeArray %6 %35
1510 %37 = OpTypePointer Private %36
1511 %38 = OpVariable %37 Private
1512 %54 = OpTypeFloat 32
1513 %55 = OpTypeVector %54 4
1514 %56 = OpTypePointer Private %55
1515 %57 = OpVariable %56 Private
1516 %59 = OpTypeVector %54 3
1517 %60 = OpTypeMatrix %59 2
1518 %61 = OpTypePointer Private %60
1519 %62 = OpVariable %61 Private
1520 %64 = OpTypePointer Private %54
1521 %69 = OpConstant %54 2
1522 %71 = OpConstant %6 1
1523 %72 = OpConstant %6 2
1524 %201 = OpConstant %15 2
1525 %73 = OpConstant %6 3
1526 %202 = OpConstant %15 3
1527 %203 = OpConstant %6 1
1528 %204 = OpConstant %6 9
1529 %4 = OpFunction %2 None %3
1530 %5 = OpLabel
1531 OpReturn
1532 OpFunctionEnd
1533 %12 = OpFunction %2 None %8
1534 %9 = OpFunctionParameter %7
1535 %10 = OpFunctionParameter %102
1536 %11 = OpFunctionParameter %7
1537 %13 = OpLabel
1538 %14 = OpVariable %7 Function
1539 %27 = OpVariable %26 Function
1540 %22 = OpLoad %6 %11
1541 %1002 = OpULessThanEqual %100 %22 %33
1542 %2002 = OpSelect %6 %1002 %22 %33
1543 %24 = OpAccessChain %23 %20 %21 %2002
1544 %25 = OpLoad %6 %24
1545 OpStore %14 %25
1546 %28 = OpLoad %15 %10
1547 %1003 = OpULessThanEqual %100 %28 %200
1548 %2003 = OpSelect %15 %1003 %28 %200
1549 %30 = OpAccessChain %29 %20 %2003
1550 %31 = OpLoad %17 %30
1551 OpStore %27 %31
1552 %32 = OpLoad %6 %9
1553 %1004 = OpULessThanEqual %100 %32 %33
1554 %2004 = OpSelect %6 %1004 %32 %33
1555 %34 = OpInBoundsAccessChain %7 %27 %2004
1556 OpStore %34 %33
1557 %39 = OpLoad %6 %9
1558 %40 = OpAccessChain %23 %38 %33
1559 %41 = OpLoad %6 %40
1560 %1006 = OpULessThanEqual %100 %39 %204
1561 %2006 = OpSelect %6 %1006 %39 %204
1562 %42 = OpInBoundsAccessChain %23 %38 %2006
1563 OpStore %42 %41
1564 %43 = OpLoad %15 %10
1565 %44 = OpLoad %6 %11
1566 %45 = OpLoad %6 %9
1567 %46 = OpLoad %15 %10
1568 %47 = OpIAdd %6 %45 %46
1569 %1007 = OpULessThanEqual %100 %47 %204
1570 %2007 = OpSelect %6 %1007 %47 %204
1571 %48 = OpAccessChain %23 %38 %2007
1572 %49 = OpLoad %6 %48
1573 %1008 = OpULessThanEqual %100 %43 %200
1574 %2008 = OpSelect %15 %1008 %43 %200
1575 %1009 = OpULessThanEqual %100 %44 %33
1576 %2009 = OpSelect %6 %1009 %44 %33
1577 %50 = OpInBoundsAccessChain %23 %20 %2008 %2009
1578 %51 = OpLoad %6 %50
1579 %52 = OpIAdd %6 %51 %49
1580 %1010 = OpULessThanEqual %100 %43 %200
1581 %2010 = OpSelect %15 %1010 %43 %200
1582 %1011 = OpULessThanEqual %100 %44 %33
1583 %2011 = OpSelect %6 %1011 %44 %33
1584 %53 = OpAccessChain %23 %20 %2010 %2011
1585 OpStore %53 %52
1586 %58 = OpLoad %15 %10
1587 %63 = OpLoad %6 %11
1588 %1013 = OpULessThanEqual %100 %63 %72
1589 %2013 = OpSelect %6 %1013 %63 %72
1590 %65 = OpAccessChain %64 %62 %21 %2013
1591 %1014 = OpULessThanEqual %100 %45 %71
1592 %2014 = OpSelect %6 %1014 %45 %71
1593 %1015 = OpULessThanEqual %100 %46 %201
1594 %2015 = OpSelect %15 %1015 %46 %201
1595 %101 = OpAccessChain %64 %62 %2014 %2015
1596 %66 = OpLoad %54 %65
1597 %1016 = OpULessThanEqual %100 %58 %202
1598 %2016 = OpSelect %15 %1016 %58 %202
1599 %67 = OpAccessChain %64 %57 %2016
1600 OpStore %67 %66
1601 %68 = OpLoad %6 %9
1602 %1017 = OpULessThanEqual %100 %68 %73
1603 %2017 = OpSelect %6 %1017 %68 %73
1604 %70 = OpInBoundsAccessChain %64 %57 %2017
1605 OpStore %70 %69
1606 OpReturn
1607 OpFunctionEnd
1608 )";
1609 ASSERT_TRUE(IsEqual(env, added_as_livesafe_code, context2.get()));
1610 }
1611
TEST(TransformationAddFunctionTest,LivesafeCanCallLivesafe)1612 TEST(TransformationAddFunctionTest, LivesafeCanCallLivesafe) {
1613 std::string shader = R"(
1614 OpCapability Shader
1615 %1 = OpExtInstImport "GLSL.std.450"
1616 OpMemoryModel Logical GLSL450
1617 OpEntryPoint Fragment %4 "main"
1618 OpExecutionMode %4 OriginUpperLeft
1619 OpSource ESSL 310
1620 %2 = OpTypeVoid
1621 %3 = OpTypeFunction %2
1622 %4 = OpFunction %2 None %3
1623 %5 = OpLabel
1624 OpReturn
1625 OpFunctionEnd
1626 %6 = OpFunction %2 None %3
1627 %7 = OpLabel
1628 OpReturn
1629 OpFunctionEnd
1630 )";
1631
1632 const auto env = SPV_ENV_UNIVERSAL_1_4;
1633 const auto consumer = nullptr;
1634
1635 std::vector<protobufs::Instruction> instructions;
1636
1637 instructions.push_back(MakeInstructionMessage(
1638 SpvOpFunction, 2, 8,
1639 {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, {SpvFunctionControlMaskNone}},
1640 {SPV_OPERAND_TYPE_TYPE_ID, {3}}}));
1641 instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 9, {}));
1642 instructions.push_back(MakeInstructionMessage(SpvOpFunctionCall, 2, 11,
1643 {{SPV_OPERAND_TYPE_ID, {6}}}));
1644 instructions.push_back(MakeInstructionMessage(SpvOpReturn, 0, 0, {}));
1645 instructions.push_back(MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {}));
1646
1647 spvtools::ValidatorOptions validator_options;
1648
1649 const auto context1 = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1650 const auto context2 = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1651 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
1652 context1.get(), validator_options, kConsoleMessageConsumer));
1653
1654 TransformationContext transformation_context1(
1655 MakeUnique<FactManager>(context1.get()), validator_options);
1656 TransformationContext transformation_context2(
1657 MakeUnique<FactManager>(context2.get()), validator_options);
1658
1659 // Mark function 6 as livesafe.
1660 transformation_context2.GetFactManager()->AddFactFunctionIsLivesafe(6);
1661
1662 TransformationAddFunction add_dead_function(instructions);
1663 ASSERT_TRUE(
1664 add_dead_function.IsApplicable(context1.get(), transformation_context1));
1665 ApplyAndCheckFreshIds(add_dead_function, context1.get(),
1666 &transformation_context1);
1667 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
1668 context1.get(), validator_options, kConsoleMessageConsumer));
1669 // The function should not be deemed livesafe
1670 ASSERT_FALSE(transformation_context1.GetFactManager()->FunctionIsLivesafe(8));
1671 // All variables/parameters in the function should be deemed irrelevant.
1672 ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant(
1673 context1.get(), transformation_context1, 8, 0));
1674
1675 std::string added_as_live_or_dead_code = R"(
1676 OpCapability Shader
1677 %1 = OpExtInstImport "GLSL.std.450"
1678 OpMemoryModel Logical GLSL450
1679 OpEntryPoint Fragment %4 "main"
1680 OpExecutionMode %4 OriginUpperLeft
1681 OpSource ESSL 310
1682 %2 = OpTypeVoid
1683 %3 = OpTypeFunction %2
1684 %4 = OpFunction %2 None %3
1685 %5 = OpLabel
1686 OpReturn
1687 OpFunctionEnd
1688 %6 = OpFunction %2 None %3
1689 %7 = OpLabel
1690 OpReturn
1691 OpFunctionEnd
1692 %8 = OpFunction %2 None %3
1693 %9 = OpLabel
1694 %11 = OpFunctionCall %2 %6
1695 OpReturn
1696 OpFunctionEnd
1697 )";
1698 ASSERT_TRUE(IsEqual(env, added_as_live_or_dead_code, context1.get()));
1699
1700 TransformationAddFunction add_livesafe_function(instructions, 0, 0, {}, 0,
1701 {});
1702 ASSERT_TRUE(add_livesafe_function.IsApplicable(context2.get(),
1703 transformation_context2));
1704 ApplyAndCheckFreshIds(add_livesafe_function, context2.get(),
1705 &transformation_context2);
1706 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
1707 context2.get(), validator_options, kConsoleMessageConsumer));
1708 // The function should be deemed livesafe
1709 ASSERT_TRUE(transformation_context2.GetFactManager()->FunctionIsLivesafe(8));
1710 // All variables/parameters in the function should be deemed irrelevant.
1711 ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant(
1712 context2.get(), transformation_context2, 8, 0));
1713 ASSERT_TRUE(IsEqual(env, added_as_live_or_dead_code, context2.get()));
1714 }
1715
TEST(TransformationAddFunctionTest,LivesafeOnlyCallsLivesafe)1716 TEST(TransformationAddFunctionTest, LivesafeOnlyCallsLivesafe) {
1717 std::string shader = R"(
1718 OpCapability Shader
1719 %1 = OpExtInstImport "GLSL.std.450"
1720 OpMemoryModel Logical GLSL450
1721 OpEntryPoint Fragment %4 "main"
1722 OpExecutionMode %4 OriginUpperLeft
1723 OpSource ESSL 310
1724 %2 = OpTypeVoid
1725 %3 = OpTypeFunction %2
1726 %4 = OpFunction %2 None %3
1727 %5 = OpLabel
1728 OpReturn
1729 OpFunctionEnd
1730 %6 = OpFunction %2 None %3
1731 %7 = OpLabel
1732 OpKill
1733 OpFunctionEnd
1734 )";
1735
1736 const auto env = SPV_ENV_UNIVERSAL_1_4;
1737 const auto consumer = nullptr;
1738
1739 std::vector<protobufs::Instruction> instructions;
1740
1741 instructions.push_back(MakeInstructionMessage(
1742 SpvOpFunction, 2, 8,
1743 {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, {SpvFunctionControlMaskNone}},
1744 {SPV_OPERAND_TYPE_TYPE_ID, {3}}}));
1745 instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 9, {}));
1746 instructions.push_back(MakeInstructionMessage(SpvOpFunctionCall, 2, 11,
1747 {{SPV_OPERAND_TYPE_ID, {6}}}));
1748 instructions.push_back(MakeInstructionMessage(SpvOpReturn, 0, 0, {}));
1749 instructions.push_back(MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {}));
1750
1751 spvtools::ValidatorOptions validator_options;
1752
1753 const auto context1 = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1754 const auto context2 = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1755 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
1756 context1.get(), validator_options, kConsoleMessageConsumer));
1757
1758 TransformationContext transformation_context1(
1759 MakeUnique<FactManager>(context1.get()), validator_options);
1760 TransformationContext transformation_context2(
1761 MakeUnique<FactManager>(context2.get()), validator_options);
1762
1763 TransformationAddFunction add_dead_function(instructions);
1764 ASSERT_TRUE(
1765 add_dead_function.IsApplicable(context1.get(), transformation_context1));
1766 ApplyAndCheckFreshIds(add_dead_function, context1.get(),
1767 &transformation_context1);
1768 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
1769 context1.get(), validator_options, kConsoleMessageConsumer));
1770 // The function should not be deemed livesafe
1771 ASSERT_FALSE(transformation_context1.GetFactManager()->FunctionIsLivesafe(8));
1772 // All variables/parameters in the function should be deemed irrelevant.
1773 ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant(
1774 context1.get(), transformation_context1, 8, 0));
1775
1776 std::string added_as_dead_code = R"(
1777 OpCapability Shader
1778 %1 = OpExtInstImport "GLSL.std.450"
1779 OpMemoryModel Logical GLSL450
1780 OpEntryPoint Fragment %4 "main"
1781 OpExecutionMode %4 OriginUpperLeft
1782 OpSource ESSL 310
1783 %2 = OpTypeVoid
1784 %3 = OpTypeFunction %2
1785 %4 = OpFunction %2 None %3
1786 %5 = OpLabel
1787 OpReturn
1788 OpFunctionEnd
1789 %6 = OpFunction %2 None %3
1790 %7 = OpLabel
1791 OpKill
1792 OpFunctionEnd
1793 %8 = OpFunction %2 None %3
1794 %9 = OpLabel
1795 %11 = OpFunctionCall %2 %6
1796 OpReturn
1797 OpFunctionEnd
1798 )";
1799 ASSERT_TRUE(IsEqual(env, added_as_dead_code, context1.get()));
1800
1801 TransformationAddFunction add_livesafe_function(instructions, 0, 0, {}, 0,
1802 {});
1803 ASSERT_FALSE(add_livesafe_function.IsApplicable(context2.get(),
1804 transformation_context2));
1805 }
1806
TEST(TransformationAddFunctionTest,LoopLimitersBackEdgeBlockEndsWithConditional1)1807 TEST(TransformationAddFunctionTest,
1808 LoopLimitersBackEdgeBlockEndsWithConditional1) {
1809 std::string shader = R"(
1810 OpCapability Shader
1811 %1 = OpExtInstImport "GLSL.std.450"
1812 OpMemoryModel Logical GLSL450
1813 OpEntryPoint Fragment %4 "main"
1814 OpExecutionMode %4 OriginUpperLeft
1815 OpSource ESSL 310
1816 %2 = OpTypeVoid
1817 %3 = OpTypeFunction %2
1818 %8 = OpTypeInt 32 1
1819 %9 = OpTypePointer Function %8
1820 %11 = OpConstant %8 0
1821 %18 = OpConstant %8 10
1822 %19 = OpTypeBool
1823 %26 = OpConstantTrue %19
1824 %27 = OpConstantFalse %19
1825 %28 = OpTypeInt 32 0
1826 %29 = OpTypePointer Function %28
1827 %30 = OpConstant %28 0
1828 %31 = OpConstant %28 1
1829 %32 = OpConstant %28 5
1830 %22 = OpConstant %8 1
1831 %4 = OpFunction %2 None %3
1832 %5 = OpLabel
1833 OpReturn
1834 OpFunctionEnd
1835 )";
1836
1837 std::string donor = R"(
1838 OpCapability Shader
1839 %1 = OpExtInstImport "GLSL.std.450"
1840 OpMemoryModel Logical GLSL450
1841 OpEntryPoint Fragment %4 "main"
1842 OpExecutionMode %4 OriginUpperLeft
1843 OpSource ESSL 310
1844 %2 = OpTypeVoid
1845 %3 = OpTypeFunction %2
1846 %8 = OpTypeInt 32 1
1847 %9 = OpTypePointer Function %8
1848 %11 = OpConstant %8 0
1849 %18 = OpConstant %8 10
1850 %19 = OpTypeBool
1851 %26 = OpConstantTrue %19
1852 %27 = OpConstantFalse %19
1853 %28 = OpTypeInt 32 0
1854 %29 = OpTypePointer Function %28
1855 %30 = OpConstant %28 0
1856 %31 = OpConstant %28 1
1857 %32 = OpConstant %28 5
1858 %22 = OpConstant %8 1
1859 %4 = OpFunction %2 None %3
1860 %5 = OpLabel
1861 OpReturn
1862 OpFunctionEnd
1863 %6 = OpFunction %2 None %3
1864 %7 = OpLabel
1865 %10 = OpVariable %9 Function
1866 OpStore %10 %11
1867 OpBranch %12
1868 %12 = OpLabel
1869 OpLoopMerge %14 %15 None
1870 OpBranch %15
1871 %15 = OpLabel
1872 %17 = OpLoad %8 %10
1873 %20 = OpSLessThan %19 %17 %18
1874 %21 = OpLoad %8 %10
1875 %23 = OpIAdd %8 %21 %22
1876 OpStore %10 %23
1877 OpBranchConditional %20 %12 %14
1878 %14 = OpLabel
1879 OpReturn
1880 OpFunctionEnd
1881 )";
1882 const auto env = SPV_ENV_UNIVERSAL_1_4;
1883 const auto consumer = nullptr;
1884 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
1885 spvtools::ValidatorOptions validator_options;
1886 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1887 kConsoleMessageConsumer));
1888 TransformationContext transformation_context(
1889 MakeUnique<FactManager>(context.get()), validator_options);
1890 // Make a sequence of instruction messages corresponding to function %6 in
1891 // |donor|.
1892 std::vector<protobufs::Instruction> instructions =
1893 GetInstructionsForFunction(env, consumer, donor, 6);
1894
1895 protobufs::LoopLimiterInfo loop_limiter_info;
1896 loop_limiter_info.set_loop_header_id(12);
1897 loop_limiter_info.set_load_id(102);
1898 loop_limiter_info.set_increment_id(103);
1899 loop_limiter_info.set_compare_id(104);
1900 loop_limiter_info.set_logical_op_id(105);
1901 TransformationAddFunction add_livesafe_function(instructions, 100, 32,
1902 {loop_limiter_info}, 0, {});
1903 ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(),
1904 transformation_context));
1905 ApplyAndCheckFreshIds(add_livesafe_function, context.get(),
1906 &transformation_context);
1907 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
1908 kConsoleMessageConsumer));
1909 std::string expected = R"(
1910 OpCapability Shader
1911 %1 = OpExtInstImport "GLSL.std.450"
1912 OpMemoryModel Logical GLSL450
1913 OpEntryPoint Fragment %4 "main"
1914 OpExecutionMode %4 OriginUpperLeft
1915 OpSource ESSL 310
1916 %2 = OpTypeVoid
1917 %3 = OpTypeFunction %2
1918 %8 = OpTypeInt 32 1
1919 %9 = OpTypePointer Function %8
1920 %11 = OpConstant %8 0
1921 %18 = OpConstant %8 10
1922 %19 = OpTypeBool
1923 %26 = OpConstantTrue %19
1924 %27 = OpConstantFalse %19
1925 %28 = OpTypeInt 32 0
1926 %29 = OpTypePointer Function %28
1927 %30 = OpConstant %28 0
1928 %31 = OpConstant %28 1
1929 %32 = OpConstant %28 5
1930 %22 = OpConstant %8 1
1931 %4 = OpFunction %2 None %3
1932 %5 = OpLabel
1933 OpReturn
1934 OpFunctionEnd
1935 %6 = OpFunction %2 None %3
1936 %7 = OpLabel
1937 %100 = OpVariable %29 Function %30
1938 %10 = OpVariable %9 Function
1939 OpStore %10 %11
1940 OpBranch %12
1941 %12 = OpLabel
1942 OpLoopMerge %14 %15 None
1943 OpBranch %15
1944 %15 = OpLabel
1945 %17 = OpLoad %8 %10
1946 %20 = OpSLessThan %19 %17 %18
1947 %21 = OpLoad %8 %10
1948 %23 = OpIAdd %8 %21 %22
1949 OpStore %10 %23
1950 %102 = OpLoad %28 %100
1951 %103 = OpIAdd %28 %102 %31
1952 OpStore %100 %103
1953 %104 = OpULessThan %19 %102 %32
1954 %105 = OpLogicalAnd %19 %20 %104
1955 OpBranchConditional %105 %12 %14
1956 %14 = OpLabel
1957 OpReturn
1958 OpFunctionEnd
1959 )";
1960 ASSERT_TRUE(IsEqual(env, expected, context.get()));
1961 }
1962
TEST(TransformationAddFunctionTest,LoopLimitersBackEdgeBlockEndsWithConditional2)1963 TEST(TransformationAddFunctionTest,
1964 LoopLimitersBackEdgeBlockEndsWithConditional2) {
1965 std::string shader = R"(
1966 OpCapability Shader
1967 %1 = OpExtInstImport "GLSL.std.450"
1968 OpMemoryModel Logical GLSL450
1969 OpEntryPoint Fragment %4 "main"
1970 OpExecutionMode %4 OriginUpperLeft
1971 OpSource ESSL 310
1972 %2 = OpTypeVoid
1973 %3 = OpTypeFunction %2
1974 %8 = OpTypeInt 32 1
1975 %9 = OpTypePointer Function %8
1976 %11 = OpConstant %8 0
1977 %18 = OpConstant %8 10
1978 %19 = OpTypeBool
1979 %26 = OpConstantTrue %19
1980 %27 = OpConstantFalse %19
1981 %28 = OpTypeInt 32 0
1982 %29 = OpTypePointer Function %28
1983 %30 = OpConstant %28 0
1984 %31 = OpConstant %28 1
1985 %32 = OpConstant %28 5
1986 %22 = OpConstant %8 1
1987 %4 = OpFunction %2 None %3
1988 %5 = OpLabel
1989 OpReturn
1990 OpFunctionEnd
1991 )";
1992
1993 std::string donor = R"(
1994 OpCapability Shader
1995 %1 = OpExtInstImport "GLSL.std.450"
1996 OpMemoryModel Logical GLSL450
1997 OpEntryPoint Fragment %4 "main"
1998 OpExecutionMode %4 OriginUpperLeft
1999 OpSource ESSL 310
2000 %2 = OpTypeVoid
2001 %3 = OpTypeFunction %2
2002 %8 = OpTypeInt 32 1
2003 %9 = OpTypePointer Function %8
2004 %11 = OpConstant %8 0
2005 %18 = OpConstant %8 10
2006 %19 = OpTypeBool
2007 %26 = OpConstantTrue %19
2008 %27 = OpConstantFalse %19
2009 %28 = OpTypeInt 32 0
2010 %29 = OpTypePointer Function %28
2011 %30 = OpConstant %28 0
2012 %31 = OpConstant %28 1
2013 %32 = OpConstant %28 5
2014 %22 = OpConstant %8 1
2015 %4 = OpFunction %2 None %3
2016 %5 = OpLabel
2017 OpReturn
2018 OpFunctionEnd
2019 %6 = OpFunction %2 None %3
2020 %7 = OpLabel
2021 %10 = OpVariable %9 Function
2022 OpStore %10 %11
2023 OpBranch %12
2024 %12 = OpLabel
2025 OpLoopMerge %14 %15 None
2026 OpBranch %15
2027 %15 = OpLabel
2028 %17 = OpLoad %8 %10
2029 %20 = OpSLessThan %19 %17 %18
2030 %21 = OpLoad %8 %10
2031 %23 = OpIAdd %8 %21 %22
2032 OpStore %10 %23
2033 %50 = OpLogicalNot %19 %20
2034 OpBranchConditional %50 %14 %12
2035 %14 = OpLabel
2036 OpReturn
2037 OpFunctionEnd
2038 )";
2039 const auto env = SPV_ENV_UNIVERSAL_1_4;
2040 const auto consumer = nullptr;
2041 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
2042 spvtools::ValidatorOptions validator_options;
2043 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
2044 kConsoleMessageConsumer));
2045 TransformationContext transformation_context(
2046 MakeUnique<FactManager>(context.get()), validator_options);
2047 // Make a sequence of instruction messages corresponding to function %6 in
2048 // |donor|.
2049 std::vector<protobufs::Instruction> instructions =
2050 GetInstructionsForFunction(env, consumer, donor, 6);
2051
2052 protobufs::LoopLimiterInfo loop_limiter_info;
2053 loop_limiter_info.set_loop_header_id(12);
2054 loop_limiter_info.set_load_id(102);
2055 loop_limiter_info.set_increment_id(103);
2056 loop_limiter_info.set_compare_id(104);
2057 loop_limiter_info.set_logical_op_id(105);
2058 TransformationAddFunction add_livesafe_function(instructions, 100, 32,
2059 {loop_limiter_info}, 0, {});
2060 ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(),
2061 transformation_context));
2062 ApplyAndCheckFreshIds(add_livesafe_function, context.get(),
2063 &transformation_context);
2064 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
2065 kConsoleMessageConsumer));
2066 std::string expected = R"(
2067 OpCapability Shader
2068 %1 = OpExtInstImport "GLSL.std.450"
2069 OpMemoryModel Logical GLSL450
2070 OpEntryPoint Fragment %4 "main"
2071 OpExecutionMode %4 OriginUpperLeft
2072 OpSource ESSL 310
2073 %2 = OpTypeVoid
2074 %3 = OpTypeFunction %2
2075 %8 = OpTypeInt 32 1
2076 %9 = OpTypePointer Function %8
2077 %11 = OpConstant %8 0
2078 %18 = OpConstant %8 10
2079 %19 = OpTypeBool
2080 %26 = OpConstantTrue %19
2081 %27 = OpConstantFalse %19
2082 %28 = OpTypeInt 32 0
2083 %29 = OpTypePointer Function %28
2084 %30 = OpConstant %28 0
2085 %31 = OpConstant %28 1
2086 %32 = OpConstant %28 5
2087 %22 = OpConstant %8 1
2088 %4 = OpFunction %2 None %3
2089 %5 = OpLabel
2090 OpReturn
2091 OpFunctionEnd
2092 %6 = OpFunction %2 None %3
2093 %7 = OpLabel
2094 %100 = OpVariable %29 Function %30
2095 %10 = OpVariable %9 Function
2096 OpStore %10 %11
2097 OpBranch %12
2098 %12 = OpLabel
2099 OpLoopMerge %14 %15 None
2100 OpBranch %15
2101 %15 = OpLabel
2102 %17 = OpLoad %8 %10
2103 %20 = OpSLessThan %19 %17 %18
2104 %21 = OpLoad %8 %10
2105 %23 = OpIAdd %8 %21 %22
2106 OpStore %10 %23
2107 %50 = OpLogicalNot %19 %20
2108 %102 = OpLoad %28 %100
2109 %103 = OpIAdd %28 %102 %31
2110 OpStore %100 %103
2111 %104 = OpUGreaterThanEqual %19 %102 %32
2112 %105 = OpLogicalOr %19 %50 %104
2113 OpBranchConditional %105 %14 %12
2114 %14 = OpLabel
2115 OpReturn
2116 OpFunctionEnd
2117 )";
2118 ASSERT_TRUE(IsEqual(env, expected, context.get()));
2119 }
2120
TEST(TransformationAddFunctionTest,LoopLimitersHeaderIsBackEdgeBlock)2121 TEST(TransformationAddFunctionTest, LoopLimitersHeaderIsBackEdgeBlock) {
2122 std::string shader = R"(
2123 OpCapability Shader
2124 %1 = OpExtInstImport "GLSL.std.450"
2125 OpMemoryModel Logical GLSL450
2126 OpEntryPoint Fragment %4 "main"
2127 OpExecutionMode %4 OriginUpperLeft
2128 OpSource ESSL 310
2129 %2 = OpTypeVoid
2130 %3 = OpTypeFunction %2
2131 %8 = OpTypeInt 32 1
2132 %9 = OpTypePointer Function %8
2133 %11 = OpConstant %8 0
2134 %18 = OpConstant %8 10
2135 %19 = OpTypeBool
2136 %26 = OpConstantTrue %19
2137 %27 = OpConstantFalse %19
2138 %28 = OpTypeInt 32 0
2139 %29 = OpTypePointer Function %28
2140 %30 = OpConstant %28 0
2141 %31 = OpConstant %28 1
2142 %32 = OpConstant %28 5
2143 %22 = OpConstant %8 1
2144 %4 = OpFunction %2 None %3
2145 %5 = OpLabel
2146 OpReturn
2147 OpFunctionEnd
2148 )";
2149
2150 std::string donor = R"(
2151 OpCapability Shader
2152 %1 = OpExtInstImport "GLSL.std.450"
2153 OpMemoryModel Logical GLSL450
2154 OpEntryPoint Fragment %4 "main"
2155 OpExecutionMode %4 OriginUpperLeft
2156 OpSource ESSL 310
2157 %2 = OpTypeVoid
2158 %3 = OpTypeFunction %2
2159 %8 = OpTypeInt 32 1
2160 %9 = OpTypePointer Function %8
2161 %11 = OpConstant %8 0
2162 %18 = OpConstant %8 10
2163 %19 = OpTypeBool
2164 %26 = OpConstantTrue %19
2165 %27 = OpConstantFalse %19
2166 %28 = OpTypeInt 32 0
2167 %29 = OpTypePointer Function %28
2168 %30 = OpConstant %28 0
2169 %31 = OpConstant %28 1
2170 %32 = OpConstant %28 5
2171 %22 = OpConstant %8 1
2172 %4 = OpFunction %2 None %3
2173 %5 = OpLabel
2174 OpReturn
2175 OpFunctionEnd
2176 %6 = OpFunction %2 None %3
2177 %7 = OpLabel
2178 %10 = OpVariable %9 Function
2179 OpStore %10 %11
2180 OpBranch %12
2181 %12 = OpLabel
2182 %17 = OpLoad %8 %10
2183 %20 = OpSLessThan %19 %17 %18
2184 %21 = OpLoad %8 %10
2185 %23 = OpIAdd %8 %21 %22
2186 OpStore %10 %23
2187 %50 = OpLogicalNot %19 %20
2188 OpLoopMerge %14 %12 None
2189 OpBranchConditional %50 %14 %12
2190 %14 = OpLabel
2191 OpReturn
2192 OpFunctionEnd
2193 )";
2194 const auto env = SPV_ENV_UNIVERSAL_1_4;
2195 const auto consumer = nullptr;
2196 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
2197 spvtools::ValidatorOptions validator_options;
2198 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
2199 kConsoleMessageConsumer));
2200 TransformationContext transformation_context(
2201 MakeUnique<FactManager>(context.get()), validator_options);
2202 // Make a sequence of instruction messages corresponding to function %6 in
2203 // |donor|.
2204 std::vector<protobufs::Instruction> instructions =
2205 GetInstructionsForFunction(env, consumer, donor, 6);
2206
2207 protobufs::LoopLimiterInfo loop_limiter_info;
2208 loop_limiter_info.set_loop_header_id(12);
2209 loop_limiter_info.set_load_id(102);
2210 loop_limiter_info.set_increment_id(103);
2211 loop_limiter_info.set_compare_id(104);
2212 loop_limiter_info.set_logical_op_id(105);
2213 TransformationAddFunction add_livesafe_function(instructions, 100, 32,
2214 {loop_limiter_info}, 0, {});
2215 ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(),
2216 transformation_context));
2217 ApplyAndCheckFreshIds(add_livesafe_function, context.get(),
2218 &transformation_context);
2219 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
2220 kConsoleMessageConsumer));
2221 std::string expected = R"(
2222 OpCapability Shader
2223 %1 = OpExtInstImport "GLSL.std.450"
2224 OpMemoryModel Logical GLSL450
2225 OpEntryPoint Fragment %4 "main"
2226 OpExecutionMode %4 OriginUpperLeft
2227 OpSource ESSL 310
2228 %2 = OpTypeVoid
2229 %3 = OpTypeFunction %2
2230 %8 = OpTypeInt 32 1
2231 %9 = OpTypePointer Function %8
2232 %11 = OpConstant %8 0
2233 %18 = OpConstant %8 10
2234 %19 = OpTypeBool
2235 %26 = OpConstantTrue %19
2236 %27 = OpConstantFalse %19
2237 %28 = OpTypeInt 32 0
2238 %29 = OpTypePointer Function %28
2239 %30 = OpConstant %28 0
2240 %31 = OpConstant %28 1
2241 %32 = OpConstant %28 5
2242 %22 = OpConstant %8 1
2243 %4 = OpFunction %2 None %3
2244 %5 = OpLabel
2245 OpReturn
2246 OpFunctionEnd
2247 %6 = OpFunction %2 None %3
2248 %7 = OpLabel
2249 %100 = OpVariable %29 Function %30
2250 %10 = OpVariable %9 Function
2251 OpStore %10 %11
2252 OpBranch %12
2253 %12 = OpLabel
2254 %17 = OpLoad %8 %10
2255 %20 = OpSLessThan %19 %17 %18
2256 %21 = OpLoad %8 %10
2257 %23 = OpIAdd %8 %21 %22
2258 OpStore %10 %23
2259 %50 = OpLogicalNot %19 %20
2260 %102 = OpLoad %28 %100
2261 %103 = OpIAdd %28 %102 %31
2262 OpStore %100 %103
2263 %104 = OpUGreaterThanEqual %19 %102 %32
2264 %105 = OpLogicalOr %19 %50 %104
2265 OpLoopMerge %14 %12 None
2266 OpBranchConditional %105 %14 %12
2267 %14 = OpLabel
2268 OpReturn
2269 OpFunctionEnd
2270 )";
2271 ASSERT_TRUE(IsEqual(env, expected, context.get()));
2272 }
2273
TEST(TransformationAddFunctionTest,InfiniteLoop)2274 TEST(TransformationAddFunctionTest, InfiniteLoop) {
2275 std::string shader = R"(
2276 OpCapability Shader
2277 %1 = OpExtInstImport "GLSL.std.450"
2278 OpMemoryModel Logical GLSL450
2279 OpEntryPoint Fragment %4 "main"
2280 OpExecutionMode %4 OriginUpperLeft
2281 OpSource ESSL 310
2282 %2 = OpTypeVoid
2283 %3 = OpTypeFunction %2
2284 %8 = OpTypeInt 32 1
2285 %9 = OpTypePointer Function %8
2286 %11 = OpConstant %8 0
2287 %18 = OpConstant %8 10
2288 %19 = OpTypeBool
2289 %26 = OpConstantTrue %19
2290 %27 = OpConstantFalse %19
2291 %28 = OpTypeInt 32 0
2292 %29 = OpTypePointer Function %28
2293 %30 = OpConstant %28 0
2294 %31 = OpConstant %28 1
2295 %32 = OpConstant %28 5
2296 %22 = OpConstant %8 1
2297 %4 = OpFunction %2 None %3
2298 %5 = OpLabel
2299 OpReturn
2300 OpFunctionEnd
2301 )";
2302
2303 std::string donor = R"(
2304 OpCapability Shader
2305 %1 = OpExtInstImport "GLSL.std.450"
2306 OpMemoryModel Logical GLSL450
2307 OpEntryPoint Fragment %4 "main"
2308 OpExecutionMode %4 OriginUpperLeft
2309 OpSource ESSL 310
2310 %2 = OpTypeVoid
2311 %3 = OpTypeFunction %2
2312 %8 = OpTypeInt 32 1
2313 %9 = OpTypePointer Function %8
2314 %11 = OpConstant %8 0
2315 %18 = OpConstant %8 10
2316 %19 = OpTypeBool
2317 %26 = OpConstantTrue %19
2318 %27 = OpConstantFalse %19
2319 %28 = OpTypeInt 32 0
2320 %29 = OpTypePointer Function %28
2321 %30 = OpConstant %28 0
2322 %31 = OpConstant %28 1
2323 %32 = OpConstant %28 5
2324 %22 = OpConstant %8 1
2325 %4 = OpFunction %2 None %3
2326 %5 = OpLabel
2327 OpReturn
2328 OpFunctionEnd
2329 %6 = OpFunction %2 None %3
2330 %7 = OpLabel
2331 %10 = OpVariable %9 Function
2332 OpStore %10 %11
2333 OpBranch %12
2334 %12 = OpLabel
2335 OpLoopMerge %14 %12 None
2336 OpBranch %12
2337 %14 = OpLabel
2338 OpReturn
2339 OpFunctionEnd
2340 )";
2341 const auto env = SPV_ENV_UNIVERSAL_1_4;
2342 const auto consumer = nullptr;
2343 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
2344 spvtools::ValidatorOptions validator_options;
2345 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
2346 kConsoleMessageConsumer));
2347 TransformationContext transformation_context(
2348 MakeUnique<FactManager>(context.get()), validator_options);
2349 // Make a sequence of instruction messages corresponding to function %6 in
2350 // |donor|.
2351 std::vector<protobufs::Instruction> instructions =
2352 GetInstructionsForFunction(env, consumer, donor, 6);
2353
2354 protobufs::LoopLimiterInfo loop_limiter_info;
2355 loop_limiter_info.set_loop_header_id(12);
2356 loop_limiter_info.set_load_id(102);
2357 loop_limiter_info.set_increment_id(103);
2358 loop_limiter_info.set_compare_id(104);
2359 loop_limiter_info.set_logical_op_id(105);
2360 TransformationAddFunction add_livesafe_function(instructions, 100, 32,
2361 {loop_limiter_info}, 0, {});
2362
2363 // To make sure the loop's merge block is reachable, it must be dominated by
2364 // the loop header.
2365 ASSERT_FALSE(add_livesafe_function.IsApplicable(context.get(),
2366 transformation_context));
2367 }
2368
TEST(TransformationAddFunctionTest,UnreachableContinueConstruct)2369 TEST(TransformationAddFunctionTest, UnreachableContinueConstruct) {
2370 // This captures the case where the loop's continue construct is statically
2371 // unreachable. In this case the loop cannot iterate and so we do not add
2372 // a loop limiter. (The reason we do not just add one anyway is that
2373 // detecting which block would be the back-edge block is difficult in the
2374 // absence of reliable dominance information.)
2375 std::string shader = R"(
2376 OpCapability Shader
2377 %1 = OpExtInstImport "GLSL.std.450"
2378 OpMemoryModel Logical GLSL450
2379 OpEntryPoint Fragment %4 "main"
2380 OpExecutionMode %4 OriginUpperLeft
2381 OpSource ESSL 310
2382 %2 = OpTypeVoid
2383 %3 = OpTypeFunction %2
2384 %8 = OpTypeInt 32 1
2385 %9 = OpTypePointer Function %8
2386 %11 = OpConstant %8 0
2387 %18 = OpConstant %8 10
2388 %19 = OpTypeBool
2389 %23 = OpConstant %8 1
2390 %26 = OpConstantTrue %19
2391 %27 = OpConstantFalse %19
2392 %28 = OpTypeInt 32 0
2393 %29 = OpTypePointer Function %28
2394 %30 = OpConstant %28 0
2395 %31 = OpConstant %28 1
2396 %32 = OpConstant %28 5
2397 %4 = OpFunction %2 None %3
2398 %5 = OpLabel
2399 OpReturn
2400 OpFunctionEnd
2401 )";
2402
2403 std::string donor = R"(
2404 OpCapability Shader
2405 %1 = OpExtInstImport "GLSL.std.450"
2406 OpMemoryModel Logical GLSL450
2407 OpEntryPoint Fragment %4 "main"
2408 OpExecutionMode %4 OriginUpperLeft
2409 OpSource ESSL 310
2410 %2 = OpTypeVoid
2411 %3 = OpTypeFunction %2
2412 %8 = OpTypeInt 32 1
2413 %9 = OpTypePointer Function %8
2414 %11 = OpConstant %8 0
2415 %18 = OpConstant %8 10
2416 %19 = OpTypeBool
2417 %23 = OpConstant %8 1
2418 %26 = OpConstantTrue %19
2419 %27 = OpConstantFalse %19
2420 %28 = OpTypeInt 32 0
2421 %29 = OpTypePointer Function %28
2422 %30 = OpConstant %28 0
2423 %31 = OpConstant %28 1
2424 %32 = OpConstant %28 5
2425 %4 = OpFunction %2 None %3
2426 %5 = OpLabel
2427 OpReturn
2428 OpFunctionEnd
2429 %6 = OpFunction %2 None %3
2430 %7 = OpLabel
2431 %10 = OpVariable %9 Function
2432 OpStore %10 %11
2433 OpBranch %12
2434 %12 = OpLabel
2435 OpLoopMerge %14 %15 None
2436 OpBranch %16
2437 %16 = OpLabel
2438 %17 = OpLoad %8 %10
2439 %20 = OpSLessThan %19 %17 %18
2440 OpBranchConditional %20 %13 %14
2441 %13 = OpLabel
2442 OpBranch %14
2443 %15 = OpLabel
2444 %22 = OpLoad %8 %10
2445 %24 = OpIAdd %8 %22 %23
2446 OpStore %10 %24
2447 OpBranch %12
2448 %14 = OpLabel
2449 OpReturn
2450 OpFunctionEnd
2451 )";
2452
2453 const auto env = SPV_ENV_UNIVERSAL_1_4;
2454 const auto consumer = nullptr;
2455 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
2456 spvtools::ValidatorOptions validator_options;
2457 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
2458 kConsoleMessageConsumer));
2459 TransformationContext transformation_context(
2460 MakeUnique<FactManager>(context.get()), validator_options);
2461 // Make a sequence of instruction messages corresponding to function %6 in
2462 // |donor|.
2463 std::vector<protobufs::Instruction> instructions =
2464 GetInstructionsForFunction(env, consumer, donor, 6);
2465
2466 protobufs::LoopLimiterInfo loop_limiter_info;
2467 loop_limiter_info.set_loop_header_id(12);
2468 loop_limiter_info.set_load_id(102);
2469 loop_limiter_info.set_increment_id(103);
2470 loop_limiter_info.set_compare_id(104);
2471 loop_limiter_info.set_logical_op_id(105);
2472 TransformationAddFunction add_livesafe_function(instructions, 100, 32,
2473 {loop_limiter_info}, 0, {});
2474 ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(),
2475 transformation_context));
2476 ApplyAndCheckFreshIds(add_livesafe_function, context.get(),
2477 &transformation_context);
2478 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
2479 kConsoleMessageConsumer));
2480 std::string expected = R"(
2481 OpCapability Shader
2482 %1 = OpExtInstImport "GLSL.std.450"
2483 OpMemoryModel Logical GLSL450
2484 OpEntryPoint Fragment %4 "main"
2485 OpExecutionMode %4 OriginUpperLeft
2486 OpSource ESSL 310
2487 %2 = OpTypeVoid
2488 %3 = OpTypeFunction %2
2489 %8 = OpTypeInt 32 1
2490 %9 = OpTypePointer Function %8
2491 %11 = OpConstant %8 0
2492 %18 = OpConstant %8 10
2493 %19 = OpTypeBool
2494 %23 = OpConstant %8 1
2495 %26 = OpConstantTrue %19
2496 %27 = OpConstantFalse %19
2497 %28 = OpTypeInt 32 0
2498 %29 = OpTypePointer Function %28
2499 %30 = OpConstant %28 0
2500 %31 = OpConstant %28 1
2501 %32 = OpConstant %28 5
2502 %4 = OpFunction %2 None %3
2503 %5 = OpLabel
2504 OpReturn
2505 OpFunctionEnd
2506 %6 = OpFunction %2 None %3
2507 %7 = OpLabel
2508 %100 = OpVariable %29 Function %30
2509 %10 = OpVariable %9 Function
2510 OpStore %10 %11
2511 OpBranch %12
2512 %12 = OpLabel
2513 OpLoopMerge %14 %15 None
2514 OpBranch %16
2515 %16 = OpLabel
2516 %17 = OpLoad %8 %10
2517 %20 = OpSLessThan %19 %17 %18
2518 OpBranchConditional %20 %13 %14
2519 %13 = OpLabel
2520 OpBranch %14
2521 %15 = OpLabel
2522 %22 = OpLoad %8 %10
2523 %24 = OpIAdd %8 %22 %23
2524 OpStore %10 %24
2525 OpBranch %12
2526 %14 = OpLabel
2527 OpReturn
2528 OpFunctionEnd
2529 )";
2530 ASSERT_TRUE(IsEqual(env, expected, context.get()));
2531 }
2532
TEST(TransformationAddFunctionTest,LoopLimitersAndOpPhi1)2533 TEST(TransformationAddFunctionTest, LoopLimitersAndOpPhi1) {
2534 // This captures the scenario where breaking a loop due to a loop limiter
2535 // requires patching up OpPhi instructions occurring at the loop merge block.
2536
2537 std::string shader = R"(
2538 OpCapability Shader
2539 %1 = OpExtInstImport "GLSL.std.450"
2540 OpMemoryModel Logical GLSL450
2541 OpEntryPoint Fragment %4 "main"
2542 OpExecutionMode %4 OriginUpperLeft
2543 OpSource ESSL 310
2544 %2 = OpTypeVoid
2545 %3 = OpTypeFunction %2
2546 %6 = OpTypeInt 32 1
2547 %50 = OpTypeInt 32 0
2548 %51 = OpConstant %50 0
2549 %52 = OpConstant %50 1
2550 %53 = OpTypePointer Function %50
2551 %7 = OpTypeFunction %6
2552 %10 = OpTypePointer Function %6
2553 %12 = OpConstant %6 0
2554 %19 = OpConstant %6 100
2555 %20 = OpTypeBool
2556 %23 = OpConstant %6 20
2557 %28 = OpConstant %6 1
2558 %4 = OpFunction %2 None %3
2559 %5 = OpLabel
2560 OpReturn
2561 OpFunctionEnd
2562 )";
2563
2564 std::string donor = R"(
2565 OpCapability Shader
2566 %1 = OpExtInstImport "GLSL.std.450"
2567 OpMemoryModel Logical GLSL450
2568 OpEntryPoint Fragment %4 "main"
2569 OpExecutionMode %4 OriginUpperLeft
2570 OpSource ESSL 310
2571 %2 = OpTypeVoid
2572 %3 = OpTypeFunction %2
2573 %6 = OpTypeInt 32 1
2574 %7 = OpTypeFunction %6
2575 %10 = OpTypePointer Function %6
2576 %12 = OpConstant %6 0
2577 %19 = OpConstant %6 100
2578 %20 = OpTypeBool
2579 %23 = OpConstant %6 20
2580 %28 = OpConstant %6 1
2581 %4 = OpFunction %2 None %3
2582 %5 = OpLabel
2583 %36 = OpFunctionCall %6 %8
2584 OpReturn
2585 OpFunctionEnd
2586 %8 = OpFunction %6 None %7
2587 %9 = OpLabel
2588 %11 = OpVariable %10 Function
2589 OpStore %11 %12
2590 OpBranch %13
2591 %13 = OpLabel
2592 %37 = OpPhi %6 %12 %9 %32 %16
2593 OpLoopMerge %15 %16 None
2594 OpBranch %17
2595 %17 = OpLabel
2596 %21 = OpSLessThan %20 %37 %19
2597 OpBranchConditional %21 %14 %15
2598 %14 = OpLabel
2599 %24 = OpSGreaterThan %20 %37 %23
2600 OpSelectionMerge %26 None
2601 OpBranchConditional %24 %25 %26
2602 %25 = OpLabel
2603 %29 = OpIAdd %6 %37 %28
2604 OpStore %11 %29
2605 OpBranch %15
2606 %26 = OpLabel
2607 OpBranch %16
2608 %16 = OpLabel
2609 %32 = OpIAdd %6 %37 %28
2610 OpStore %11 %32
2611 OpBranch %13
2612 %15 = OpLabel
2613 %38 = OpPhi %6 %37 %17 %29 %25
2614 OpReturnValue %38
2615 OpFunctionEnd
2616 )";
2617
2618 const auto env = SPV_ENV_UNIVERSAL_1_4;
2619 const auto consumer = nullptr;
2620 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
2621 spvtools::ValidatorOptions validator_options;
2622 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
2623 kConsoleMessageConsumer));
2624 TransformationContext transformation_context(
2625 MakeUnique<FactManager>(context.get()), validator_options);
2626 // Make a sequence of instruction messages corresponding to function %8 in
2627 // |donor|.
2628 std::vector<protobufs::Instruction> instructions =
2629 GetInstructionsForFunction(env, consumer, donor, 8);
2630
2631 protobufs::LoopLimiterInfo loop_limiter_info;
2632 loop_limiter_info.set_loop_header_id(13);
2633 loop_limiter_info.set_load_id(102);
2634 loop_limiter_info.set_increment_id(103);
2635 loop_limiter_info.set_compare_id(104);
2636 loop_limiter_info.set_logical_op_id(105);
2637
2638 TransformationAddFunction no_op_phi_data(instructions, 100, 28,
2639 {loop_limiter_info}, 0, {});
2640 // The loop limiter info is not good enough; it does not include ids to patch
2641 // up the OpPhi at the loop merge.
2642 ASSERT_FALSE(
2643 no_op_phi_data.IsApplicable(context.get(), transformation_context));
2644
2645 // Add a phi id for the new edge from the loop back edge block to the loop
2646 // merge.
2647 loop_limiter_info.add_phi_id(28);
2648 TransformationAddFunction with_op_phi_data(instructions, 100, 28,
2649 {loop_limiter_info}, 0, {});
2650 ASSERT_TRUE(
2651 with_op_phi_data.IsApplicable(context.get(), transformation_context));
2652 ApplyAndCheckFreshIds(with_op_phi_data, context.get(),
2653 &transformation_context);
2654 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
2655 kConsoleMessageConsumer));
2656 std::string expected = R"(
2657 OpCapability Shader
2658 %1 = OpExtInstImport "GLSL.std.450"
2659 OpMemoryModel Logical GLSL450
2660 OpEntryPoint Fragment %4 "main"
2661 OpExecutionMode %4 OriginUpperLeft
2662 OpSource ESSL 310
2663 %2 = OpTypeVoid
2664 %3 = OpTypeFunction %2
2665 %6 = OpTypeInt 32 1
2666 %50 = OpTypeInt 32 0
2667 %51 = OpConstant %50 0
2668 %52 = OpConstant %50 1
2669 %53 = OpTypePointer Function %50
2670 %7 = OpTypeFunction %6
2671 %10 = OpTypePointer Function %6
2672 %12 = OpConstant %6 0
2673 %19 = OpConstant %6 100
2674 %20 = OpTypeBool
2675 %23 = OpConstant %6 20
2676 %28 = OpConstant %6 1
2677 %4 = OpFunction %2 None %3
2678 %5 = OpLabel
2679 OpReturn
2680 OpFunctionEnd
2681 %8 = OpFunction %6 None %7
2682 %9 = OpLabel
2683 %100 = OpVariable %53 Function %51
2684 %11 = OpVariable %10 Function
2685 OpStore %11 %12
2686 OpBranch %13
2687 %13 = OpLabel
2688 %37 = OpPhi %6 %12 %9 %32 %16
2689 OpLoopMerge %15 %16 None
2690 OpBranch %17
2691 %17 = OpLabel
2692 %21 = OpSLessThan %20 %37 %19
2693 OpBranchConditional %21 %14 %15
2694 %14 = OpLabel
2695 %24 = OpSGreaterThan %20 %37 %23
2696 OpSelectionMerge %26 None
2697 OpBranchConditional %24 %25 %26
2698 %25 = OpLabel
2699 %29 = OpIAdd %6 %37 %28
2700 OpStore %11 %29
2701 OpBranch %15
2702 %26 = OpLabel
2703 OpBranch %16
2704 %16 = OpLabel
2705 %32 = OpIAdd %6 %37 %28
2706 OpStore %11 %32
2707 %102 = OpLoad %50 %100
2708 %103 = OpIAdd %50 %102 %52
2709 OpStore %100 %103
2710 %104 = OpUGreaterThanEqual %20 %102 %28
2711 OpBranchConditional %104 %15 %13
2712 %15 = OpLabel
2713 %38 = OpPhi %6 %37 %17 %29 %25 %28 %16
2714 OpReturnValue %38
2715 OpFunctionEnd
2716 )";
2717 ASSERT_TRUE(IsEqual(env, expected, context.get()));
2718 }
2719
TEST(TransformationAddFunctionTest,LoopLimitersAndOpPhi2)2720 TEST(TransformationAddFunctionTest, LoopLimitersAndOpPhi2) {
2721 // This captures the scenario where the loop merge block already has an OpPhi
2722 // with the loop back edge block as a predecessor.
2723
2724 std::string shader = R"(
2725 OpCapability Shader
2726 %1 = OpExtInstImport "GLSL.std.450"
2727 OpMemoryModel Logical GLSL450
2728 OpEntryPoint Fragment %4 "main"
2729 OpExecutionMode %4 OriginUpperLeft
2730 OpSource ESSL 310
2731 %2 = OpTypeVoid
2732 %3 = OpTypeFunction %2
2733 %6 = OpTypeInt 32 1
2734 %50 = OpTypeInt 32 0
2735 %51 = OpConstant %50 0
2736 %52 = OpConstant %50 1
2737 %53 = OpTypePointer Function %50
2738 %7 = OpTypeFunction %6
2739 %10 = OpTypePointer Function %6
2740 %12 = OpConstant %6 0
2741 %19 = OpConstant %6 100
2742 %20 = OpTypeBool
2743 %60 = OpConstantTrue %20
2744 %23 = OpConstant %6 20
2745 %28 = OpConstant %6 1
2746 %4 = OpFunction %2 None %3
2747 %5 = OpLabel
2748 OpReturn
2749 OpFunctionEnd
2750 )";
2751
2752 std::string donor = R"(
2753 OpCapability Shader
2754 %1 = OpExtInstImport "GLSL.std.450"
2755 OpMemoryModel Logical GLSL450
2756 OpEntryPoint Fragment %4 "main"
2757 OpExecutionMode %4 OriginUpperLeft
2758 OpSource ESSL 310
2759 %2 = OpTypeVoid
2760 %3 = OpTypeFunction %2
2761 %6 = OpTypeInt 32 1
2762 %50 = OpTypeInt 32 0
2763 %51 = OpConstant %50 0
2764 %52 = OpConstant %50 1
2765 %53 = OpTypePointer Function %50
2766 %7 = OpTypeFunction %6
2767 %10 = OpTypePointer Function %6
2768 %12 = OpConstant %6 0
2769 %19 = OpConstant %6 100
2770 %20 = OpTypeBool
2771 %60 = OpConstantTrue %20
2772 %23 = OpConstant %6 20
2773 %28 = OpConstant %6 1
2774 %4 = OpFunction %2 None %3
2775 %5 = OpLabel
2776 OpReturn
2777 OpFunctionEnd
2778 %8 = OpFunction %6 None %7
2779 %9 = OpLabel
2780 %11 = OpVariable %10 Function
2781 OpStore %11 %12
2782 OpBranch %13
2783 %13 = OpLabel
2784 %37 = OpPhi %6 %12 %9 %32 %16
2785 OpLoopMerge %15 %16 None
2786 OpBranch %17
2787 %17 = OpLabel
2788 %21 = OpSLessThan %20 %37 %19
2789 OpBranchConditional %21 %14 %15
2790 %14 = OpLabel
2791 %24 = OpSGreaterThan %20 %37 %23
2792 OpSelectionMerge %26 None
2793 OpBranchConditional %24 %25 %26
2794 %25 = OpLabel
2795 %29 = OpIAdd %6 %37 %28
2796 OpStore %11 %29
2797 OpBranch %15
2798 %26 = OpLabel
2799 OpBranch %16
2800 %16 = OpLabel
2801 %32 = OpIAdd %6 %37 %28
2802 OpStore %11 %32
2803 OpBranchConditional %60 %15 %13
2804 %15 = OpLabel
2805 %38 = OpPhi %6 %37 %17 %29 %25 %23 %16
2806 OpReturnValue %38
2807 OpFunctionEnd
2808 )";
2809
2810 const auto env = SPV_ENV_UNIVERSAL_1_4;
2811 const auto consumer = nullptr;
2812 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
2813 spvtools::ValidatorOptions validator_options;
2814 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
2815 kConsoleMessageConsumer));
2816 TransformationContext transformation_context(
2817 MakeUnique<FactManager>(context.get()), validator_options);
2818 // Make a sequence of instruction messages corresponding to function %8 in
2819 // |donor|.
2820 std::vector<protobufs::Instruction> instructions =
2821 GetInstructionsForFunction(env, consumer, donor, 8);
2822
2823 protobufs::LoopLimiterInfo loop_limiter_info;
2824 loop_limiter_info.set_loop_header_id(13);
2825 loop_limiter_info.set_load_id(102);
2826 loop_limiter_info.set_increment_id(103);
2827 loop_limiter_info.set_compare_id(104);
2828 loop_limiter_info.set_logical_op_id(105);
2829
2830 TransformationAddFunction transformation(instructions, 100, 28,
2831 {loop_limiter_info}, 0, {});
2832 ASSERT_TRUE(
2833 transformation.IsApplicable(context.get(), transformation_context));
2834 ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
2835 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
2836 kConsoleMessageConsumer));
2837 std::string expected = R"(
2838 OpCapability Shader
2839 %1 = OpExtInstImport "GLSL.std.450"
2840 OpMemoryModel Logical GLSL450
2841 OpEntryPoint Fragment %4 "main"
2842 OpExecutionMode %4 OriginUpperLeft
2843 OpSource ESSL 310
2844 %2 = OpTypeVoid
2845 %3 = OpTypeFunction %2
2846 %6 = OpTypeInt 32 1
2847 %50 = OpTypeInt 32 0
2848 %51 = OpConstant %50 0
2849 %52 = OpConstant %50 1
2850 %53 = OpTypePointer Function %50
2851 %7 = OpTypeFunction %6
2852 %10 = OpTypePointer Function %6
2853 %12 = OpConstant %6 0
2854 %19 = OpConstant %6 100
2855 %20 = OpTypeBool
2856 %60 = OpConstantTrue %20
2857 %23 = OpConstant %6 20
2858 %28 = OpConstant %6 1
2859 %4 = OpFunction %2 None %3
2860 %5 = OpLabel
2861 OpReturn
2862 OpFunctionEnd
2863 %8 = OpFunction %6 None %7
2864 %9 = OpLabel
2865 %100 = OpVariable %53 Function %51
2866 %11 = OpVariable %10 Function
2867 OpStore %11 %12
2868 OpBranch %13
2869 %13 = OpLabel
2870 %37 = OpPhi %6 %12 %9 %32 %16
2871 OpLoopMerge %15 %16 None
2872 OpBranch %17
2873 %17 = OpLabel
2874 %21 = OpSLessThan %20 %37 %19
2875 OpBranchConditional %21 %14 %15
2876 %14 = OpLabel
2877 %24 = OpSGreaterThan %20 %37 %23
2878 OpSelectionMerge %26 None
2879 OpBranchConditional %24 %25 %26
2880 %25 = OpLabel
2881 %29 = OpIAdd %6 %37 %28
2882 OpStore %11 %29
2883 OpBranch %15
2884 %26 = OpLabel
2885 OpBranch %16
2886 %16 = OpLabel
2887 %32 = OpIAdd %6 %37 %28
2888 OpStore %11 %32
2889 %102 = OpLoad %50 %100
2890 %103 = OpIAdd %50 %102 %52
2891 OpStore %100 %103
2892 %104 = OpUGreaterThanEqual %20 %102 %28
2893 %105 = OpLogicalOr %20 %60 %104
2894 OpBranchConditional %105 %15 %13
2895 %15 = OpLabel
2896 %38 = OpPhi %6 %37 %17 %29 %25 %23 %16
2897 OpReturnValue %38
2898 OpFunctionEnd
2899 )";
2900 ASSERT_TRUE(IsEqual(env, expected, context.get()));
2901 }
2902
TEST(TransformationAddFunctionTest,StaticallyOutOfBoundsArrayAccess)2903 TEST(TransformationAddFunctionTest, StaticallyOutOfBoundsArrayAccess) {
2904 std::string shader = R"(
2905 OpCapability Shader
2906 %1 = OpExtInstImport "GLSL.std.450"
2907 OpMemoryModel Logical GLSL450
2908 OpEntryPoint Fragment %4 "main"
2909 OpExecutionMode %4 OriginUpperLeft
2910 OpSource ESSL 310
2911 %2 = OpTypeVoid
2912 %3 = OpTypeFunction %2
2913 %8 = OpTypeInt 32 1
2914 %9 = OpTypeInt 32 0
2915 %10 = OpConstant %9 3
2916 %11 = OpTypeArray %8 %10
2917 %12 = OpTypePointer Private %11
2918 %13 = OpVariable %12 Private
2919 %14 = OpConstant %8 3
2920 %20 = OpConstant %8 2
2921 %15 = OpConstant %8 1
2922 %21 = OpTypeBool
2923 %16 = OpTypePointer Private %8
2924 %4 = OpFunction %2 None %3
2925 %5 = OpLabel
2926 OpReturn
2927 OpFunctionEnd
2928 )";
2929
2930 std::string donor = R"(
2931 OpCapability Shader
2932 %1 = OpExtInstImport "GLSL.std.450"
2933 OpMemoryModel Logical GLSL450
2934 OpEntryPoint Fragment %4 "main"
2935 OpExecutionMode %4 OriginUpperLeft
2936 OpSource ESSL 310
2937 %2 = OpTypeVoid
2938 %3 = OpTypeFunction %2
2939 %8 = OpTypeInt 32 1
2940 %9 = OpTypeInt 32 0
2941 %10 = OpConstant %9 3
2942 %11 = OpTypeArray %8 %10
2943 %12 = OpTypePointer Private %11
2944 %13 = OpVariable %12 Private
2945 %14 = OpConstant %8 3
2946 %15 = OpConstant %8 1
2947 %16 = OpTypePointer Private %8
2948 %4 = OpFunction %2 None %3
2949 %5 = OpLabel
2950 OpReturn
2951 OpFunctionEnd
2952 %6 = OpFunction %2 None %3
2953 %7 = OpLabel
2954 %17 = OpAccessChain %16 %13 %14
2955 OpStore %17 %15
2956 OpReturn
2957 OpFunctionEnd
2958 )";
2959 const auto env = SPV_ENV_UNIVERSAL_1_4;
2960 const auto consumer = nullptr;
2961 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
2962 spvtools::ValidatorOptions validator_options;
2963 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
2964 kConsoleMessageConsumer));
2965 TransformationContext transformation_context(
2966 MakeUnique<FactManager>(context.get()), validator_options);
2967 // Make a sequence of instruction messages corresponding to function %6 in
2968 // |donor|.
2969 std::vector<protobufs::Instruction> instructions =
2970 GetInstructionsForFunction(env, consumer, donor, 6);
2971
2972 TransformationAddFunction add_livesafe_function(
2973 instructions, 0, 0, {}, 0, {MakeAccessClampingInfo(17, {{100, 101}})});
2974 ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(),
2975 transformation_context));
2976 ApplyAndCheckFreshIds(add_livesafe_function, context.get(),
2977 &transformation_context);
2978 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
2979 kConsoleMessageConsumer));
2980 std::string expected = R"(
2981 OpCapability Shader
2982 %1 = OpExtInstImport "GLSL.std.450"
2983 OpMemoryModel Logical GLSL450
2984 OpEntryPoint Fragment %4 "main"
2985 OpExecutionMode %4 OriginUpperLeft
2986 OpSource ESSL 310
2987 %2 = OpTypeVoid
2988 %3 = OpTypeFunction %2
2989 %8 = OpTypeInt 32 1
2990 %9 = OpTypeInt 32 0
2991 %10 = OpConstant %9 3
2992 %11 = OpTypeArray %8 %10
2993 %12 = OpTypePointer Private %11
2994 %13 = OpVariable %12 Private
2995 %14 = OpConstant %8 3
2996 %20 = OpConstant %8 2
2997 %15 = OpConstant %8 1
2998 %21 = OpTypeBool
2999 %16 = OpTypePointer Private %8
3000 %4 = OpFunction %2 None %3
3001 %5 = OpLabel
3002 OpReturn
3003 OpFunctionEnd
3004 %6 = OpFunction %2 None %3
3005 %7 = OpLabel
3006 %100 = OpULessThanEqual %21 %14 %20
3007 %101 = OpSelect %8 %100 %14 %20
3008 %17 = OpAccessChain %16 %13 %101
3009 OpStore %17 %15
3010 OpReturn
3011 OpFunctionEnd
3012 )";
3013 ASSERT_TRUE(IsEqual(env, expected, context.get()));
3014 }
3015
3016 } // namespace
3017 } // namespace fuzz
3018 } // namespace spvtools
3019