Merge blocks in reducer (#2353)

Fixes #2120 

Enhanced the reducer so that it can merge blocks together, leveraging the functionality extracted from the block_merge pass in the optimizer.
This commit is contained in:
Alastair Donaldson 2019-02-01 14:56:54 +00:00 committed by GitHub
parent 453b7c85c8
commit 37861ac106
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 732 additions and 1 deletions

View File

@ -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

View File

@ -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

View File

@ -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_

View File

@ -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<std::unique_ptr<ReductionOpportunity>>
MergeBlocksReductionOpportunityFinder::GetAvailableOpportunities(
opt::IRContext* context) const {
std::vector<std::unique_ptr<ReductionOpportunity>> 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<MergeBlocksReductionOpportunity>(
context, &function, &block));
}
}
}
return result;
}
} // namespace reduce
} // namespace spvtools

View File

@ -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<std::unique_ptr<ReductionOpportunity>> GetAvailableOpportunities(
opt::IRContext* context) const final;
private:
};
} // namespace reduce
} // namespace spvtools
#endif // SOURCE_REDUCE_MERGE_BLOCKS_REDUCTION_OPPORTUNITY_FINDER_H_

View File

@ -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

View File

@ -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

View File

@ -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<MergeBlocksReductionOpportunityFinder>());
reducer.SetMessageConsumer(spvtools::utils::CLIMessageConsumer);