diff --git a/source/fuzz/transformation_outline_function.cpp b/source/fuzz/transformation_outline_function.cpp index 39a4b0142..d84545a29 100644 --- a/source/fuzz/transformation_outline_function.cpp +++ b/source/fuzz/transformation_outline_function.cpp @@ -198,10 +198,23 @@ bool TransformationOutlineFunction::IsApplicable( for (auto& block : *entry_block->GetParent()) { if (&block == exit_block) { // It is OK (and typically expected) for the exit block of the region to - // have successors outside the region. It is also OK for the exit block - // to head a structured control flow construct - the block containing the - // call to the outlined function will end up heading this construct if - // outlining takes place. + // have successors outside the region. + // + // It is also OK for the exit block to head a structured control flow + // construct - the block containing the call to the outlined function will + // end up heading this construct if outlining takes place. However, we + // must ensure that if the exit block heads a loop, the continue target + // for this loop is outside the region. + if (auto loop_merge = block.GetLoopMergeInst()) { + // The exit block heads a loop + auto continue_target = + ir_context->cfg()->block(loop_merge->GetSingleWordOperand(1)); + if (region_set.count(continue_target)) { + // The continue target for the loop is in the region. + return false; + } + } + continue; } diff --git a/test/fuzz/transformation_outline_function_test.cpp b/test/fuzz/transformation_outline_function_test.cpp index 4b0f5ea7e..01bba317a 100644 --- a/test/fuzz/transformation_outline_function_test.cpp +++ b/test/fuzz/transformation_outline_function_test.cpp @@ -2465,7 +2465,6 @@ TEST(TransformationOutlineFunctionTest, // This checks that we cannot outline a region of code if it produces a // pointer result id that gets used outside the region. This avoids creating // a struct with a pointer member. - std::string shader = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" @@ -2520,6 +2519,66 @@ TEST(TransformationOutlineFunctionTest, transformation.IsApplicable(context.get(), transformation_context)); } +TEST(TransformationOutlineFunctionTest, + ExitBlockHeadsLoopButContinueConstructIsInRegion) { + // This checks that it is not possible outline a region that ends in a loop + // head if the continue target for the loop is inside the region. + 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 + %15 = OpTypeInt 32 1 + %35 = OpTypeBool + %39 = OpConstant %15 1 + %40 = OpConstantTrue %35 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %22 + %22 = OpLabel + OpBranch %23 + %23 = OpLabel + %24 = OpPhi %15 %39 %22 %39 %25 + OpLoopMerge %26 %25 None + OpBranchConditional %40 %25 %26 + %25 = OpLabel + OpBranch %23 + %26 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); + + TransformationOutlineFunction transformation( + /*entry_block*/ 22, + /*exit_block*/ 23, + /*new_function_struct_return_type_id*/ 200, + /*new_function_type_id*/ 201, + /*new_function_id*/ 202, + /*new_function_region_entry_block*/ 203, + /*new_caller_result_id*/ 204, + /*new_callee_result_id*/ 205, + /*input_id_to_fresh_id*/ {}, + /*output_id_to_fresh_id*/ {}); + + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); +} + TEST(TransformationOutlineFunctionTest, Miscellaneous1) { // This tests outlining of some non-trivial code.