diff --git a/source/reduce/CMakeLists.txt b/source/reduce/CMakeLists.txt index 2f8c460d..309e89b8 100644 --- a/source/reduce/CMakeLists.txt +++ b/source/reduce/CMakeLists.txt @@ -14,6 +14,8 @@ set(SPIRV_TOOLS_REDUCE_SOURCES change_operand_reduction_opportunity.h change_operand_to_undef_reduction_opportunity.h + merge_blocks_reduction_opportunity.h + merge_blocks_reduction_opportunity_finder.h operand_to_const_reduction_opportunity_finder.h operand_to_undef_reduction_opportunity_finder.h operand_to_dominating_id_reduction_opportunity_finder.h @@ -30,6 +32,8 @@ set(SPIRV_TOOLS_REDUCE_SOURCES change_operand_reduction_opportunity.cpp change_operand_to_undef_reduction_opportunity.cpp + merge_blocks_reduction_opportunity.cpp + merge_blocks_reduction_opportunity_finder.cpp operand_to_const_reduction_opportunity_finder.cpp operand_to_undef_reduction_opportunity_finder.cpp operand_to_dominating_id_reduction_opportunity_finder.cpp diff --git a/source/reduce/merge_blocks_reduction_opportunity.cpp b/source/reduce/merge_blocks_reduction_opportunity.cpp new file mode 100644 index 00000000..8ee4562a --- /dev/null +++ b/source/reduce/merge_blocks_reduction_opportunity.cpp @@ -0,0 +1,67 @@ +// 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 "merge_blocks_reduction_opportunity.h" +#include "source/opt/block_merge_util.h" + +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace reduce { + +using namespace opt; + +MergeBlocksReductionOpportunity::MergeBlocksReductionOpportunity( + IRContext* context, Function* function, BasicBlock* block) { + // Precondition: the terminator has to be OpBranch. + assert(block->terminator()->opcode() == SpvOpBranch); + context_ = context; + function_ = function; + // Get the successor block associated with the OpBranch. + successor_block_ = + context->cfg()->block(block->terminator()->GetSingleWordInOperand(0)); +} + +bool MergeBlocksReductionOpportunity::PreconditionHolds() { + // By construction, it is not possible for the merging of A->B to disable the + // merging of C->D, even when B and C are the same block. + return true; +} + +void MergeBlocksReductionOpportunity::Apply() { + // While the original block that targeted the successor may not exist anymore + // (it might have been merged with another block), some block must exist that + // targets the successor. Find it. + + const auto predecessors = context_->cfg()->preds(successor_block_->id()); + assert(1 == predecessors.size() && + "For a successor to be merged into its predecessor, exactly one " + "predecessor must be present."); + const uint32_t predecessor_id = predecessors[0]; + + for (auto bi = function_->begin(); bi != function_->end(); ++bi) { + if (bi->id() == predecessor_id) { + blockmergeutil::MergeWithSuccessor(context_, function_, bi); + // Block merging changes the control flow graph, so invalidate it. + context_->InvalidateAnalysesExceptFor(IRContext::Analysis::kAnalysisNone); + return; + } + } + + assert(false && + "Unreachable: we should have found a block with the desired id."); +} + +} // namespace reduce +} // namespace spvtools diff --git a/source/reduce/merge_blocks_reduction_opportunity.h b/source/reduce/merge_blocks_reduction_opportunity.h new file mode 100644 index 00000000..aa78f857 --- /dev/null +++ b/source/reduce/merge_blocks_reduction_opportunity.h @@ -0,0 +1,53 @@ +// 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. + +#ifndef SOURCE_REDUCE_MERGE_BLOCKS_REDUCTION_OPPORTUNITY_H_ +#define SOURCE_REDUCE_MERGE_BLOCKS_REDUCTION_OPPORTUNITY_H_ + +#include "reduction_opportunity.h" +#include "source/opt/basic_block.h" +#include "source/opt/function.h" + +namespace spvtools { +namespace reduce { + +// An opportunity to merge two blocks into one. +class MergeBlocksReductionOpportunity : public ReductionOpportunity { + public: + // Creates the opportunity to merge |block| with its successor, where |block| + // is inside |function|, and |context| is the enclosing IR context. + MergeBlocksReductionOpportunity(opt::IRContext* context, + opt::Function* function, + opt::BasicBlock* block); + + bool PreconditionHolds() override; + + protected: + void Apply() override; + + private: + opt::IRContext* context_; + opt::Function* function_; + + // Rather than holding on to the block that can be merged with its successor, + // we hold on to its successor. This is because the predecessor block might + // get merged with *its* predecessor, and so will no longer exist, while the + // successor will continue to exist until this opportunity gets applied. + opt::BasicBlock* successor_block_; +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_MERGE_BLOCKS_REDUCTION_OPPORTUNITY_H_ diff --git a/source/reduce/merge_blocks_reduction_opportunity_finder.cpp b/source/reduce/merge_blocks_reduction_opportunity_finder.cpp new file mode 100644 index 00000000..0546e1ff --- /dev/null +++ b/source/reduce/merge_blocks_reduction_opportunity_finder.cpp @@ -0,0 +1,48 @@ +// 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/reduce/merge_blocks_reduction_opportunity_finder.h" +#include "source/opt/block_merge_util.h" +#include "source/reduce/merge_blocks_reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +using namespace opt; + +std::string MergeBlocksReductionOpportunityFinder::GetName() const { + return "MergeBlocksReductionOpportunityFinder"; +} + +std::vector> +MergeBlocksReductionOpportunityFinder::GetAvailableOpportunities( + opt::IRContext* context) const { + std::vector> result; + + // Consider every block in every function. + for (auto& function : *context->module()) { + for (auto& block : function) { + // See whether it is possible to merge this block with its successor. + if (blockmergeutil::CanMergeWithSuccessor(context, &block)) { + // It is, so record an opportunity to do this. + result.push_back(spvtools::MakeUnique( + context, &function, &block)); + } + } + } + return result; +} + +} // namespace reduce +} // namespace spvtools diff --git a/source/reduce/merge_blocks_reduction_opportunity_finder.h b/source/reduce/merge_blocks_reduction_opportunity_finder.h new file mode 100644 index 00000000..dbf82fec --- /dev/null +++ b/source/reduce/merge_blocks_reduction_opportunity_finder.h @@ -0,0 +1,42 @@ +// 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. + +#ifndef SOURCE_REDUCE_MERGE_BLOCKS_REDUCTION_OPPORTUNITY_FINDER_H_ +#define SOURCE_REDUCE_MERGE_BLOCKS_REDUCTION_OPPORTUNITY_FINDER_H_ + +#include "source/reduce/reduction_opportunity_finder.h" + +namespace spvtools { +namespace reduce { + +// A finder of opportunities to merge blocks together. +class MergeBlocksReductionOpportunityFinder + : public ReductionOpportunityFinder { + public: + MergeBlocksReductionOpportunityFinder() = default; + + ~MergeBlocksReductionOpportunityFinder() override = default; + + std::string GetName() const final; + + std::vector> GetAvailableOpportunities( + opt::IRContext* context) const final; + + private: +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_MERGE_BLOCKS_REDUCTION_OPPORTUNITY_FINDER_H_ diff --git a/test/reduce/CMakeLists.txt b/test/reduce/CMakeLists.txt index b1fdaff4..c1c9d767 100644 --- a/test/reduce/CMakeLists.txt +++ b/test/reduce/CMakeLists.txt @@ -13,7 +13,8 @@ # limitations under the License. add_spvtools_unittest(TARGET reduce - SRCS operand_to_constant_test.cpp + SRCS merge_blocks_test.cpp + operand_to_constant_test.cpp operand_to_undef_test.cpp operand_to_dominating_id_test.cpp reduce_test_util.cpp diff --git a/test/reduce/merge_blocks_test.cpp b/test/reduce/merge_blocks_test.cpp new file mode 100644 index 00000000..e57578e3 --- /dev/null +++ b/test/reduce/merge_blocks_test.cpp @@ -0,0 +1,513 @@ +// 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 "reduce_test_util.h" +#include "source/opt/build_module.h" +#include "source/reduce/merge_blocks_reduction_opportunity_finder.h" +#include "source/reduce/reduction_opportunity.h" + +namespace spvtools { +namespace reduce { +namespace { + +TEST(MergeBlocksReductionPassTest, BasicCheck) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "x" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 1 + %10 = OpConstant %6 2 + %11 = OpConstant %6 3 + %12 = OpConstant %6 4 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + OpBranch %13 + %13 = OpLabel + OpStore %8 %9 + OpBranch %14 + %14 = OpLabel + OpStore %8 %10 + OpBranch %15 + %15 = OpLabel + OpStore %8 %11 + OpBranch %16 + %16 = OpLabel + OpStore %8 %12 + OpBranch %17 + %17 = OpLabel + OpReturn + OpFunctionEnd + )"; + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, shader, kReduceAssembleOption); + const auto ops = + MergeBlocksReductionOpportunityFinder().GetAvailableOpportunities( + context.get()); + ASSERT_EQ(5, ops.size()); + + // Try order 3, 0, 2, 4, 1 + + ASSERT_TRUE(ops[3]->PreconditionHolds()); + ops[3]->TryToApply(); + + std::string after_op_3 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "x" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 1 + %10 = OpConstant %6 2 + %11 = OpConstant %6 3 + %12 = OpConstant %6 4 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + OpBranch %13 + %13 = OpLabel + OpStore %8 %9 + OpBranch %14 + %14 = OpLabel + OpStore %8 %10 + OpBranch %15 + %15 = OpLabel + OpStore %8 %11 + OpStore %8 %12 + OpBranch %17 + %17 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CheckEqual(env, after_op_3, context.get()); + + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + + std::string after_op_0 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "x" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 1 + %10 = OpConstant %6 2 + %11 = OpConstant %6 3 + %12 = OpConstant %6 4 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %14 + %14 = OpLabel + OpStore %8 %10 + OpBranch %15 + %15 = OpLabel + OpStore %8 %11 + OpStore %8 %12 + OpBranch %17 + %17 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CheckEqual(env, after_op_0, context.get()); + + ASSERT_TRUE(ops[2]->PreconditionHolds()); + ops[2]->TryToApply(); + + std::string after_op_2 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "x" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 1 + %10 = OpConstant %6 2 + %11 = OpConstant %6 3 + %12 = OpConstant %6 4 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %14 + %14 = OpLabel + OpStore %8 %10 + OpStore %8 %11 + OpStore %8 %12 + OpBranch %17 + %17 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CheckEqual(env, after_op_2, context.get()); + + ASSERT_TRUE(ops[4]->PreconditionHolds()); + ops[4]->TryToApply(); + + std::string after_op_4 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "x" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 1 + %10 = OpConstant %6 2 + %11 = OpConstant %6 3 + %12 = OpConstant %6 4 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %14 + %14 = OpLabel + OpStore %8 %10 + OpStore %8 %11 + OpStore %8 %12 + OpReturn + OpFunctionEnd + )"; + + CheckEqual(env, after_op_4, context.get()); + + ASSERT_TRUE(ops[1]->PreconditionHolds()); + ops[1]->TryToApply(); + + std::string after_op_1 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "x" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 1 + %10 = OpConstant %6 2 + %11 = OpConstant %6 3 + %12 = OpConstant %6 4 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + OpStore %8 %9 + OpStore %8 %10 + OpStore %8 %11 + OpStore %8 %12 + OpReturn + OpFunctionEnd + )"; + + CheckEqual(env, after_op_1, context.get()); +} + +TEST(MergeBlocksReductionPassTest, Loops) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "x" + OpName %10 "i" + OpName %29 "i" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 1 + %11 = OpConstant %6 0 + %18 = OpConstant %6 10 + %19 = OpTypeBool + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %29 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %45 + %45 = OpLabel + OpStore %10 %11 + OpBranch %12 + %12 = OpLabel + OpLoopMerge %14 %15 None + OpBranch %16 + %16 = OpLabel + %17 = OpLoad %6 %10 + OpBranch %46 + %46 = OpLabel + %20 = OpSLessThan %19 %17 %18 + OpBranchConditional %20 %13 %14 + %13 = OpLabel + %21 = OpLoad %6 %10 + OpBranch %47 + %47 = OpLabel + %22 = OpLoad %6 %8 + %23 = OpIAdd %6 %22 %21 + OpStore %8 %23 + %24 = OpLoad %6 %10 + %25 = OpLoad %6 %8 + %26 = OpIAdd %6 %25 %24 + OpStore %8 %26 + OpBranch %48 + %48 = OpLabel + OpBranch %15 + %15 = OpLabel + %27 = OpLoad %6 %10 + %28 = OpIAdd %6 %27 %9 + OpStore %10 %28 + OpBranch %12 + %14 = OpLabel + OpStore %29 %11 + OpBranch %49 + %49 = OpLabel + OpBranch %30 + %30 = OpLabel + OpLoopMerge %32 %33 None + OpBranch %34 + %34 = OpLabel + %35 = OpLoad %6 %29 + %36 = OpSLessThan %19 %35 %18 + OpBranch %50 + %50 = OpLabel + OpBranchConditional %36 %31 %32 + %31 = OpLabel + %37 = OpLoad %6 %29 + %38 = OpLoad %6 %8 + %39 = OpIAdd %6 %38 %37 + OpStore %8 %39 + %40 = OpLoad %6 %29 + %41 = OpLoad %6 %8 + %42 = OpIAdd %6 %41 %40 + OpStore %8 %42 + OpBranch %33 + %33 = OpLabel + %43 = OpLoad %6 %29 + %44 = OpIAdd %6 %43 %9 + OpBranch %51 + %51 = OpLabel + OpStore %29 %44 + OpBranch %30 + %32 = OpLabel + OpReturn + OpFunctionEnd + )"; + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, shader, kReduceAssembleOption); + const auto ops = + MergeBlocksReductionOpportunityFinder().GetAvailableOpportunities( + context.get()); + ASSERT_EQ(11, ops.size()); + + for (auto& ri : ops) { + ASSERT_TRUE(ri->PreconditionHolds()); + ri->TryToApply(); + } + + std::string after = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "x" + OpName %10 "i" + OpName %29 "i" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 1 + %11 = OpConstant %6 0 + %18 = OpConstant %6 10 + %19 = OpTypeBool + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %29 = OpVariable %7 Function + OpStore %8 %9 + OpStore %10 %11 + OpBranch %12 + %12 = OpLabel + %17 = OpLoad %6 %10 + %20 = OpSLessThan %19 %17 %18 + OpLoopMerge %14 %13 None + OpBranchConditional %20 %13 %14 + %13 = OpLabel + %21 = OpLoad %6 %10 + %22 = OpLoad %6 %8 + %23 = OpIAdd %6 %22 %21 + OpStore %8 %23 + %24 = OpLoad %6 %10 + %25 = OpLoad %6 %8 + %26 = OpIAdd %6 %25 %24 + OpStore %8 %26 + %27 = OpLoad %6 %10 + %28 = OpIAdd %6 %27 %9 + OpStore %10 %28 + OpBranch %12 + %14 = OpLabel + OpStore %29 %11 + OpBranch %30 + %30 = OpLabel + %35 = OpLoad %6 %29 + %36 = OpSLessThan %19 %35 %18 + OpLoopMerge %32 %31 None + OpBranchConditional %36 %31 %32 + %31 = OpLabel + %37 = OpLoad %6 %29 + %38 = OpLoad %6 %8 + %39 = OpIAdd %6 %38 %37 + OpStore %8 %39 + %40 = OpLoad %6 %29 + %41 = OpLoad %6 %8 + %42 = OpIAdd %6 %41 %40 + OpStore %8 %42 + %43 = OpLoad %6 %29 + %44 = OpIAdd %6 %43 %9 + OpStore %29 %44 + OpBranch %30 + %32 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CheckEqual(env, after, context.get()); +} + +TEST(MergeBlocksReductionPassTest, MergeWithOpPhi) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "x" + OpName %10 "y" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + OpStore %8 %9 + %11 = OpLoad %6 %8 + OpBranch %12 + %12 = OpLabel + %13 = OpPhi %6 %11 %5 + OpStore %10 %13 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, shader, kReduceAssembleOption); + const auto ops = + MergeBlocksReductionOpportunityFinder().GetAvailableOpportunities( + context.get()); + ASSERT_EQ(1, ops.size()); + + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + + std::string after = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "x" + OpName %10 "y" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + OpStore %8 %9 + %11 = OpLoad %6 %8 + OpStore %10 %11 + OpReturn + OpFunctionEnd + )"; + + CheckEqual(env, after, context.get()); +} + +} // namespace +} // namespace reduce +} // namespace spvtools diff --git a/tools/reduce/reduce.cpp b/tools/reduce/reduce.cpp index 4d497688..e119e1ef 100644 --- a/tools/reduce/reduce.cpp +++ b/tools/reduce/reduce.cpp @@ -20,6 +20,7 @@ #include "source/opt/build_module.h" #include "source/opt/ir_context.h" #include "source/opt/log.h" +#include "source/reduce/merge_blocks_reduction_opportunity_finder.h" #include "source/reduce/operand_to_const_reduction_opportunity_finder.h" #include "source/reduce/operand_to_dominating_id_reduction_opportunity_finder.h" #include "source/reduce/operand_to_undef_reduction_opportunity_finder.h" @@ -240,6 +241,8 @@ int main(int argc, const char** argv) { reducer.AddReductionPass( spvtools::MakeUnique< StructuredLoopToSelectionReductionOpportunityFinder>()); + reducer.AddReductionPass( + spvtools::MakeUnique()); reducer.SetMessageConsumer(spvtools::utils::CLIMessageConsumer);