| // 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 "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); | |
| assert(IsValid(env, donor_context.get()) && "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 |fact_manager| 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 FactManager& fact_manager, | |
| 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, &fact_manager, | |
| &found_non_irrelevant_parameter](opt::Instruction* inst) { | |
| if (context->get_def_use_mgr()->GetDef(inst->type_id())->opcode() == | |
| SpvOpTypePointer && | |
| !fact_manager.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() != SpvOpVariable) { | |
| // 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) == | |
| fact_manager.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); | |
| ASSERT_TRUE(IsValid(env, context.get())); | |
| FactManager fact_manager; | |
| TransformationAddFunction transformation1(std::vector<protobufs::Instruction>( | |
| {MakeInstructionMessage( | |
| SpvOpFunction, 8, 13, | |
| {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, {SpvFunctionControlMaskNone}}, | |
| {SPV_OPERAND_TYPE_ID, {10}}}), | |
| MakeInstructionMessage(SpvOpFunctionParameter, 7, 11, {}), | |
| MakeInstructionMessage(SpvOpFunctionParameter, 9, 12, {}), | |
| MakeInstructionMessage(SpvOpLabel, 0, 14, {}), | |
| MakeInstructionMessage( | |
| SpvOpVariable, 9, 17, | |
| {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}}), | |
| MakeInstructionMessage( | |
| SpvOpVariable, 7, 19, | |
| {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}}), | |
| MakeInstructionMessage( | |
| SpvOpStore, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {17}}, {SPV_OPERAND_TYPE_ID, {18}}}), | |
| MakeInstructionMessage( | |
| SpvOpStore, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {19}}, {SPV_OPERAND_TYPE_ID, {20}}}), | |
| MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {21}}}), | |
| MakeInstructionMessage(SpvOpLabel, 0, 21, {}), | |
| MakeInstructionMessage( | |
| SpvOpLoopMerge, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {23}}, | |
| {SPV_OPERAND_TYPE_ID, {24}}, | |
| {SPV_OPERAND_TYPE_LOOP_CONTROL, {SpvLoopControlMaskNone}}}), | |
| MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {25}}}), | |
| MakeInstructionMessage(SpvOpLabel, 0, 25, {}), | |
| MakeInstructionMessage(SpvOpLoad, 6, 26, {{SPV_OPERAND_TYPE_ID, {19}}}), | |
| MakeInstructionMessage(SpvOpLoad, 6, 27, {{SPV_OPERAND_TYPE_ID, {11}}}), | |
| MakeInstructionMessage( | |
| SpvOpSLessThan, 28, 29, | |
| {{SPV_OPERAND_TYPE_ID, {26}}, {SPV_OPERAND_TYPE_ID, {27}}}), | |
| MakeInstructionMessage(SpvOpBranchConditional, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {29}}, | |
| {SPV_OPERAND_TYPE_ID, {22}}, | |
| {SPV_OPERAND_TYPE_ID, {23}}}), | |
| MakeInstructionMessage(SpvOpLabel, 0, 22, {}), | |
| MakeInstructionMessage(SpvOpLoad, 8, 30, {{SPV_OPERAND_TYPE_ID, {12}}}), | |
| MakeInstructionMessage(SpvOpLoad, 6, 31, {{SPV_OPERAND_TYPE_ID, {19}}}), | |
| MakeInstructionMessage(SpvOpConvertSToF, 8, 32, | |
| {{SPV_OPERAND_TYPE_ID, {31}}}), | |
| MakeInstructionMessage( | |
| SpvOpFMul, 8, 33, | |
| {{SPV_OPERAND_TYPE_ID, {30}}, {SPV_OPERAND_TYPE_ID, {32}}}), | |
| MakeInstructionMessage(SpvOpLoad, 8, 34, {{SPV_OPERAND_TYPE_ID, {17}}}), | |
| MakeInstructionMessage( | |
| SpvOpFAdd, 8, 35, | |
| {{SPV_OPERAND_TYPE_ID, {34}}, {SPV_OPERAND_TYPE_ID, {33}}}), | |
| MakeInstructionMessage( | |
| SpvOpStore, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {17}}, {SPV_OPERAND_TYPE_ID, {35}}}), | |
| MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {24}}}), | |
| MakeInstructionMessage(SpvOpLabel, 0, 24, {}), | |
| MakeInstructionMessage(SpvOpLoad, 6, 36, {{SPV_OPERAND_TYPE_ID, {19}}}), | |
| MakeInstructionMessage( | |
| SpvOpIAdd, 6, 38, | |
| {{SPV_OPERAND_TYPE_ID, {36}}, {SPV_OPERAND_TYPE_ID, {37}}}), | |
| MakeInstructionMessage( | |
| SpvOpStore, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {19}}, {SPV_OPERAND_TYPE_ID, {38}}}), | |
| MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {21}}}), | |
| MakeInstructionMessage(SpvOpLabel, 0, 23, {}), | |
| MakeInstructionMessage(SpvOpLoad, 8, 39, {{SPV_OPERAND_TYPE_ID, {17}}}), | |
| MakeInstructionMessage(SpvOpReturnValue, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {39}}}), | |
| MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {})})); | |
| ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager)); | |
| transformation1.Apply(context.get(), &fact_manager); | |
| ASSERT_TRUE(IsValid(env, context.get())); | |
| 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(fact_manager.BlockIsDead(14)); | |
| ASSERT_TRUE(fact_manager.BlockIsDead(21)); | |
| ASSERT_TRUE(fact_manager.BlockIsDead(22)); | |
| ASSERT_TRUE(fact_manager.BlockIsDead(23)); | |
| ASSERT_TRUE(fact_manager.BlockIsDead(24)); | |
| ASSERT_TRUE(fact_manager.BlockIsDead(25)); | |
| TransformationAddFunction transformation2(std::vector<protobufs::Instruction>( | |
| {MakeInstructionMessage( | |
| SpvOpFunction, 2, 15, | |
| {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, {SpvFunctionControlMaskNone}}, | |
| {SPV_OPERAND_TYPE_ID, {3}}}), | |
| MakeInstructionMessage(SpvOpLabel, 0, 16, {}), | |
| MakeInstructionMessage( | |
| SpvOpVariable, 7, 44, | |
| {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}}), | |
| MakeInstructionMessage( | |
| SpvOpVariable, 9, 45, | |
| {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}}), | |
| MakeInstructionMessage( | |
| SpvOpVariable, 7, 48, | |
| {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}}), | |
| MakeInstructionMessage( | |
| SpvOpVariable, 9, 49, | |
| {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}}), | |
| MakeInstructionMessage( | |
| SpvOpStore, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {44}}, {SPV_OPERAND_TYPE_ID, {20}}}), | |
| MakeInstructionMessage( | |
| SpvOpStore, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {45}}, {SPV_OPERAND_TYPE_ID, {18}}}), | |
| MakeInstructionMessage(SpvOpFunctionCall, 8, 46, | |
| {{SPV_OPERAND_TYPE_ID, {13}}, | |
| {SPV_OPERAND_TYPE_ID, {44}}, | |
| {SPV_OPERAND_TYPE_ID, {45}}}), | |
| MakeInstructionMessage( | |
| SpvOpStore, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {48}}, {SPV_OPERAND_TYPE_ID, {37}}}), | |
| MakeInstructionMessage( | |
| SpvOpStore, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {49}}, {SPV_OPERAND_TYPE_ID, {47}}}), | |
| MakeInstructionMessage(SpvOpFunctionCall, 8, 50, | |
| {{SPV_OPERAND_TYPE_ID, {13}}, | |
| {SPV_OPERAND_TYPE_ID, {48}}, | |
| {SPV_OPERAND_TYPE_ID, {49}}}), | |
| MakeInstructionMessage( | |
| SpvOpFAdd, 8, 51, | |
| {{SPV_OPERAND_TYPE_ID, {46}}, {SPV_OPERAND_TYPE_ID, {50}}}), | |
| MakeInstructionMessage( | |
| SpvOpStore, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {43}}, {SPV_OPERAND_TYPE_ID, {51}}}), | |
| MakeInstructionMessage(SpvOpReturn, 0, 0, {}), | |
| MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {})})); | |
| ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager)); | |
| transformation2.Apply(context.get(), &fact_manager); | |
| ASSERT_TRUE(IsValid(env, context.get())); | |
| 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(fact_manager.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); | |
| ASSERT_TRUE(IsValid(env, context.get())); | |
| FactManager fact_manager; | |
| // No instructions | |
| ASSERT_FALSE( | |
| TransformationAddFunction(std::vector<protobufs::Instruction>({})) | |
| .IsApplicable(context.get(), fact_manager)); | |
| // No function begin | |
| ASSERT_FALSE( | |
| TransformationAddFunction( | |
| std::vector<protobufs::Instruction>( | |
| {MakeInstructionMessage(SpvOpFunctionParameter, 7, 11, {}), | |
| MakeInstructionMessage(SpvOpFunctionParameter, 9, 12, {}), | |
| MakeInstructionMessage(SpvOpLabel, 0, 14, {})})) | |
| .IsApplicable(context.get(), fact_manager)); | |
| // No OpLabel | |
| ASSERT_FALSE( | |
| TransformationAddFunction( | |
| std::vector<protobufs::Instruction>( | |
| {MakeInstructionMessage(SpvOpFunction, 8, 13, | |
| {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, | |
| {SpvFunctionControlMaskNone}}, | |
| {SPV_OPERAND_TYPE_ID, {10}}}), | |
| MakeInstructionMessage(SpvOpReturnValue, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {39}}}), | |
| MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {})})) | |
| .IsApplicable(context.get(), fact_manager)); | |
| // Abrupt end of instructions | |
| ASSERT_FALSE(TransformationAddFunction( | |
| std::vector<protobufs::Instruction>({MakeInstructionMessage( | |
| SpvOpFunction, 8, 13, | |
| {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, | |
| {SpvFunctionControlMaskNone}}, | |
| {SPV_OPERAND_TYPE_ID, {10}}})})) | |
| .IsApplicable(context.get(), fact_manager)); | |
| // No function end | |
| ASSERT_FALSE( | |
| TransformationAddFunction( | |
| std::vector<protobufs::Instruction>( | |
| {MakeInstructionMessage(SpvOpFunction, 8, 13, | |
| {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, | |
| {SpvFunctionControlMaskNone}}, | |
| {SPV_OPERAND_TYPE_ID, {10}}}), | |
| MakeInstructionMessage(SpvOpLabel, 0, 14, {}), | |
| MakeInstructionMessage(SpvOpReturnValue, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {39}}})})) | |
| .IsApplicable(context.get(), fact_manager)); | |
| } | |
| 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( | |
| SpvOpFunction, 2, 30, | |
| {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, {SpvFunctionControlMaskNone}}, | |
| {SPV_OPERAND_TYPE_TYPE_ID, {3}}})); | |
| instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 31, {})); | |
| instructions.push_back( | |
| MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {20}}})); | |
| instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 20, {})); | |
| instructions.push_back(MakeInstructionMessage( | |
| SpvOpLoopMerge, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {21}}, | |
| {SPV_OPERAND_TYPE_ID, {22}}, | |
| {SPV_OPERAND_TYPE_LOOP_CONTROL, {SpvLoopControlMaskNone}}})); | |
| instructions.push_back(MakeInstructionMessage(SpvOpBranchConditional, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {12}}, | |
| {SPV_OPERAND_TYPE_ID, {23}}, | |
| {SPV_OPERAND_TYPE_ID, {21}}})); | |
| instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 23, {})); | |
| instructions.push_back(MakeInstructionMessage( | |
| SpvOpLoopMerge, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {25}}, | |
| {SPV_OPERAND_TYPE_ID, {26}}, | |
| {SPV_OPERAND_TYPE_LOOP_CONTROL, {SpvLoopControlMaskNone}}})); | |
| instructions.push_back( | |
| MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {28}}})); | |
| instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 28, {})); | |
| instructions.push_back(MakeInstructionMessage(SpvOpBranchConditional, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {12}}, | |
| {SPV_OPERAND_TYPE_ID, {26}}, | |
| {SPV_OPERAND_TYPE_ID, {25}}})); | |
| instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 26, {})); | |
| instructions.push_back( | |
| MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {23}}})); | |
| instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 25, {})); | |
| instructions.push_back(MakeInstructionMessage( | |
| SpvOpLoopMerge, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {24}}, | |
| {SPV_OPERAND_TYPE_ID, {27}}, | |
| {SPV_OPERAND_TYPE_LOOP_CONTROL, {SpvLoopControlMaskNone}}})); | |
| instructions.push_back(MakeInstructionMessage(SpvOpBranchConditional, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {12}}, | |
| {SPV_OPERAND_TYPE_ID, {24}}, | |
| {SPV_OPERAND_TYPE_ID, {27}}})); | |
| instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 27, {})); | |
| instructions.push_back( | |
| MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {25}}})); | |
| instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 24, {})); | |
| instructions.push_back( | |
| MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {22}}})); | |
| instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 22, {})); | |
| instructions.push_back( | |
| MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {20}}})); | |
| instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 21, {})); | |
| instructions.push_back(MakeInstructionMessage(SpvOpReturn, 0, 0, {})); | |
| instructions.push_back(MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {})); | |
| FactManager fact_manager1; | |
| FactManager fact_manager2; | |
| const auto context1 = BuildModule(env, consumer, shader, kFuzzAssembleOption); | |
| const auto context2 = BuildModule(env, consumer, shader, kFuzzAssembleOption); | |
| ASSERT_TRUE(IsValid(env, context1.get())); | |
| TransformationAddFunction add_dead_function(instructions); | |
| ASSERT_TRUE(add_dead_function.IsApplicable(context1.get(), fact_manager1)); | |
| add_dead_function.Apply(context1.get(), &fact_manager1); | |
| ASSERT_TRUE(IsValid(env, context1.get())); | |
| // The added function should not be deemed livesafe. | |
| ASSERT_FALSE(fact_manager1.FunctionIsLivesafe(30)); | |
| // All variables/parameters in the function should be deemed irrelevant. | |
| ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant( | |
| context1.get(), fact_manager1, 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(), fact_manager2)); | |
| add_livesafe_function.Apply(context2.get(), &fact_manager2); | |
| ASSERT_TRUE(IsValid(env, context2.get())); | |
| // The added function should indeed be deemed livesafe. | |
| ASSERT_TRUE(fact_manager2.FunctionIsLivesafe(30)); | |
| // All variables/parameters in the function should be deemed irrelevant, | |
| // except the loop limiter. | |
| ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant( | |
| context2.get(), fact_manager2, 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( | |
| SpvOpFunction, 2, 10, | |
| {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, {SpvFunctionControlMaskNone}}, | |
| {SPV_OPERAND_TYPE_TYPE_ID, {8}}})); | |
| instructions.push_back( | |
| MakeInstructionMessage(SpvOpFunctionParameter, 7, 9, {})); | |
| instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 11, {})); | |
| instructions.push_back( | |
| MakeInstructionMessage(SpvOpLoad, 6, 12, {{SPV_OPERAND_TYPE_ID, {9}}})); | |
| instructions.push_back(MakeInstructionMessage( | |
| SpvOpIEqual, 14, 15, | |
| {{SPV_OPERAND_TYPE_ID, {12}}, {SPV_OPERAND_TYPE_ID, {13}}})); | |
| instructions.push_back(MakeInstructionMessage( | |
| SpvOpSelectionMerge, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {17}}, | |
| {SPV_OPERAND_TYPE_SELECTION_CONTROL, {SpvSelectionControlMaskNone}}})); | |
| instructions.push_back(MakeInstructionMessage(SpvOpBranchConditional, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {15}}, | |
| {SPV_OPERAND_TYPE_ID, {16}}, | |
| {SPV_OPERAND_TYPE_ID, {17}}})); | |
| instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 16, {})); | |
| instructions.push_back(MakeInstructionMessage(SpvOpUnreachable, 0, 0, {})); | |
| instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 17, {})); | |
| instructions.push_back(MakeInstructionMessage(SpvOpKill, 0, 0, {})); | |
| instructions.push_back(MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {})); | |
| FactManager fact_manager1; | |
| FactManager fact_manager2; | |
| const auto context1 = BuildModule(env, consumer, shader, kFuzzAssembleOption); | |
| const auto context2 = BuildModule(env, consumer, shader, kFuzzAssembleOption); | |
| ASSERT_TRUE(IsValid(env, context1.get())); | |
| TransformationAddFunction add_dead_function(instructions); | |
| ASSERT_TRUE(add_dead_function.IsApplicable(context1.get(), fact_manager1)); | |
| add_dead_function.Apply(context1.get(), &fact_manager1); | |
| ASSERT_TRUE(IsValid(env, context1.get())); | |
| // The added function should not be deemed livesafe. | |
| ASSERT_FALSE(fact_manager1.FunctionIsLivesafe(10)); | |
| // All variables/parameters in the function should be deemed irrelevant. | |
| ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant( | |
| context1.get(), fact_manager1, 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(), fact_manager2)); | |
| add_livesafe_function.Apply(context2.get(), &fact_manager2); | |
| ASSERT_TRUE(IsValid(env, context2.get())); | |
| // The added function should indeed be deemed livesafe. | |
| ASSERT_TRUE(fact_manager2.FunctionIsLivesafe(10)); | |
| // All variables/parameters in the function should be deemed irrelevant. | |
| ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant( | |
| context2.get(), fact_manager2, 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( | |
| SpvOpFunction, 6, 10, | |
| {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, {SpvFunctionControlMaskNone}}, | |
| {SPV_OPERAND_TYPE_TYPE_ID, {50}}})); | |
| instructions.push_back( | |
| MakeInstructionMessage(SpvOpFunctionParameter, 7, 9, {})); | |
| instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 11, {})); | |
| instructions.push_back( | |
| MakeInstructionMessage(SpvOpLoad, 6, 12, {{SPV_OPERAND_TYPE_ID, {9}}})); | |
| instructions.push_back(MakeInstructionMessage( | |
| SpvOpIEqual, 14, 15, | |
| {{SPV_OPERAND_TYPE_ID, {12}}, {SPV_OPERAND_TYPE_ID, {13}}})); | |
| instructions.push_back(MakeInstructionMessage( | |
| SpvOpSelectionMerge, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {17}}, | |
| {SPV_OPERAND_TYPE_SELECTION_CONTROL, {SpvSelectionControlMaskNone}}})); | |
| instructions.push_back(MakeInstructionMessage(SpvOpBranchConditional, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {15}}, | |
| {SPV_OPERAND_TYPE_ID, {16}}, | |
| {SPV_OPERAND_TYPE_ID, {17}}})); | |
| instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 16, {})); | |
| instructions.push_back(MakeInstructionMessage(SpvOpUnreachable, 0, 0, {})); | |
| instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 17, {})); | |
| instructions.push_back(MakeInstructionMessage(SpvOpKill, 0, 0, {})); | |
| instructions.push_back(MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {})); | |
| FactManager fact_manager1; | |
| FactManager fact_manager2; | |
| const auto context1 = BuildModule(env, consumer, shader, kFuzzAssembleOption); | |
| const auto context2 = BuildModule(env, consumer, shader, kFuzzAssembleOption); | |
| ASSERT_TRUE(IsValid(env, context1.get())); | |
| TransformationAddFunction add_dead_function(instructions); | |
| ASSERT_TRUE(add_dead_function.IsApplicable(context1.get(), fact_manager1)); | |
| add_dead_function.Apply(context1.get(), &fact_manager1); | |
| ASSERT_TRUE(IsValid(env, context1.get())); | |
| // The added function should not be deemed livesafe. | |
| ASSERT_FALSE(fact_manager1.FunctionIsLivesafe(10)); | |
| // All variables/parameters in the function should be deemed irrelevant. | |
| ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant( | |
| context1.get(), fact_manager1, 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(), fact_manager2)); | |
| add_livesafe_function.Apply(context2.get(), &fact_manager2); | |
| ASSERT_TRUE(IsValid(env, context2.get())); | |
| // The added function should indeed be deemed livesafe. | |
| ASSERT_TRUE(fact_manager2.FunctionIsLivesafe(10)); | |
| // All variables/parameters in the function should be deemed irrelevant. | |
| ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant( | |
| context2.get(), fact_manager2, 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( | |
| SpvOpFunction, 2, 12, | |
| {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, {SpvFunctionControlMaskNone}}, | |
| {SPV_OPERAND_TYPE_TYPE_ID, {8}}})); | |
| instructions.push_back( | |
| MakeInstructionMessage(SpvOpFunctionParameter, 7, 9, {})); | |
| instructions.push_back( | |
| MakeInstructionMessage(SpvOpFunctionParameter, 102, 10, {})); | |
| instructions.push_back( | |
| MakeInstructionMessage(SpvOpFunctionParameter, 7, 11, {})); | |
| instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 13, {})); | |
| instructions.push_back(MakeInstructionMessage( | |
| SpvOpVariable, 7, 14, | |
| {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}})); | |
| instructions.push_back(MakeInstructionMessage( | |
| SpvOpVariable, 26, 27, | |
| {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}})); | |
| instructions.push_back( | |
| MakeInstructionMessage(SpvOpLoad, 6, 22, {{SPV_OPERAND_TYPE_ID, {11}}})); | |
| instructions.push_back(MakeInstructionMessage(SpvOpAccessChain, 23, 24, | |
| {{SPV_OPERAND_TYPE_ID, {20}}, | |
| {SPV_OPERAND_TYPE_ID, {21}}, | |
| {SPV_OPERAND_TYPE_ID, {22}}})); | |
| instructions.push_back( | |
| MakeInstructionMessage(SpvOpLoad, 6, 25, {{SPV_OPERAND_TYPE_ID, {24}}})); | |
| instructions.push_back(MakeInstructionMessage( | |
| SpvOpStore, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {14}}, {SPV_OPERAND_TYPE_ID, {25}}})); | |
| instructions.push_back( | |
| MakeInstructionMessage(SpvOpLoad, 15, 28, {{SPV_OPERAND_TYPE_ID, {10}}})); | |
| instructions.push_back(MakeInstructionMessage( | |
| SpvOpAccessChain, 29, 30, | |
| {{SPV_OPERAND_TYPE_ID, {20}}, {SPV_OPERAND_TYPE_ID, {28}}})); | |
| instructions.push_back( | |
| MakeInstructionMessage(SpvOpLoad, 17, 31, {{SPV_OPERAND_TYPE_ID, {30}}})); | |
| instructions.push_back(MakeInstructionMessage( | |
| SpvOpStore, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {27}}, {SPV_OPERAND_TYPE_ID, {31}}})); | |
| instructions.push_back( | |
| MakeInstructionMessage(SpvOpLoad, 6, 32, {{SPV_OPERAND_TYPE_ID, {9}}})); | |
| instructions.push_back(MakeInstructionMessage( | |
| SpvOpInBoundsAccessChain, 7, 34, | |
| {{SPV_OPERAND_TYPE_ID, {27}}, {SPV_OPERAND_TYPE_ID, {32}}})); | |
| instructions.push_back(MakeInstructionMessage( | |
| SpvOpStore, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {34}}, {SPV_OPERAND_TYPE_ID, {33}}})); | |
| instructions.push_back( | |
| MakeInstructionMessage(SpvOpLoad, 6, 39, {{SPV_OPERAND_TYPE_ID, {9}}})); | |
| instructions.push_back(MakeInstructionMessage( | |
| SpvOpAccessChain, 23, 40, | |
| {{SPV_OPERAND_TYPE_ID, {38}}, {SPV_OPERAND_TYPE_ID, {33}}})); | |
| instructions.push_back( | |
| MakeInstructionMessage(SpvOpLoad, 6, 41, {{SPV_OPERAND_TYPE_ID, {40}}})); | |
| instructions.push_back(MakeInstructionMessage( | |
| SpvOpInBoundsAccessChain, 23, 42, | |
| {{SPV_OPERAND_TYPE_ID, {38}}, {SPV_OPERAND_TYPE_ID, {39}}})); | |
| instructions.push_back(MakeInstructionMessage( | |
| SpvOpStore, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {42}}, {SPV_OPERAND_TYPE_ID, {41}}})); | |
| instructions.push_back( | |
| MakeInstructionMessage(SpvOpLoad, 15, 43, {{SPV_OPERAND_TYPE_ID, {10}}})); | |
| instructions.push_back( | |
| MakeInstructionMessage(SpvOpLoad, 6, 44, {{SPV_OPERAND_TYPE_ID, {11}}})); | |
| instructions.push_back( | |
| MakeInstructionMessage(SpvOpLoad, 6, 45, {{SPV_OPERAND_TYPE_ID, {9}}})); | |
| instructions.push_back( | |
| MakeInstructionMessage(SpvOpLoad, 15, 46, {{SPV_OPERAND_TYPE_ID, {10}}})); | |
| instructions.push_back(MakeInstructionMessage( | |
| SpvOpIAdd, 6, 47, | |
| {{SPV_OPERAND_TYPE_ID, {45}}, {SPV_OPERAND_TYPE_ID, {46}}})); | |
| instructions.push_back(MakeInstructionMessage( | |
| SpvOpAccessChain, 23, 48, | |
| {{SPV_OPERAND_TYPE_ID, {38}}, {SPV_OPERAND_TYPE_ID, {47}}})); | |
| instructions.push_back( | |
| MakeInstructionMessage(SpvOpLoad, 6, 49, {{SPV_OPERAND_TYPE_ID, {48}}})); | |
| instructions.push_back(MakeInstructionMessage(SpvOpInBoundsAccessChain, 23, | |
| 50, | |
| {{SPV_OPERAND_TYPE_ID, {20}}, | |
| {SPV_OPERAND_TYPE_ID, {43}}, | |
| {SPV_OPERAND_TYPE_ID, {44}}})); | |
| instructions.push_back( | |
| MakeInstructionMessage(SpvOpLoad, 6, 51, {{SPV_OPERAND_TYPE_ID, {50}}})); | |
| instructions.push_back(MakeInstructionMessage( | |
| SpvOpIAdd, 6, 52, | |
| {{SPV_OPERAND_TYPE_ID, {51}}, {SPV_OPERAND_TYPE_ID, {49}}})); | |
| instructions.push_back(MakeInstructionMessage(SpvOpAccessChain, 23, 53, | |
| {{SPV_OPERAND_TYPE_ID, {20}}, | |
| {SPV_OPERAND_TYPE_ID, {43}}, | |
| {SPV_OPERAND_TYPE_ID, {44}}})); | |
| instructions.push_back(MakeInstructionMessage( | |
| SpvOpStore, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {53}}, {SPV_OPERAND_TYPE_ID, {52}}})); | |
| instructions.push_back( | |
| MakeInstructionMessage(SpvOpLoad, 15, 58, {{SPV_OPERAND_TYPE_ID, {10}}})); | |
| instructions.push_back( | |
| MakeInstructionMessage(SpvOpLoad, 6, 63, {{SPV_OPERAND_TYPE_ID, {11}}})); | |
| instructions.push_back(MakeInstructionMessage(SpvOpAccessChain, 64, 65, | |
| {{SPV_OPERAND_TYPE_ID, {62}}, | |
| {SPV_OPERAND_TYPE_ID, {21}}, | |
| {SPV_OPERAND_TYPE_ID, {63}}})); | |
| instructions.push_back(MakeInstructionMessage(SpvOpAccessChain, 64, 101, | |
| {{SPV_OPERAND_TYPE_ID, {62}}, | |
| {SPV_OPERAND_TYPE_ID, {45}}, | |
| {SPV_OPERAND_TYPE_ID, {46}}})); | |
| instructions.push_back( | |
| MakeInstructionMessage(SpvOpLoad, 54, 66, {{SPV_OPERAND_TYPE_ID, {65}}})); | |
| instructions.push_back(MakeInstructionMessage( | |
| SpvOpAccessChain, 64, 67, | |
| {{SPV_OPERAND_TYPE_ID, {57}}, {SPV_OPERAND_TYPE_ID, {58}}})); | |
| instructions.push_back(MakeInstructionMessage( | |
| SpvOpStore, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {67}}, {SPV_OPERAND_TYPE_ID, {66}}})); | |
| instructions.push_back( | |
| MakeInstructionMessage(SpvOpLoad, 6, 68, {{SPV_OPERAND_TYPE_ID, {9}}})); | |
| instructions.push_back(MakeInstructionMessage( | |
| SpvOpInBoundsAccessChain, 64, 70, | |
| {{SPV_OPERAND_TYPE_ID, {57}}, {SPV_OPERAND_TYPE_ID, {68}}})); | |
| instructions.push_back(MakeInstructionMessage( | |
| SpvOpStore, 0, 0, | |
| {{SPV_OPERAND_TYPE_ID, {70}}, {SPV_OPERAND_TYPE_ID, {69}}})); | |
| instructions.push_back(MakeInstructionMessage(SpvOpReturn, 0, 0, {})); | |
| instructions.push_back(MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {})); | |
| FactManager fact_manager1; | |
| FactManager fact_manager2; | |
| const auto context1 = BuildModule(env, consumer, shader, kFuzzAssembleOption); | |
| const auto context2 = BuildModule(env, consumer, shader, kFuzzAssembleOption); | |
| ASSERT_TRUE(IsValid(env, context1.get())); | |
| TransformationAddFunction add_dead_function(instructions); | |
| ASSERT_TRUE(add_dead_function.IsApplicable(context1.get(), fact_manager1)); | |
| add_dead_function.Apply(context1.get(), &fact_manager1); | |
| ASSERT_TRUE(IsValid(env, context1.get())); | |
| // The function should not be deemed livesafe | |
| ASSERT_FALSE(fact_manager1.FunctionIsLivesafe(12)); | |
| // All variables/parameters in the function should be deemed irrelevant. | |
| ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant( | |
| context1.get(), fact_manager1, 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(), fact_manager2)); | |
| add_livesafe_function.Apply(context2.get(), &fact_manager2); | |
| ASSERT_TRUE(IsValid(env, context2.get())); | |
| // The function should be deemed livesafe | |
| ASSERT_TRUE(fact_manager2.FunctionIsLivesafe(12)); | |
| // All variables/parameters in the function should be deemed irrelevant. | |
| ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant( | |
| context2.get(), fact_manager2, 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( | |
| SpvOpFunction, 2, 8, | |
| {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, {SpvFunctionControlMaskNone}}, | |
| {SPV_OPERAND_TYPE_TYPE_ID, {3}}})); | |
| instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 9, {})); | |
| instructions.push_back(MakeInstructionMessage(SpvOpFunctionCall, 2, 11, | |
| {{SPV_OPERAND_TYPE_ID, {6}}})); | |
| instructions.push_back(MakeInstructionMessage(SpvOpReturn, 0, 0, {})); | |
| instructions.push_back(MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {})); | |
| FactManager fact_manager1; | |
| FactManager fact_manager2; | |
| // Mark function 6 as livesafe. | |
| fact_manager2.AddFactFunctionIsLivesafe(6); | |
| const auto context1 = BuildModule(env, consumer, shader, kFuzzAssembleOption); | |
| const auto context2 = BuildModule(env, consumer, shader, kFuzzAssembleOption); | |
| ASSERT_TRUE(IsValid(env, context1.get())); | |
| TransformationAddFunction add_dead_function(instructions); | |
| ASSERT_TRUE(add_dead_function.IsApplicable(context1.get(), fact_manager1)); | |
| add_dead_function.Apply(context1.get(), &fact_manager1); | |
| ASSERT_TRUE(IsValid(env, context1.get())); | |
| // The function should not be deemed livesafe | |
| ASSERT_FALSE(fact_manager1.FunctionIsLivesafe(8)); | |
| // All variables/parameters in the function should be deemed irrelevant. | |
| ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant( | |
| context1.get(), fact_manager1, 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(), fact_manager2)); | |
| add_livesafe_function.Apply(context2.get(), &fact_manager2); | |
| ASSERT_TRUE(IsValid(env, context2.get())); | |
| // The function should be deemed livesafe | |
| ASSERT_TRUE(fact_manager2.FunctionIsLivesafe(8)); | |
| // All variables/parameters in the function should be deemed irrelevant. | |
| ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant( | |
| context2.get(), fact_manager2, 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( | |
| SpvOpFunction, 2, 8, | |
| {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, {SpvFunctionControlMaskNone}}, | |
| {SPV_OPERAND_TYPE_TYPE_ID, {3}}})); | |
| instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 9, {})); | |
| instructions.push_back(MakeInstructionMessage(SpvOpFunctionCall, 2, 11, | |
| {{SPV_OPERAND_TYPE_ID, {6}}})); | |
| instructions.push_back(MakeInstructionMessage(SpvOpReturn, 0, 0, {})); | |
| instructions.push_back(MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {})); | |
| FactManager fact_manager1; | |
| FactManager fact_manager2; | |
| const auto context1 = BuildModule(env, consumer, shader, kFuzzAssembleOption); | |
| const auto context2 = BuildModule(env, consumer, shader, kFuzzAssembleOption); | |
| ASSERT_TRUE(IsValid(env, context1.get())); | |
| TransformationAddFunction add_dead_function(instructions); | |
| ASSERT_TRUE(add_dead_function.IsApplicable(context1.get(), fact_manager1)); | |
| add_dead_function.Apply(context1.get(), &fact_manager1); | |
| ASSERT_TRUE(IsValid(env, context1.get())); | |
| // The function should not be deemed livesafe | |
| ASSERT_FALSE(fact_manager1.FunctionIsLivesafe(8)); | |
| // All variables/parameters in the function should be deemed irrelevant. | |
| ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant( | |
| context1.get(), fact_manager1, 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(), fact_manager2)); | |
| } | |
| 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; | |
| FactManager fact_manager; | |
| const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); | |
| ASSERT_TRUE(IsValid(env, context.get())); | |
| // Make a sequence of instruction messages corresponding to function %8 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(), fact_manager)); | |
| add_livesafe_function.Apply(context.get(), &fact_manager); | |
| ASSERT_TRUE(IsValid(env, context.get())); | |
| 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; | |
| FactManager fact_manager; | |
| const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); | |
| ASSERT_TRUE(IsValid(env, context.get())); | |
| // Make a sequence of instruction messages corresponding to function %8 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(), fact_manager)); | |
| add_livesafe_function.Apply(context.get(), &fact_manager); | |
| ASSERT_TRUE(IsValid(env, context.get())); | |
| 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; | |
| FactManager fact_manager; | |
| const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); | |
| ASSERT_TRUE(IsValid(env, context.get())); | |
| // Make a sequence of instruction messages corresponding to function %8 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(), fact_manager)); | |
| add_livesafe_function.Apply(context.get(), &fact_manager); | |
| ASSERT_TRUE(IsValid(env, context.get())); | |
| 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, InfiniteLoop1) { | |
| 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; | |
| FactManager fact_manager; | |
| const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); | |
| ASSERT_TRUE(IsValid(env, context.get())); | |
| // Make a sequence of instruction messages corresponding to function %8 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(), fact_manager)); | |
| add_livesafe_function.Apply(context.get(), &fact_manager); | |
| ASSERT_TRUE(IsValid(env, context.get())); | |
| 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 | |
| %102 = OpLoad %28 %100 | |
| %103 = OpIAdd %28 %102 %31 | |
| OpStore %100 %103 | |
| %104 = OpUGreaterThanEqual %19 %102 %32 | |
| OpLoopMerge %14 %12 None | |
| OpBranchConditional %104 %14 %12 | |
| %14 = OpLabel | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| ASSERT_TRUE(IsEqual(env, expected, context.get())); | |
| } | |
| 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; | |
| FactManager fact_manager; | |
| const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); | |
| ASSERT_TRUE(IsValid(env, context.get())); | |
| // Make a sequence of instruction messages corresponding to function %8 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(), fact_manager)); | |
| add_livesafe_function.Apply(context.get(), &fact_manager); | |
| ASSERT_TRUE(IsValid(env, context.get())); | |
| 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; | |
| FactManager fact_manager; | |
| const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); | |
| ASSERT_TRUE(IsValid(env, context.get())); | |
| // 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(), fact_manager)); | |
| // 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(), fact_manager)); | |
| with_op_phi_data.Apply(context.get(), &fact_manager); | |
| ASSERT_TRUE(IsValid(env, context.get())); | |
| 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; | |
| FactManager fact_manager; | |
| const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); | |
| ASSERT_TRUE(IsValid(env, context.get())); | |
| // 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(), fact_manager)); | |
| transformation.Apply(context.get(), &fact_manager); | |
| ASSERT_TRUE(IsValid(env, context.get())); | |
| 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())); | |
| } | |
| } // namespace | |
| } // namespace fuzz | |
| } // namespace spvtools |