mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-12-24 16:51:06 +00:00
Loop invariant code motion initial implementation
This commit is contained in:
parent
ca4457b4b6
commit
84ccd0b9ae
@ -92,6 +92,7 @@ SPVTOOLS_OPT_SRC_FILES := \
|
||||
source/opt/instruction_list.cpp \
|
||||
source/opt/ir_context.cpp \
|
||||
source/opt/ir_loader.cpp \
|
||||
source/opt/licm_pass.cpp \
|
||||
source/opt/local_access_chain_convert_pass.cpp \
|
||||
source/opt/local_redundancy_elimination.cpp \
|
||||
source/opt/local_single_block_elim_pass.cpp \
|
||||
|
@ -463,6 +463,11 @@ Optimizer::PassToken CreateMergeReturnPass();
|
||||
// same value, and remove the redundant ones.
|
||||
Optimizer::PassToken CreateLocalRedundancyEliminationPass();
|
||||
|
||||
// Create LICM pass.
|
||||
// This pass will look for invariant instructions inside loops and hoist them to
|
||||
// the loops preheader.
|
||||
Optimizer::PassToken CreateLoopInvariantCodeMotionPass();
|
||||
|
||||
// Create global value numbering pass.
|
||||
// This pass will look for instructions where the same value is computed on all
|
||||
// paths leading to the instruction. Those instructions are deleted.
|
||||
|
@ -47,6 +47,7 @@ add_library(SPIRV-Tools-opt
|
||||
instruction.h
|
||||
ir_context.h
|
||||
ir_loader.h
|
||||
licm_pass.h
|
||||
local_access_chain_convert_pass.h
|
||||
local_redundancy_elimination.h
|
||||
local_single_block_elim_pass.h
|
||||
@ -116,6 +117,7 @@ add_library(SPIRV-Tools-opt
|
||||
instruction_list.cpp
|
||||
ir_context.cpp
|
||||
ir_loader.cpp
|
||||
licm_pass.cpp
|
||||
local_access_chain_convert_pass.cpp
|
||||
local_redundancy_elimination.cpp
|
||||
local_single_block_elim_pass.cpp
|
||||
|
@ -56,7 +56,9 @@ void CFG::RemoveNonExistingEdges(uint32_t blk_id) {
|
||||
const ir::BasicBlock* pred_blk = block(id);
|
||||
bool has_branch = false;
|
||||
pred_blk->ForEachSuccessorLabel([&has_branch, blk_id](uint32_t succ) {
|
||||
if (succ == blk_id) has_branch = true;
|
||||
if (succ == blk_id) {
|
||||
has_branch = true;
|
||||
}
|
||||
});
|
||||
if (has_branch) updated_pred_list.push_back(id);
|
||||
}
|
||||
|
@ -499,5 +499,80 @@ std::ostream& operator<<(std::ostream& str, const ir::Instruction& inst) {
|
||||
return str;
|
||||
}
|
||||
|
||||
bool Instruction::IsOpcodeCodeMotionSafe() const {
|
||||
switch (opcode_) {
|
||||
case SpvOpVectorExtractDynamic:
|
||||
case SpvOpVectorInsertDynamic:
|
||||
case SpvOpVectorShuffle:
|
||||
case SpvOpConvertFToU:
|
||||
case SpvOpConvertFToS:
|
||||
case SpvOpConvertSToF:
|
||||
case SpvOpConvertUToF:
|
||||
case SpvOpUConvert:
|
||||
case SpvOpSConvert:
|
||||
case SpvOpFConvert:
|
||||
case SpvOpQuantizeToF16:
|
||||
case SpvOpBitcast:
|
||||
case SpvOpSNegate:
|
||||
case SpvOpFNegate:
|
||||
case SpvOpIAdd:
|
||||
case SpvOpFAdd:
|
||||
case SpvOpISub:
|
||||
case SpvOpFSub:
|
||||
case SpvOpIMul:
|
||||
case SpvOpFMul:
|
||||
case SpvOpUDiv:
|
||||
case SpvOpSDiv:
|
||||
case SpvOpFDiv:
|
||||
case SpvOpUMod:
|
||||
case SpvOpSRem:
|
||||
case SpvOpSMod:
|
||||
case SpvOpFRem:
|
||||
case SpvOpFMod:
|
||||
case SpvOpVectorTimesScalar:
|
||||
case SpvOpMatrixTimesScalar:
|
||||
case SpvOpVectorTimesMatrix:
|
||||
case SpvOpMatrixTimesVector:
|
||||
case SpvOpMatrixTimesMatrix:
|
||||
case SpvOpLogicalEqual:
|
||||
case SpvOpLogicalNotEqual:
|
||||
case SpvOpLogicalOr:
|
||||
case SpvOpLogicalAnd:
|
||||
case SpvOpLogicalNot:
|
||||
case SpvOpIEqual:
|
||||
case SpvOpINotEqual:
|
||||
case SpvOpUGreaterThan:
|
||||
case SpvOpSGreaterThan:
|
||||
case SpvOpUGreaterThanEqual:
|
||||
case SpvOpSGreaterThanEqual:
|
||||
case SpvOpULessThan:
|
||||
case SpvOpSLessThan:
|
||||
case SpvOpULessThanEqual:
|
||||
case SpvOpSLessThanEqual:
|
||||
case SpvOpFOrdEqual:
|
||||
case SpvOpFUnordEqual:
|
||||
case SpvOpFOrdNotEqual:
|
||||
case SpvOpFUnordNotEqual:
|
||||
case SpvOpFOrdLessThan:
|
||||
case SpvOpFUnordLessThan:
|
||||
case SpvOpFOrdGreaterThan:
|
||||
case SpvOpFUnordGreaterThan:
|
||||
case SpvOpFOrdLessThanEqual:
|
||||
case SpvOpFUnordLessThanEqual:
|
||||
case SpvOpFOrdGreaterThanEqual:
|
||||
case SpvOpFUnordGreaterThanEqual:
|
||||
case SpvOpShiftRightLogical:
|
||||
case SpvOpShiftRightArithmetic:
|
||||
case SpvOpShiftLeftLogical:
|
||||
case SpvOpBitwiseOr:
|
||||
case SpvOpBitwiseXor:
|
||||
case SpvOpBitwiseAnd:
|
||||
case SpvOpNot:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ir
|
||||
} // namespace spvtools
|
||||
|
@ -380,6 +380,9 @@ class Instruction : public utils::IntrusiveNodeBase<Instruction> {
|
||||
// Spec constant.
|
||||
inline bool IsConstant() const;
|
||||
|
||||
// Returns true if |this| is an instruction with an opcode safe to move
|
||||
bool IsOpcodeCodeMotionSafe() const;
|
||||
|
||||
// Pretty-prints |inst|.
|
||||
//
|
||||
// Provides the disassembly of a specific instruction. Utilizes |inst|'s
|
||||
|
125
source/opt/licm_pass.cpp
Normal file
125
source/opt/licm_pass.cpp
Normal file
@ -0,0 +1,125 @@
|
||||
// Copyright (c) 2018 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 "opt/licm_pass.h"
|
||||
#include "opt/module.h"
|
||||
#include "opt/pass.h"
|
||||
|
||||
#include <queue>
|
||||
#include <utility>
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
||||
Pass::Status LICMPass::Process(ir::IRContext* c) {
|
||||
InitializeProcessing(c);
|
||||
bool modified = false;
|
||||
|
||||
if (c != nullptr) {
|
||||
modified = ProcessIRContext();
|
||||
}
|
||||
|
||||
return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
|
||||
}
|
||||
|
||||
bool LICMPass::ProcessIRContext() {
|
||||
bool modified = false;
|
||||
ir::Module* module = get_module();
|
||||
|
||||
// Process each function in the module
|
||||
for (ir::Function& f : *module) {
|
||||
modified |= ProcessFunction(&f);
|
||||
}
|
||||
return modified;
|
||||
}
|
||||
|
||||
bool LICMPass::ProcessFunction(ir::Function* f) {
|
||||
bool modified = false;
|
||||
ir::LoopDescriptor* loop_descriptor = context()->GetLoopDescriptor(f);
|
||||
|
||||
// Process each loop in the function
|
||||
for (ir::Loop& loop : *loop_descriptor) {
|
||||
// Ignore nested loops, as we will process them in order in ProcessLoop
|
||||
if (loop.IsNested()) {
|
||||
continue;
|
||||
}
|
||||
modified |= ProcessLoop(&loop, f);
|
||||
}
|
||||
return modified;
|
||||
}
|
||||
|
||||
bool LICMPass::ProcessLoop(ir::Loop* loop, ir::Function* f) {
|
||||
bool modified = false;
|
||||
|
||||
// Process all nested loops first
|
||||
for (ir::Loop* nested_loop : *loop) {
|
||||
modified |= ProcessLoop(nested_loop, f);
|
||||
}
|
||||
|
||||
std::vector<ir::BasicBlock*> loop_bbs{};
|
||||
modified |= AnalyseAndHoistFromBB(loop, f, loop->GetHeaderBlock(), &loop_bbs);
|
||||
|
||||
for (size_t i = 0; i < loop_bbs.size(); ++i) {
|
||||
ir::BasicBlock* bb = loop_bbs[i];
|
||||
// do not delete the element
|
||||
modified |= AnalyseAndHoistFromBB(loop, f, bb, &loop_bbs);
|
||||
}
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
bool LICMPass::AnalyseAndHoistFromBB(ir::Loop* loop, ir::Function* f,
|
||||
ir::BasicBlock* bb,
|
||||
std::vector<ir::BasicBlock*>* loop_bbs) {
|
||||
bool modified = false;
|
||||
std::function<void(ir::Instruction*)> hoist_inst =
|
||||
[this, &loop, &modified](ir::Instruction* inst) {
|
||||
if (loop->ShouldHoistInstruction(this->context(), inst)) {
|
||||
HoistInstruction(loop, inst);
|
||||
modified = true;
|
||||
}
|
||||
};
|
||||
|
||||
if (IsImmediatelyContainedInLoop(loop, f, bb)) {
|
||||
bb->ForEachInst(hoist_inst, false);
|
||||
}
|
||||
|
||||
opt::DominatorAnalysis* dom_analysis =
|
||||
context()->GetDominatorAnalysis(f, *cfg());
|
||||
opt::DominatorTree& dom_tree = dom_analysis->GetDomTree();
|
||||
|
||||
for (opt::DominatorTreeNode* child_dom_tree_node :
|
||||
*dom_tree.GetTreeNode(bb)) {
|
||||
if (loop->IsInsideLoop(child_dom_tree_node->bb_)) {
|
||||
loop_bbs->push_back(child_dom_tree_node->bb_);
|
||||
}
|
||||
}
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
bool LICMPass::IsImmediatelyContainedInLoop(ir::Loop* loop, ir::Function* f,
|
||||
ir::BasicBlock* bb) {
|
||||
ir::LoopDescriptor* loop_descriptor = context()->GetLoopDescriptor(f);
|
||||
return loop == (*loop_descriptor)[bb->id()];
|
||||
}
|
||||
|
||||
void LICMPass::HoistInstruction(ir::Loop* loop, ir::Instruction* inst) {
|
||||
ir::BasicBlock* pre_header_bb = loop->GetOrCreatePreHeaderBlock();
|
||||
inst->InsertBefore(std::move(&(*pre_header_bb->tail())));
|
||||
context()->set_instr_block(inst, pre_header_bb);
|
||||
}
|
||||
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
68
source/opt/licm_pass.h
Normal file
68
source/opt/licm_pass.h
Normal file
@ -0,0 +1,68 @@
|
||||
// Copyright (c) 2018 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_OPT_LICM_PASS_H_
|
||||
#define SOURCE_OPT_LICM_PASS_H_
|
||||
|
||||
#include "opt/basic_block.h"
|
||||
#include "opt/instruction.h"
|
||||
#include "opt/loop_descriptor.h"
|
||||
#include "opt/pass.h"
|
||||
|
||||
#include <queue>
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
||||
class LICMPass : public Pass {
|
||||
public:
|
||||
LICMPass() {}
|
||||
|
||||
const char* name() const override { return "loop-invariant-code-motion"; }
|
||||
Status Process(ir::IRContext*) override;
|
||||
|
||||
private:
|
||||
// Searches the IRContext for functions and processes each, moving invariants
|
||||
// outside loops within the function where possible
|
||||
// Returns true if a change was made to a function within the IRContext
|
||||
bool ProcessIRContext();
|
||||
|
||||
// Checks the function for loops, calling ProcessLoop on each one found.
|
||||
// Returns true if a change was made to the function, false otherwise.
|
||||
bool ProcessFunction(ir::Function* f);
|
||||
|
||||
// Checks for invariants in the loop and attempts to move them to the loops
|
||||
// preheader. Works from inner loop to outer when nested loops are found.
|
||||
// Returns true if a change was made to the loop, false otherwise.
|
||||
bool ProcessLoop(ir::Loop* loop, ir::Function* f);
|
||||
|
||||
// Analyses each instruction in |bb|, hoisting invariants to |pre_header_bb|.
|
||||
// Each child of |bb| wrt to |dom_tree| is pushed to |loop_bbs|
|
||||
bool AnalyseAndHoistFromBB(ir::Loop* loop, ir::Function* f,
|
||||
ir::BasicBlock* bb,
|
||||
std::vector<ir::BasicBlock*>* loop_bbs);
|
||||
|
||||
// Returns true if |bb| is immediately contained in |loop|
|
||||
bool IsImmediatelyContainedInLoop(ir::Loop* loop, ir::Function* f,
|
||||
ir::BasicBlock* bb);
|
||||
|
||||
// Move the instruction to the given BasicBlock
|
||||
// This method will update the instruction to block mapping for the context
|
||||
void HoistInstruction(ir::Loop* loop, ir::Instruction* inst);
|
||||
};
|
||||
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_OPT_LICM_PASS_H_
|
@ -32,21 +32,21 @@ namespace ir {
|
||||
Loop::Loop(IRContext* context, opt::DominatorAnalysis* dom_analysis,
|
||||
BasicBlock* header, BasicBlock* continue_target,
|
||||
BasicBlock* merge_target)
|
||||
: loop_header_(header),
|
||||
: context_(context),
|
||||
loop_header_(header),
|
||||
loop_continue_(continue_target),
|
||||
loop_merge_(merge_target),
|
||||
loop_preheader_(nullptr),
|
||||
parent_(nullptr) {
|
||||
assert(context);
|
||||
assert(dom_analysis);
|
||||
loop_preheader_ = FindLoopPreheader(context, dom_analysis);
|
||||
loop_preheader_ = FindLoopPreheader(dom_analysis);
|
||||
AddBasicBlockToLoop(header);
|
||||
AddBasicBlockToLoop(continue_target);
|
||||
}
|
||||
|
||||
BasicBlock* Loop::FindLoopPreheader(IRContext* ir_context,
|
||||
opt::DominatorAnalysis* dom_analysis) {
|
||||
CFG* cfg = ir_context->cfg();
|
||||
BasicBlock* Loop::FindLoopPreheader(opt::DominatorAnalysis* dom_analysis) {
|
||||
CFG* cfg = context_->cfg();
|
||||
opt::DominatorTree& dom_tree = dom_analysis->GetDomTree();
|
||||
opt::DominatorTreeNode* header_node = dom_tree.GetTreeNode(loop_header_);
|
||||
|
||||
@ -85,26 +85,25 @@ BasicBlock* Loop::FindLoopPreheader(IRContext* ir_context,
|
||||
}
|
||||
|
||||
bool Loop::IsInsideLoop(Instruction* inst) const {
|
||||
const BasicBlock* parent_block = inst->context()->get_instr_block(inst);
|
||||
const BasicBlock* parent_block = context_->get_instr_block(inst);
|
||||
if (!parent_block) return false;
|
||||
return IsInsideLoop(parent_block);
|
||||
}
|
||||
|
||||
bool Loop::IsBasicBlockInLoopSlow(const BasicBlock* bb) {
|
||||
assert(bb->GetParent() && "The basic block does not belong to a function");
|
||||
IRContext* context = bb->GetParent()->GetParent()->context();
|
||||
|
||||
opt::DominatorAnalysis* dom_analysis =
|
||||
context->GetDominatorAnalysis(bb->GetParent(), *context->cfg());
|
||||
context_->GetDominatorAnalysis(bb->GetParent(), *context_->cfg());
|
||||
if (!dom_analysis->Dominates(GetHeaderBlock(), bb)) return false;
|
||||
|
||||
opt::PostDominatorAnalysis* postdom_analysis =
|
||||
context->GetPostDominatorAnalysis(bb->GetParent(), *context->cfg());
|
||||
context_->GetPostDominatorAnalysis(bb->GetParent(), *context_->cfg());
|
||||
if (!postdom_analysis->Dominates(GetMergeBlock(), bb)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
BasicBlock* Loop::GetOrCreatePreHeaderBlock(ir::IRContext* context) {
|
||||
BasicBlock* Loop::GetOrCreatePreHeaderBlock() {
|
||||
if (loop_preheader_) return loop_preheader_;
|
||||
|
||||
Function* fn = loop_header_->GetParent();
|
||||
@ -117,7 +116,7 @@ BasicBlock* Loop::GetOrCreatePreHeaderBlock(ir::IRContext* context) {
|
||||
// Create the preheader basic block.
|
||||
loop_preheader_ = &*header_it.InsertBefore(std::unique_ptr<ir::BasicBlock>(
|
||||
new ir::BasicBlock(std::unique_ptr<ir::Instruction>(new ir::Instruction(
|
||||
context, SpvOpLabel, 0, context->TakeNextId(), {})))));
|
||||
context_, SpvOpLabel, 0, context_->TakeNextId(), {})))));
|
||||
loop_preheader_->SetParent(fn);
|
||||
uint32_t loop_preheader_id = loop_preheader_->id();
|
||||
|
||||
@ -132,11 +131,11 @@ BasicBlock* Loop::GetOrCreatePreHeaderBlock(ir::IRContext* context) {
|
||||
// instruction;
|
||||
// - Redirect all edges coming from outside the loop to the preheader.
|
||||
opt::InstructionBuilder builder(
|
||||
context, loop_preheader_,
|
||||
context_, loop_preheader_,
|
||||
ir::IRContext::kAnalysisDefUse |
|
||||
ir::IRContext::kAnalysisInstrToBlockMapping);
|
||||
// Patch all the phi instructions.
|
||||
loop_header_->ForEachPhiInst([&builder, context, this](Instruction* phi) {
|
||||
loop_header_->ForEachPhiInst([&builder, this](Instruction* phi) {
|
||||
std::vector<uint32_t> preheader_phi_ops;
|
||||
std::vector<uint32_t> header_phi_ops;
|
||||
for (uint32_t i = 0; i < phi->NumInOperands(); i += 2) {
|
||||
@ -158,7 +157,7 @@ BasicBlock* Loop::GetOrCreatePreHeaderBlock(ir::IRContext* context) {
|
||||
preheader_insn_def = builder.AddPhi(phi->type_id(), preheader_phi_ops);
|
||||
else
|
||||
preheader_insn_def =
|
||||
context->get_def_use_mgr()->GetDef(preheader_phi_ops[0]);
|
||||
context_->get_def_use_mgr()->GetDef(preheader_phi_ops[0]);
|
||||
// Build the new incoming edge.
|
||||
header_phi_ops.push_back(preheader_insn_def->result_id());
|
||||
header_phi_ops.push_back(loop_preheader_->id());
|
||||
@ -174,7 +173,7 @@ BasicBlock* Loop::GetOrCreatePreHeaderBlock(ir::IRContext* context) {
|
||||
builder.AddBranch(loop_header_->id());
|
||||
|
||||
// Redirect all out of loop branches to the header to the preheader.
|
||||
CFG* cfg = context->cfg();
|
||||
CFG* cfg = context_->cfg();
|
||||
cfg->RegisterBlock(loop_preheader_);
|
||||
for (uint32_t pred_id : cfg->preds(loop_header_->id())) {
|
||||
if (pred_id == loop_preheader_->id()) continue;
|
||||
@ -190,11 +189,11 @@ BasicBlock* Loop::GetOrCreatePreHeaderBlock(ir::IRContext* context) {
|
||||
// Update the loop descriptors.
|
||||
if (HasParent()) {
|
||||
GetParent()->AddBasicBlock(loop_preheader_);
|
||||
context->GetLoopDescriptor(fn)->SetBasicBlockToLoop(loop_preheader_->id(),
|
||||
GetParent());
|
||||
context_->GetLoopDescriptor(fn)->SetBasicBlockToLoop(loop_preheader_->id(),
|
||||
GetParent());
|
||||
}
|
||||
|
||||
context->InvalidateAnalysesExceptFor(
|
||||
context_->InvalidateAnalysesExceptFor(
|
||||
builder.GetPreservedAnalysis() |
|
||||
ir::IRContext::Analysis::kAnalysisLoopAnalysis |
|
||||
ir::IRContext::kAnalysisCFG);
|
||||
@ -226,8 +225,8 @@ void Loop::SetMergeBlock(BasicBlock* merge) {
|
||||
assert(IsInsideLoop(pred) &&
|
||||
"A predecessor of the merge block does not belong to the loop");
|
||||
}
|
||||
#endif // NDEBUG
|
||||
assert(!IsInsideLoop(merge) && "The merge block is in the loop");
|
||||
#endif // NDEBUG
|
||||
|
||||
SetMergeBlockImpl(merge);
|
||||
if (GetHeaderBlock()->GetLoopMergeInst()) {
|
||||
@ -235,9 +234,9 @@ void Loop::SetMergeBlock(BasicBlock* merge) {
|
||||
}
|
||||
}
|
||||
|
||||
void Loop::GetExitBlocks(IRContext* context,
|
||||
std::unordered_set<uint32_t>* exit_blocks) const {
|
||||
ir::CFG* cfg = context->cfg();
|
||||
void Loop::GetExitBlocks(std::unordered_set<uint32_t>* exit_blocks) const {
|
||||
ir::CFG* cfg = context_->cfg();
|
||||
exit_blocks->clear();
|
||||
|
||||
for (uint32_t bb_id : GetBlocks()) {
|
||||
const spvtools::ir::BasicBlock* bb = cfg->block(bb_id);
|
||||
@ -250,9 +249,10 @@ void Loop::GetExitBlocks(IRContext* context,
|
||||
}
|
||||
|
||||
void Loop::GetMergingBlocks(
|
||||
IRContext* context, std::unordered_set<uint32_t>* merging_blocks) const {
|
||||
std::unordered_set<uint32_t>* merging_blocks) const {
|
||||
assert(GetMergeBlock() && "This loop is not structured");
|
||||
ir::CFG* cfg = context->cfg();
|
||||
ir::CFG* cfg = context_->cfg();
|
||||
merging_blocks->clear();
|
||||
|
||||
std::stack<const ir::BasicBlock*> to_visit;
|
||||
to_visit.push(GetMergeBlock());
|
||||
@ -269,12 +269,14 @@ void Loop::GetMergingBlocks(
|
||||
}
|
||||
|
||||
bool Loop::IsLCSSA() const {
|
||||
IRContext* context = GetHeaderBlock()->GetParent()->GetParent()->context();
|
||||
ir::CFG* cfg = context->cfg();
|
||||
opt::analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
|
||||
ir::CFG* cfg = context_->cfg();
|
||||
opt::analysis::DefUseManager* def_use_mgr = context_->get_def_use_mgr();
|
||||
|
||||
std::unordered_set<uint32_t> exit_blocks;
|
||||
GetExitBlocks(context, &exit_blocks);
|
||||
GetExitBlocks(&exit_blocks);
|
||||
|
||||
// Declare ir_context so we can capture context_ in the below lambda
|
||||
ir::IRContext* ir_context = context_;
|
||||
|
||||
for (uint32_t bb_id : GetBlocks()) {
|
||||
for (Instruction& insn : *cfg->block(bb_id)) {
|
||||
@ -283,8 +285,8 @@ bool Loop::IsLCSSA() const {
|
||||
// - In an exit block and in a phi instruction.
|
||||
if (!def_use_mgr->WhileEachUser(
|
||||
&insn,
|
||||
[&exit_blocks, context, this](ir::Instruction* use) -> bool {
|
||||
BasicBlock* parent = context->get_instr_block(use);
|
||||
[&exit_blocks, ir_context, this](ir::Instruction* use) -> bool {
|
||||
BasicBlock* parent = ir_context->get_instr_block(use);
|
||||
assert(parent && "Invalid analysis");
|
||||
if (IsInsideLoop(parent)) return true;
|
||||
if (use->opcode() != SpvOpPhi) return false;
|
||||
@ -296,6 +298,27 @@ bool Loop::IsLCSSA() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Loop::ShouldHoistInstruction(IRContext* context, Instruction* inst) {
|
||||
return AreAllOperandsOutsideLoop(context, inst) &&
|
||||
inst->IsOpcodeCodeMotionSafe();
|
||||
}
|
||||
|
||||
bool Loop::AreAllOperandsOutsideLoop(IRContext* context, Instruction* inst) {
|
||||
opt::analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
|
||||
bool all_outside_loop = true;
|
||||
|
||||
const std::function<void(uint32_t*)> operand_outside_loop =
|
||||
[this, &def_use_mgr, &all_outside_loop](uint32_t* id) {
|
||||
if (this->IsInsideLoop(def_use_mgr->GetDef(*id))) {
|
||||
all_outside_loop = false;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
inst->ForEachInId(operand_outside_loop);
|
||||
return all_outside_loop;
|
||||
}
|
||||
|
||||
LoopDescriptor::LoopDescriptor(const Function* f) : loops_() {
|
||||
PopulateList(f);
|
||||
}
|
||||
@ -304,7 +327,6 @@ LoopDescriptor::~LoopDescriptor() { ClearLoops(); }
|
||||
|
||||
void LoopDescriptor::PopulateList(const Function* f) {
|
||||
IRContext* context = f->GetParent()->context();
|
||||
|
||||
opt::DominatorAnalysis* dom_analysis =
|
||||
context->GetDominatorAnalysis(f, *context->cfg());
|
||||
|
||||
|
@ -47,7 +47,8 @@ class Loop {
|
||||
using BasicBlockListTy = std::unordered_set<uint32_t>;
|
||||
|
||||
Loop()
|
||||
: loop_header_(nullptr),
|
||||
: context_(nullptr),
|
||||
loop_header_(nullptr),
|
||||
loop_continue_(nullptr),
|
||||
loop_merge_(nullptr),
|
||||
loop_preheader_(nullptr),
|
||||
@ -115,22 +116,20 @@ class Loop {
|
||||
|
||||
// Returns the loop pre-header, if there is no suitable preheader it will be
|
||||
// created.
|
||||
BasicBlock* GetOrCreatePreHeaderBlock(ir::IRContext* context);
|
||||
BasicBlock* GetOrCreatePreHeaderBlock();
|
||||
|
||||
// Returns true if this loop contains any nested loops.
|
||||
inline bool HasNestedLoops() const { return nested_loops_.size() != 0; }
|
||||
|
||||
// Fills |exit_blocks| with all basic blocks that are not in the loop and has
|
||||
// at least one predecessor in the loop.
|
||||
void GetExitBlocks(IRContext* context,
|
||||
std::unordered_set<uint32_t>* exit_blocks) const;
|
||||
// Clears and fills |exit_blocks| with all basic blocks that are not in the
|
||||
// loop and has at least one predecessor in the loop.
|
||||
void GetExitBlocks(std::unordered_set<uint32_t>* exit_blocks) const;
|
||||
|
||||
// Fills |merging_blocks| with all basic blocks that are post-dominated by the
|
||||
// merge block. The merge block must exist.
|
||||
// Clears and fills |merging_blocks| with all basic blocks that are
|
||||
// post-dominated by the merge block. The merge block must exist.
|
||||
// The set |merging_blocks| will only contain the merge block if it is
|
||||
// unreachable.
|
||||
void GetMergingBlocks(IRContext* context,
|
||||
std::unordered_set<uint32_t>* merging_blocks) const;
|
||||
void GetMergingBlocks(std::unordered_set<uint32_t>* merging_blocks) const;
|
||||
|
||||
// Returns true if the loop is in a Loop Closed SSA form.
|
||||
// In LCSSA form, all in-loop definitions are used in the loop or in phi
|
||||
@ -200,7 +199,15 @@ class Loop {
|
||||
// as a nested child loop.
|
||||
inline void SetParent(Loop* parent) { parent_ = parent; }
|
||||
|
||||
// Returns true is the instruction is invariant and safe to move wrt loop
|
||||
bool ShouldHoistInstruction(IRContext* context, Instruction* inst);
|
||||
|
||||
// Returns true if all operands of inst are in basic blocks not contained in
|
||||
// loop
|
||||
bool AreAllOperandsOutsideLoop(IRContext* context, Instruction* inst);
|
||||
|
||||
private:
|
||||
IRContext* context_;
|
||||
// The block which marks the start of the loop.
|
||||
BasicBlock* loop_header_;
|
||||
|
||||
@ -229,8 +236,7 @@ class Loop {
|
||||
bool IsBasicBlockInLoopSlow(const BasicBlock* bb);
|
||||
|
||||
// Returns the loop preheader if it exists, returns nullptr otherwise.
|
||||
BasicBlock* FindLoopPreheader(IRContext* context,
|
||||
opt::DominatorAnalysis* dom_analysis);
|
||||
BasicBlock* FindLoopPreheader(opt::DominatorAnalysis* dom_analysis);
|
||||
|
||||
// Sets |latch| as the loop unique continue block. No checks are performed
|
||||
// here.
|
||||
@ -266,6 +272,7 @@ class LoopDescriptor {
|
||||
other.loops_.clear();
|
||||
basic_block_to_loop_ = std::move(other.basic_block_to_loop_);
|
||||
other.basic_block_to_loop_.clear();
|
||||
dummy_top_loop_ = std::move(other.dummy_top_loop_);
|
||||
}
|
||||
|
||||
// Destructor
|
||||
|
@ -331,7 +331,7 @@ void LoopUtils::CreateLoopDedicatedExits() {
|
||||
// Gathers the set of basic block that are not in this loop and have at least
|
||||
// one predecessor in the loop and one not in the loop.
|
||||
std::unordered_set<uint32_t> exit_bb_set;
|
||||
loop_->GetExitBlocks(context_, &exit_bb_set);
|
||||
loop_->GetExitBlocks(&exit_bb_set);
|
||||
|
||||
std::unordered_set<ir::BasicBlock*> new_loop_exits;
|
||||
bool made_change = false;
|
||||
@ -448,7 +448,7 @@ void LoopUtils::MakeLoopClosedSSA() {
|
||||
std::unordered_set<ir::BasicBlock*> exit_bb;
|
||||
{
|
||||
std::unordered_set<uint32_t> exit_bb_id;
|
||||
loop_->GetExitBlocks(context_, &exit_bb_id);
|
||||
loop_->GetExitBlocks(&exit_bb_id);
|
||||
for (uint32_t bb_id : exit_bb_id) {
|
||||
exit_bb.insert(cfg.block(bb_id));
|
||||
}
|
||||
@ -463,7 +463,7 @@ void LoopUtils::MakeLoopClosedSSA() {
|
||||
// further than the merge block.
|
||||
if (loop_->GetMergeBlock()) {
|
||||
std::unordered_set<uint32_t> merging_bb_id;
|
||||
loop_->GetMergingBlocks(context_, &merging_bb_id);
|
||||
loop_->GetMergingBlocks(&merging_bb_id);
|
||||
merging_bb_id.erase(loop_->GetMergeBlock()->id());
|
||||
// Reset the exit set, now only the merge block is the exit.
|
||||
exit_bb.clear();
|
||||
|
@ -30,8 +30,8 @@ class LoopUtils {
|
||||
LoopUtils(ir::IRContext* context, ir::Loop* loop)
|
||||
: context_(context), loop_(loop) {}
|
||||
|
||||
// The make the current loop in the loop closed SSA form.
|
||||
// In the loop closed SSA, all loop exiting values goes through a dedicate SSA
|
||||
// The converts the current loop to loop closed SSA form.
|
||||
// In the loop closed SSA, all loop exiting values go through a dedicated Phi
|
||||
// instruction. For instance:
|
||||
//
|
||||
// for (...) {
|
||||
|
@ -342,6 +342,10 @@ Optimizer::PassToken CreateLocalRedundancyEliminationPass() {
|
||||
MakeUnique<opt::LocalRedundancyEliminationPass>());
|
||||
}
|
||||
|
||||
Optimizer::PassToken CreateLoopInvariantCodeMotionPass() {
|
||||
return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::LICMPass>());
|
||||
}
|
||||
|
||||
Optimizer::PassToken CreateRedundancyEliminationPass() {
|
||||
return MakeUnique<Optimizer::PassToken::Impl>(
|
||||
MakeUnique<opt::RedundancyEliminationPass>());
|
||||
|
@ -35,6 +35,7 @@
|
||||
#include "inline_exhaustive_pass.h"
|
||||
#include "inline_opaque_pass.h"
|
||||
#include "insert_extract_elim.h"
|
||||
#include "licm_pass.h"
|
||||
#include "local_access_chain_convert_pass.h"
|
||||
#include "local_redundancy_elimination.h"
|
||||
#include "local_single_block_elim_pass.h"
|
||||
|
@ -30,3 +30,39 @@ add_spvtools_unittest(TARGET lcssa_test
|
||||
lcssa.cpp
|
||||
LIBS SPIRV-Tools-opt
|
||||
)
|
||||
|
||||
add_spvtools_unittest(TARGET licm_all_loop_types
|
||||
SRCS ../function_utils.h
|
||||
hoist_all_loop_types.cpp
|
||||
LIBS SPIRV-Tools-opt
|
||||
)
|
||||
|
||||
add_spvtools_unittest(TARGET licm_hoist_independent_loops
|
||||
SRCS ../function_utils.h
|
||||
hoist_from_independent_loops.cpp
|
||||
LIBS SPIRV-Tools-opt
|
||||
)
|
||||
|
||||
add_spvtools_unittest(TARGET licm_hoist_double_nested_loops
|
||||
SRCS ../function_utils.h
|
||||
hoist_double_nested_loops.cpp
|
||||
LIBS SPIRV-Tools-opt
|
||||
)
|
||||
|
||||
add_spvtools_unittest(TARGET licm_hoist_single_nested_loops
|
||||
SRCS ../function_utils.h
|
||||
hoist_single_nested_loops.cpp
|
||||
LIBS SPIRV-Tools-opt
|
||||
)
|
||||
|
||||
add_spvtools_unittest(TARGET licm_hoist_simple_case
|
||||
SRCS ../function_utils.h
|
||||
hoist_simple_case.cpp
|
||||
LIBS SPIRV-Tools-opt
|
||||
)
|
||||
|
||||
add_spvtools_unittest(TARGET licm_hoist_no_preheader
|
||||
SRCS ../function_utils.h
|
||||
hoist_without_preheader.cpp
|
||||
LIBS SPIRV-Tools-opt
|
||||
)
|
||||
|
284
test/opt/loop_optimizations/hoist_all_loop_types.cpp
Normal file
284
test/opt/loop_optimizations/hoist_all_loop_types.cpp
Normal file
@ -0,0 +1,284 @@
|
||||
// Copyright (c) 2018 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 <string>
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
|
||||
#include "../pass_fixture.h"
|
||||
#include "opt/licm_pass.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace spvtools;
|
||||
using ::testing::UnorderedElementsAre;
|
||||
|
||||
using PassClassTest = PassTest<::testing::Test>;
|
||||
|
||||
/*
|
||||
Tests that all loop types are handled appropriately by the LICM pass.
|
||||
|
||||
Generated from the following GLSL fragment shader
|
||||
--eliminate-local-multi-store has also been run on the spv binary
|
||||
#version 440 core
|
||||
void main(){
|
||||
int i_1 = 0;
|
||||
for (i_1 = 0; i_1 < 10; i_1++) {
|
||||
}
|
||||
int i_2 = 0;
|
||||
while (i_2 < 10) {
|
||||
i_2++;
|
||||
}
|
||||
int i_3 = 0;
|
||||
do {
|
||||
i_3++;
|
||||
} while (i_3 < 10);
|
||||
int hoist = 0;
|
||||
int i_4 = 0;
|
||||
int i_5 = 0;
|
||||
int i_6 = 0;
|
||||
for (i_4 = 0; i_4 < 10; i_4++) {
|
||||
while (i_5 < 10) {
|
||||
do {
|
||||
hoist = i_1 + i_2 + i_3;
|
||||
i_6++;
|
||||
} while (i_6 < 10);
|
||||
i_5++;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
TEST_F(PassClassTest, AllLoopTypes) {
|
||||
const std::string before_hoist = R"(OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %main "main"
|
||||
OpExecutionMode %main OriginUpperLeft
|
||||
OpSource GLSL 440
|
||||
OpName %main "main"
|
||||
%void = OpTypeVoid
|
||||
%4 = OpTypeFunction %void
|
||||
%int = OpTypeInt 32 1
|
||||
%_ptr_Function_int = OpTypePointer Function %int
|
||||
%int_0 = OpConstant %int 0
|
||||
%int_10 = OpConstant %int 10
|
||||
%bool = OpTypeBool
|
||||
%int_1 = OpConstant %int 1
|
||||
%main = OpFunction %void None %4
|
||||
%11 = OpLabel
|
||||
OpBranch %12
|
||||
%12 = OpLabel
|
||||
%13 = OpPhi %int %int_0 %11 %14 %15
|
||||
OpLoopMerge %16 %15 None
|
||||
OpBranch %17
|
||||
%17 = OpLabel
|
||||
%18 = OpSLessThan %bool %13 %int_10
|
||||
OpBranchConditional %18 %19 %16
|
||||
%19 = OpLabel
|
||||
OpBranch %15
|
||||
%15 = OpLabel
|
||||
%14 = OpIAdd %int %13 %int_1
|
||||
OpBranch %12
|
||||
%16 = OpLabel
|
||||
OpBranch %20
|
||||
%20 = OpLabel
|
||||
%21 = OpPhi %int %int_0 %16 %22 %23
|
||||
OpLoopMerge %24 %23 None
|
||||
OpBranch %25
|
||||
%25 = OpLabel
|
||||
%26 = OpSLessThan %bool %21 %int_10
|
||||
OpBranchConditional %26 %27 %24
|
||||
%27 = OpLabel
|
||||
%22 = OpIAdd %int %21 %int_1
|
||||
OpBranch %23
|
||||
%23 = OpLabel
|
||||
OpBranch %20
|
||||
%24 = OpLabel
|
||||
OpBranch %28
|
||||
%28 = OpLabel
|
||||
%29 = OpPhi %int %int_0 %24 %30 %31
|
||||
OpLoopMerge %32 %31 None
|
||||
OpBranch %33
|
||||
%33 = OpLabel
|
||||
%30 = OpIAdd %int %29 %int_1
|
||||
OpBranch %31
|
||||
%31 = OpLabel
|
||||
%34 = OpSLessThan %bool %30 %int_10
|
||||
OpBranchConditional %34 %28 %32
|
||||
%32 = OpLabel
|
||||
OpBranch %35
|
||||
%35 = OpLabel
|
||||
%36 = OpPhi %int %int_0 %32 %37 %38
|
||||
%39 = OpPhi %int %int_0 %32 %40 %38
|
||||
%41 = OpPhi %int %int_0 %32 %42 %38
|
||||
%43 = OpPhi %int %int_0 %32 %44 %38
|
||||
OpLoopMerge %45 %38 None
|
||||
OpBranch %46
|
||||
%46 = OpLabel
|
||||
%47 = OpSLessThan %bool %39 %int_10
|
||||
OpBranchConditional %47 %48 %45
|
||||
%48 = OpLabel
|
||||
OpBranch %49
|
||||
%49 = OpLabel
|
||||
%37 = OpPhi %int %36 %48 %50 %51
|
||||
%42 = OpPhi %int %41 %48 %52 %51
|
||||
%44 = OpPhi %int %43 %48 %53 %51
|
||||
OpLoopMerge %54 %51 None
|
||||
OpBranch %55
|
||||
%55 = OpLabel
|
||||
%56 = OpSLessThan %bool %42 %int_10
|
||||
OpBranchConditional %56 %57 %54
|
||||
%57 = OpLabel
|
||||
OpBranch %58
|
||||
%58 = OpLabel
|
||||
%59 = OpPhi %int %37 %57 %50 %60
|
||||
%61 = OpPhi %int %44 %57 %53 %60
|
||||
OpLoopMerge %62 %60 None
|
||||
OpBranch %63
|
||||
%63 = OpLabel
|
||||
%64 = OpIAdd %int %13 %21
|
||||
%50 = OpIAdd %int %64 %30
|
||||
%53 = OpIAdd %int %61 %int_1
|
||||
OpBranch %60
|
||||
%60 = OpLabel
|
||||
%65 = OpSLessThan %bool %53 %int_10
|
||||
OpBranchConditional %65 %58 %62
|
||||
%62 = OpLabel
|
||||
%52 = OpIAdd %int %42 %int_1
|
||||
OpBranch %51
|
||||
%51 = OpLabel
|
||||
OpBranch %49
|
||||
%54 = OpLabel
|
||||
OpBranch %38
|
||||
%38 = OpLabel
|
||||
%40 = OpIAdd %int %39 %int_1
|
||||
OpBranch %35
|
||||
%45 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const std::string after_hoist = R"(OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %main "main"
|
||||
OpExecutionMode %main OriginUpperLeft
|
||||
OpSource GLSL 440
|
||||
OpName %main "main"
|
||||
%void = OpTypeVoid
|
||||
%4 = OpTypeFunction %void
|
||||
%int = OpTypeInt 32 1
|
||||
%_ptr_Function_int = OpTypePointer Function %int
|
||||
%int_0 = OpConstant %int 0
|
||||
%int_10 = OpConstant %int 10
|
||||
%bool = OpTypeBool
|
||||
%int_1 = OpConstant %int 1
|
||||
%main = OpFunction %void None %4
|
||||
%11 = OpLabel
|
||||
OpBranch %12
|
||||
%12 = OpLabel
|
||||
%13 = OpPhi %int %int_0 %11 %14 %15
|
||||
OpLoopMerge %16 %15 None
|
||||
OpBranch %17
|
||||
%17 = OpLabel
|
||||
%18 = OpSLessThan %bool %13 %int_10
|
||||
OpBranchConditional %18 %19 %16
|
||||
%19 = OpLabel
|
||||
OpBranch %15
|
||||
%15 = OpLabel
|
||||
%14 = OpIAdd %int %13 %int_1
|
||||
OpBranch %12
|
||||
%16 = OpLabel
|
||||
OpBranch %20
|
||||
%20 = OpLabel
|
||||
%21 = OpPhi %int %int_0 %16 %22 %23
|
||||
OpLoopMerge %24 %23 None
|
||||
OpBranch %25
|
||||
%25 = OpLabel
|
||||
%26 = OpSLessThan %bool %21 %int_10
|
||||
OpBranchConditional %26 %27 %24
|
||||
%27 = OpLabel
|
||||
%22 = OpIAdd %int %21 %int_1
|
||||
OpBranch %23
|
||||
%23 = OpLabel
|
||||
OpBranch %20
|
||||
%24 = OpLabel
|
||||
OpBranch %28
|
||||
%28 = OpLabel
|
||||
%29 = OpPhi %int %int_0 %24 %30 %31
|
||||
OpLoopMerge %32 %31 None
|
||||
OpBranch %33
|
||||
%33 = OpLabel
|
||||
%30 = OpIAdd %int %29 %int_1
|
||||
OpBranch %31
|
||||
%31 = OpLabel
|
||||
%34 = OpSLessThan %bool %30 %int_10
|
||||
OpBranchConditional %34 %28 %32
|
||||
%32 = OpLabel
|
||||
%64 = OpIAdd %int %13 %21
|
||||
%50 = OpIAdd %int %64 %30
|
||||
OpBranch %35
|
||||
%35 = OpLabel
|
||||
%36 = OpPhi %int %int_0 %32 %37 %38
|
||||
%39 = OpPhi %int %int_0 %32 %40 %38
|
||||
%41 = OpPhi %int %int_0 %32 %42 %38
|
||||
%43 = OpPhi %int %int_0 %32 %44 %38
|
||||
OpLoopMerge %45 %38 None
|
||||
OpBranch %46
|
||||
%46 = OpLabel
|
||||
%47 = OpSLessThan %bool %39 %int_10
|
||||
OpBranchConditional %47 %48 %45
|
||||
%48 = OpLabel
|
||||
OpBranch %49
|
||||
%49 = OpLabel
|
||||
%37 = OpPhi %int %36 %48 %50 %51
|
||||
%42 = OpPhi %int %41 %48 %52 %51
|
||||
%44 = OpPhi %int %43 %48 %53 %51
|
||||
OpLoopMerge %54 %51 None
|
||||
OpBranch %55
|
||||
%55 = OpLabel
|
||||
%56 = OpSLessThan %bool %42 %int_10
|
||||
OpBranchConditional %56 %57 %54
|
||||
%57 = OpLabel
|
||||
OpBranch %58
|
||||
%58 = OpLabel
|
||||
%59 = OpPhi %int %37 %57 %50 %60
|
||||
%61 = OpPhi %int %44 %57 %53 %60
|
||||
OpLoopMerge %62 %60 None
|
||||
OpBranch %63
|
||||
%63 = OpLabel
|
||||
%53 = OpIAdd %int %61 %int_1
|
||||
OpBranch %60
|
||||
%60 = OpLabel
|
||||
%65 = OpSLessThan %bool %53 %int_10
|
||||
OpBranchConditional %65 %58 %62
|
||||
%62 = OpLabel
|
||||
%52 = OpIAdd %int %42 %int_1
|
||||
OpBranch %51
|
||||
%51 = OpLabel
|
||||
OpBranch %49
|
||||
%54 = OpLabel
|
||||
OpBranch %38
|
||||
%38 = OpLabel
|
||||
%40 = OpIAdd %int %39 %int_1
|
||||
OpBranch %35
|
||||
%45 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SinglePassRunAndCheck<opt::LICMPass>(before_hoist, after_hoist, true);
|
||||
}
|
||||
|
||||
} // namespace
|
161
test/opt/loop_optimizations/hoist_double_nested_loops.cpp
Normal file
161
test/opt/loop_optimizations/hoist_double_nested_loops.cpp
Normal file
@ -0,0 +1,161 @@
|
||||
// Copyright (c) 2018 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 <string>
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
|
||||
#include "../pass_fixture.h"
|
||||
#include "opt/licm_pass.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace spvtools;
|
||||
using ::testing::UnorderedElementsAre;
|
||||
|
||||
using PassClassTest = PassTest<::testing::Test>;
|
||||
|
||||
/*
|
||||
Tests that the LICM pass will move invariants through multiple loops
|
||||
|
||||
Generated from the following GLSL fragment shader
|
||||
--eliminate-local-multi-store has also been run on the spv binary
|
||||
#version 440 core
|
||||
void main(){
|
||||
int a = 2;
|
||||
int b = 1;
|
||||
int hoist = 0;
|
||||
for (int i = 0; i < 10; i++) {
|
||||
for (int j = 0; j < 10; j++) {
|
||||
// hoist 'hoist = a - b' out of both loops
|
||||
hoist = a - b;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
TEST_F(PassClassTest, NestedDoubleHoist) {
|
||||
const std::string before_hoist = R"(OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %main "main"
|
||||
OpExecutionMode %main OriginUpperLeft
|
||||
OpSource GLSL 440
|
||||
OpName %main "main"
|
||||
%void = OpTypeVoid
|
||||
%4 = OpTypeFunction %void
|
||||
%int = OpTypeInt 32 1
|
||||
%_ptr_Function_int = OpTypePointer Function %int
|
||||
%int_2 = OpConstant %int 2
|
||||
%int_1 = OpConstant %int 1
|
||||
%int_0 = OpConstant %int 0
|
||||
%int_10 = OpConstant %int 10
|
||||
%bool = OpTypeBool
|
||||
%12 = OpUndef %int
|
||||
%main = OpFunction %void None %4
|
||||
%13 = OpLabel
|
||||
OpBranch %14
|
||||
%14 = OpLabel
|
||||
%15 = OpPhi %int %int_0 %13 %16 %17
|
||||
%18 = OpPhi %int %int_0 %13 %19 %17
|
||||
%20 = OpPhi %int %12 %13 %21 %17
|
||||
OpLoopMerge %22 %17 None
|
||||
OpBranch %23
|
||||
%23 = OpLabel
|
||||
%24 = OpSLessThan %bool %18 %int_10
|
||||
OpBranchConditional %24 %25 %22
|
||||
%25 = OpLabel
|
||||
OpBranch %26
|
||||
%26 = OpLabel
|
||||
%16 = OpPhi %int %15 %25 %27 %28
|
||||
%21 = OpPhi %int %int_0 %25 %29 %28
|
||||
OpLoopMerge %30 %28 None
|
||||
OpBranch %31
|
||||
%31 = OpLabel
|
||||
%32 = OpSLessThan %bool %21 %int_10
|
||||
OpBranchConditional %32 %33 %30
|
||||
%33 = OpLabel
|
||||
%27 = OpISub %int %int_2 %int_1
|
||||
OpBranch %28
|
||||
%28 = OpLabel
|
||||
%29 = OpIAdd %int %21 %int_1
|
||||
OpBranch %26
|
||||
%30 = OpLabel
|
||||
OpBranch %17
|
||||
%17 = OpLabel
|
||||
%19 = OpIAdd %int %18 %int_1
|
||||
OpBranch %14
|
||||
%22 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const std::string after_hoist = R"(OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %main "main"
|
||||
OpExecutionMode %main OriginUpperLeft
|
||||
OpSource GLSL 440
|
||||
OpName %main "main"
|
||||
%void = OpTypeVoid
|
||||
%4 = OpTypeFunction %void
|
||||
%int = OpTypeInt 32 1
|
||||
%_ptr_Function_int = OpTypePointer Function %int
|
||||
%int_2 = OpConstant %int 2
|
||||
%int_1 = OpConstant %int 1
|
||||
%int_0 = OpConstant %int 0
|
||||
%int_10 = OpConstant %int 10
|
||||
%bool = OpTypeBool
|
||||
%12 = OpUndef %int
|
||||
%main = OpFunction %void None %4
|
||||
%13 = OpLabel
|
||||
%27 = OpISub %int %int_2 %int_1
|
||||
OpBranch %14
|
||||
%14 = OpLabel
|
||||
%15 = OpPhi %int %int_0 %13 %16 %17
|
||||
%18 = OpPhi %int %int_0 %13 %19 %17
|
||||
%20 = OpPhi %int %12 %13 %21 %17
|
||||
OpLoopMerge %22 %17 None
|
||||
OpBranch %23
|
||||
%23 = OpLabel
|
||||
%24 = OpSLessThan %bool %18 %int_10
|
||||
OpBranchConditional %24 %25 %22
|
||||
%25 = OpLabel
|
||||
OpBranch %26
|
||||
%26 = OpLabel
|
||||
%16 = OpPhi %int %15 %25 %27 %28
|
||||
%21 = OpPhi %int %int_0 %25 %29 %28
|
||||
OpLoopMerge %30 %28 None
|
||||
OpBranch %31
|
||||
%31 = OpLabel
|
||||
%32 = OpSLessThan %bool %21 %int_10
|
||||
OpBranchConditional %32 %33 %30
|
||||
%33 = OpLabel
|
||||
OpBranch %28
|
||||
%28 = OpLabel
|
||||
%29 = OpIAdd %int %21 %int_1
|
||||
OpBranch %26
|
||||
%30 = OpLabel
|
||||
OpBranch %17
|
||||
%17 = OpLabel
|
||||
%19 = OpIAdd %int %18 %int_1
|
||||
OpBranch %14
|
||||
%22 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SinglePassRunAndCheck<opt::LICMPass>(before_hoist, after_hoist, true);
|
||||
}
|
||||
|
||||
} // namespace
|
200
test/opt/loop_optimizations/hoist_from_independent_loops.cpp
Normal file
200
test/opt/loop_optimizations/hoist_from_independent_loops.cpp
Normal file
@ -0,0 +1,200 @@
|
||||
// Copyright (c) 2018 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 <string>
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
|
||||
#include "../pass_fixture.h"
|
||||
#include "opt/licm_pass.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace spvtools;
|
||||
using ::testing::UnorderedElementsAre;
|
||||
|
||||
using PassClassTest = PassTest<::testing::Test>;
|
||||
|
||||
/*
|
||||
Tests that the LICM pass will analyse multiple independent loops in a function
|
||||
|
||||
Generated from the following GLSL fragment shader
|
||||
--eliminate-local-multi-store has also been run on the spv binary
|
||||
#version 440 core
|
||||
void main(){
|
||||
int a = 1;
|
||||
int b = 2;
|
||||
int hoist = 0;
|
||||
for (int i = 0; i < 10; i++) {
|
||||
// invariant
|
||||
hoist = a + b;
|
||||
}
|
||||
for (int i = 0; i < 10; i++) {
|
||||
// invariant
|
||||
hoist = a + b;
|
||||
}
|
||||
int c = 1;
|
||||
int d = 2;
|
||||
int hoist2 = 0;
|
||||
for (int i = 0; i < 10; i++) {
|
||||
// invariant
|
||||
hoist2 = c + d;
|
||||
}
|
||||
}
|
||||
*/
|
||||
TEST_F(PassClassTest, HoistFromIndependentLoops) {
|
||||
const std::string before_hoist = R"(OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %main "main"
|
||||
OpExecutionMode %main OriginUpperLeft
|
||||
OpSource GLSL 440
|
||||
OpName %main "main"
|
||||
%void = OpTypeVoid
|
||||
%4 = OpTypeFunction %void
|
||||
%int = OpTypeInt 32 1
|
||||
%_ptr_Function_int = OpTypePointer Function %int
|
||||
%int_1 = OpConstant %int 1
|
||||
%int_2 = OpConstant %int 2
|
||||
%int_0 = OpConstant %int 0
|
||||
%int_10 = OpConstant %int 10
|
||||
%bool = OpTypeBool
|
||||
%main = OpFunction %void None %4
|
||||
%12 = OpLabel
|
||||
OpBranch %13
|
||||
%13 = OpLabel
|
||||
%14 = OpPhi %int %int_0 %12 %15 %16
|
||||
%17 = OpPhi %int %int_0 %12 %18 %16
|
||||
OpLoopMerge %19 %16 None
|
||||
OpBranch %20
|
||||
%20 = OpLabel
|
||||
%21 = OpSLessThan %bool %17 %int_10
|
||||
OpBranchConditional %21 %22 %19
|
||||
%22 = OpLabel
|
||||
%15 = OpIAdd %int %int_1 %int_2
|
||||
OpBranch %16
|
||||
%16 = OpLabel
|
||||
%18 = OpIAdd %int %17 %int_1
|
||||
OpBranch %13
|
||||
%19 = OpLabel
|
||||
OpBranch %23
|
||||
%23 = OpLabel
|
||||
%24 = OpPhi %int %14 %19 %25 %26
|
||||
%27 = OpPhi %int %int_0 %19 %28 %26
|
||||
OpLoopMerge %29 %26 None
|
||||
OpBranch %30
|
||||
%30 = OpLabel
|
||||
%31 = OpSLessThan %bool %27 %int_10
|
||||
OpBranchConditional %31 %32 %29
|
||||
%32 = OpLabel
|
||||
%25 = OpIAdd %int %int_1 %int_2
|
||||
OpBranch %26
|
||||
%26 = OpLabel
|
||||
%28 = OpIAdd %int %27 %int_1
|
||||
OpBranch %23
|
||||
%29 = OpLabel
|
||||
OpBranch %33
|
||||
%33 = OpLabel
|
||||
%34 = OpPhi %int %int_0 %29 %35 %36
|
||||
%37 = OpPhi %int %int_0 %29 %38 %36
|
||||
OpLoopMerge %39 %36 None
|
||||
OpBranch %40
|
||||
%40 = OpLabel
|
||||
%41 = OpSLessThan %bool %37 %int_10
|
||||
OpBranchConditional %41 %42 %39
|
||||
%42 = OpLabel
|
||||
%35 = OpIAdd %int %int_1 %int_2
|
||||
OpBranch %36
|
||||
%36 = OpLabel
|
||||
%38 = OpIAdd %int %37 %int_1
|
||||
OpBranch %33
|
||||
%39 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const std::string after_hoist = R"(OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %main "main"
|
||||
OpExecutionMode %main OriginUpperLeft
|
||||
OpSource GLSL 440
|
||||
OpName %main "main"
|
||||
%void = OpTypeVoid
|
||||
%4 = OpTypeFunction %void
|
||||
%int = OpTypeInt 32 1
|
||||
%_ptr_Function_int = OpTypePointer Function %int
|
||||
%int_1 = OpConstant %int 1
|
||||
%int_2 = OpConstant %int 2
|
||||
%int_0 = OpConstant %int 0
|
||||
%int_10 = OpConstant %int 10
|
||||
%bool = OpTypeBool
|
||||
%main = OpFunction %void None %4
|
||||
%12 = OpLabel
|
||||
%15 = OpIAdd %int %int_1 %int_2
|
||||
OpBranch %13
|
||||
%13 = OpLabel
|
||||
%14 = OpPhi %int %int_0 %12 %15 %16
|
||||
%17 = OpPhi %int %int_0 %12 %18 %16
|
||||
OpLoopMerge %19 %16 None
|
||||
OpBranch %20
|
||||
%20 = OpLabel
|
||||
%21 = OpSLessThan %bool %17 %int_10
|
||||
OpBranchConditional %21 %22 %19
|
||||
%22 = OpLabel
|
||||
OpBranch %16
|
||||
%16 = OpLabel
|
||||
%18 = OpIAdd %int %17 %int_1
|
||||
OpBranch %13
|
||||
%19 = OpLabel
|
||||
%25 = OpIAdd %int %int_1 %int_2
|
||||
OpBranch %23
|
||||
%23 = OpLabel
|
||||
%24 = OpPhi %int %14 %19 %25 %26
|
||||
%27 = OpPhi %int %int_0 %19 %28 %26
|
||||
OpLoopMerge %29 %26 None
|
||||
OpBranch %30
|
||||
%30 = OpLabel
|
||||
%31 = OpSLessThan %bool %27 %int_10
|
||||
OpBranchConditional %31 %32 %29
|
||||
%32 = OpLabel
|
||||
OpBranch %26
|
||||
%26 = OpLabel
|
||||
%28 = OpIAdd %int %27 %int_1
|
||||
OpBranch %23
|
||||
%29 = OpLabel
|
||||
%35 = OpIAdd %int %int_1 %int_2
|
||||
OpBranch %33
|
||||
%33 = OpLabel
|
||||
%34 = OpPhi %int %int_0 %29 %35 %36
|
||||
%37 = OpPhi %int %int_0 %29 %38 %36
|
||||
OpLoopMerge %39 %36 None
|
||||
OpBranch %40
|
||||
%40 = OpLabel
|
||||
%41 = OpSLessThan %bool %37 %int_10
|
||||
OpBranchConditional %41 %42 %39
|
||||
%42 = OpLabel
|
||||
OpBranch %36
|
||||
%36 = OpLabel
|
||||
%38 = OpIAdd %int %37 %int_1
|
||||
OpBranch %33
|
||||
%39 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SinglePassRunAndCheck<opt::LICMPass>(before_hoist, after_hoist, true);
|
||||
}
|
||||
|
||||
} // namespace
|
125
test/opt/loop_optimizations/hoist_simple_case.cpp
Normal file
125
test/opt/loop_optimizations/hoist_simple_case.cpp
Normal file
@ -0,0 +1,125 @@
|
||||
// Copyright (c) 2018 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 <string>
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
|
||||
#include "../pass_fixture.h"
|
||||
#include "opt/licm_pass.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace spvtools;
|
||||
using ::testing::UnorderedElementsAre;
|
||||
|
||||
using PassClassTest = PassTest<::testing::Test>;
|
||||
|
||||
/*
|
||||
A simple test for the LICM pass
|
||||
|
||||
Generated from the following GLSL fragment shader
|
||||
--eliminate-local-multi-store has also been run on the spv binary
|
||||
#version 440 core
|
||||
void main(){
|
||||
int a = 1;
|
||||
int b = 2;
|
||||
int hoist = 0;
|
||||
for (int i = 0; i < 10; i++) {
|
||||
// invariant
|
||||
hoist = a + b;
|
||||
}
|
||||
}
|
||||
*/
|
||||
TEST_F(PassClassTest, SimpleHoist) {
|
||||
const std::string before_hoist = R"(OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %main "main"
|
||||
OpExecutionMode %main OriginUpperLeft
|
||||
OpSource GLSL 440
|
||||
OpName %main "main"
|
||||
%void = OpTypeVoid
|
||||
%4 = OpTypeFunction %void
|
||||
%int = OpTypeInt 32 1
|
||||
%_ptr_Function_int = OpTypePointer Function %int
|
||||
%int_1 = OpConstant %int 1
|
||||
%int_2 = OpConstant %int 2
|
||||
%int_0 = OpConstant %int 0
|
||||
%int_10 = OpConstant %int 10
|
||||
%bool = OpTypeBool
|
||||
%main = OpFunction %void None %4
|
||||
%12 = OpLabel
|
||||
OpBranch %13
|
||||
%13 = OpLabel
|
||||
%14 = OpPhi %int %int_0 %12 %15 %16
|
||||
%17 = OpPhi %int %int_0 %12 %18 %16
|
||||
OpLoopMerge %19 %16 None
|
||||
OpBranch %20
|
||||
%20 = OpLabel
|
||||
%21 = OpSLessThan %bool %17 %int_10
|
||||
OpBranchConditional %21 %22 %19
|
||||
%22 = OpLabel
|
||||
%15 = OpIAdd %int %int_1 %int_2
|
||||
OpBranch %16
|
||||
%16 = OpLabel
|
||||
%18 = OpIAdd %int %17 %int_1
|
||||
OpBranch %13
|
||||
%19 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const std::string after_hoist = R"(OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %main "main"
|
||||
OpExecutionMode %main OriginUpperLeft
|
||||
OpSource GLSL 440
|
||||
OpName %main "main"
|
||||
%void = OpTypeVoid
|
||||
%4 = OpTypeFunction %void
|
||||
%int = OpTypeInt 32 1
|
||||
%_ptr_Function_int = OpTypePointer Function %int
|
||||
%int_1 = OpConstant %int 1
|
||||
%int_2 = OpConstant %int 2
|
||||
%int_0 = OpConstant %int 0
|
||||
%int_10 = OpConstant %int 10
|
||||
%bool = OpTypeBool
|
||||
%main = OpFunction %void None %4
|
||||
%12 = OpLabel
|
||||
%15 = OpIAdd %int %int_1 %int_2
|
||||
OpBranch %13
|
||||
%13 = OpLabel
|
||||
%14 = OpPhi %int %int_0 %12 %15 %16
|
||||
%17 = OpPhi %int %int_0 %12 %18 %16
|
||||
OpLoopMerge %19 %16 None
|
||||
OpBranch %20
|
||||
%20 = OpLabel
|
||||
%21 = OpSLessThan %bool %17 %int_10
|
||||
OpBranchConditional %21 %22 %19
|
||||
%22 = OpLabel
|
||||
OpBranch %16
|
||||
%16 = OpLabel
|
||||
%18 = OpIAdd %int %17 %int_1
|
||||
OpBranch %13
|
||||
%19 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SinglePassRunAndCheck<opt::LICMPass>(before_hoist, after_hoist, true);
|
||||
}
|
||||
|
||||
} // namespace
|
161
test/opt/loop_optimizations/hoist_single_nested_loops.cpp
Normal file
161
test/opt/loop_optimizations/hoist_single_nested_loops.cpp
Normal file
@ -0,0 +1,161 @@
|
||||
// Copyright (c) 2018 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 <string>
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
|
||||
#include "../pass_fixture.h"
|
||||
#include "opt/licm_pass.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace spvtools;
|
||||
using ::testing::UnorderedElementsAre;
|
||||
|
||||
using PassClassTest = PassTest<::testing::Test>;
|
||||
|
||||
/*
|
||||
Tests that the LICM pass will detect an move an invariant from a nested loop,
|
||||
but not it's parent loop
|
||||
|
||||
Generated from the following GLSL fragment shader
|
||||
--eliminate-local-multi-store has also been run on the spv binary
|
||||
#version 440 core
|
||||
void main(){
|
||||
int a = 2;
|
||||
int hoist = 0;
|
||||
for (int i = 0; i < 10; i++) {
|
||||
for (int j = 0; j < 10; j++) {
|
||||
// hoist 'hoist = a - i' out of j loop, but not i loop
|
||||
hoist = a - i;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
TEST_F(PassClassTest, NestedSingleHoist) {
|
||||
const std::string before_hoist = R"(OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %main "main"
|
||||
OpExecutionMode %main OriginUpperLeft
|
||||
OpSource GLSL 440
|
||||
OpName %main "main"
|
||||
%void = OpTypeVoid
|
||||
%4 = OpTypeFunction %void
|
||||
%int = OpTypeInt 32 1
|
||||
%_ptr_Function_int = OpTypePointer Function %int
|
||||
%int_2 = OpConstant %int 2
|
||||
%int_0 = OpConstant %int 0
|
||||
%int_10 = OpConstant %int 10
|
||||
%bool = OpTypeBool
|
||||
%int_1 = OpConstant %int 1
|
||||
%12 = OpUndef %int
|
||||
%main = OpFunction %void None %4
|
||||
%13 = OpLabel
|
||||
OpBranch %14
|
||||
%14 = OpLabel
|
||||
%15 = OpPhi %int %int_0 %13 %16 %17
|
||||
%18 = OpPhi %int %int_0 %13 %19 %17
|
||||
%20 = OpPhi %int %12 %13 %21 %17
|
||||
OpLoopMerge %22 %17 None
|
||||
OpBranch %23
|
||||
%23 = OpLabel
|
||||
%24 = OpSLessThan %bool %18 %int_10
|
||||
OpBranchConditional %24 %25 %22
|
||||
%25 = OpLabel
|
||||
OpBranch %26
|
||||
%26 = OpLabel
|
||||
%16 = OpPhi %int %15 %25 %27 %28
|
||||
%21 = OpPhi %int %int_0 %25 %29 %28
|
||||
OpLoopMerge %30 %28 None
|
||||
OpBranch %31
|
||||
%31 = OpLabel
|
||||
%32 = OpSLessThan %bool %21 %int_10
|
||||
OpBranchConditional %32 %33 %30
|
||||
%33 = OpLabel
|
||||
%27 = OpISub %int %int_2 %18
|
||||
OpBranch %28
|
||||
%28 = OpLabel
|
||||
%29 = OpIAdd %int %21 %int_1
|
||||
OpBranch %26
|
||||
%30 = OpLabel
|
||||
OpBranch %17
|
||||
%17 = OpLabel
|
||||
%19 = OpIAdd %int %18 %int_1
|
||||
OpBranch %14
|
||||
%22 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const std::string after_hoist = R"(OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %main "main"
|
||||
OpExecutionMode %main OriginUpperLeft
|
||||
OpSource GLSL 440
|
||||
OpName %main "main"
|
||||
%void = OpTypeVoid
|
||||
%4 = OpTypeFunction %void
|
||||
%int = OpTypeInt 32 1
|
||||
%_ptr_Function_int = OpTypePointer Function %int
|
||||
%int_2 = OpConstant %int 2
|
||||
%int_0 = OpConstant %int 0
|
||||
%int_10 = OpConstant %int 10
|
||||
%bool = OpTypeBool
|
||||
%int_1 = OpConstant %int 1
|
||||
%12 = OpUndef %int
|
||||
%main = OpFunction %void None %4
|
||||
%13 = OpLabel
|
||||
OpBranch %14
|
||||
%14 = OpLabel
|
||||
%15 = OpPhi %int %int_0 %13 %16 %17
|
||||
%18 = OpPhi %int %int_0 %13 %19 %17
|
||||
%20 = OpPhi %int %12 %13 %21 %17
|
||||
OpLoopMerge %22 %17 None
|
||||
OpBranch %23
|
||||
%23 = OpLabel
|
||||
%24 = OpSLessThan %bool %18 %int_10
|
||||
OpBranchConditional %24 %25 %22
|
||||
%25 = OpLabel
|
||||
%27 = OpISub %int %int_2 %18
|
||||
OpBranch %26
|
||||
%26 = OpLabel
|
||||
%16 = OpPhi %int %15 %25 %27 %28
|
||||
%21 = OpPhi %int %int_0 %25 %29 %28
|
||||
OpLoopMerge %30 %28 None
|
||||
OpBranch %31
|
||||
%31 = OpLabel
|
||||
%32 = OpSLessThan %bool %21 %int_10
|
||||
OpBranchConditional %32 %33 %30
|
||||
%33 = OpLabel
|
||||
OpBranch %28
|
||||
%28 = OpLabel
|
||||
%29 = OpIAdd %int %21 %int_1
|
||||
OpBranch %26
|
||||
%30 = OpLabel
|
||||
OpBranch %17
|
||||
%17 = OpLabel
|
||||
%19 = OpIAdd %int %18 %int_1
|
||||
OpBranch %14
|
||||
%22 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SinglePassRunAndCheck<opt::LICMPass>(before_hoist, after_hoist, true);
|
||||
}
|
||||
|
||||
} // namespace
|
171
test/opt/loop_optimizations/hoist_without_preheader.cpp
Normal file
171
test/opt/loop_optimizations/hoist_without_preheader.cpp
Normal file
@ -0,0 +1,171 @@
|
||||
// Copyright (c) 2018 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 <string>
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
|
||||
#include "../pass_fixture.h"
|
||||
#include "opt/licm_pass.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace spvtools;
|
||||
using ::testing::UnorderedElementsAre;
|
||||
|
||||
using PassClassTest = PassTest<::testing::Test>;
|
||||
|
||||
/*
|
||||
Tests that the LICM pass will generate a preheader when one is not present
|
||||
|
||||
Generated from the following GLSL fragment shader
|
||||
--eliminate-local-multi-store has also been run on the spv binary
|
||||
#version 440 core
|
||||
void main(){
|
||||
int a = 1;
|
||||
int b = 2;
|
||||
int hoist = 0;
|
||||
for (int i = 0; i < 10; i++) {
|
||||
if (i == 5) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < 10; i++) {
|
||||
hoist = a + b;
|
||||
}
|
||||
}
|
||||
*/
|
||||
TEST_F(PassClassTest, HoistWithoutPreheader) {
|
||||
const std::string before_hoist = R"(OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %main "main"
|
||||
OpExecutionMode %main OriginUpperLeft
|
||||
OpSource GLSL 440
|
||||
OpName %main "main"
|
||||
%void = OpTypeVoid
|
||||
%4 = OpTypeFunction %void
|
||||
%int = OpTypeInt 32 1
|
||||
%_ptr_Function_int = OpTypePointer Function %int
|
||||
%int_1 = OpConstant %int 1
|
||||
%int_2 = OpConstant %int 2
|
||||
%int_0 = OpConstant %int 0
|
||||
%int_10 = OpConstant %int 10
|
||||
%bool = OpTypeBool
|
||||
%int_5 = OpConstant %int 5
|
||||
%main = OpFunction %void None %4
|
||||
%13 = OpLabel
|
||||
OpBranch %14
|
||||
%14 = OpLabel
|
||||
%15 = OpPhi %int %int_0 %13 %16 %17
|
||||
OpLoopMerge %25 %17 None
|
||||
OpBranch %19
|
||||
%19 = OpLabel
|
||||
%20 = OpSLessThan %bool %15 %int_10
|
||||
OpBranchConditional %20 %21 %25
|
||||
%21 = OpLabel
|
||||
%22 = OpIEqual %bool %15 %int_5
|
||||
OpSelectionMerge %23 None
|
||||
OpBranchConditional %22 %24 %23
|
||||
%24 = OpLabel
|
||||
OpBranch %25
|
||||
%23 = OpLabel
|
||||
OpBranch %17
|
||||
%17 = OpLabel
|
||||
%16 = OpIAdd %int %15 %int_1
|
||||
OpBranch %14
|
||||
%25 = OpLabel
|
||||
%26 = OpPhi %int %int_0 %24 %int_0 %19 %27 %28
|
||||
%29 = OpPhi %int %int_0 %24 %int_0 %19 %30 %28
|
||||
OpLoopMerge %31 %28 None
|
||||
OpBranch %32
|
||||
%32 = OpLabel
|
||||
%33 = OpSLessThan %bool %29 %int_10
|
||||
OpBranchConditional %33 %34 %31
|
||||
%34 = OpLabel
|
||||
%27 = OpIAdd %int %int_1 %int_2
|
||||
OpBranch %28
|
||||
%28 = OpLabel
|
||||
%30 = OpIAdd %int %29 %int_1
|
||||
OpBranch %25
|
||||
%31 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const std::string after_hoist = R"(OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %main "main"
|
||||
OpExecutionMode %main OriginUpperLeft
|
||||
OpSource GLSL 440
|
||||
OpName %main "main"
|
||||
%void = OpTypeVoid
|
||||
%4 = OpTypeFunction %void
|
||||
%int = OpTypeInt 32 1
|
||||
%_ptr_Function_int = OpTypePointer Function %int
|
||||
%int_1 = OpConstant %int 1
|
||||
%int_2 = OpConstant %int 2
|
||||
%int_0 = OpConstant %int 0
|
||||
%int_10 = OpConstant %int 10
|
||||
%bool = OpTypeBool
|
||||
%int_5 = OpConstant %int 5
|
||||
%main = OpFunction %void None %4
|
||||
%13 = OpLabel
|
||||
OpBranch %14
|
||||
%14 = OpLabel
|
||||
%15 = OpPhi %int %int_0 %13 %16 %17
|
||||
OpLoopMerge %18 %17 None
|
||||
OpBranch %19
|
||||
%19 = OpLabel
|
||||
%20 = OpSLessThan %bool %15 %int_10
|
||||
OpBranchConditional %20 %21 %34
|
||||
%21 = OpLabel
|
||||
%22 = OpIEqual %bool %15 %int_5
|
||||
OpSelectionMerge %23 None
|
||||
OpBranchConditional %22 %24 %23
|
||||
%24 = OpLabel
|
||||
OpBranch %34
|
||||
%23 = OpLabel
|
||||
OpBranch %17
|
||||
%17 = OpLabel
|
||||
%16 = OpIAdd %int %15 %int_1
|
||||
OpBranch %14
|
||||
%34 = OpLabel
|
||||
%35 = OpPhi %int %int_0 %24 %int_0 %19
|
||||
%36 = OpPhi %int %int_0 %24 %int_0 %19
|
||||
%26 = OpIAdd %int %int_1 %int_2
|
||||
OpBranch %18
|
||||
%18 = OpLabel
|
||||
%25 = OpPhi %int %26 %27 %35 %34
|
||||
%28 = OpPhi %int %29 %27 %36 %34
|
||||
OpLoopMerge %30 %27 None
|
||||
OpBranch %31
|
||||
%31 = OpLabel
|
||||
%32 = OpSLessThan %bool %28 %int_10
|
||||
OpBranchConditional %32 %33 %30
|
||||
%33 = OpLabel
|
||||
OpBranch %27
|
||||
%27 = OpLabel
|
||||
%29 = OpIAdd %int %28 %int_1
|
||||
OpBranch %18
|
||||
%30 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SinglePassRunAndCheck<opt::LICMPass>(before_hoist, after_hoist, true);
|
||||
}
|
||||
|
||||
} // namespace
|
@ -200,7 +200,7 @@ TEST_F(PassClassTest, LoopWithNoPreHeader) {
|
||||
|
||||
ir::Loop* loop = ld[27];
|
||||
EXPECT_EQ(loop->GetPreHeaderBlock(), nullptr);
|
||||
EXPECT_NE(loop->GetOrCreatePreHeaderBlock(context.get()), nullptr);
|
||||
EXPECT_NE(loop->GetOrCreatePreHeaderBlock(), nullptr);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
@ -725,7 +725,7 @@ TEST_F(PassClassTest, CreatePreheaderTest) {
|
||||
{
|
||||
ir::Loop& loop = *ld[33];
|
||||
EXPECT_EQ(loop.GetPreHeaderBlock(), nullptr);
|
||||
EXPECT_NE(loop.GetOrCreatePreHeaderBlock(context.get()), nullptr);
|
||||
EXPECT_NE(loop.GetOrCreatePreHeaderBlock(), nullptr);
|
||||
// Make sure the loop descriptor was properly updated.
|
||||
EXPECT_EQ(ld[loop.GetPreHeaderBlock()], ld[16]);
|
||||
{
|
||||
@ -762,7 +762,7 @@ TEST_F(PassClassTest, CreatePreheaderTest) {
|
||||
{
|
||||
ir::Loop& loop = *ld[41];
|
||||
EXPECT_EQ(loop.GetPreHeaderBlock(), nullptr);
|
||||
EXPECT_NE(loop.GetOrCreatePreHeaderBlock(context.get()), nullptr);
|
||||
EXPECT_NE(loop.GetOrCreatePreHeaderBlock(), nullptr);
|
||||
EXPECT_EQ(ld[loop.GetPreHeaderBlock()], nullptr);
|
||||
EXPECT_EQ(cfg->preds(loop.GetPreHeaderBlock()->id()).size(), 1u);
|
||||
EXPECT_EQ(cfg->preds(loop.GetPreHeaderBlock()->id())[0], 25u);
|
||||
|
@ -456,6 +456,8 @@ OptStatus ParseFlags(int argc, const char** argv, Optimizer* optimizer,
|
||||
optimizer->RegisterPass(CreateCFGCleanupPass());
|
||||
} else if (0 == strcmp(cur_arg, "--local-redundancy-elimination")) {
|
||||
optimizer->RegisterPass(CreateLocalRedundancyEliminationPass());
|
||||
} else if (0 == strcmp(cur_arg, "--loop-invariant-code-motion")) {
|
||||
optimizer->RegisterPass(CreateLoopInvariantCodeMotionPass());
|
||||
} else if (0 == strcmp(cur_arg, "--redundancy-elimination")) {
|
||||
optimizer->RegisterPass(CreateRedundancyEliminationPass());
|
||||
} else if (0 == strcmp(cur_arg, "--private-to-local")) {
|
||||
|
Loading…
Reference in New Issue
Block a user