mirror of
https://gitee.com/openharmony/third_party_spirv-tools
synced 2024-11-23 07:20:28 +00:00
CFG cleanup pass - Remove unreachable blocks.
- Adds a new pass CFGCleanupPass. This serves as an umbrella pass to remove unnecessary cruft from a CFG. - Currently, the only cleanup operation done is the removal of unreachable basic blocks. - Adds unit tests. - Adds a flag to spirvopt to execute the pass (--cfg-cleanup).
This commit is contained in:
parent
f17326c9bd
commit
c75704ec08
@ -50,6 +50,7 @@ SPVTOOLS_OPT_SRC_FILES := \
|
||||
source/opt/basic_block.cpp \
|
||||
source/opt/block_merge_pass.cpp \
|
||||
source/opt/build_module.cpp \
|
||||
source/opt/cfg_cleanup_pass.cpp \
|
||||
source/opt/compact_ids_pass.cpp \
|
||||
source/opt/common_uniform_elim_pass.cpp \
|
||||
source/opt/dead_branch_elim_pass.cpp \
|
||||
|
@ -393,6 +393,14 @@ Optimizer::PassToken CreateCompactIdsPass();
|
||||
// Creates a remove duplicate capabilities pass.
|
||||
Optimizer::PassToken CreateRemoveDuplicatesPass();
|
||||
|
||||
// Creates a CFG cleanup pass.
|
||||
// This pass removes cruft from the control flow graph of functions that are
|
||||
// reachable from entry points and exported functions. It currently includes the
|
||||
// following functionality:
|
||||
//
|
||||
// - Removal of unreachable basic blocks.
|
||||
Optimizer::PassToken CreateCFGCleanupPass();
|
||||
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SPIRV_TOOLS_OPTIMIZER_HPP_
|
||||
|
@ -16,6 +16,7 @@ add_library(SPIRV-Tools-opt
|
||||
basic_block.h
|
||||
block_merge_pass.h
|
||||
build_module.h
|
||||
cfg_cleanup_pass.h
|
||||
common_uniform_elim_pass.h
|
||||
compact_ids_pass.h
|
||||
constants.h
|
||||
@ -59,6 +60,7 @@ add_library(SPIRV-Tools-opt
|
||||
basic_block.cpp
|
||||
block_merge_pass.cpp
|
||||
build_module.cpp
|
||||
cfg_cleanup_pass.cpp
|
||||
common_uniform_elim_pass.cpp
|
||||
compact_ids_pass.cpp
|
||||
dead_branch_elim_pass.cpp
|
||||
|
170
source/opt/cfg_cleanup_pass.cpp
Normal file
170
source/opt/cfg_cleanup_pass.cpp
Normal file
@ -0,0 +1,170 @@
|
||||
// Copyright (c) 2017 Google Inc.
|
||||
//
|
||||
// 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.
|
||||
|
||||
// This file implements a pass to cleanup the CFG to remove superfluous
|
||||
// constructs (e.g., unreachable basic blocks, empty control flow structures,
|
||||
// etc)
|
||||
|
||||
#include <queue>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "cfg_cleanup_pass.h"
|
||||
|
||||
#include "function.h"
|
||||
#include "module.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
||||
void CFGCleanupPass::RemoveFromReachedPhiOperands(const ir::BasicBlock& block,
|
||||
ir::Instruction* inst) {
|
||||
uint32_t inst_id = inst->result_id();
|
||||
if (inst_id == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
analysis::UseList* uses = def_use_mgr_->GetUses(inst_id);
|
||||
if (uses == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto u : *uses) {
|
||||
if (u.inst->opcode() != SpvOpPhi) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ir::Instruction* phi_inst = u.inst;
|
||||
std::vector<ir::Operand> keep_operands;
|
||||
for (uint32_t i = 0; i < phi_inst->NumOperands(); i++) {
|
||||
const ir::Operand& var_op = phi_inst->GetOperand(i);
|
||||
if (i >= 2 && i < phi_inst->NumOperands() - 1) {
|
||||
// PHI arguments start at index 2. Each argument consists of two
|
||||
// operands: the variable id and the originating block id.
|
||||
const ir::Operand& block_op = phi_inst->GetOperand(i + 1);
|
||||
assert(var_op.words.size() == 1 && block_op.words.size() == 1 &&
|
||||
"Phi operands should have exactly one word in them.");
|
||||
uint32_t var_id = var_op.words.front();
|
||||
uint32_t block_id = block_op.words.front();
|
||||
if (var_id == inst_id && block_id == block.id()) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
keep_operands.push_back(var_op);
|
||||
}
|
||||
|
||||
phi_inst->ReplaceOperands(keep_operands);
|
||||
}
|
||||
}
|
||||
|
||||
void CFGCleanupPass::RemoveBlock(ir::Function::iterator* bi) {
|
||||
auto& block = **bi;
|
||||
block.ForEachInst([&block, this](ir::Instruction* inst) {
|
||||
// Note that we do not kill the block label instruction here. The label
|
||||
// instruction is needed to identify the block, which is needed by the
|
||||
// removal of PHI operands.
|
||||
if (inst != block.GetLabelInst()) {
|
||||
RemoveFromReachedPhiOperands(block, inst);
|
||||
KillNamesAndDecorates(inst);
|
||||
def_use_mgr_->KillInst(inst);
|
||||
}
|
||||
});
|
||||
|
||||
// Remove the label instruction last.
|
||||
def_use_mgr_->KillInst(block.GetLabelInst());
|
||||
|
||||
*bi = bi->Erase();
|
||||
}
|
||||
|
||||
bool CFGCleanupPass::RemoveUnreachableBlocks(ir::Function* func) {
|
||||
bool modified = false;
|
||||
|
||||
// Mark reachable all blocks reachable from the function's entry block.
|
||||
std::unordered_set<ir::BasicBlock*> reachable_blocks;
|
||||
std::unordered_set<ir::BasicBlock*> visited_blocks;
|
||||
std::queue<ir::BasicBlock*> worklist;
|
||||
reachable_blocks.insert(func->entry().get());
|
||||
|
||||
// Initially mark the function entry point as reachable.
|
||||
worklist.push(func->entry().get());
|
||||
|
||||
auto mark_reachable = [&reachable_blocks, &visited_blocks, &worklist,
|
||||
this](uint32_t label_id) {
|
||||
auto successor = label2block_[label_id];
|
||||
if (visited_blocks.count(successor) == 0) {
|
||||
reachable_blocks.insert(successor);
|
||||
worklist.push(successor);
|
||||
visited_blocks.insert(successor);
|
||||
}
|
||||
};
|
||||
|
||||
// Transitively mark all blocks reachable from the entry as reachable.
|
||||
while (!worklist.empty()) {
|
||||
ir::BasicBlock* block = worklist.front();
|
||||
worklist.pop();
|
||||
|
||||
// All the successors of a live block are also live.
|
||||
block->ForEachSuccessorLabel(mark_reachable);
|
||||
|
||||
// All the Merge and ContinueTarget blocks of a live block are also live.
|
||||
block->ForMergeAndContinueLabel(mark_reachable);
|
||||
}
|
||||
|
||||
// Erase unreachable blocks.
|
||||
for (auto ebi = func->begin(); ebi != func->end();) {
|
||||
if (reachable_blocks.count(&*ebi) == 0) {
|
||||
RemoveBlock(&ebi);
|
||||
modified = true;
|
||||
} else {
|
||||
++ebi;
|
||||
}
|
||||
}
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
bool CFGCleanupPass::CFGCleanup(ir::Function* func) {
|
||||
bool modified = false;
|
||||
modified |= RemoveUnreachableBlocks(func);
|
||||
return modified;
|
||||
}
|
||||
|
||||
void CFGCleanupPass::Initialize(ir::Module* module) {
|
||||
// Initialize the DefUse manager.
|
||||
module_ = module;
|
||||
def_use_mgr_.reset(new analysis::DefUseManager(consumer(), module));
|
||||
FindNamedOrDecoratedIds();
|
||||
|
||||
// Initialize block lookup map.
|
||||
label2block_.clear();
|
||||
for (auto& fn : *module) {
|
||||
for (auto& block : fn) {
|
||||
label2block_[block.id()] = █
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Pass::Status CFGCleanupPass::Process(ir::Module* module) {
|
||||
Initialize(module);
|
||||
|
||||
// Process all entry point functions.
|
||||
ProcessFunction pfn = [this](ir::Function* fp) { return CFGCleanup(fp); };
|
||||
bool modified = ProcessReachableCallTree(pfn, module);
|
||||
return modified ? Pass::Status::SuccessWithChange
|
||||
: Pass::Status::SuccessWithoutChange;
|
||||
}
|
||||
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
58
source/opt/cfg_cleanup_pass.h
Normal file
58
source/opt/cfg_cleanup_pass.h
Normal file
@ -0,0 +1,58 @@
|
||||
// Copyright (c) 2017 Google Inc.
|
||||
//
|
||||
// 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 LIBSPIRV_OPT_CFG_CLEANUP_PASS_H_
|
||||
#define LIBSPIRV_OPT_CFG_CLEANUP_PASS_H_
|
||||
|
||||
#include "function.h"
|
||||
#include "module.h"
|
||||
#include "mem_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
||||
class CFGCleanupPass : public MemPass {
|
||||
public:
|
||||
CFGCleanupPass() = default;
|
||||
const char* name() const override { return "cfg-cleanup"; }
|
||||
Status Process(ir::Module*) override;
|
||||
|
||||
private:
|
||||
// Call all the cleanup helper functions on |func|.
|
||||
bool CFGCleanup(ir::Function* func);
|
||||
|
||||
// Remove all the unreachable basic blocks in |func|.
|
||||
bool RemoveUnreachableBlocks(ir::Function* func);
|
||||
|
||||
// Remove the block pointed by the iterator |*bi|. This also removes
|
||||
// all the instructions in the pointed-to block.
|
||||
void RemoveBlock(ir::Function::iterator *bi);
|
||||
|
||||
// Initialize the pass.
|
||||
void Initialize(ir::Module* module);
|
||||
|
||||
// Remove the result from |inst| from every Phi instruction reached by
|
||||
// |inst|. The instruction is coming from basic block |block|.
|
||||
void RemoveFromReachedPhiOperands(const ir::BasicBlock& block,
|
||||
ir::Instruction* inst);
|
||||
|
||||
// Map from block's label id to block. TODO(dnovillo): Basic blocks ought to
|
||||
// have basic blocks in their pred/succ list.
|
||||
std::unordered_map<uint32_t, ir::BasicBlock*> label2block_;
|
||||
};
|
||||
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
||||
#endif
|
@ -70,6 +70,9 @@ class Function {
|
||||
// Returns function's return type id
|
||||
inline uint32_t type_id() const { return def_inst_->type_id(); }
|
||||
|
||||
// Returns the entry basic block for this function.
|
||||
const std::unique_ptr<BasicBlock>& entry() const { return blocks_.front(); }
|
||||
|
||||
iterator begin() { return iterator(&blocks_, blocks_.begin()); }
|
||||
iterator end() { return iterator(&blocks_, blocks_.end()); }
|
||||
const_iterator cbegin() const {
|
||||
|
@ -89,5 +89,11 @@ void Instruction::ToBinaryWithoutAttachedDebugInsts(
|
||||
binary->insert(binary->end(), operand.words.begin(), operand.words.end());
|
||||
}
|
||||
|
||||
void Instruction::ReplaceOperands(const std::vector<Operand>& new_operands) {
|
||||
operands_.clear();
|
||||
operands_.insert(operands_.begin(), new_operands.begin(), new_operands.end());
|
||||
operands_.shrink_to_fit();
|
||||
}
|
||||
|
||||
} // namespace ir
|
||||
} // namespace spvtools
|
||||
|
@ -202,8 +202,13 @@ class Instruction {
|
||||
// Pushes the binary segments for this instruction into the back of *|binary|.
|
||||
void ToBinaryWithoutAttachedDebugInsts(std::vector<uint32_t>* binary) const;
|
||||
|
||||
// Replaces the operands to the instruction with |new_operands|. The caller
|
||||
// is responsible for building a complete and valid list of operands for
|
||||
// this instruction.
|
||||
void ReplaceOperands(const std::vector<Operand>& new_operands);
|
||||
|
||||
private:
|
||||
// Returns the toal count of result type id and result id.
|
||||
// Returns the total count of result type id and result id.
|
||||
uint32_t TypeResultIdCount() const {
|
||||
return (type_id_ != 0) + (result_id_ != 0);
|
||||
}
|
||||
|
@ -230,6 +230,11 @@ Optimizer::PassToken CreateCompactIdsPass() {
|
||||
MakeUnique<opt::CompactIdsPass>());
|
||||
}
|
||||
|
||||
Optimizer::PassToken CreateCFGCleanupPass() {
|
||||
return MakeUnique<Optimizer::PassToken::Impl>(
|
||||
MakeUnique<opt::CFGCleanupPass>());
|
||||
}
|
||||
|
||||
std::vector<const char*> Optimizer::GetPassNames() const {
|
||||
std::vector<const char*> v;
|
||||
for (uint32_t i = 0; i < impl_->pass_manager.NumPasses(); i++) {
|
||||
|
@ -18,6 +18,7 @@
|
||||
// A single header to include all passes.
|
||||
|
||||
#include "block_merge_pass.h"
|
||||
#include "cfg_cleanup_pass.h"
|
||||
#include "common_uniform_elim_pass.h"
|
||||
#include "compact_ids_pass.h"
|
||||
#include "dead_branch_elim_pass.h"
|
||||
|
@ -183,3 +183,8 @@ add_spvtools_unittest(TARGET pass_strength_reduction
|
||||
SRCS strength_reduction_test.cpp pass_utils.cpp
|
||||
LIBS SPIRV-Tools-opt
|
||||
)
|
||||
|
||||
add_spvtools_unittest(TARGET cfg_cleanup
|
||||
SRCS cfg_cleanup_test.cpp pass_utils.cpp
|
||||
LIBS SPIRV-Tools-opt
|
||||
)
|
||||
|
236
test/opt/cfg_cleanup_test.cpp
Normal file
236
test/opt/cfg_cleanup_test.cpp
Normal file
@ -0,0 +1,236 @@
|
||||
// Copyright (c) 2017 Google Inc.
|
||||
//
|
||||
// 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 "pass_fixture.h"
|
||||
#include "pass_utils.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace spvtools;
|
||||
|
||||
using CFGCleanupTest = PassTest<::testing::Test>;
|
||||
|
||||
TEST_F(CFGCleanupTest, RemoveUnreachableBlocks) {
|
||||
const std::string declarations = R"(OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %main "main" %inf %outf4
|
||||
OpExecutionMode %main OriginUpperLeft
|
||||
OpSource GLSL 450
|
||||
OpName %main "main"
|
||||
OpName %inf "inf"
|
||||
OpName %outf4 "outf4"
|
||||
OpDecorate %inf Location 0
|
||||
OpDecorate %outf4 Location 0
|
||||
%void = OpTypeVoid
|
||||
%6 = OpTypeFunction %void
|
||||
%float = OpTypeFloat 32
|
||||
%_ptr_Input_float = OpTypePointer Input %float
|
||||
%inf = OpVariable %_ptr_Input_float Input
|
||||
%float_2 = OpConstant %float 2
|
||||
%bool = OpTypeBool
|
||||
%v4float = OpTypeVector %float 4
|
||||
%_ptr_Output_v4float = OpTypePointer Output %v4float
|
||||
%outf4 = OpVariable %_ptr_Output_v4float Output
|
||||
%float_n0_5 = OpConstant %float -0.5
|
||||
)";
|
||||
|
||||
const std::string body_before = R"(%main = OpFunction %void None %6
|
||||
%14 = OpLabel
|
||||
OpSelectionMerge %17 None
|
||||
OpBranch %18
|
||||
%19 = OpLabel
|
||||
%20 = OpLoad %float %inf
|
||||
%21 = OpCompositeConstruct %v4float %20 %20 %20 %20
|
||||
OpStore %outf4 %21
|
||||
OpBranch %17
|
||||
%18 = OpLabel
|
||||
%22 = OpLoad %float %inf
|
||||
%23 = OpFAdd %float %22 %float_n0_5
|
||||
%24 = OpCompositeConstruct %v4float %23 %23 %23 %23
|
||||
OpStore %outf4 %24
|
||||
OpBranch %17
|
||||
%17 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const std::string body_after = R"(%main = OpFunction %void None %6
|
||||
%14 = OpLabel
|
||||
OpSelectionMerge %15 None
|
||||
OpBranch %16
|
||||
%16 = OpLabel
|
||||
%20 = OpLoad %float %inf
|
||||
%21 = OpFAdd %float %20 %float_n0_5
|
||||
%22 = OpCompositeConstruct %v4float %21 %21 %21 %21
|
||||
OpStore %outf4 %22
|
||||
OpBranch %15
|
||||
%15 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SinglePassRunAndCheck<opt::CFGCleanupPass>(
|
||||
declarations + body_before, declarations + body_after, true, true);
|
||||
}
|
||||
|
||||
TEST_F(CFGCleanupTest, RemoveDecorations) {
|
||||
const std::string before = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %main "main"
|
||||
OpName %main "main"
|
||||
OpName %x "x"
|
||||
OpName %dead "dead"
|
||||
OpDecorate %x RelaxedPrecision
|
||||
OpDecorate %dead RelaxedPrecision
|
||||
%void = OpTypeVoid
|
||||
%6 = OpTypeFunction %void
|
||||
%float = OpTypeFloat 32
|
||||
%_ptr_Function_float = OpTypePointer Function %float
|
||||
%float_2 = OpConstant %float 2
|
||||
%float_4 = OpConstant %float 4
|
||||
|
||||
%main = OpFunction %void None %6
|
||||
%14 = OpLabel
|
||||
%x = OpVariable %_ptr_Function_float Function
|
||||
OpSelectionMerge %17 None
|
||||
OpBranch %18
|
||||
%19 = OpLabel
|
||||
%dead = OpVariable %_ptr_Function_float Function
|
||||
OpStore %dead %float_2
|
||||
OpBranch %17
|
||||
%18 = OpLabel
|
||||
OpStore %x %float_4
|
||||
OpBranch %17
|
||||
%17 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const std::string after = R"(OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %main "main"
|
||||
OpName %main "main"
|
||||
OpName %x "x"
|
||||
OpDecorate %x RelaxedPrecision
|
||||
%void = OpTypeVoid
|
||||
%6 = OpTypeFunction %void
|
||||
%float = OpTypeFloat 32
|
||||
%_ptr_Function_float = OpTypePointer Function %float
|
||||
%float_2 = OpConstant %float 2
|
||||
%float_4 = OpConstant %float 4
|
||||
%main = OpFunction %void None %6
|
||||
%11 = OpLabel
|
||||
%x = OpVariable %_ptr_Function_float Function
|
||||
OpSelectionMerge %12 None
|
||||
OpBranch %13
|
||||
%13 = OpLabel
|
||||
OpStore %x %float_4
|
||||
OpBranch %12
|
||||
%12 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SinglePassRunAndCheck<opt::CFGCleanupPass>(before, after, true, true);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(CFGCleanupTest, UpdatePhis) {
|
||||
const std::string before = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %main "main" %y %outparm
|
||||
OpName %main "main"
|
||||
OpName %y "y"
|
||||
OpName %outparm "outparm"
|
||||
OpDecorate %y Flat
|
||||
OpDecorate %y Location 0
|
||||
OpDecorate %outparm Location 0
|
||||
%void = OpTypeVoid
|
||||
%3 = OpTypeFunction %void
|
||||
%int = OpTypeInt 32 1
|
||||
%_ptr_Function_int = OpTypePointer Function %int
|
||||
%_ptr_Input_int = OpTypePointer Input %int
|
||||
%y = OpVariable %_ptr_Input_int Input
|
||||
%int_10 = OpConstant %int 10
|
||||
%bool = OpTypeBool
|
||||
%int_42 = OpConstant %int 42
|
||||
%int_23 = OpConstant %int 23
|
||||
%int_5 = OpConstant %int 5
|
||||
%_ptr_Output_int = OpTypePointer Output %int
|
||||
%outparm = OpVariable %_ptr_Output_int Output
|
||||
%main = OpFunction %void None %3
|
||||
%5 = OpLabel
|
||||
%11 = OpLoad %int %y
|
||||
OpBranch %21
|
||||
%16 = OpLabel
|
||||
%20 = OpIAdd %int %11 %int_42
|
||||
OpBranch %17
|
||||
%21 = OpLabel
|
||||
%24 = OpISub %int %11 %int_23
|
||||
OpBranch %17
|
||||
%17 = OpLabel
|
||||
%31 = OpPhi %int %20 %16 %24 %21
|
||||
%27 = OpIAdd %int %31 %int_5
|
||||
OpStore %outparm %27
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const std::string after = R"(OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %main "main" %y %outparm
|
||||
OpName %main "main"
|
||||
OpName %y "y"
|
||||
OpName %outparm "outparm"
|
||||
OpDecorate %y Flat
|
||||
OpDecorate %y Location 0
|
||||
OpDecorate %outparm Location 0
|
||||
%void = OpTypeVoid
|
||||
%6 = OpTypeFunction %void
|
||||
%int = OpTypeInt 32 1
|
||||
%_ptr_Function_int = OpTypePointer Function %int
|
||||
%_ptr_Input_int = OpTypePointer Input %int
|
||||
%y = OpVariable %_ptr_Input_int Input
|
||||
%int_10 = OpConstant %int 10
|
||||
%bool = OpTypeBool
|
||||
%int_42 = OpConstant %int 42
|
||||
%int_23 = OpConstant %int 23
|
||||
%int_5 = OpConstant %int 5
|
||||
%_ptr_Output_int = OpTypePointer Output %int
|
||||
%outparm = OpVariable %_ptr_Output_int Output
|
||||
%main = OpFunction %void None %6
|
||||
%16 = OpLabel
|
||||
%17 = OpLoad %int %y
|
||||
OpBranch %18
|
||||
%18 = OpLabel
|
||||
%22 = OpISub %int %17 %int_23
|
||||
OpBranch %21
|
||||
%21 = OpLabel
|
||||
%23 = OpPhi %int %22 %18
|
||||
%24 = OpIAdd %int %23 %int_5
|
||||
OpStore %outparm %24
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SinglePassRunAndCheck<opt::CFGCleanupPass>(before, after, true, true);
|
||||
}
|
||||
} // anonymous namespace
|
@ -99,6 +99,10 @@ Options:
|
||||
--compact-ids
|
||||
Remap result ids to a compact range starting from %%1 and without
|
||||
any gaps.
|
||||
--cfg-cleanup
|
||||
Cleanup the control flow graph. This will remove any unnecessary
|
||||
code from the CFG like unreachable code. Performed on entry
|
||||
point call tree functions and exported functions.
|
||||
--inline-entry-points-exhaustive
|
||||
Exhaustively inline all function calls in entry point call tree
|
||||
functions. Currently does not inline calls to functions with
|
||||
@ -359,6 +363,8 @@ OptStatus ParseFlags(int argc, const char** argv, Optimizer* optimizer,
|
||||
optimizer->RegisterPass(CreateFlattenDecorationPass());
|
||||
} else if (0 == strcmp(cur_arg, "--compact-ids")) {
|
||||
optimizer->RegisterPass(CreateCompactIdsPass());
|
||||
} else if (0 == strcmp(cur_arg, "--cfg-cleanup")) {
|
||||
optimizer->RegisterPass(CreateCFGCleanupPass());
|
||||
} else if (0 == strcmp(cur_arg, "-O")) {
|
||||
optimizer->RegisterPerformancePasses();
|
||||
} else if (0 == strcmp(cur_arg, "-Os")) {
|
||||
|
Loading…
Reference in New Issue
Block a user