| // Copyright (c) 2019 Google LLC | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| #include "source/fuzz/transformation_add_function.h" | |
| #include "gtest/gtest.h" | |
| #include "source/fuzz/fuzzer_util.h" | |
| #include "source/fuzz/instruction_message.h" | |
| #include "test/fuzz/fuzz_test_util.h" | |
| namespace spvtools { | |
| namespace fuzz { | |
| namespace { | |
| protobufs::AccessChainClampingInfo MakeAccessClampingInfo( | |
| uint32_t access_chain_id, | |
| const std::vector<std::pair<uint32_t, uint32_t>>& compare_and_select_ids) { | |
| protobufs::AccessChainClampingInfo result; | |
| result.set_access_chain_id(access_chain_id); | |
| for (auto& compare_and_select_id : compare_and_select_ids) { | |
| auto pair = result.add_compare_and_select_ids(); | |
| pair->set_first(compare_and_select_id.first); | |
| pair->set_second(compare_and_select_id.second); | |
| } | |
| return result; | |
| } | |
| std::vector<protobufs::Instruction> GetInstructionsForFunction( | |
| spv_target_env env, const MessageConsumer& consumer, | |
| const std::string& donor, uint32_t function_id) { | |
| std::vector<protobufs::Instruction> result; | |
| const auto donor_context = | |
| BuildModule(env, consumer, donor, kFuzzAssembleOption); | |
| spvtools::ValidatorOptions validator_options; | |
| assert(fuzzerutil::IsValidAndWellFormed( | |
| donor_context.get(), validator_options, kConsoleMessageConsumer) && | |
| "The given donor must be valid."); | |
| for (auto& function : *donor_context->module()) { | |
| if (function.result_id() == function_id) { | |
| function.ForEachInst([&result](opt::Instruction* inst) { | |
| opt::Instruction::OperandList input_operands; | |
| for (uint32_t i = 0; i < inst->NumInOperands(); i++) { | |
| input_operands.push_back(inst->GetInOperand(i)); | |
| } | |
| result.push_back(MakeInstructionMessage(inst->opcode(), inst->type_id(), | |
| inst->result_id(), | |
| input_operands)); | |
| }); | |
| break; | |
| } | |
| } | |
| assert(!result.empty() && "The required function should have been found."); | |
| return result; | |
| } | |
| // Returns true if and only if every pointer parameter and variable associated | |
| // with |function_id| in |context| is known by |transformation_context| to be | |
| // irrelevant, with the exception of |loop_limiter_id|, which must not be | |
| // irrelevant. (It can be 0 if no loop limiter is expected, and 0 should not be | |
| // deemed irrelevant). | |
| bool AllVariablesAndParametersExceptLoopLimiterAreIrrelevant( | |
| opt::IRContext* context, | |
| const TransformationContext& transformation_context, uint32_t function_id, | |
| uint32_t loop_limiter_id) { | |
| // Look at all the functions until the function of interest is found. | |
| for (auto& function : *context->module()) { | |
| if (function.result_id() != function_id) { | |
| continue; | |
| } | |
| // Check that the parameters are all irrelevant. | |
| bool found_non_irrelevant_parameter = false; | |
| function.ForEachParam([context, &transformation_context, | |
| &found_non_irrelevant_parameter]( | |
| opt::Instruction* inst) { | |
| if (context->get_def_use_mgr()->GetDef(inst->type_id())->opcode() == | |
| spv::Op::OpTypePointer && | |
| !transformation_context.GetFactManager()->PointeeValueIsIrrelevant( | |
| inst->result_id())) { | |
| found_non_irrelevant_parameter = true; | |
| } | |
| }); | |
| if (found_non_irrelevant_parameter) { | |
| // A non-irrelevant parameter was found. | |
| return false; | |
| } | |
| // Look through the instructions in the function's first block. | |
| for (auto& inst : *function.begin()) { | |
| if (inst.opcode() != spv::Op::OpVariable) { | |
| // We have found a non-variable instruction; this means we have gotten | |
| // past all variables, so we are done. | |
| return true; | |
| } | |
| // The variable should be irrelevant if and only if it is not the loop | |
| // limiter. | |
| if ((inst.result_id() == loop_limiter_id) == | |
| transformation_context.GetFactManager()->PointeeValueIsIrrelevant( | |
| inst.result_id())) { | |
| return false; | |
| } | |
| } | |
| assert(false && | |
| "We should have processed all variables and returned by " | |
| "this point."); | |
| } | |
| assert(false && "We should have found the function of interest."); | |
| return true; | |
| } | |
| TEST(TransformationAddFunctionTest, BasicTest) { | |
| std::string shader = R"( | |
| OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %4 "main" | |
| OpExecutionMode %4 OriginUpperLeft | |
| OpSource ESSL 310 | |
| %2 = OpTypeVoid | |
| %3 = OpTypeFunction %2 | |
| %6 = OpTypeInt 32 1 | |
| %7 = OpTypePointer Function %6 | |
| %8 = OpTypeFloat 32 | |
| %9 = OpTypePointer Function %8 | |
| %10 = OpTypeFunction %8 %7 %9 | |
| %18 = OpConstant %8 0 | |
| %20 = OpConstant %6 0 | |
| %28 = OpTypeBool | |
| %37 = OpConstant %6 1 | |
| %42 = OpTypePointer Private %8 | |
| %43 = OpVariable %42 Private | |
| %47 = OpConstant %8 1 | |
| %4 = OpFunction %2 None %3 | |
| %5 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| const auto env = SPV_ENV_UNIVERSAL_1_4; | |
| const auto consumer = nullptr; | |
| const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); | |
| spvtools::ValidatorOptions validator_options; | |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, | |
| kConsoleMessageConsumer)); | |
| TransformationContext transformation_context( | |
| MakeUnique<FactManager>(context.get()), validator_options); | |
| TransformationAddFunction transformation1(std::vector<protobufs::Instruction>( | |
| {MakeInstructionMessage(spv::Op::OpFunction, 8, 13, | |
| {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, | |
| {uint32_t(spv::FunctionControlMask::MaskNone)}}, | |
| {SPV_OPERAND_TYPE_ID, {10}}}), | |
| MakeInstructionMessage(spv::Op::OpFunctionParameter, 7, 11, {}), | |
| MakeInstructionMessage(spv::Op::OpFunctionParameter, 9, 12, {}), | |
| MakeInstructionMessage(spv::Op::OpLabel, 0, 14, {}), | |
| MakeInstructionMessage(spv::Op::OpVariable, 9, 17, | |
| {{SPV_OPERAND_TYPE_STORAGE_CLASS, | |
| {uint32_t(spv::StorageClass::Function)}}}), | |
| MakeInstructionMessage(spv::Op::OpVariable, 7, 19, | |
| {{SPV_OPERAND_TYPE_STORAGE_CLASS, | |
| {uint32_t(spv::StorageClass::Function)}}}), | |
| MakeInstructionMessage( | |
| spv::Op::OpStore, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {17}}, {SPV_OPERAND_TYPE_ID, {18}}}), | |
| MakeInstructionMessage( | |
| spv::Op::OpStore, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {19}}, {SPV_OPERAND_TYPE_ID, {20}}}), | |
| MakeInstructionMessage(spv::Op::OpBranch, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {21}}}), | |
| MakeInstructionMessage(spv::Op::OpLabel, 0, 21, {}), | |
| MakeInstructionMessage(spv::Op::OpLoopMerge, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {23}}, | |
| {SPV_OPERAND_TYPE_ID, {24}}, | |
| {SPV_OPERAND_TYPE_LOOP_CONTROL, | |
| {uint32_t(spv::LoopControlMask::MaskNone)}}}), | |
| MakeInstructionMessage(spv::Op::OpBranch, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {25}}}), | |
| MakeInstructionMessage(spv::Op::OpLabel, 0, 25, {}), | |
| MakeInstructionMessage(spv::Op::OpLoad, 6, 26, | |
| {{SPV_OPERAND_TYPE_ID, {19}}}), | |
| MakeInstructionMessage(spv::Op::OpLoad, 6, 27, | |
| {{SPV_OPERAND_TYPE_ID, {11}}}), | |
| MakeInstructionMessage( | |
| spv::Op::OpSLessThan, 28, 29, | |
| {{SPV_OPERAND_TYPE_ID, {26}}, {SPV_OPERAND_TYPE_ID, {27}}}), | |
| MakeInstructionMessage(spv::Op::OpBranchConditional, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {29}}, | |
| {SPV_OPERAND_TYPE_ID, {22}}, | |
| {SPV_OPERAND_TYPE_ID, {23}}}), | |
| MakeInstructionMessage(spv::Op::OpLabel, 0, 22, {}), | |
| MakeInstructionMessage(spv::Op::OpLoad, 8, 30, | |
| {{SPV_OPERAND_TYPE_ID, {12}}}), | |
| MakeInstructionMessage(spv::Op::OpLoad, 6, 31, | |
| {{SPV_OPERAND_TYPE_ID, {19}}}), | |
| MakeInstructionMessage(spv::Op::OpConvertSToF, 8, 32, | |
| {{SPV_OPERAND_TYPE_ID, {31}}}), | |
| MakeInstructionMessage( | |
| spv::Op::OpFMul, 8, 33, | |
| {{SPV_OPERAND_TYPE_ID, {30}}, {SPV_OPERAND_TYPE_ID, {32}}}), | |
| MakeInstructionMessage(spv::Op::OpLoad, 8, 34, | |
| {{SPV_OPERAND_TYPE_ID, {17}}}), | |
| MakeInstructionMessage( | |
| spv::Op::OpFAdd, 8, 35, | |
| {{SPV_OPERAND_TYPE_ID, {34}}, {SPV_OPERAND_TYPE_ID, {33}}}), | |
| MakeInstructionMessage( | |
| spv::Op::OpStore, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {17}}, {SPV_OPERAND_TYPE_ID, {35}}}), | |
| MakeInstructionMessage(spv::Op::OpBranch, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {24}}}), | |
| MakeInstructionMessage(spv::Op::OpLabel, 0, 24, {}), | |
| MakeInstructionMessage(spv::Op::OpLoad, 6, 36, | |
| {{SPV_OPERAND_TYPE_ID, {19}}}), | |
| MakeInstructionMessage( | |
| spv::Op::OpIAdd, 6, 38, | |
| {{SPV_OPERAND_TYPE_ID, {36}}, {SPV_OPERAND_TYPE_ID, {37}}}), | |
| MakeInstructionMessage( | |
| spv::Op::OpStore, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {19}}, {SPV_OPERAND_TYPE_ID, {38}}}), | |
| MakeInstructionMessage(spv::Op::OpBranch, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {21}}}), | |
| MakeInstructionMessage(spv::Op::OpLabel, 0, 23, {}), | |
| MakeInstructionMessage(spv::Op::OpLoad, 8, 39, | |
| {{SPV_OPERAND_TYPE_ID, {17}}}), | |
| MakeInstructionMessage(spv::Op::OpReturnValue, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {39}}}), | |
| MakeInstructionMessage(spv::Op::OpFunctionEnd, 0, 0, {})})); | |
| ASSERT_TRUE( | |
| transformation1.IsApplicable(context.get(), transformation_context)); | |
| ApplyAndCheckFreshIds(transformation1, context.get(), | |
| &transformation_context); | |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, | |
| kConsoleMessageConsumer)); | |
| std::string after_transformation1 = R"( | |
| OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %4 "main" | |
| OpExecutionMode %4 OriginUpperLeft | |
| OpSource ESSL 310 | |
| %2 = OpTypeVoid | |
| %3 = OpTypeFunction %2 | |
| %6 = OpTypeInt 32 1 | |
| %7 = OpTypePointer Function %6 | |
| %8 = OpTypeFloat 32 | |
| %9 = OpTypePointer Function %8 | |
| %10 = OpTypeFunction %8 %7 %9 | |
| %18 = OpConstant %8 0 | |
| %20 = OpConstant %6 0 | |
| %28 = OpTypeBool | |
| %37 = OpConstant %6 1 | |
| %42 = OpTypePointer Private %8 | |
| %43 = OpVariable %42 Private | |
| %47 = OpConstant %8 1 | |
| %4 = OpFunction %2 None %3 | |
| %5 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| %13 = OpFunction %8 None %10 | |
| %11 = OpFunctionParameter %7 | |
| %12 = OpFunctionParameter %9 | |
| %14 = OpLabel | |
| %17 = OpVariable %9 Function | |
| %19 = OpVariable %7 Function | |
| OpStore %17 %18 | |
| OpStore %19 %20 | |
| OpBranch %21 | |
| %21 = OpLabel | |
| OpLoopMerge %23 %24 None | |
| OpBranch %25 | |
| %25 = OpLabel | |
| %26 = OpLoad %6 %19 | |
| %27 = OpLoad %6 %11 | |
| %29 = OpSLessThan %28 %26 %27 | |
| OpBranchConditional %29 %22 %23 | |
| %22 = OpLabel | |
| %30 = OpLoad %8 %12 | |
| %31 = OpLoad %6 %19 | |
| %32 = OpConvertSToF %8 %31 | |
| %33 = OpFMul %8 %30 %32 | |
| %34 = OpLoad %8 %17 | |
| %35 = OpFAdd %8 %34 %33 | |
| OpStore %17 %35 | |
| OpBranch %24 | |
| %24 = OpLabel | |
| %36 = OpLoad %6 %19 | |
| %38 = OpIAdd %6 %36 %37 | |
| OpStore %19 %38 | |
| OpBranch %21 | |
| %23 = OpLabel | |
| %39 = OpLoad %8 %17 | |
| OpReturnValue %39 | |
| OpFunctionEnd | |
| )"; | |
| ASSERT_TRUE(IsEqual(env, after_transformation1, context.get())); | |
| ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(14)); | |
| ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(21)); | |
| ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(22)); | |
| ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(23)); | |
| ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(24)); | |
| ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(25)); | |
| TransformationAddFunction transformation2(std::vector<protobufs::Instruction>( | |
| {MakeInstructionMessage(spv::Op::OpFunction, 2, 15, | |
| {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, | |
| {uint32_t(spv::FunctionControlMask::MaskNone)}}, | |
| {SPV_OPERAND_TYPE_ID, {3}}}), | |
| MakeInstructionMessage(spv::Op::OpLabel, 0, 16, {}), | |
| MakeInstructionMessage(spv::Op::OpVariable, 7, 44, | |
| {{SPV_OPERAND_TYPE_STORAGE_CLASS, | |
| {uint32_t(spv::StorageClass::Function)}}}), | |
| MakeInstructionMessage(spv::Op::OpVariable, 9, 45, | |
| {{SPV_OPERAND_TYPE_STORAGE_CLASS, | |
| {uint32_t(spv::StorageClass::Function)}}}), | |
| MakeInstructionMessage(spv::Op::OpVariable, 7, 48, | |
| {{SPV_OPERAND_TYPE_STORAGE_CLASS, | |
| {uint32_t(spv::StorageClass::Function)}}}), | |
| MakeInstructionMessage(spv::Op::OpVariable, 9, 49, | |
| {{SPV_OPERAND_TYPE_STORAGE_CLASS, | |
| {uint32_t(spv::StorageClass::Function)}}}), | |
| MakeInstructionMessage( | |
| spv::Op::OpStore, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {44}}, {SPV_OPERAND_TYPE_ID, {20}}}), | |
| MakeInstructionMessage( | |
| spv::Op::OpStore, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {45}}, {SPV_OPERAND_TYPE_ID, {18}}}), | |
| MakeInstructionMessage(spv::Op::OpFunctionCall, 8, 46, | |
| {{SPV_OPERAND_TYPE_ID, {13}}, | |
| {SPV_OPERAND_TYPE_ID, {44}}, | |
| {SPV_OPERAND_TYPE_ID, {45}}}), | |
| MakeInstructionMessage( | |
| spv::Op::OpStore, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {48}}, {SPV_OPERAND_TYPE_ID, {37}}}), | |
| MakeInstructionMessage( | |
| spv::Op::OpStore, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {49}}, {SPV_OPERAND_TYPE_ID, {47}}}), | |
| MakeInstructionMessage(spv::Op::OpFunctionCall, 8, 50, | |
| {{SPV_OPERAND_TYPE_ID, {13}}, | |
| {SPV_OPERAND_TYPE_ID, {48}}, | |
| {SPV_OPERAND_TYPE_ID, {49}}}), | |
| MakeInstructionMessage( | |
| spv::Op::OpFAdd, 8, 51, | |
| {{SPV_OPERAND_TYPE_ID, {46}}, {SPV_OPERAND_TYPE_ID, {50}}}), | |
| MakeInstructionMessage( | |
| spv::Op::OpStore, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {43}}, {SPV_OPERAND_TYPE_ID, {51}}}), | |
| MakeInstructionMessage(spv::Op::OpReturn, 0, 0, {}), | |
| MakeInstructionMessage(spv::Op::OpFunctionEnd, 0, 0, {})})); | |
| ASSERT_TRUE( | |
| transformation2.IsApplicable(context.get(), transformation_context)); | |
| ApplyAndCheckFreshIds(transformation2, context.get(), | |
| &transformation_context); | |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, | |
| kConsoleMessageConsumer)); | |
| std::string after_transformation2 = R"( | |
| OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %4 "main" | |
| OpExecutionMode %4 OriginUpperLeft | |
| OpSource ESSL 310 | |
| %2 = OpTypeVoid | |
| %3 = OpTypeFunction %2 | |
| %6 = OpTypeInt 32 1 | |
| %7 = OpTypePointer Function %6 | |
| %8 = OpTypeFloat 32 | |
| %9 = OpTypePointer Function %8 | |
| %10 = OpTypeFunction %8 %7 %9 | |
| %18 = OpConstant %8 0 | |
| %20 = OpConstant %6 0 | |
| %28 = OpTypeBool | |
| %37 = OpConstant %6 1 | |
| %42 = OpTypePointer Private %8 | |
| %43 = OpVariable %42 Private | |
| %47 = OpConstant %8 1 | |
| %4 = OpFunction %2 None %3 | |
| %5 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| %13 = OpFunction %8 None %10 | |
| %11 = OpFunctionParameter %7 | |
| %12 = OpFunctionParameter %9 | |
| %14 = OpLabel | |
| %17 = OpVariable %9 Function | |
| %19 = OpVariable %7 Function | |
| OpStore %17 %18 | |
| OpStore %19 %20 | |
| OpBranch %21 | |
| %21 = OpLabel | |
| OpLoopMerge %23 %24 None | |
| OpBranch %25 | |
| %25 = OpLabel | |
| %26 = OpLoad %6 %19 | |
| %27 = OpLoad %6 %11 | |
| %29 = OpSLessThan %28 %26 %27 | |
| OpBranchConditional %29 %22 %23 | |
| %22 = OpLabel | |
| %30 = OpLoad %8 %12 | |
| %31 = OpLoad %6 %19 | |
| %32 = OpConvertSToF %8 %31 | |
| %33 = OpFMul %8 %30 %32 | |
| %34 = OpLoad %8 %17 | |
| %35 = OpFAdd %8 %34 %33 | |
| OpStore %17 %35 | |
| OpBranch %24 | |
| %24 = OpLabel | |
| %36 = OpLoad %6 %19 | |
| %38 = OpIAdd %6 %36 %37 | |
| OpStore %19 %38 | |
| OpBranch %21 | |
| %23 = OpLabel | |
| %39 = OpLoad %8 %17 | |
| OpReturnValue %39 | |
| OpFunctionEnd | |
| %15 = OpFunction %2 None %3 | |
| %16 = OpLabel | |
| %44 = OpVariable %7 Function | |
| %45 = OpVariable %9 Function | |
| %48 = OpVariable %7 Function | |
| %49 = OpVariable %9 Function | |
| OpStore %44 %20 | |
| OpStore %45 %18 | |
| %46 = OpFunctionCall %8 %13 %44 %45 | |
| OpStore %48 %37 | |
| OpStore %49 %47 | |
| %50 = OpFunctionCall %8 %13 %48 %49 | |
| %51 = OpFAdd %8 %46 %50 | |
| OpStore %43 %51 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| ASSERT_TRUE(IsEqual(env, after_transformation2, context.get())); | |
| ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(16)); | |
| } | |
| TEST(TransformationAddFunctionTest, InapplicableTransformations) { | |
| std::string shader = R"( | |
| OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %4 "main" | |
| OpExecutionMode %4 OriginUpperLeft | |
| OpSource ESSL 310 | |
| %2 = OpTypeVoid | |
| %3 = OpTypeFunction %2 | |
| %6 = OpTypeInt 32 1 | |
| %7 = OpTypePointer Function %6 | |
| %8 = OpTypeFloat 32 | |
| %9 = OpTypePointer Function %8 | |
| %10 = OpTypeFunction %8 %7 %9 | |
| %18 = OpConstant %8 0 | |
| %20 = OpConstant %6 0 | |
| %28 = OpTypeBool | |
| %37 = OpConstant %6 1 | |
| %42 = OpTypePointer Private %8 | |
| %43 = OpVariable %42 Private | |
| %47 = OpConstant %8 1 | |
| %4 = OpFunction %2 None %3 | |
| %5 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| %13 = OpFunction %8 None %10 | |
| %11 = OpFunctionParameter %7 | |
| %12 = OpFunctionParameter %9 | |
| %14 = OpLabel | |
| %17 = OpVariable %9 Function | |
| %19 = OpVariable %7 Function | |
| OpStore %17 %18 | |
| OpStore %19 %20 | |
| OpBranch %21 | |
| %21 = OpLabel | |
| OpLoopMerge %23 %24 None | |
| OpBranch %25 | |
| %25 = OpLabel | |
| %26 = OpLoad %6 %19 | |
| %27 = OpLoad %6 %11 | |
| %29 = OpSLessThan %28 %26 %27 | |
| OpBranchConditional %29 %22 %23 | |
| %22 = OpLabel | |
| %30 = OpLoad %8 %12 | |
| %31 = OpLoad %6 %19 | |
| %32 = OpConvertSToF %8 %31 | |
| %33 = OpFMul %8 %30 %32 | |
| %34 = OpLoad %8 %17 | |
| %35 = OpFAdd %8 %34 %33 | |
| OpStore %17 %35 | |
| OpBranch %24 | |
| %24 = OpLabel | |
| %36 = OpLoad %6 %19 | |
| %38 = OpIAdd %6 %36 %37 | |
| OpStore %19 %38 | |
| OpBranch %21 | |
| %23 = OpLabel | |
| %39 = OpLoad %8 %17 | |
| OpReturnValue %39 | |
| OpFunctionEnd | |
| )"; | |
| const auto env = SPV_ENV_UNIVERSAL_1_4; | |
| const auto consumer = nullptr; | |
| const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); | |
| spvtools::ValidatorOptions validator_options; | |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, | |
| kConsoleMessageConsumer)); | |
| TransformationContext transformation_context( | |
| MakeUnique<FactManager>(context.get()), validator_options); | |
| // No instructions | |
| ASSERT_FALSE( | |
| TransformationAddFunction(std::vector<protobufs::Instruction>({})) | |
| .IsApplicable(context.get(), transformation_context)); | |
| // No function begin | |
| ASSERT_FALSE( | |
| TransformationAddFunction( | |
| std::vector<protobufs::Instruction>( | |
| {MakeInstructionMessage(spv::Op::OpFunctionParameter, 7, 11, {}), | |
| MakeInstructionMessage(spv::Op::OpFunctionParameter, 9, 12, {}), | |
| MakeInstructionMessage(spv::Op::OpLabel, 0, 14, {})})) | |
| .IsApplicable(context.get(), transformation_context)); | |
| // No OpLabel | |
| ASSERT_FALSE( | |
| TransformationAddFunction( | |
| std::vector<protobufs::Instruction>( | |
| {MakeInstructionMessage( | |
| spv::Op::OpFunction, 8, 13, | |
| {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, | |
| {uint32_t(spv::FunctionControlMask::MaskNone)}}, | |
| {SPV_OPERAND_TYPE_ID, {10}}}), | |
| MakeInstructionMessage(spv::Op::OpReturnValue, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {39}}}), | |
| MakeInstructionMessage(spv::Op::OpFunctionEnd, 0, 0, {})})) | |
| .IsApplicable(context.get(), transformation_context)); | |
| // Abrupt end of instructions | |
| ASSERT_FALSE(TransformationAddFunction( | |
| std::vector<protobufs::Instruction>({MakeInstructionMessage( | |
| spv::Op::OpFunction, 8, 13, | |
| {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, | |
| {uint32_t(spv::FunctionControlMask::MaskNone)}}, | |
| {SPV_OPERAND_TYPE_ID, {10}}})})) | |
| .IsApplicable(context.get(), transformation_context)); | |
| // No function end | |
| ASSERT_FALSE(TransformationAddFunction( | |
| std::vector<protobufs::Instruction>( | |
| {MakeInstructionMessage( | |
| spv::Op::OpFunction, 8, 13, | |
| {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, | |
| {uint32_t(spv::FunctionControlMask::MaskNone)}}, | |
| {SPV_OPERAND_TYPE_ID, {10}}}), | |
| MakeInstructionMessage(spv::Op::OpLabel, 0, 14, {}), | |
| MakeInstructionMessage(spv::Op::OpReturnValue, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {39}}})})) | |
| .IsApplicable(context.get(), transformation_context)); | |
| } | |
| TEST(TransformationAddFunctionTest, LoopLimiters) { | |
| std::string shader = R"( | |
| OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %4 "main" | |
| OpExecutionMode %4 OriginUpperLeft | |
| OpSource ESSL 310 | |
| %2 = OpTypeVoid | |
| %3 = OpTypeFunction %2 | |
| %6 = OpTypeInt 32 0 | |
| %7 = OpTypePointer Function %6 | |
| %8 = OpConstant %6 0 | |
| %9 = OpConstant %6 1 | |
| %10 = OpConstant %6 5 | |
| %11 = OpTypeBool | |
| %12 = OpConstantTrue %11 | |
| %4 = OpFunction %2 None %3 | |
| %5 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| const auto env = SPV_ENV_UNIVERSAL_1_4; | |
| const auto consumer = nullptr; | |
| std::vector<protobufs::Instruction> instructions; | |
| instructions.push_back( | |
| MakeInstructionMessage(spv::Op::OpFunction, 2, 30, | |
| {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, | |
| {uint32_t(spv::FunctionControlMask::MaskNone)}}, | |
| {SPV_OPERAND_TYPE_TYPE_ID, {3}}})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpLabel, 0, 31, {})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpBranch, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {20}}})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpLabel, 0, 20, {})); | |
| instructions.push_back( | |
| MakeInstructionMessage(spv::Op::OpLoopMerge, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {21}}, | |
| {SPV_OPERAND_TYPE_ID, {22}}, | |
| {SPV_OPERAND_TYPE_LOOP_CONTROL, | |
| {uint32_t(spv::LoopControlMask::MaskNone)}}})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpBranchConditional, 0, | |
| 0, | |
| {{SPV_OPERAND_TYPE_ID, {12}}, | |
| {SPV_OPERAND_TYPE_ID, {23}}, | |
| {SPV_OPERAND_TYPE_ID, {21}}})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpLabel, 0, 23, {})); | |
| instructions.push_back( | |
| MakeInstructionMessage(spv::Op::OpLoopMerge, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {25}}, | |
| {SPV_OPERAND_TYPE_ID, {26}}, | |
| {SPV_OPERAND_TYPE_LOOP_CONTROL, | |
| {uint32_t(spv::LoopControlMask::MaskNone)}}})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpBranch, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {28}}})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpLabel, 0, 28, {})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpBranchConditional, 0, | |
| 0, | |
| {{SPV_OPERAND_TYPE_ID, {12}}, | |
| {SPV_OPERAND_TYPE_ID, {26}}, | |
| {SPV_OPERAND_TYPE_ID, {25}}})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpLabel, 0, 26, {})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpBranch, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {23}}})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpLabel, 0, 25, {})); | |
| instructions.push_back( | |
| MakeInstructionMessage(spv::Op::OpLoopMerge, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {24}}, | |
| {SPV_OPERAND_TYPE_ID, {27}}, | |
| {SPV_OPERAND_TYPE_LOOP_CONTROL, | |
| {uint32_t(spv::LoopControlMask::MaskNone)}}})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpBranchConditional, 0, | |
| 0, | |
| {{SPV_OPERAND_TYPE_ID, {12}}, | |
| {SPV_OPERAND_TYPE_ID, {24}}, | |
| {SPV_OPERAND_TYPE_ID, {27}}})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpLabel, 0, 27, {})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpBranch, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {25}}})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpLabel, 0, 24, {})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpBranch, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {22}}})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpLabel, 0, 22, {})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpBranch, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {20}}})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpLabel, 0, 21, {})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpReturn, 0, 0, {})); | |
| instructions.push_back( | |
| MakeInstructionMessage(spv::Op::OpFunctionEnd, 0, 0, {})); | |
| spvtools::ValidatorOptions validator_options; | |
| const auto context1 = BuildModule(env, consumer, shader, kFuzzAssembleOption); | |
| const auto context2 = BuildModule(env, consumer, shader, kFuzzAssembleOption); | |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( | |
| context1.get(), validator_options, kConsoleMessageConsumer)); | |
| TransformationContext transformation_context1( | |
| MakeUnique<FactManager>(context1.get()), validator_options); | |
| TransformationContext transformation_context2( | |
| MakeUnique<FactManager>(context2.get()), validator_options); | |
| TransformationAddFunction add_dead_function(instructions); | |
| ASSERT_TRUE( | |
| add_dead_function.IsApplicable(context1.get(), transformation_context1)); | |
| ApplyAndCheckFreshIds(add_dead_function, context1.get(), | |
| &transformation_context1); | |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( | |
| context1.get(), validator_options, kConsoleMessageConsumer)); | |
| // The added function should not be deemed livesafe. | |
| ASSERT_FALSE( | |
| transformation_context1.GetFactManager()->FunctionIsLivesafe(30)); | |
| // All variables/parameters in the function should be deemed irrelevant. | |
| ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant( | |
| context1.get(), transformation_context1, 30, 0)); | |
| std::string added_as_dead_code = R"( | |
| OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %4 "main" | |
| OpExecutionMode %4 OriginUpperLeft | |
| OpSource ESSL 310 | |
| %2 = OpTypeVoid | |
| %3 = OpTypeFunction %2 | |
| %6 = OpTypeInt 32 0 | |
| %7 = OpTypePointer Function %6 | |
| %8 = OpConstant %6 0 | |
| %9 = OpConstant %6 1 | |
| %10 = OpConstant %6 5 | |
| %11 = OpTypeBool | |
| %12 = OpConstantTrue %11 | |
| %4 = OpFunction %2 None %3 | |
| %5 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| %30 = OpFunction %2 None %3 | |
| %31 = OpLabel | |
| OpBranch %20 | |
| %20 = OpLabel | |
| OpLoopMerge %21 %22 None | |
| OpBranchConditional %12 %23 %21 | |
| %23 = OpLabel | |
| OpLoopMerge %25 %26 None | |
| OpBranch %28 | |
| %28 = OpLabel | |
| OpBranchConditional %12 %26 %25 | |
| %26 = OpLabel | |
| OpBranch %23 | |
| %25 = OpLabel | |
| OpLoopMerge %24 %27 None | |
| OpBranchConditional %12 %24 %27 | |
| %27 = OpLabel | |
| OpBranch %25 | |
| %24 = OpLabel | |
| OpBranch %22 | |
| %22 = OpLabel | |
| OpBranch %20 | |
| %21 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| ASSERT_TRUE(IsEqual(env, added_as_dead_code, context1.get())); | |
| protobufs::LoopLimiterInfo loop_limiter1; | |
| loop_limiter1.set_loop_header_id(20); | |
| loop_limiter1.set_load_id(101); | |
| loop_limiter1.set_increment_id(102); | |
| loop_limiter1.set_compare_id(103); | |
| loop_limiter1.set_logical_op_id(104); | |
| protobufs::LoopLimiterInfo loop_limiter2; | |
| loop_limiter2.set_loop_header_id(23); | |
| loop_limiter2.set_load_id(105); | |
| loop_limiter2.set_increment_id(106); | |
| loop_limiter2.set_compare_id(107); | |
| loop_limiter2.set_logical_op_id(108); | |
| protobufs::LoopLimiterInfo loop_limiter3; | |
| loop_limiter3.set_loop_header_id(25); | |
| loop_limiter3.set_load_id(109); | |
| loop_limiter3.set_increment_id(110); | |
| loop_limiter3.set_compare_id(111); | |
| loop_limiter3.set_logical_op_id(112); | |
| std::vector<protobufs::LoopLimiterInfo> loop_limiters = { | |
| loop_limiter1, loop_limiter2, loop_limiter3}; | |
| TransformationAddFunction add_livesafe_function(instructions, 100, 10, | |
| loop_limiters, 0, {}); | |
| ASSERT_TRUE(add_livesafe_function.IsApplicable(context2.get(), | |
| transformation_context2)); | |
| ApplyAndCheckFreshIds(add_livesafe_function, context2.get(), | |
| &transformation_context2); | |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( | |
| context2.get(), validator_options, kConsoleMessageConsumer)); | |
| // The added function should indeed be deemed livesafe. | |
| ASSERT_TRUE(transformation_context2.GetFactManager()->FunctionIsLivesafe(30)); | |
| // All variables/parameters in the function should be deemed irrelevant, | |
| // except the loop limiter. | |
| ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant( | |
| context2.get(), transformation_context2, 30, 100)); | |
| std::string added_as_livesafe_code = R"( | |
| OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %4 "main" | |
| OpExecutionMode %4 OriginUpperLeft | |
| OpSource ESSL 310 | |
| %2 = OpTypeVoid | |
| %3 = OpTypeFunction %2 | |
| %6 = OpTypeInt 32 0 | |
| %7 = OpTypePointer Function %6 | |
| %8 = OpConstant %6 0 | |
| %9 = OpConstant %6 1 | |
| %10 = OpConstant %6 5 | |
| %11 = OpTypeBool | |
| %12 = OpConstantTrue %11 | |
| %4 = OpFunction %2 None %3 | |
| %5 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| %30 = OpFunction %2 None %3 | |
| %31 = OpLabel | |
| %100 = OpVariable %7 Function %8 | |
| OpBranch %20 | |
| %20 = OpLabel | |
| OpLoopMerge %21 %22 None | |
| OpBranchConditional %12 %23 %21 | |
| %23 = OpLabel | |
| OpLoopMerge %25 %26 None | |
| OpBranch %28 | |
| %28 = OpLabel | |
| OpBranchConditional %12 %26 %25 | |
| %26 = OpLabel | |
| %105 = OpLoad %6 %100 | |
| %106 = OpIAdd %6 %105 %9 | |
| OpStore %100 %106 | |
| %107 = OpUGreaterThanEqual %11 %105 %10 | |
| OpBranchConditional %107 %25 %23 | |
| %25 = OpLabel | |
| OpLoopMerge %24 %27 None | |
| OpBranchConditional %12 %24 %27 | |
| %27 = OpLabel | |
| %109 = OpLoad %6 %100 | |
| %110 = OpIAdd %6 %109 %9 | |
| OpStore %100 %110 | |
| %111 = OpUGreaterThanEqual %11 %109 %10 | |
| OpBranchConditional %111 %24 %25 | |
| %24 = OpLabel | |
| OpBranch %22 | |
| %22 = OpLabel | |
| %101 = OpLoad %6 %100 | |
| %102 = OpIAdd %6 %101 %9 | |
| OpStore %100 %102 | |
| %103 = OpUGreaterThanEqual %11 %101 %10 | |
| OpBranchConditional %103 %21 %20 | |
| %21 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| ASSERT_TRUE(IsEqual(env, added_as_livesafe_code, context2.get())); | |
| } | |
| TEST(TransformationAddFunctionTest, KillAndUnreachableInVoidFunction) { | |
| std::string shader = R"( | |
| OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %4 "main" | |
| OpExecutionMode %4 OriginUpperLeft | |
| OpSource ESSL 310 | |
| %2 = OpTypeVoid | |
| %3 = OpTypeFunction %2 | |
| %6 = OpTypeInt 32 1 | |
| %7 = OpTypePointer Function %6 | |
| %8 = OpTypeFunction %2 %7 | |
| %13 = OpConstant %6 2 | |
| %14 = OpTypeBool | |
| %4 = OpFunction %2 None %3 | |
| %5 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| const auto env = SPV_ENV_UNIVERSAL_1_4; | |
| const auto consumer = nullptr; | |
| std::vector<protobufs::Instruction> instructions; | |
| instructions.push_back( | |
| MakeInstructionMessage(spv::Op::OpFunction, 2, 10, | |
| {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, | |
| {uint32_t(spv::FunctionControlMask::MaskNone)}}, | |
| {SPV_OPERAND_TYPE_TYPE_ID, {8}}})); | |
| instructions.push_back( | |
| MakeInstructionMessage(spv::Op::OpFunctionParameter, 7, 9, {})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpLabel, 0, 11, {})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpLoad, 6, 12, | |
| {{SPV_OPERAND_TYPE_ID, {9}}})); | |
| instructions.push_back(MakeInstructionMessage( | |
| spv::Op::OpIEqual, 14, 15, | |
| {{SPV_OPERAND_TYPE_ID, {12}}, {SPV_OPERAND_TYPE_ID, {13}}})); | |
| instructions.push_back(MakeInstructionMessage( | |
| spv::Op::OpSelectionMerge, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {17}}, | |
| {SPV_OPERAND_TYPE_SELECTION_CONTROL, | |
| {uint32_t(spv::SelectionControlMask::MaskNone)}}})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpBranchConditional, 0, | |
| 0, | |
| {{SPV_OPERAND_TYPE_ID, {15}}, | |
| {SPV_OPERAND_TYPE_ID, {16}}, | |
| {SPV_OPERAND_TYPE_ID, {17}}})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpLabel, 0, 16, {})); | |
| instructions.push_back( | |
| MakeInstructionMessage(spv::Op::OpUnreachable, 0, 0, {})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpLabel, 0, 17, {})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpKill, 0, 0, {})); | |
| instructions.push_back( | |
| MakeInstructionMessage(spv::Op::OpFunctionEnd, 0, 0, {})); | |
| spvtools::ValidatorOptions validator_options; | |
| const auto context1 = BuildModule(env, consumer, shader, kFuzzAssembleOption); | |
| const auto context2 = BuildModule(env, consumer, shader, kFuzzAssembleOption); | |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( | |
| context1.get(), validator_options, kConsoleMessageConsumer)); | |
| TransformationContext transformation_context1( | |
| MakeUnique<FactManager>(context1.get()), validator_options); | |
| TransformationContext transformation_context2( | |
| MakeUnique<FactManager>(context2.get()), validator_options); | |
| TransformationAddFunction add_dead_function(instructions); | |
| ASSERT_TRUE( | |
| add_dead_function.IsApplicable(context1.get(), transformation_context1)); | |
| ApplyAndCheckFreshIds(add_dead_function, context1.get(), | |
| &transformation_context1); | |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( | |
| context1.get(), validator_options, kConsoleMessageConsumer)); | |
| // The added function should not be deemed livesafe. | |
| ASSERT_FALSE( | |
| transformation_context1.GetFactManager()->FunctionIsLivesafe(10)); | |
| // All variables/parameters in the function should be deemed irrelevant. | |
| ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant( | |
| context1.get(), transformation_context1, 10, 0)); | |
| std::string added_as_dead_code = R"( | |
| OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %4 "main" | |
| OpExecutionMode %4 OriginUpperLeft | |
| OpSource ESSL 310 | |
| %2 = OpTypeVoid | |
| %3 = OpTypeFunction %2 | |
| %6 = OpTypeInt 32 1 | |
| %7 = OpTypePointer Function %6 | |
| %8 = OpTypeFunction %2 %7 | |
| %13 = OpConstant %6 2 | |
| %14 = OpTypeBool | |
| %4 = OpFunction %2 None %3 | |
| %5 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| %10 = OpFunction %2 None %8 | |
| %9 = OpFunctionParameter %7 | |
| %11 = OpLabel | |
| %12 = OpLoad %6 %9 | |
| %15 = OpIEqual %14 %12 %13 | |
| OpSelectionMerge %17 None | |
| OpBranchConditional %15 %16 %17 | |
| %16 = OpLabel | |
| OpUnreachable | |
| %17 = OpLabel | |
| OpKill | |
| OpFunctionEnd | |
| )"; | |
| ASSERT_TRUE(IsEqual(env, added_as_dead_code, context1.get())); | |
| TransformationAddFunction add_livesafe_function(instructions, 0, 0, {}, 0, | |
| {}); | |
| ASSERT_TRUE(add_livesafe_function.IsApplicable(context2.get(), | |
| transformation_context2)); | |
| ApplyAndCheckFreshIds(add_livesafe_function, context2.get(), | |
| &transformation_context2); | |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( | |
| context2.get(), validator_options, kConsoleMessageConsumer)); | |
| // The added function should indeed be deemed livesafe. | |
| ASSERT_TRUE(transformation_context2.GetFactManager()->FunctionIsLivesafe(10)); | |
| // All variables/parameters in the function should be deemed irrelevant. | |
| ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant( | |
| context2.get(), transformation_context2, 10, 0)); | |
| std::string added_as_livesafe_code = R"( | |
| OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %4 "main" | |
| OpExecutionMode %4 OriginUpperLeft | |
| OpSource ESSL 310 | |
| %2 = OpTypeVoid | |
| %3 = OpTypeFunction %2 | |
| %6 = OpTypeInt 32 1 | |
| %7 = OpTypePointer Function %6 | |
| %8 = OpTypeFunction %2 %7 | |
| %13 = OpConstant %6 2 | |
| %14 = OpTypeBool | |
| %4 = OpFunction %2 None %3 | |
| %5 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| %10 = OpFunction %2 None %8 | |
| %9 = OpFunctionParameter %7 | |
| %11 = OpLabel | |
| %12 = OpLoad %6 %9 | |
| %15 = OpIEqual %14 %12 %13 | |
| OpSelectionMerge %17 None | |
| OpBranchConditional %15 %16 %17 | |
| %16 = OpLabel | |
| OpReturn | |
| %17 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| ASSERT_TRUE(IsEqual(env, added_as_livesafe_code, context2.get())); | |
| } | |
| TEST(TransformationAddFunctionTest, KillAndUnreachableInNonVoidFunction) { | |
| std::string shader = R"( | |
| OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %4 "main" | |
| OpExecutionMode %4 OriginUpperLeft | |
| OpSource ESSL 310 | |
| %2 = OpTypeVoid | |
| %3 = OpTypeFunction %2 | |
| %6 = OpTypeInt 32 1 | |
| %7 = OpTypePointer Function %6 | |
| %8 = OpTypeFunction %2 %7 | |
| %50 = OpTypeFunction %6 %7 | |
| %13 = OpConstant %6 2 | |
| %14 = OpTypeBool | |
| %4 = OpFunction %2 None %3 | |
| %5 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| const auto env = SPV_ENV_UNIVERSAL_1_4; | |
| const auto consumer = nullptr; | |
| std::vector<protobufs::Instruction> instructions; | |
| instructions.push_back( | |
| MakeInstructionMessage(spv::Op::OpFunction, 6, 10, | |
| {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, | |
| {uint32_t(spv::FunctionControlMask::MaskNone)}}, | |
| {SPV_OPERAND_TYPE_TYPE_ID, {50}}})); | |
| instructions.push_back( | |
| MakeInstructionMessage(spv::Op::OpFunctionParameter, 7, 9, {})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpLabel, 0, 11, {})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpLoad, 6, 12, | |
| {{SPV_OPERAND_TYPE_ID, {9}}})); | |
| instructions.push_back(MakeInstructionMessage( | |
| spv::Op::OpIEqual, 14, 15, | |
| {{SPV_OPERAND_TYPE_ID, {12}}, {SPV_OPERAND_TYPE_ID, {13}}})); | |
| instructions.push_back(MakeInstructionMessage( | |
| spv::Op::OpSelectionMerge, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {17}}, | |
| {SPV_OPERAND_TYPE_SELECTION_CONTROL, | |
| {uint32_t(spv::SelectionControlMask::MaskNone)}}})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpBranchConditional, 0, | |
| 0, | |
| {{SPV_OPERAND_TYPE_ID, {15}}, | |
| {SPV_OPERAND_TYPE_ID, {16}}, | |
| {SPV_OPERAND_TYPE_ID, {17}}})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpLabel, 0, 16, {})); | |
| instructions.push_back( | |
| MakeInstructionMessage(spv::Op::OpUnreachable, 0, 0, {})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpLabel, 0, 17, {})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpKill, 0, 0, {})); | |
| instructions.push_back( | |
| MakeInstructionMessage(spv::Op::OpFunctionEnd, 0, 0, {})); | |
| spvtools::ValidatorOptions validator_options; | |
| const auto context1 = BuildModule(env, consumer, shader, kFuzzAssembleOption); | |
| const auto context2 = BuildModule(env, consumer, shader, kFuzzAssembleOption); | |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( | |
| context1.get(), validator_options, kConsoleMessageConsumer)); | |
| TransformationContext transformation_context1( | |
| MakeUnique<FactManager>(context1.get()), validator_options); | |
| TransformationContext transformation_context2( | |
| MakeUnique<FactManager>(context2.get()), validator_options); | |
| TransformationAddFunction add_dead_function(instructions); | |
| ASSERT_TRUE( | |
| add_dead_function.IsApplicable(context1.get(), transformation_context1)); | |
| ApplyAndCheckFreshIds(add_dead_function, context1.get(), | |
| &transformation_context1); | |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( | |
| context1.get(), validator_options, kConsoleMessageConsumer)); | |
| // The added function should not be deemed livesafe. | |
| ASSERT_FALSE( | |
| transformation_context1.GetFactManager()->FunctionIsLivesafe(10)); | |
| // All variables/parameters in the function should be deemed irrelevant. | |
| ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant( | |
| context1.get(), transformation_context1, 10, 0)); | |
| std::string added_as_dead_code = R"( | |
| OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %4 "main" | |
| OpExecutionMode %4 OriginUpperLeft | |
| OpSource ESSL 310 | |
| %2 = OpTypeVoid | |
| %3 = OpTypeFunction %2 | |
| %6 = OpTypeInt 32 1 | |
| %7 = OpTypePointer Function %6 | |
| %8 = OpTypeFunction %2 %7 | |
| %50 = OpTypeFunction %6 %7 | |
| %13 = OpConstant %6 2 | |
| %14 = OpTypeBool | |
| %4 = OpFunction %2 None %3 | |
| %5 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| %10 = OpFunction %6 None %50 | |
| %9 = OpFunctionParameter %7 | |
| %11 = OpLabel | |
| %12 = OpLoad %6 %9 | |
| %15 = OpIEqual %14 %12 %13 | |
| OpSelectionMerge %17 None | |
| OpBranchConditional %15 %16 %17 | |
| %16 = OpLabel | |
| OpUnreachable | |
| %17 = OpLabel | |
| OpKill | |
| OpFunctionEnd | |
| )"; | |
| ASSERT_TRUE(IsEqual(env, added_as_dead_code, context1.get())); | |
| TransformationAddFunction add_livesafe_function(instructions, 0, 0, {}, 13, | |
| {}); | |
| ASSERT_TRUE(add_livesafe_function.IsApplicable(context2.get(), | |
| transformation_context2)); | |
| ApplyAndCheckFreshIds(add_livesafe_function, context2.get(), | |
| &transformation_context2); | |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( | |
| context2.get(), validator_options, kConsoleMessageConsumer)); | |
| // The added function should indeed be deemed livesafe. | |
| ASSERT_TRUE(transformation_context2.GetFactManager()->FunctionIsLivesafe(10)); | |
| // All variables/parameters in the function should be deemed irrelevant. | |
| ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant( | |
| context2.get(), transformation_context2, 10, 0)); | |
| std::string added_as_livesafe_code = R"( | |
| OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %4 "main" | |
| OpExecutionMode %4 OriginUpperLeft | |
| OpSource ESSL 310 | |
| %2 = OpTypeVoid | |
| %3 = OpTypeFunction %2 | |
| %6 = OpTypeInt 32 1 | |
| %7 = OpTypePointer Function %6 | |
| %8 = OpTypeFunction %2 %7 | |
| %50 = OpTypeFunction %6 %7 | |
| %13 = OpConstant %6 2 | |
| %14 = OpTypeBool | |
| %4 = OpFunction %2 None %3 | |
| %5 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| %10 = OpFunction %6 None %50 | |
| %9 = OpFunctionParameter %7 | |
| %11 = OpLabel | |
| %12 = OpLoad %6 %9 | |
| %15 = OpIEqual %14 %12 %13 | |
| OpSelectionMerge %17 None | |
| OpBranchConditional %15 %16 %17 | |
| %16 = OpLabel | |
| OpReturnValue %13 | |
| %17 = OpLabel | |
| OpReturnValue %13 | |
| OpFunctionEnd | |
| )"; | |
| ASSERT_TRUE(IsEqual(env, added_as_livesafe_code, context2.get())); | |
| } | |
| TEST(TransformationAddFunctionTest, ClampedAccessChains) { | |
| std::string shader = R"( | |
| OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %4 "main" | |
| OpExecutionMode %4 OriginUpperLeft | |
| OpSource ESSL 310 | |
| %2 = OpTypeVoid | |
| %100 = OpTypeBool | |
| %3 = OpTypeFunction %2 | |
| %6 = OpTypeInt 32 1 | |
| %7 = OpTypePointer Function %6 | |
| %15 = OpTypeInt 32 0 | |
| %102 = OpTypePointer Function %15 | |
| %8 = OpTypeFunction %2 %7 %102 %7 | |
| %16 = OpConstant %15 5 | |
| %17 = OpTypeArray %6 %16 | |
| %18 = OpTypeArray %17 %16 | |
| %19 = OpTypePointer Private %18 | |
| %20 = OpVariable %19 Private | |
| %21 = OpConstant %6 0 | |
| %23 = OpTypePointer Private %6 | |
| %26 = OpTypePointer Function %17 | |
| %29 = OpTypePointer Private %17 | |
| %33 = OpConstant %6 4 | |
| %200 = OpConstant %15 4 | |
| %35 = OpConstant %15 10 | |
| %36 = OpTypeArray %6 %35 | |
| %37 = OpTypePointer Private %36 | |
| %38 = OpVariable %37 Private | |
| %54 = OpTypeFloat 32 | |
| %55 = OpTypeVector %54 4 | |
| %56 = OpTypePointer Private %55 | |
| %57 = OpVariable %56 Private | |
| %59 = OpTypeVector %54 3 | |
| %60 = OpTypeMatrix %59 2 | |
| %61 = OpTypePointer Private %60 | |
| %62 = OpVariable %61 Private | |
| %64 = OpTypePointer Private %54 | |
| %69 = OpConstant %54 2 | |
| %71 = OpConstant %6 1 | |
| %72 = OpConstant %6 2 | |
| %201 = OpConstant %15 2 | |
| %73 = OpConstant %6 3 | |
| %202 = OpConstant %15 3 | |
| %203 = OpConstant %6 1 | |
| %204 = OpConstant %6 9 | |
| %4 = OpFunction %2 None %3 | |
| %5 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| const auto env = SPV_ENV_UNIVERSAL_1_4; | |
| const auto consumer = nullptr; | |
| std::vector<protobufs::Instruction> instructions; | |
| instructions.push_back( | |
| MakeInstructionMessage(spv::Op::OpFunction, 2, 12, | |
| {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, | |
| {uint32_t(spv::FunctionControlMask::MaskNone)}}, | |
| {SPV_OPERAND_TYPE_TYPE_ID, {8}}})); | |
| instructions.push_back( | |
| MakeInstructionMessage(spv::Op::OpFunctionParameter, 7, 9, {})); | |
| instructions.push_back( | |
| MakeInstructionMessage(spv::Op::OpFunctionParameter, 102, 10, {})); | |
| instructions.push_back( | |
| MakeInstructionMessage(spv::Op::OpFunctionParameter, 7, 11, {})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpLabel, 0, 13, {})); | |
| instructions.push_back( | |
| MakeInstructionMessage(spv::Op::OpVariable, 7, 14, | |
| {{SPV_OPERAND_TYPE_STORAGE_CLASS, | |
| {uint32_t(spv::StorageClass::Function)}}})); | |
| instructions.push_back( | |
| MakeInstructionMessage(spv::Op::OpVariable, 26, 27, | |
| {{SPV_OPERAND_TYPE_STORAGE_CLASS, | |
| {uint32_t(spv::StorageClass::Function)}}})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpLoad, 6, 22, | |
| {{SPV_OPERAND_TYPE_ID, {11}}})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpAccessChain, 23, 24, | |
| {{SPV_OPERAND_TYPE_ID, {20}}, | |
| {SPV_OPERAND_TYPE_ID, {21}}, | |
| {SPV_OPERAND_TYPE_ID, {22}}})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpLoad, 6, 25, | |
| {{SPV_OPERAND_TYPE_ID, {24}}})); | |
| instructions.push_back(MakeInstructionMessage( | |
| spv::Op::OpStore, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {14}}, {SPV_OPERAND_TYPE_ID, {25}}})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpLoad, 15, 28, | |
| {{SPV_OPERAND_TYPE_ID, {10}}})); | |
| instructions.push_back(MakeInstructionMessage( | |
| spv::Op::OpAccessChain, 29, 30, | |
| {{SPV_OPERAND_TYPE_ID, {20}}, {SPV_OPERAND_TYPE_ID, {28}}})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpLoad, 17, 31, | |
| {{SPV_OPERAND_TYPE_ID, {30}}})); | |
| instructions.push_back(MakeInstructionMessage( | |
| spv::Op::OpStore, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {27}}, {SPV_OPERAND_TYPE_ID, {31}}})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpLoad, 6, 32, | |
| {{SPV_OPERAND_TYPE_ID, {9}}})); | |
| instructions.push_back(MakeInstructionMessage( | |
| spv::Op::OpInBoundsAccessChain, 7, 34, | |
| {{SPV_OPERAND_TYPE_ID, {27}}, {SPV_OPERAND_TYPE_ID, {32}}})); | |
| instructions.push_back(MakeInstructionMessage( | |
| spv::Op::OpStore, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {34}}, {SPV_OPERAND_TYPE_ID, {33}}})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpLoad, 6, 39, | |
| {{SPV_OPERAND_TYPE_ID, {9}}})); | |
| instructions.push_back(MakeInstructionMessage( | |
| spv::Op::OpAccessChain, 23, 40, | |
| {{SPV_OPERAND_TYPE_ID, {38}}, {SPV_OPERAND_TYPE_ID, {33}}})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpLoad, 6, 41, | |
| {{SPV_OPERAND_TYPE_ID, {40}}})); | |
| instructions.push_back(MakeInstructionMessage( | |
| spv::Op::OpInBoundsAccessChain, 23, 42, | |
| {{SPV_OPERAND_TYPE_ID, {38}}, {SPV_OPERAND_TYPE_ID, {39}}})); | |
| instructions.push_back(MakeInstructionMessage( | |
| spv::Op::OpStore, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {42}}, {SPV_OPERAND_TYPE_ID, {41}}})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpLoad, 15, 43, | |
| {{SPV_OPERAND_TYPE_ID, {10}}})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpLoad, 6, 44, | |
| {{SPV_OPERAND_TYPE_ID, {11}}})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpLoad, 6, 45, | |
| {{SPV_OPERAND_TYPE_ID, {9}}})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpLoad, 15, 46, | |
| {{SPV_OPERAND_TYPE_ID, {10}}})); | |
| instructions.push_back(MakeInstructionMessage( | |
| spv::Op::OpIAdd, 6, 47, | |
| {{SPV_OPERAND_TYPE_ID, {45}}, {SPV_OPERAND_TYPE_ID, {46}}})); | |
| instructions.push_back(MakeInstructionMessage( | |
| spv::Op::OpAccessChain, 23, 48, | |
| {{SPV_OPERAND_TYPE_ID, {38}}, {SPV_OPERAND_TYPE_ID, {47}}})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpLoad, 6, 49, | |
| {{SPV_OPERAND_TYPE_ID, {48}}})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpInBoundsAccessChain, | |
| 23, 50, | |
| {{SPV_OPERAND_TYPE_ID, {20}}, | |
| {SPV_OPERAND_TYPE_ID, {43}}, | |
| {SPV_OPERAND_TYPE_ID, {44}}})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpLoad, 6, 51, | |
| {{SPV_OPERAND_TYPE_ID, {50}}})); | |
| instructions.push_back(MakeInstructionMessage( | |
| spv::Op::OpIAdd, 6, 52, | |
| {{SPV_OPERAND_TYPE_ID, {51}}, {SPV_OPERAND_TYPE_ID, {49}}})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpAccessChain, 23, 53, | |
| {{SPV_OPERAND_TYPE_ID, {20}}, | |
| {SPV_OPERAND_TYPE_ID, {43}}, | |
| {SPV_OPERAND_TYPE_ID, {44}}})); | |
| instructions.push_back(MakeInstructionMessage( | |
| spv::Op::OpStore, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {53}}, {SPV_OPERAND_TYPE_ID, {52}}})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpLoad, 15, 58, | |
| {{SPV_OPERAND_TYPE_ID, {10}}})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpLoad, 6, 63, | |
| {{SPV_OPERAND_TYPE_ID, {11}}})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpAccessChain, 64, 65, | |
| {{SPV_OPERAND_TYPE_ID, {62}}, | |
| {SPV_OPERAND_TYPE_ID, {21}}, | |
| {SPV_OPERAND_TYPE_ID, {63}}})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpAccessChain, 64, 101, | |
| {{SPV_OPERAND_TYPE_ID, {62}}, | |
| {SPV_OPERAND_TYPE_ID, {45}}, | |
| {SPV_OPERAND_TYPE_ID, {46}}})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpLoad, 54, 66, | |
| {{SPV_OPERAND_TYPE_ID, {65}}})); | |
| instructions.push_back(MakeInstructionMessage( | |
| spv::Op::OpAccessChain, 64, 67, | |
| {{SPV_OPERAND_TYPE_ID, {57}}, {SPV_OPERAND_TYPE_ID, {58}}})); | |
| instructions.push_back(MakeInstructionMessage( | |
| spv::Op::OpStore, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {67}}, {SPV_OPERAND_TYPE_ID, {66}}})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpLoad, 6, 68, | |
| {{SPV_OPERAND_TYPE_ID, {9}}})); | |
| instructions.push_back(MakeInstructionMessage( | |
| spv::Op::OpInBoundsAccessChain, 64, 70, | |
| {{SPV_OPERAND_TYPE_ID, {57}}, {SPV_OPERAND_TYPE_ID, {68}}})); | |
| instructions.push_back(MakeInstructionMessage( | |
| spv::Op::OpStore, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {70}}, {SPV_OPERAND_TYPE_ID, {69}}})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpReturn, 0, 0, {})); | |
| instructions.push_back( | |
| MakeInstructionMessage(spv::Op::OpFunctionEnd, 0, 0, {})); | |
| spvtools::ValidatorOptions validator_options; | |
| const auto context1 = BuildModule(env, consumer, shader, kFuzzAssembleOption); | |
| const auto context2 = BuildModule(env, consumer, shader, kFuzzAssembleOption); | |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( | |
| context1.get(), validator_options, kConsoleMessageConsumer)); | |
| TransformationContext transformation_context1( | |
| MakeUnique<FactManager>(context1.get()), validator_options); | |
| TransformationContext transformation_context2( | |
| MakeUnique<FactManager>(context2.get()), validator_options); | |
| TransformationAddFunction add_dead_function(instructions); | |
| ASSERT_TRUE( | |
| add_dead_function.IsApplicable(context1.get(), transformation_context1)); | |
| ApplyAndCheckFreshIds(add_dead_function, context1.get(), | |
| &transformation_context1); | |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( | |
| context1.get(), validator_options, kConsoleMessageConsumer)); | |
| // The function should not be deemed livesafe | |
| ASSERT_FALSE( | |
| transformation_context1.GetFactManager()->FunctionIsLivesafe(12)); | |
| // All variables/parameters in the function should be deemed irrelevant. | |
| ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant( | |
| context1.get(), transformation_context1, 12, 0)); | |
| std::string added_as_dead_code = R"( | |
| OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %4 "main" | |
| OpExecutionMode %4 OriginUpperLeft | |
| OpSource ESSL 310 | |
| %2 = OpTypeVoid | |
| %100 = OpTypeBool | |
| %3 = OpTypeFunction %2 | |
| %6 = OpTypeInt 32 1 | |
| %7 = OpTypePointer Function %6 | |
| %15 = OpTypeInt 32 0 | |
| %102 = OpTypePointer Function %15 | |
| %8 = OpTypeFunction %2 %7 %102 %7 | |
| %16 = OpConstant %15 5 | |
| %17 = OpTypeArray %6 %16 | |
| %18 = OpTypeArray %17 %16 | |
| %19 = OpTypePointer Private %18 | |
| %20 = OpVariable %19 Private | |
| %21 = OpConstant %6 0 | |
| %23 = OpTypePointer Private %6 | |
| %26 = OpTypePointer Function %17 | |
| %29 = OpTypePointer Private %17 | |
| %33 = OpConstant %6 4 | |
| %200 = OpConstant %15 4 | |
| %35 = OpConstant %15 10 | |
| %36 = OpTypeArray %6 %35 | |
| %37 = OpTypePointer Private %36 | |
| %38 = OpVariable %37 Private | |
| %54 = OpTypeFloat 32 | |
| %55 = OpTypeVector %54 4 | |
| %56 = OpTypePointer Private %55 | |
| %57 = OpVariable %56 Private | |
| %59 = OpTypeVector %54 3 | |
| %60 = OpTypeMatrix %59 2 | |
| %61 = OpTypePointer Private %60 | |
| %62 = OpVariable %61 Private | |
| %64 = OpTypePointer Private %54 | |
| %69 = OpConstant %54 2 | |
| %71 = OpConstant %6 1 | |
| %72 = OpConstant %6 2 | |
| %201 = OpConstant %15 2 | |
| %73 = OpConstant %6 3 | |
| %202 = OpConstant %15 3 | |
| %203 = OpConstant %6 1 | |
| %204 = OpConstant %6 9 | |
| %4 = OpFunction %2 None %3 | |
| %5 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| %12 = OpFunction %2 None %8 | |
| %9 = OpFunctionParameter %7 | |
| %10 = OpFunctionParameter %102 | |
| %11 = OpFunctionParameter %7 | |
| %13 = OpLabel | |
| %14 = OpVariable %7 Function | |
| %27 = OpVariable %26 Function | |
| %22 = OpLoad %6 %11 | |
| %24 = OpAccessChain %23 %20 %21 %22 | |
| %25 = OpLoad %6 %24 | |
| OpStore %14 %25 | |
| %28 = OpLoad %15 %10 | |
| %30 = OpAccessChain %29 %20 %28 | |
| %31 = OpLoad %17 %30 | |
| OpStore %27 %31 | |
| %32 = OpLoad %6 %9 | |
| %34 = OpInBoundsAccessChain %7 %27 %32 | |
| OpStore %34 %33 | |
| %39 = OpLoad %6 %9 | |
| %40 = OpAccessChain %23 %38 %33 | |
| %41 = OpLoad %6 %40 | |
| %42 = OpInBoundsAccessChain %23 %38 %39 | |
| OpStore %42 %41 | |
| %43 = OpLoad %15 %10 | |
| %44 = OpLoad %6 %11 | |
| %45 = OpLoad %6 %9 | |
| %46 = OpLoad %15 %10 | |
| %47 = OpIAdd %6 %45 %46 | |
| %48 = OpAccessChain %23 %38 %47 | |
| %49 = OpLoad %6 %48 | |
| %50 = OpInBoundsAccessChain %23 %20 %43 %44 | |
| %51 = OpLoad %6 %50 | |
| %52 = OpIAdd %6 %51 %49 | |
| %53 = OpAccessChain %23 %20 %43 %44 | |
| OpStore %53 %52 | |
| %58 = OpLoad %15 %10 | |
| %63 = OpLoad %6 %11 | |
| %65 = OpAccessChain %64 %62 %21 %63 | |
| %101 = OpAccessChain %64 %62 %45 %46 | |
| %66 = OpLoad %54 %65 | |
| %67 = OpAccessChain %64 %57 %58 | |
| OpStore %67 %66 | |
| %68 = OpLoad %6 %9 | |
| %70 = OpInBoundsAccessChain %64 %57 %68 | |
| OpStore %70 %69 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| ASSERT_TRUE(IsEqual(env, added_as_dead_code, context1.get())); | |
| std::vector<protobufs::AccessChainClampingInfo> access_chain_clamping_info; | |
| access_chain_clamping_info.push_back( | |
| MakeAccessClampingInfo(24, {{1001, 2001}, {1002, 2002}})); | |
| access_chain_clamping_info.push_back( | |
| MakeAccessClampingInfo(30, {{1003, 2003}})); | |
| access_chain_clamping_info.push_back( | |
| MakeAccessClampingInfo(34, {{1004, 2004}})); | |
| access_chain_clamping_info.push_back( | |
| MakeAccessClampingInfo(40, {{1005, 2005}})); | |
| access_chain_clamping_info.push_back( | |
| MakeAccessClampingInfo(42, {{1006, 2006}})); | |
| access_chain_clamping_info.push_back( | |
| MakeAccessClampingInfo(48, {{1007, 2007}})); | |
| access_chain_clamping_info.push_back( | |
| MakeAccessClampingInfo(50, {{1008, 2008}, {1009, 2009}})); | |
| access_chain_clamping_info.push_back( | |
| MakeAccessClampingInfo(53, {{1010, 2010}, {1011, 2011}})); | |
| access_chain_clamping_info.push_back( | |
| MakeAccessClampingInfo(65, {{1012, 2012}, {1013, 2013}})); | |
| access_chain_clamping_info.push_back( | |
| MakeAccessClampingInfo(101, {{1014, 2014}, {1015, 2015}})); | |
| access_chain_clamping_info.push_back( | |
| MakeAccessClampingInfo(67, {{1016, 2016}})); | |
| access_chain_clamping_info.push_back( | |
| MakeAccessClampingInfo(70, {{1017, 2017}})); | |
| TransformationAddFunction add_livesafe_function(instructions, 0, 0, {}, 13, | |
| access_chain_clamping_info); | |
| ASSERT_TRUE(add_livesafe_function.IsApplicable(context2.get(), | |
| transformation_context2)); | |
| ApplyAndCheckFreshIds(add_livesafe_function, context2.get(), | |
| &transformation_context2); | |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( | |
| context2.get(), validator_options, kConsoleMessageConsumer)); | |
| // The function should be deemed livesafe | |
| ASSERT_TRUE(transformation_context2.GetFactManager()->FunctionIsLivesafe(12)); | |
| // All variables/parameters in the function should be deemed irrelevant. | |
| ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant( | |
| context2.get(), transformation_context2, 12, 0)); | |
| std::string added_as_livesafe_code = R"( | |
| OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %4 "main" | |
| OpExecutionMode %4 OriginUpperLeft | |
| OpSource ESSL 310 | |
| %2 = OpTypeVoid | |
| %100 = OpTypeBool | |
| %3 = OpTypeFunction %2 | |
| %6 = OpTypeInt 32 1 | |
| %7 = OpTypePointer Function %6 | |
| %15 = OpTypeInt 32 0 | |
| %102 = OpTypePointer Function %15 | |
| %8 = OpTypeFunction %2 %7 %102 %7 | |
| %16 = OpConstant %15 5 | |
| %17 = OpTypeArray %6 %16 | |
| %18 = OpTypeArray %17 %16 | |
| %19 = OpTypePointer Private %18 | |
| %20 = OpVariable %19 Private | |
| %21 = OpConstant %6 0 | |
| %23 = OpTypePointer Private %6 | |
| %26 = OpTypePointer Function %17 | |
| %29 = OpTypePointer Private %17 | |
| %33 = OpConstant %6 4 | |
| %200 = OpConstant %15 4 | |
| %35 = OpConstant %15 10 | |
| %36 = OpTypeArray %6 %35 | |
| %37 = OpTypePointer Private %36 | |
| %38 = OpVariable %37 Private | |
| %54 = OpTypeFloat 32 | |
| %55 = OpTypeVector %54 4 | |
| %56 = OpTypePointer Private %55 | |
| %57 = OpVariable %56 Private | |
| %59 = OpTypeVector %54 3 | |
| %60 = OpTypeMatrix %59 2 | |
| %61 = OpTypePointer Private %60 | |
| %62 = OpVariable %61 Private | |
| %64 = OpTypePointer Private %54 | |
| %69 = OpConstant %54 2 | |
| %71 = OpConstant %6 1 | |
| %72 = OpConstant %6 2 | |
| %201 = OpConstant %15 2 | |
| %73 = OpConstant %6 3 | |
| %202 = OpConstant %15 3 | |
| %203 = OpConstant %6 1 | |
| %204 = OpConstant %6 9 | |
| %4 = OpFunction %2 None %3 | |
| %5 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| %12 = OpFunction %2 None %8 | |
| %9 = OpFunctionParameter %7 | |
| %10 = OpFunctionParameter %102 | |
| %11 = OpFunctionParameter %7 | |
| %13 = OpLabel | |
| %14 = OpVariable %7 Function | |
| %27 = OpVariable %26 Function | |
| %22 = OpLoad %6 %11 | |
| %1002 = OpULessThanEqual %100 %22 %33 | |
| %2002 = OpSelect %6 %1002 %22 %33 | |
| %24 = OpAccessChain %23 %20 %21 %2002 | |
| %25 = OpLoad %6 %24 | |
| OpStore %14 %25 | |
| %28 = OpLoad %15 %10 | |
| %1003 = OpULessThanEqual %100 %28 %200 | |
| %2003 = OpSelect %15 %1003 %28 %200 | |
| %30 = OpAccessChain %29 %20 %2003 | |
| %31 = OpLoad %17 %30 | |
| OpStore %27 %31 | |
| %32 = OpLoad %6 %9 | |
| %1004 = OpULessThanEqual %100 %32 %33 | |
| %2004 = OpSelect %6 %1004 %32 %33 | |
| %34 = OpInBoundsAccessChain %7 %27 %2004 | |
| OpStore %34 %33 | |
| %39 = OpLoad %6 %9 | |
| %40 = OpAccessChain %23 %38 %33 | |
| %41 = OpLoad %6 %40 | |
| %1006 = OpULessThanEqual %100 %39 %204 | |
| %2006 = OpSelect %6 %1006 %39 %204 | |
| %42 = OpInBoundsAccessChain %23 %38 %2006 | |
| OpStore %42 %41 | |
| %43 = OpLoad %15 %10 | |
| %44 = OpLoad %6 %11 | |
| %45 = OpLoad %6 %9 | |
| %46 = OpLoad %15 %10 | |
| %47 = OpIAdd %6 %45 %46 | |
| %1007 = OpULessThanEqual %100 %47 %204 | |
| %2007 = OpSelect %6 %1007 %47 %204 | |
| %48 = OpAccessChain %23 %38 %2007 | |
| %49 = OpLoad %6 %48 | |
| %1008 = OpULessThanEqual %100 %43 %200 | |
| %2008 = OpSelect %15 %1008 %43 %200 | |
| %1009 = OpULessThanEqual %100 %44 %33 | |
| %2009 = OpSelect %6 %1009 %44 %33 | |
| %50 = OpInBoundsAccessChain %23 %20 %2008 %2009 | |
| %51 = OpLoad %6 %50 | |
| %52 = OpIAdd %6 %51 %49 | |
| %1010 = OpULessThanEqual %100 %43 %200 | |
| %2010 = OpSelect %15 %1010 %43 %200 | |
| %1011 = OpULessThanEqual %100 %44 %33 | |
| %2011 = OpSelect %6 %1011 %44 %33 | |
| %53 = OpAccessChain %23 %20 %2010 %2011 | |
| OpStore %53 %52 | |
| %58 = OpLoad %15 %10 | |
| %63 = OpLoad %6 %11 | |
| %1013 = OpULessThanEqual %100 %63 %72 | |
| %2013 = OpSelect %6 %1013 %63 %72 | |
| %65 = OpAccessChain %64 %62 %21 %2013 | |
| %1014 = OpULessThanEqual %100 %45 %71 | |
| %2014 = OpSelect %6 %1014 %45 %71 | |
| %1015 = OpULessThanEqual %100 %46 %201 | |
| %2015 = OpSelect %15 %1015 %46 %201 | |
| %101 = OpAccessChain %64 %62 %2014 %2015 | |
| %66 = OpLoad %54 %65 | |
| %1016 = OpULessThanEqual %100 %58 %202 | |
| %2016 = OpSelect %15 %1016 %58 %202 | |
| %67 = OpAccessChain %64 %57 %2016 | |
| OpStore %67 %66 | |
| %68 = OpLoad %6 %9 | |
| %1017 = OpULessThanEqual %100 %68 %73 | |
| %2017 = OpSelect %6 %1017 %68 %73 | |
| %70 = OpInBoundsAccessChain %64 %57 %2017 | |
| OpStore %70 %69 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| ASSERT_TRUE(IsEqual(env, added_as_livesafe_code, context2.get())); | |
| } | |
| TEST(TransformationAddFunctionTest, LivesafeCanCallLivesafe) { | |
| std::string shader = R"( | |
| OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %4 "main" | |
| OpExecutionMode %4 OriginUpperLeft | |
| OpSource ESSL 310 | |
| %2 = OpTypeVoid | |
| %3 = OpTypeFunction %2 | |
| %4 = OpFunction %2 None %3 | |
| %5 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| %6 = OpFunction %2 None %3 | |
| %7 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| const auto env = SPV_ENV_UNIVERSAL_1_4; | |
| const auto consumer = nullptr; | |
| std::vector<protobufs::Instruction> instructions; | |
| instructions.push_back( | |
| MakeInstructionMessage(spv::Op::OpFunction, 2, 8, | |
| {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, | |
| {uint32_t(spv::FunctionControlMask::MaskNone)}}, | |
| {SPV_OPERAND_TYPE_TYPE_ID, {3}}})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpLabel, 0, 9, {})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpFunctionCall, 2, 11, | |
| {{SPV_OPERAND_TYPE_ID, {6}}})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpReturn, 0, 0, {})); | |
| instructions.push_back( | |
| MakeInstructionMessage(spv::Op::OpFunctionEnd, 0, 0, {})); | |
| spvtools::ValidatorOptions validator_options; | |
| const auto context1 = BuildModule(env, consumer, shader, kFuzzAssembleOption); | |
| const auto context2 = BuildModule(env, consumer, shader, kFuzzAssembleOption); | |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( | |
| context1.get(), validator_options, kConsoleMessageConsumer)); | |
| TransformationContext transformation_context1( | |
| MakeUnique<FactManager>(context1.get()), validator_options); | |
| TransformationContext transformation_context2( | |
| MakeUnique<FactManager>(context2.get()), validator_options); | |
| // Mark function 6 as livesafe. | |
| transformation_context2.GetFactManager()->AddFactFunctionIsLivesafe(6); | |
| TransformationAddFunction add_dead_function(instructions); | |
| ASSERT_TRUE( | |
| add_dead_function.IsApplicable(context1.get(), transformation_context1)); | |
| ApplyAndCheckFreshIds(add_dead_function, context1.get(), | |
| &transformation_context1); | |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( | |
| context1.get(), validator_options, kConsoleMessageConsumer)); | |
| // The function should not be deemed livesafe | |
| ASSERT_FALSE(transformation_context1.GetFactManager()->FunctionIsLivesafe(8)); | |
| // All variables/parameters in the function should be deemed irrelevant. | |
| ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant( | |
| context1.get(), transformation_context1, 8, 0)); | |
| std::string added_as_live_or_dead_code = R"( | |
| OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %4 "main" | |
| OpExecutionMode %4 OriginUpperLeft | |
| OpSource ESSL 310 | |
| %2 = OpTypeVoid | |
| %3 = OpTypeFunction %2 | |
| %4 = OpFunction %2 None %3 | |
| %5 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| %6 = OpFunction %2 None %3 | |
| %7 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| %8 = OpFunction %2 None %3 | |
| %9 = OpLabel | |
| %11 = OpFunctionCall %2 %6 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| ASSERT_TRUE(IsEqual(env, added_as_live_or_dead_code, context1.get())); | |
| TransformationAddFunction add_livesafe_function(instructions, 0, 0, {}, 0, | |
| {}); | |
| ASSERT_TRUE(add_livesafe_function.IsApplicable(context2.get(), | |
| transformation_context2)); | |
| ApplyAndCheckFreshIds(add_livesafe_function, context2.get(), | |
| &transformation_context2); | |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( | |
| context2.get(), validator_options, kConsoleMessageConsumer)); | |
| // The function should be deemed livesafe | |
| ASSERT_TRUE(transformation_context2.GetFactManager()->FunctionIsLivesafe(8)); | |
| // All variables/parameters in the function should be deemed irrelevant. | |
| ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant( | |
| context2.get(), transformation_context2, 8, 0)); | |
| ASSERT_TRUE(IsEqual(env, added_as_live_or_dead_code, context2.get())); | |
| } | |
| TEST(TransformationAddFunctionTest, LivesafeOnlyCallsLivesafe) { | |
| std::string shader = R"( | |
| OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %4 "main" | |
| OpExecutionMode %4 OriginUpperLeft | |
| OpSource ESSL 310 | |
| %2 = OpTypeVoid | |
| %3 = OpTypeFunction %2 | |
| %4 = OpFunction %2 None %3 | |
| %5 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| %6 = OpFunction %2 None %3 | |
| %7 = OpLabel | |
| OpKill | |
| OpFunctionEnd | |
| )"; | |
| const auto env = SPV_ENV_UNIVERSAL_1_4; | |
| const auto consumer = nullptr; | |
| std::vector<protobufs::Instruction> instructions; | |
| instructions.push_back( | |
| MakeInstructionMessage(spv::Op::OpFunction, 2, 8, | |
| {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, | |
| {uint32_t(spv::FunctionControlMask::MaskNone)}}, | |
| {SPV_OPERAND_TYPE_TYPE_ID, {3}}})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpLabel, 0, 9, {})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpFunctionCall, 2, 11, | |
| {{SPV_OPERAND_TYPE_ID, {6}}})); | |
| instructions.push_back(MakeInstructionMessage(spv::Op::OpReturn, 0, 0, {})); | |
| instructions.push_back( | |
| MakeInstructionMessage(spv::Op::OpFunctionEnd, 0, 0, {})); | |
| spvtools::ValidatorOptions validator_options; | |
| const auto context1 = BuildModule(env, consumer, shader, kFuzzAssembleOption); | |
| const auto context2 = BuildModule(env, consumer, shader, kFuzzAssembleOption); | |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( | |
| context1.get(), validator_options, kConsoleMessageConsumer)); | |
| TransformationContext transformation_context1( | |
| MakeUnique<FactManager>(context1.get()), validator_options); | |
| TransformationContext transformation_context2( | |
| MakeUnique<FactManager>(context2.get()), validator_options); | |
| TransformationAddFunction add_dead_function(instructions); | |
| ASSERT_TRUE( | |
| add_dead_function.IsApplicable(context1.get(), transformation_context1)); | |
| ApplyAndCheckFreshIds(add_dead_function, context1.get(), | |
| &transformation_context1); | |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( | |
| context1.get(), validator_options, kConsoleMessageConsumer)); | |
| // The function should not be deemed livesafe | |
| ASSERT_FALSE(transformation_context1.GetFactManager()->FunctionIsLivesafe(8)); | |
| // All variables/parameters in the function should be deemed irrelevant. | |
| ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant( | |
| context1.get(), transformation_context1, 8, 0)); | |
| std::string added_as_dead_code = R"( | |
| OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %4 "main" | |
| OpExecutionMode %4 OriginUpperLeft | |
| OpSource ESSL 310 | |
| %2 = OpTypeVoid | |
| %3 = OpTypeFunction %2 | |
| %4 = OpFunction %2 None %3 | |
| %5 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| %6 = OpFunction %2 None %3 | |
| %7 = OpLabel | |
| OpKill | |
| OpFunctionEnd | |
| %8 = OpFunction %2 None %3 | |
| %9 = OpLabel | |
| %11 = OpFunctionCall %2 %6 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| ASSERT_TRUE(IsEqual(env, added_as_dead_code, context1.get())); | |
| TransformationAddFunction add_livesafe_function(instructions, 0, 0, {}, 0, | |
| {}); | |
| ASSERT_FALSE(add_livesafe_function.IsApplicable(context2.get(), | |
| transformation_context2)); | |
| } | |
| TEST(TransformationAddFunctionTest, | |
| LoopLimitersBackEdgeBlockEndsWithConditional1) { | |
| std::string shader = R"( | |
| OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %4 "main" | |
| OpExecutionMode %4 OriginUpperLeft | |
| OpSource ESSL 310 | |
| %2 = OpTypeVoid | |
| %3 = OpTypeFunction %2 | |
| %8 = OpTypeInt 32 1 | |
| %9 = OpTypePointer Function %8 | |
| %11 = OpConstant %8 0 | |
| %18 = OpConstant %8 10 | |
| %19 = OpTypeBool | |
| %26 = OpConstantTrue %19 | |
| %27 = OpConstantFalse %19 | |
| %28 = OpTypeInt 32 0 | |
| %29 = OpTypePointer Function %28 | |
| %30 = OpConstant %28 0 | |
| %31 = OpConstant %28 1 | |
| %32 = OpConstant %28 5 | |
| %22 = OpConstant %8 1 | |
| %4 = OpFunction %2 None %3 | |
| %5 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| std::string donor = R"( | |
| OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %4 "main" | |
| OpExecutionMode %4 OriginUpperLeft | |
| OpSource ESSL 310 | |
| %2 = OpTypeVoid | |
| %3 = OpTypeFunction %2 | |
| %8 = OpTypeInt 32 1 | |
| %9 = OpTypePointer Function %8 | |
| %11 = OpConstant %8 0 | |
| %18 = OpConstant %8 10 | |
| %19 = OpTypeBool | |
| %26 = OpConstantTrue %19 | |
| %27 = OpConstantFalse %19 | |
| %28 = OpTypeInt 32 0 | |
| %29 = OpTypePointer Function %28 | |
| %30 = OpConstant %28 0 | |
| %31 = OpConstant %28 1 | |
| %32 = OpConstant %28 5 | |
| %22 = OpConstant %8 1 | |
| %4 = OpFunction %2 None %3 | |
| %5 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| %6 = OpFunction %2 None %3 | |
| %7 = OpLabel | |
| %10 = OpVariable %9 Function | |
| OpStore %10 %11 | |
| OpBranch %12 | |
| %12 = OpLabel | |
| OpLoopMerge %14 %15 None | |
| OpBranch %15 | |
| %15 = OpLabel | |
| %17 = OpLoad %8 %10 | |
| %20 = OpSLessThan %19 %17 %18 | |
| %21 = OpLoad %8 %10 | |
| %23 = OpIAdd %8 %21 %22 | |
| OpStore %10 %23 | |
| OpBranchConditional %20 %12 %14 | |
| %14 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| const auto env = SPV_ENV_UNIVERSAL_1_4; | |
| const auto consumer = nullptr; | |
| const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); | |
| spvtools::ValidatorOptions validator_options; | |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, | |
| kConsoleMessageConsumer)); | |
| TransformationContext transformation_context( | |
| MakeUnique<FactManager>(context.get()), validator_options); | |
| // Make a sequence of instruction messages corresponding to function %6 in | |
| // |donor|. | |
| std::vector<protobufs::Instruction> instructions = | |
| GetInstructionsForFunction(env, consumer, donor, 6); | |
| protobufs::LoopLimiterInfo loop_limiter_info; | |
| loop_limiter_info.set_loop_header_id(12); | |
| loop_limiter_info.set_load_id(102); | |
| loop_limiter_info.set_increment_id(103); | |
| loop_limiter_info.set_compare_id(104); | |
| loop_limiter_info.set_logical_op_id(105); | |
| TransformationAddFunction add_livesafe_function(instructions, 100, 32, | |
| {loop_limiter_info}, 0, {}); | |
| ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(), | |
| transformation_context)); | |
| ApplyAndCheckFreshIds(add_livesafe_function, context.get(), | |
| &transformation_context); | |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, | |
| kConsoleMessageConsumer)); | |
| std::string expected = R"( | |
| OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %4 "main" | |
| OpExecutionMode %4 OriginUpperLeft | |
| OpSource ESSL 310 | |
| %2 = OpTypeVoid | |
| %3 = OpTypeFunction %2 | |
| %8 = OpTypeInt 32 1 | |
| %9 = OpTypePointer Function %8 | |
| %11 = OpConstant %8 0 | |
| %18 = OpConstant %8 10 | |
| %19 = OpTypeBool | |
| %26 = OpConstantTrue %19 | |
| %27 = OpConstantFalse %19 | |
| %28 = OpTypeInt 32 0 | |
| %29 = OpTypePointer Function %28 | |
| %30 = OpConstant %28 0 | |
| %31 = OpConstant %28 1 | |
| %32 = OpConstant %28 5 | |
| %22 = OpConstant %8 1 | |
| %4 = OpFunction %2 None %3 | |
| %5 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| %6 = OpFunction %2 None %3 | |
| %7 = OpLabel | |
| %100 = OpVariable %29 Function %30 | |
| %10 = OpVariable %9 Function | |
| OpStore %10 %11 | |
| OpBranch %12 | |
| %12 = OpLabel | |
| OpLoopMerge %14 %15 None | |
| OpBranch %15 | |
| %15 = OpLabel | |
| %17 = OpLoad %8 %10 | |
| %20 = OpSLessThan %19 %17 %18 | |
| %21 = OpLoad %8 %10 | |
| %23 = OpIAdd %8 %21 %22 | |
| OpStore %10 %23 | |
| %102 = OpLoad %28 %100 | |
| %103 = OpIAdd %28 %102 %31 | |
| OpStore %100 %103 | |
| %104 = OpULessThan %19 %102 %32 | |
| %105 = OpLogicalAnd %19 %20 %104 | |
| OpBranchConditional %105 %12 %14 | |
| %14 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| ASSERT_TRUE(IsEqual(env, expected, context.get())); | |
| } | |
| TEST(TransformationAddFunctionTest, | |
| LoopLimitersBackEdgeBlockEndsWithConditional2) { | |
| std::string shader = R"( | |
| OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %4 "main" | |
| OpExecutionMode %4 OriginUpperLeft | |
| OpSource ESSL 310 | |
| %2 = OpTypeVoid | |
| %3 = OpTypeFunction %2 | |
| %8 = OpTypeInt 32 1 | |
| %9 = OpTypePointer Function %8 | |
| %11 = OpConstant %8 0 | |
| %18 = OpConstant %8 10 | |
| %19 = OpTypeBool | |
| %26 = OpConstantTrue %19 | |
| %27 = OpConstantFalse %19 | |
| %28 = OpTypeInt 32 0 | |
| %29 = OpTypePointer Function %28 | |
| %30 = OpConstant %28 0 | |
| %31 = OpConstant %28 1 | |
| %32 = OpConstant %28 5 | |
| %22 = OpConstant %8 1 | |
| %4 = OpFunction %2 None %3 | |
| %5 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| std::string donor = R"( | |
| OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %4 "main" | |
| OpExecutionMode %4 OriginUpperLeft | |
| OpSource ESSL 310 | |
| %2 = OpTypeVoid | |
| %3 = OpTypeFunction %2 | |
| %8 = OpTypeInt 32 1 | |
| %9 = OpTypePointer Function %8 | |
| %11 = OpConstant %8 0 | |
| %18 = OpConstant %8 10 | |
| %19 = OpTypeBool | |
| %26 = OpConstantTrue %19 | |
| %27 = OpConstantFalse %19 | |
| %28 = OpTypeInt 32 0 | |
| %29 = OpTypePointer Function %28 | |
| %30 = OpConstant %28 0 | |
| %31 = OpConstant %28 1 | |
| %32 = OpConstant %28 5 | |
| %22 = OpConstant %8 1 | |
| %4 = OpFunction %2 None %3 | |
| %5 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| %6 = OpFunction %2 None %3 | |
| %7 = OpLabel | |
| %10 = OpVariable %9 Function | |
| OpStore %10 %11 | |
| OpBranch %12 | |
| %12 = OpLabel | |
| OpLoopMerge %14 %15 None | |
| OpBranch %15 | |
| %15 = OpLabel | |
| %17 = OpLoad %8 %10 | |
| %20 = OpSLessThan %19 %17 %18 | |
| %21 = OpLoad %8 %10 | |
| %23 = OpIAdd %8 %21 %22 | |
| OpStore %10 %23 | |
| %50 = OpLogicalNot %19 %20 | |
| OpBranchConditional %50 %14 %12 | |
| %14 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| const auto env = SPV_ENV_UNIVERSAL_1_4; | |
| const auto consumer = nullptr; | |
| const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); | |
| spvtools::ValidatorOptions validator_options; | |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, | |
| kConsoleMessageConsumer)); | |
| TransformationContext transformation_context( | |
| MakeUnique<FactManager>(context.get()), validator_options); | |
| // Make a sequence of instruction messages corresponding to function %6 in | |
| // |donor|. | |
| std::vector<protobufs::Instruction> instructions = | |
| GetInstructionsForFunction(env, consumer, donor, 6); | |
| protobufs::LoopLimiterInfo loop_limiter_info; | |
| loop_limiter_info.set_loop_header_id(12); | |
| loop_limiter_info.set_load_id(102); | |
| loop_limiter_info.set_increment_id(103); | |
| loop_limiter_info.set_compare_id(104); | |
| loop_limiter_info.set_logical_op_id(105); | |
| TransformationAddFunction add_livesafe_function(instructions, 100, 32, | |
| {loop_limiter_info}, 0, {}); | |
| ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(), | |
| transformation_context)); | |
| ApplyAndCheckFreshIds(add_livesafe_function, context.get(), | |
| &transformation_context); | |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, | |
| kConsoleMessageConsumer)); | |
| std::string expected = R"( | |
| OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %4 "main" | |
| OpExecutionMode %4 OriginUpperLeft | |
| OpSource ESSL 310 | |
| %2 = OpTypeVoid | |
| %3 = OpTypeFunction %2 | |
| %8 = OpTypeInt 32 1 | |
| %9 = OpTypePointer Function %8 | |
| %11 = OpConstant %8 0 | |
| %18 = OpConstant %8 10 | |
| %19 = OpTypeBool | |
| %26 = OpConstantTrue %19 | |
| %27 = OpConstantFalse %19 | |
| %28 = OpTypeInt 32 0 | |
| %29 = OpTypePointer Function %28 | |
| %30 = OpConstant %28 0 | |
| %31 = OpConstant %28 1 | |
| %32 = OpConstant %28 5 | |
| %22 = OpConstant %8 1 | |
| %4 = OpFunction %2 None %3 | |
| %5 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| %6 = OpFunction %2 None %3 | |
| %7 = OpLabel | |
| %100 = OpVariable %29 Function %30 | |
| %10 = OpVariable %9 Function | |
| OpStore %10 %11 | |
| OpBranch %12 | |
| %12 = OpLabel | |
| OpLoopMerge %14 %15 None | |
| OpBranch %15 | |
| %15 = OpLabel | |
| %17 = OpLoad %8 %10 | |
| %20 = OpSLessThan %19 %17 %18 | |
| %21 = OpLoad %8 %10 | |
| %23 = OpIAdd %8 %21 %22 | |
| OpStore %10 %23 | |
| %50 = OpLogicalNot %19 %20 | |
| %102 = OpLoad %28 %100 | |
| %103 = OpIAdd %28 %102 %31 | |
| OpStore %100 %103 | |
| %104 = OpUGreaterThanEqual %19 %102 %32 | |
| %105 = OpLogicalOr %19 %50 %104 | |
| OpBranchConditional %105 %14 %12 | |
| %14 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| ASSERT_TRUE(IsEqual(env, expected, context.get())); | |
| } | |
| TEST(TransformationAddFunctionTest, LoopLimitersHeaderIsBackEdgeBlock) { | |
| std::string shader = R"( | |
| OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %4 "main" | |
| OpExecutionMode %4 OriginUpperLeft | |
| OpSource ESSL 310 | |
| %2 = OpTypeVoid | |
| %3 = OpTypeFunction %2 | |
| %8 = OpTypeInt 32 1 | |
| %9 = OpTypePointer Function %8 | |
| %11 = OpConstant %8 0 | |
| %18 = OpConstant %8 10 | |
| %19 = OpTypeBool | |
| %26 = OpConstantTrue %19 | |
| %27 = OpConstantFalse %19 | |
| %28 = OpTypeInt 32 0 | |
| %29 = OpTypePointer Function %28 | |
| %30 = OpConstant %28 0 | |
| %31 = OpConstant %28 1 | |
| %32 = OpConstant %28 5 | |
| %22 = OpConstant %8 1 | |
| %4 = OpFunction %2 None %3 | |
| %5 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| std::string donor = R"( | |
| OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %4 "main" | |
| OpExecutionMode %4 OriginUpperLeft | |
| OpSource ESSL 310 | |
| %2 = OpTypeVoid | |
| %3 = OpTypeFunction %2 | |
| %8 = OpTypeInt 32 1 | |
| %9 = OpTypePointer Function %8 | |
| %11 = OpConstant %8 0 | |
| %18 = OpConstant %8 10 | |
| %19 = OpTypeBool | |
| %26 = OpConstantTrue %19 | |
| %27 = OpConstantFalse %19 | |
| %28 = OpTypeInt 32 0 | |
| %29 = OpTypePointer Function %28 | |
| %30 = OpConstant %28 0 | |
| %31 = OpConstant %28 1 | |
| %32 = OpConstant %28 5 | |
| %22 = OpConstant %8 1 | |
| %4 = OpFunction %2 None %3 | |
| %5 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| %6 = OpFunction %2 None %3 | |
| %7 = OpLabel | |
| %10 = OpVariable %9 Function | |
| OpStore %10 %11 | |
| OpBranch %12 | |
| %12 = OpLabel | |
| %17 = OpLoad %8 %10 | |
| %20 = OpSLessThan %19 %17 %18 | |
| %21 = OpLoad %8 %10 | |
| %23 = OpIAdd %8 %21 %22 | |
| OpStore %10 %23 | |
| %50 = OpLogicalNot %19 %20 | |
| OpLoopMerge %14 %12 None | |
| OpBranchConditional %50 %14 %12 | |
| %14 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| const auto env = SPV_ENV_UNIVERSAL_1_4; | |
| const auto consumer = nullptr; | |
| const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); | |
| spvtools::ValidatorOptions validator_options; | |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, | |
| kConsoleMessageConsumer)); | |
| TransformationContext transformation_context( | |
| MakeUnique<FactManager>(context.get()), validator_options); | |
| // Make a sequence of instruction messages corresponding to function %6 in | |
| // |donor|. | |
| std::vector<protobufs::Instruction> instructions = | |
| GetInstructionsForFunction(env, consumer, donor, 6); | |
| protobufs::LoopLimiterInfo loop_limiter_info; | |
| loop_limiter_info.set_loop_header_id(12); | |
| loop_limiter_info.set_load_id(102); | |
| loop_limiter_info.set_increment_id(103); | |
| loop_limiter_info.set_compare_id(104); | |
| loop_limiter_info.set_logical_op_id(105); | |
| TransformationAddFunction add_livesafe_function(instructions, 100, 32, | |
| {loop_limiter_info}, 0, {}); | |
| ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(), | |
| transformation_context)); | |
| ApplyAndCheckFreshIds(add_livesafe_function, context.get(), | |
| &transformation_context); | |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, | |
| kConsoleMessageConsumer)); | |
| std::string expected = R"( | |
| OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %4 "main" | |
| OpExecutionMode %4 OriginUpperLeft | |
| OpSource ESSL 310 | |
| %2 = OpTypeVoid | |
| %3 = OpTypeFunction %2 | |
| %8 = OpTypeInt 32 1 | |
| %9 = OpTypePointer Function %8 | |
| %11 = OpConstant %8 0 | |
| %18 = OpConstant %8 10 | |
| %19 = OpTypeBool | |
| %26 = OpConstantTrue %19 | |
| %27 = OpConstantFalse %19 | |
| %28 = OpTypeInt 32 0 | |
| %29 = OpTypePointer Function %28 | |
| %30 = OpConstant %28 0 | |
| %31 = OpConstant %28 1 | |
| %32 = OpConstant %28 5 | |
| %22 = OpConstant %8 1 | |
| %4 = OpFunction %2 None %3 | |
| %5 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| %6 = OpFunction %2 None %3 | |
| %7 = OpLabel | |
| %100 = OpVariable %29 Function %30 | |
| %10 = OpVariable %9 Function | |
| OpStore %10 %11 | |
| OpBranch %12 | |
| %12 = OpLabel | |
| %17 = OpLoad %8 %10 | |
| %20 = OpSLessThan %19 %17 %18 | |
| %21 = OpLoad %8 %10 | |
| %23 = OpIAdd %8 %21 %22 | |
| OpStore %10 %23 | |
| %50 = OpLogicalNot %19 %20 | |
| %102 = OpLoad %28 %100 | |
| %103 = OpIAdd %28 %102 %31 | |
| OpStore %100 %103 | |
| %104 = OpUGreaterThanEqual %19 %102 %32 | |
| %105 = OpLogicalOr %19 %50 %104 | |
| OpLoopMerge %14 %12 None | |
| OpBranchConditional %105 %14 %12 | |
| %14 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| ASSERT_TRUE(IsEqual(env, expected, context.get())); | |
| } | |
| TEST(TransformationAddFunctionTest, InfiniteLoop) { | |
| std::string shader = R"( | |
| OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %4 "main" | |
| OpExecutionMode %4 OriginUpperLeft | |
| OpSource ESSL 310 | |
| %2 = OpTypeVoid | |
| %3 = OpTypeFunction %2 | |
| %8 = OpTypeInt 32 1 | |
| %9 = OpTypePointer Function %8 | |
| %11 = OpConstant %8 0 | |
| %18 = OpConstant %8 10 | |
| %19 = OpTypeBool | |
| %26 = OpConstantTrue %19 | |
| %27 = OpConstantFalse %19 | |
| %28 = OpTypeInt 32 0 | |
| %29 = OpTypePointer Function %28 | |
| %30 = OpConstant %28 0 | |
| %31 = OpConstant %28 1 | |
| %32 = OpConstant %28 5 | |
| %22 = OpConstant %8 1 | |
| %4 = OpFunction %2 None %3 | |
| %5 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| std::string donor = R"( | |
| OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %4 "main" | |
| OpExecutionMode %4 OriginUpperLeft | |
| OpSource ESSL 310 | |
| %2 = OpTypeVoid | |
| %3 = OpTypeFunction %2 | |
| %8 = OpTypeInt 32 1 | |
| %9 = OpTypePointer Function %8 | |
| %11 = OpConstant %8 0 | |
| %18 = OpConstant %8 10 | |
| %19 = OpTypeBool | |
| %26 = OpConstantTrue %19 | |
| %27 = OpConstantFalse %19 | |
| %28 = OpTypeInt 32 0 | |
| %29 = OpTypePointer Function %28 | |
| %30 = OpConstant %28 0 | |
| %31 = OpConstant %28 1 | |
| %32 = OpConstant %28 5 | |
| %22 = OpConstant %8 1 | |
| %4 = OpFunction %2 None %3 | |
| %5 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| %6 = OpFunction %2 None %3 | |
| %7 = OpLabel | |
| %10 = OpVariable %9 Function | |
| OpStore %10 %11 | |
| OpBranch %12 | |
| %12 = OpLabel | |
| OpLoopMerge %14 %12 None | |
| OpBranch %12 | |
| %14 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| const auto env = SPV_ENV_UNIVERSAL_1_4; | |
| const auto consumer = nullptr; | |
| const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); | |
| spvtools::ValidatorOptions validator_options; | |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, | |
| kConsoleMessageConsumer)); | |
| TransformationContext transformation_context( | |
| MakeUnique<FactManager>(context.get()), validator_options); | |
| // Make a sequence of instruction messages corresponding to function %6 in | |
| // |donor|. | |
| std::vector<protobufs::Instruction> instructions = | |
| GetInstructionsForFunction(env, consumer, donor, 6); | |
| protobufs::LoopLimiterInfo loop_limiter_info; | |
| loop_limiter_info.set_loop_header_id(12); | |
| loop_limiter_info.set_load_id(102); | |
| loop_limiter_info.set_increment_id(103); | |
| loop_limiter_info.set_compare_id(104); | |
| loop_limiter_info.set_logical_op_id(105); | |
| TransformationAddFunction add_livesafe_function(instructions, 100, 32, | |
| {loop_limiter_info}, 0, {}); | |
| // To make sure the loop's merge block is reachable, it must be dominated by | |
| // the loop header. | |
| ASSERT_FALSE(add_livesafe_function.IsApplicable(context.get(), | |
| transformation_context)); | |
| } | |
| TEST(TransformationAddFunctionTest, UnreachableContinueConstruct) { | |
| // This captures the case where the loop's continue construct is statically | |
| // unreachable. In this case the loop cannot iterate and so we do not add | |
| // a loop limiter. (The reason we do not just add one anyway is that | |
| // detecting which block would be the back-edge block is difficult in the | |
| // absence of reliable dominance information.) | |
| std::string shader = R"( | |
| OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %4 "main" | |
| OpExecutionMode %4 OriginUpperLeft | |
| OpSource ESSL 310 | |
| %2 = OpTypeVoid | |
| %3 = OpTypeFunction %2 | |
| %8 = OpTypeInt 32 1 | |
| %9 = OpTypePointer Function %8 | |
| %11 = OpConstant %8 0 | |
| %18 = OpConstant %8 10 | |
| %19 = OpTypeBool | |
| %23 = OpConstant %8 1 | |
| %26 = OpConstantTrue %19 | |
| %27 = OpConstantFalse %19 | |
| %28 = OpTypeInt 32 0 | |
| %29 = OpTypePointer Function %28 | |
| %30 = OpConstant %28 0 | |
| %31 = OpConstant %28 1 | |
| %32 = OpConstant %28 5 | |
| %4 = OpFunction %2 None %3 | |
| %5 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| std::string donor = R"( | |
| OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %4 "main" | |
| OpExecutionMode %4 OriginUpperLeft | |
| OpSource ESSL 310 | |
| %2 = OpTypeVoid | |
| %3 = OpTypeFunction %2 | |
| %8 = OpTypeInt 32 1 | |
| %9 = OpTypePointer Function %8 | |
| %11 = OpConstant %8 0 | |
| %18 = OpConstant %8 10 | |
| %19 = OpTypeBool | |
| %23 = OpConstant %8 1 | |
| %26 = OpConstantTrue %19 | |
| %27 = OpConstantFalse %19 | |
| %28 = OpTypeInt 32 0 | |
| %29 = OpTypePointer Function %28 | |
| %30 = OpConstant %28 0 | |
| %31 = OpConstant %28 1 | |
| %32 = OpConstant %28 5 | |
| %4 = OpFunction %2 None %3 | |
| %5 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| %6 = OpFunction %2 None %3 | |
| %7 = OpLabel | |
| %10 = OpVariable %9 Function | |
| OpStore %10 %11 | |
| OpBranch %12 | |
| %12 = OpLabel | |
| OpLoopMerge %14 %15 None | |
| OpBranch %16 | |
| %16 = OpLabel | |
| %17 = OpLoad %8 %10 | |
| %20 = OpSLessThan %19 %17 %18 | |
| OpBranchConditional %20 %13 %14 | |
| %13 = OpLabel | |
| OpBranch %14 | |
| %15 = OpLabel | |
| %22 = OpLoad %8 %10 | |
| %24 = OpIAdd %8 %22 %23 | |
| OpStore %10 %24 | |
| OpBranch %12 | |
| %14 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| const auto env = SPV_ENV_UNIVERSAL_1_4; | |
| const auto consumer = nullptr; | |
| const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); | |
| spvtools::ValidatorOptions validator_options; | |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, | |
| kConsoleMessageConsumer)); | |
| TransformationContext transformation_context( | |
| MakeUnique<FactManager>(context.get()), validator_options); | |
| // Make a sequence of instruction messages corresponding to function %6 in | |
| // |donor|. | |
| std::vector<protobufs::Instruction> instructions = | |
| GetInstructionsForFunction(env, consumer, donor, 6); | |
| protobufs::LoopLimiterInfo loop_limiter_info; | |
| loop_limiter_info.set_loop_header_id(12); | |
| loop_limiter_info.set_load_id(102); | |
| loop_limiter_info.set_increment_id(103); | |
| loop_limiter_info.set_compare_id(104); | |
| loop_limiter_info.set_logical_op_id(105); | |
| TransformationAddFunction add_livesafe_function(instructions, 100, 32, | |
| {loop_limiter_info}, 0, {}); | |
| ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(), | |
| transformation_context)); | |
| ApplyAndCheckFreshIds(add_livesafe_function, context.get(), | |
| &transformation_context); | |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, | |
| kConsoleMessageConsumer)); | |
| std::string expected = R"( | |
| OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %4 "main" | |
| OpExecutionMode %4 OriginUpperLeft | |
| OpSource ESSL 310 | |
| %2 = OpTypeVoid | |
| %3 = OpTypeFunction %2 | |
| %8 = OpTypeInt 32 1 | |
| %9 = OpTypePointer Function %8 | |
| %11 = OpConstant %8 0 | |
| %18 = OpConstant %8 10 | |
| %19 = OpTypeBool | |
| %23 = OpConstant %8 1 | |
| %26 = OpConstantTrue %19 | |
| %27 = OpConstantFalse %19 | |
| %28 = OpTypeInt 32 0 | |
| %29 = OpTypePointer Function %28 | |
| %30 = OpConstant %28 0 | |
| %31 = OpConstant %28 1 | |
| %32 = OpConstant %28 5 | |
| %4 = OpFunction %2 None %3 | |
| %5 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| %6 = OpFunction %2 None %3 | |
| %7 = OpLabel | |
| %100 = OpVariable %29 Function %30 | |
| %10 = OpVariable %9 Function | |
| OpStore %10 %11 | |
| OpBranch %12 | |
| %12 = OpLabel | |
| OpLoopMerge %14 %15 None | |
| OpBranch %16 | |
| %16 = OpLabel | |
| %17 = OpLoad %8 %10 | |
| %20 = OpSLessThan %19 %17 %18 | |
| OpBranchConditional %20 %13 %14 | |
| %13 = OpLabel | |
| OpBranch %14 | |
| %15 = OpLabel | |
| %22 = OpLoad %8 %10 | |
| %24 = OpIAdd %8 %22 %23 | |
| OpStore %10 %24 | |
| OpBranch %12 | |
| %14 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| ASSERT_TRUE(IsEqual(env, expected, context.get())); | |
| } | |
| TEST(TransformationAddFunctionTest, LoopLimitersAndOpPhi1) { | |
| // This captures the scenario where breaking a loop due to a loop limiter | |
| // requires patching up OpPhi instructions occurring at the loop merge block. | |
| std::string shader = R"( | |
| OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %4 "main" | |
| OpExecutionMode %4 OriginUpperLeft | |
| OpSource ESSL 310 | |
| %2 = OpTypeVoid | |
| %3 = OpTypeFunction %2 | |
| %6 = OpTypeInt 32 1 | |
| %50 = OpTypeInt 32 0 | |
| %51 = OpConstant %50 0 | |
| %52 = OpConstant %50 1 | |
| %53 = OpTypePointer Function %50 | |
| %7 = OpTypeFunction %6 | |
| %10 = OpTypePointer Function %6 | |
| %12 = OpConstant %6 0 | |
| %19 = OpConstant %6 100 | |
| %20 = OpTypeBool | |
| %23 = OpConstant %6 20 | |
| %28 = OpConstant %6 1 | |
| %4 = OpFunction %2 None %3 | |
| %5 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| std::string donor = R"( | |
| OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %4 "main" | |
| OpExecutionMode %4 OriginUpperLeft | |
| OpSource ESSL 310 | |
| %2 = OpTypeVoid | |
| %3 = OpTypeFunction %2 | |
| %6 = OpTypeInt 32 1 | |
| %7 = OpTypeFunction %6 | |
| %10 = OpTypePointer Function %6 | |
| %12 = OpConstant %6 0 | |
| %19 = OpConstant %6 100 | |
| %20 = OpTypeBool | |
| %23 = OpConstant %6 20 | |
| %28 = OpConstant %6 1 | |
| %4 = OpFunction %2 None %3 | |
| %5 = OpLabel | |
| %36 = OpFunctionCall %6 %8 | |
| OpReturn | |
| OpFunctionEnd | |
| %8 = OpFunction %6 None %7 | |
| %9 = OpLabel | |
| %11 = OpVariable %10 Function | |
| OpStore %11 %12 | |
| OpBranch %13 | |
| %13 = OpLabel | |
| %37 = OpPhi %6 %12 %9 %32 %16 | |
| OpLoopMerge %15 %16 None | |
| OpBranch %17 | |
| %17 = OpLabel | |
| %21 = OpSLessThan %20 %37 %19 | |
| OpBranchConditional %21 %14 %15 | |
| %14 = OpLabel | |
| %24 = OpSGreaterThan %20 %37 %23 | |
| OpSelectionMerge %26 None | |
| OpBranchConditional %24 %25 %26 | |
| %25 = OpLabel | |
| %29 = OpIAdd %6 %37 %28 | |
| OpStore %11 %29 | |
| OpBranch %15 | |
| %26 = OpLabel | |
| OpBranch %16 | |
| %16 = OpLabel | |
| %32 = OpIAdd %6 %37 %28 | |
| OpStore %11 %32 | |
| OpBranch %13 | |
| %15 = OpLabel | |
| %38 = OpPhi %6 %37 %17 %29 %25 | |
| OpReturnValue %38 | |
| OpFunctionEnd | |
| )"; | |
| const auto env = SPV_ENV_UNIVERSAL_1_4; | |
| const auto consumer = nullptr; | |
| const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); | |
| spvtools::ValidatorOptions validator_options; | |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, | |
| kConsoleMessageConsumer)); | |
| TransformationContext transformation_context( | |
| MakeUnique<FactManager>(context.get()), validator_options); | |
| // Make a sequence of instruction messages corresponding to function %8 in | |
| // |donor|. | |
| std::vector<protobufs::Instruction> instructions = | |
| GetInstructionsForFunction(env, consumer, donor, 8); | |
| protobufs::LoopLimiterInfo loop_limiter_info; | |
| loop_limiter_info.set_loop_header_id(13); | |
| loop_limiter_info.set_load_id(102); | |
| loop_limiter_info.set_increment_id(103); | |
| loop_limiter_info.set_compare_id(104); | |
| loop_limiter_info.set_logical_op_id(105); | |
| TransformationAddFunction no_op_phi_data(instructions, 100, 28, | |
| {loop_limiter_info}, 0, {}); | |
| // The loop limiter info is not good enough; it does not include ids to patch | |
| // up the OpPhi at the loop merge. | |
| ASSERT_FALSE( | |
| no_op_phi_data.IsApplicable(context.get(), transformation_context)); | |
| // Add a phi id for the new edge from the loop back edge block to the loop | |
| // merge. | |
| loop_limiter_info.add_phi_id(28); | |
| TransformationAddFunction with_op_phi_data(instructions, 100, 28, | |
| {loop_limiter_info}, 0, {}); | |
| ASSERT_TRUE( | |
| with_op_phi_data.IsApplicable(context.get(), transformation_context)); | |
| ApplyAndCheckFreshIds(with_op_phi_data, context.get(), | |
| &transformation_context); | |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, | |
| kConsoleMessageConsumer)); | |
| std::string expected = R"( | |
| OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %4 "main" | |
| OpExecutionMode %4 OriginUpperLeft | |
| OpSource ESSL 310 | |
| %2 = OpTypeVoid | |
| %3 = OpTypeFunction %2 | |
| %6 = OpTypeInt 32 1 | |
| %50 = OpTypeInt 32 0 | |
| %51 = OpConstant %50 0 | |
| %52 = OpConstant %50 1 | |
| %53 = OpTypePointer Function %50 | |
| %7 = OpTypeFunction %6 | |
| %10 = OpTypePointer Function %6 | |
| %12 = OpConstant %6 0 | |
| %19 = OpConstant %6 100 | |
| %20 = OpTypeBool | |
| %23 = OpConstant %6 20 | |
| %28 = OpConstant %6 1 | |
| %4 = OpFunction %2 None %3 | |
| %5 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| %8 = OpFunction %6 None %7 | |
| %9 = OpLabel | |
| %100 = OpVariable %53 Function %51 | |
| %11 = OpVariable %10 Function | |
| OpStore %11 %12 | |
| OpBranch %13 | |
| %13 = OpLabel | |
| %37 = OpPhi %6 %12 %9 %32 %16 | |
| OpLoopMerge %15 %16 None | |
| OpBranch %17 | |
| %17 = OpLabel | |
| %21 = OpSLessThan %20 %37 %19 | |
| OpBranchConditional %21 %14 %15 | |
| %14 = OpLabel | |
| %24 = OpSGreaterThan %20 %37 %23 | |
| OpSelectionMerge %26 None | |
| OpBranchConditional %24 %25 %26 | |
| %25 = OpLabel | |
| %29 = OpIAdd %6 %37 %28 | |
| OpStore %11 %29 | |
| OpBranch %15 | |
| %26 = OpLabel | |
| OpBranch %16 | |
| %16 = OpLabel | |
| %32 = OpIAdd %6 %37 %28 | |
| OpStore %11 %32 | |
| %102 = OpLoad %50 %100 | |
| %103 = OpIAdd %50 %102 %52 | |
| OpStore %100 %103 | |
| %104 = OpUGreaterThanEqual %20 %102 %28 | |
| OpBranchConditional %104 %15 %13 | |
| %15 = OpLabel | |
| %38 = OpPhi %6 %37 %17 %29 %25 %28 %16 | |
| OpReturnValue %38 | |
| OpFunctionEnd | |
| )"; | |
| ASSERT_TRUE(IsEqual(env, expected, context.get())); | |
| } | |
| TEST(TransformationAddFunctionTest, LoopLimitersAndOpPhi2) { | |
| // This captures the scenario where the loop merge block already has an OpPhi | |
| // with the loop back edge block as a predecessor. | |
| std::string shader = R"( | |
| OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %4 "main" | |
| OpExecutionMode %4 OriginUpperLeft | |
| OpSource ESSL 310 | |
| %2 = OpTypeVoid | |
| %3 = OpTypeFunction %2 | |
| %6 = OpTypeInt 32 1 | |
| %50 = OpTypeInt 32 0 | |
| %51 = OpConstant %50 0 | |
| %52 = OpConstant %50 1 | |
| %53 = OpTypePointer Function %50 | |
| %7 = OpTypeFunction %6 | |
| %10 = OpTypePointer Function %6 | |
| %12 = OpConstant %6 0 | |
| %19 = OpConstant %6 100 | |
| %20 = OpTypeBool | |
| %60 = OpConstantTrue %20 | |
| %23 = OpConstant %6 20 | |
| %28 = OpConstant %6 1 | |
| %4 = OpFunction %2 None %3 | |
| %5 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| std::string donor = R"( | |
| OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %4 "main" | |
| OpExecutionMode %4 OriginUpperLeft | |
| OpSource ESSL 310 | |
| %2 = OpTypeVoid | |
| %3 = OpTypeFunction %2 | |
| %6 = OpTypeInt 32 1 | |
| %50 = OpTypeInt 32 0 | |
| %51 = OpConstant %50 0 | |
| %52 = OpConstant %50 1 | |
| %53 = OpTypePointer Function %50 | |
| %7 = OpTypeFunction %6 | |
| %10 = OpTypePointer Function %6 | |
| %12 = OpConstant %6 0 | |
| %19 = OpConstant %6 100 | |
| %20 = OpTypeBool | |
| %60 = OpConstantTrue %20 | |
| %23 = OpConstant %6 20 | |
| %28 = OpConstant %6 1 | |
| %4 = OpFunction %2 None %3 | |
| %5 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| %8 = OpFunction %6 None %7 | |
| %9 = OpLabel | |
| %11 = OpVariable %10 Function | |
| OpStore %11 %12 | |
| OpBranch %13 | |
| %13 = OpLabel | |
| %37 = OpPhi %6 %12 %9 %32 %16 | |
| OpLoopMerge %15 %16 None | |
| OpBranch %17 | |
| %17 = OpLabel | |
| %21 = OpSLessThan %20 %37 %19 | |
| OpBranchConditional %21 %14 %15 | |
| %14 = OpLabel | |
| %24 = OpSGreaterThan %20 %37 %23 | |
| OpSelectionMerge %26 None | |
| OpBranchConditional %24 %25 %26 | |
| %25 = OpLabel | |
| %29 = OpIAdd %6 %37 %28 | |
| OpStore %11 %29 | |
| OpBranch %15 | |
| %26 = OpLabel | |
| OpBranch %16 | |
| %16 = OpLabel | |
| %32 = OpIAdd %6 %37 %28 | |
| OpStore %11 %32 | |
| OpBranchConditional %60 %15 %13 | |
| %15 = OpLabel | |
| %38 = OpPhi %6 %37 %17 %29 %25 %23 %16 | |
| OpReturnValue %38 | |
| OpFunctionEnd | |
| )"; | |
| const auto env = SPV_ENV_UNIVERSAL_1_4; | |
| const auto consumer = nullptr; | |
| const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); | |
| spvtools::ValidatorOptions validator_options; | |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, | |
| kConsoleMessageConsumer)); | |
| TransformationContext transformation_context( | |
| MakeUnique<FactManager>(context.get()), validator_options); | |
| // Make a sequence of instruction messages corresponding to function %8 in | |
| // |donor|. | |
| std::vector<protobufs::Instruction> instructions = | |
| GetInstructionsForFunction(env, consumer, donor, 8); | |
| protobufs::LoopLimiterInfo loop_limiter_info; | |
| loop_limiter_info.set_loop_header_id(13); | |
| loop_limiter_info.set_load_id(102); | |
| loop_limiter_info.set_increment_id(103); | |
| loop_limiter_info.set_compare_id(104); | |
| loop_limiter_info.set_logical_op_id(105); | |
| TransformationAddFunction transformation(instructions, 100, 28, | |
| {loop_limiter_info}, 0, {}); | |
| ASSERT_TRUE( | |
| transformation.IsApplicable(context.get(), transformation_context)); | |
| ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); | |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, | |
| kConsoleMessageConsumer)); | |
| std::string expected = R"( | |
| OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %4 "main" | |
| OpExecutionMode %4 OriginUpperLeft | |
| OpSource ESSL 310 | |
| %2 = OpTypeVoid | |
| %3 = OpTypeFunction %2 | |
| %6 = OpTypeInt 32 1 | |
| %50 = OpTypeInt 32 0 | |
| %51 = OpConstant %50 0 | |
| %52 = OpConstant %50 1 | |
| %53 = OpTypePointer Function %50 | |
| %7 = OpTypeFunction %6 | |
| %10 = OpTypePointer Function %6 | |
| %12 = OpConstant %6 0 | |
| %19 = OpConstant %6 100 | |
| %20 = OpTypeBool | |
| %60 = OpConstantTrue %20 | |
| %23 = OpConstant %6 20 | |
| %28 = OpConstant %6 1 | |
| %4 = OpFunction %2 None %3 | |
| %5 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| %8 = OpFunction %6 None %7 | |
| %9 = OpLabel | |
| %100 = OpVariable %53 Function %51 | |
| %11 = OpVariable %10 Function | |
| OpStore %11 %12 | |
| OpBranch %13 | |
| %13 = OpLabel | |
| %37 = OpPhi %6 %12 %9 %32 %16 | |
| OpLoopMerge %15 %16 None | |
| OpBranch %17 | |
| %17 = OpLabel | |
| %21 = OpSLessThan %20 %37 %19 | |
| OpBranchConditional %21 %14 %15 | |
| %14 = OpLabel | |
| %24 = OpSGreaterThan %20 %37 %23 | |
| OpSelectionMerge %26 None | |
| OpBranchConditional %24 %25 %26 | |
| %25 = OpLabel | |
| %29 = OpIAdd %6 %37 %28 | |
| OpStore %11 %29 | |
| OpBranch %15 | |
| %26 = OpLabel | |
| OpBranch %16 | |
| %16 = OpLabel | |
| %32 = OpIAdd %6 %37 %28 | |
| OpStore %11 %32 | |
| %102 = OpLoad %50 %100 | |
| %103 = OpIAdd %50 %102 %52 | |
| OpStore %100 %103 | |
| %104 = OpUGreaterThanEqual %20 %102 %28 | |
| %105 = OpLogicalOr %20 %60 %104 | |
| OpBranchConditional %105 %15 %13 | |
| %15 = OpLabel | |
| %38 = OpPhi %6 %37 %17 %29 %25 %23 %16 | |
| OpReturnValue %38 | |
| OpFunctionEnd | |
| )"; | |
| ASSERT_TRUE(IsEqual(env, expected, context.get())); | |
| } | |
| TEST(TransformationAddFunctionTest, StaticallyOutOfBoundsArrayAccess) { | |
| std::string shader = R"( | |
| OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %4 "main" | |
| OpExecutionMode %4 OriginUpperLeft | |
| OpSource ESSL 310 | |
| %2 = OpTypeVoid | |
| %3 = OpTypeFunction %2 | |
| %8 = OpTypeInt 32 1 | |
| %9 = OpTypeInt 32 0 | |
| %10 = OpConstant %9 3 | |
| %11 = OpTypeArray %8 %10 | |
| %12 = OpTypePointer Private %11 | |
| %13 = OpVariable %12 Private | |
| %14 = OpConstant %8 3 | |
| %20 = OpConstant %8 2 | |
| %15 = OpConstant %8 1 | |
| %21 = OpTypeBool | |
| %16 = OpTypePointer Private %8 | |
| %4 = OpFunction %2 None %3 | |
| %5 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| std::string donor = R"( | |
| OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %4 "main" | |
| OpExecutionMode %4 OriginUpperLeft | |
| OpSource ESSL 310 | |
| %2 = OpTypeVoid | |
| %3 = OpTypeFunction %2 | |
| %8 = OpTypeInt 32 1 | |
| %9 = OpTypeInt 32 0 | |
| %10 = OpConstant %9 3 | |
| %11 = OpTypeArray %8 %10 | |
| %12 = OpTypePointer Private %11 | |
| %13 = OpVariable %12 Private | |
| %14 = OpConstant %8 3 | |
| %15 = OpConstant %8 1 | |
| %16 = OpTypePointer Private %8 | |
| %4 = OpFunction %2 None %3 | |
| %5 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| %6 = OpFunction %2 None %3 | |
| %7 = OpLabel | |
| %17 = OpAccessChain %16 %13 %14 | |
| OpStore %17 %15 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| const auto env = SPV_ENV_UNIVERSAL_1_4; | |
| const auto consumer = nullptr; | |
| const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); | |
| spvtools::ValidatorOptions validator_options; | |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, | |
| kConsoleMessageConsumer)); | |
| TransformationContext transformation_context( | |
| MakeUnique<FactManager>(context.get()), validator_options); | |
| // Make a sequence of instruction messages corresponding to function %6 in | |
| // |donor|. | |
| std::vector<protobufs::Instruction> instructions = | |
| GetInstructionsForFunction(env, consumer, donor, 6); | |
| TransformationAddFunction add_livesafe_function( | |
| instructions, 0, 0, {}, 0, {MakeAccessClampingInfo(17, {{100, 101}})}); | |
| ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(), | |
| transformation_context)); | |
| ApplyAndCheckFreshIds(add_livesafe_function, context.get(), | |
| &transformation_context); | |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, | |
| kConsoleMessageConsumer)); | |
| std::string expected = R"( | |
| OpCapability Shader | |
| %1 = OpExtInstImport "GLSL.std.450" | |
| OpMemoryModel Logical GLSL450 | |
| OpEntryPoint Fragment %4 "main" | |
| OpExecutionMode %4 OriginUpperLeft | |
| OpSource ESSL 310 | |
| %2 = OpTypeVoid | |
| %3 = OpTypeFunction %2 | |
| %8 = OpTypeInt 32 1 | |
| %9 = OpTypeInt 32 0 | |
| %10 = OpConstant %9 3 | |
| %11 = OpTypeArray %8 %10 | |
| %12 = OpTypePointer Private %11 | |
| %13 = OpVariable %12 Private | |
| %14 = OpConstant %8 3 | |
| %20 = OpConstant %8 2 | |
| %15 = OpConstant %8 1 | |
| %21 = OpTypeBool | |
| %16 = OpTypePointer Private %8 | |
| %4 = OpFunction %2 None %3 | |
| %5 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| %6 = OpFunction %2 None %3 | |
| %7 = OpLabel | |
| %100 = OpULessThanEqual %21 %14 %20 | |
| %101 = OpSelect %8 %100 %14 %20 | |
| %17 = OpAccessChain %16 %13 %101 | |
| OpStore %17 %15 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| ASSERT_TRUE(IsEqual(env, expected, context.get())); | |
| } | |
| } // namespace | |
| } // namespace fuzz | |
| } // namespace spvtools |