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