mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-11-22 19:50:05 +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/instruction_list.cpp \
|
||||||
source/opt/ir_context.cpp \
|
source/opt/ir_context.cpp \
|
||||||
source/opt/ir_loader.cpp \
|
source/opt/ir_loader.cpp \
|
||||||
|
source/opt/licm_pass.cpp \
|
||||||
source/opt/local_access_chain_convert_pass.cpp \
|
source/opt/local_access_chain_convert_pass.cpp \
|
||||||
source/opt/local_redundancy_elimination.cpp \
|
source/opt/local_redundancy_elimination.cpp \
|
||||||
source/opt/local_single_block_elim_pass.cpp \
|
source/opt/local_single_block_elim_pass.cpp \
|
||||||
|
@ -463,6 +463,11 @@ Optimizer::PassToken CreateMergeReturnPass();
|
|||||||
// same value, and remove the redundant ones.
|
// same value, and remove the redundant ones.
|
||||||
Optimizer::PassToken CreateLocalRedundancyEliminationPass();
|
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.
|
// Create global value numbering pass.
|
||||||
// This pass will look for instructions where the same value is computed on all
|
// This pass will look for instructions where the same value is computed on all
|
||||||
// paths leading to the instruction. Those instructions are deleted.
|
// paths leading to the instruction. Those instructions are deleted.
|
||||||
|
@ -47,6 +47,7 @@ add_library(SPIRV-Tools-opt
|
|||||||
instruction.h
|
instruction.h
|
||||||
ir_context.h
|
ir_context.h
|
||||||
ir_loader.h
|
ir_loader.h
|
||||||
|
licm_pass.h
|
||||||
local_access_chain_convert_pass.h
|
local_access_chain_convert_pass.h
|
||||||
local_redundancy_elimination.h
|
local_redundancy_elimination.h
|
||||||
local_single_block_elim_pass.h
|
local_single_block_elim_pass.h
|
||||||
@ -116,6 +117,7 @@ add_library(SPIRV-Tools-opt
|
|||||||
instruction_list.cpp
|
instruction_list.cpp
|
||||||
ir_context.cpp
|
ir_context.cpp
|
||||||
ir_loader.cpp
|
ir_loader.cpp
|
||||||
|
licm_pass.cpp
|
||||||
local_access_chain_convert_pass.cpp
|
local_access_chain_convert_pass.cpp
|
||||||
local_redundancy_elimination.cpp
|
local_redundancy_elimination.cpp
|
||||||
local_single_block_elim_pass.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);
|
const ir::BasicBlock* pred_blk = block(id);
|
||||||
bool has_branch = false;
|
bool has_branch = false;
|
||||||
pred_blk->ForEachSuccessorLabel([&has_branch, blk_id](uint32_t succ) {
|
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);
|
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;
|
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 ir
|
||||||
} // namespace spvtools
|
} // namespace spvtools
|
||||||
|
@ -380,6 +380,9 @@ class Instruction : public utils::IntrusiveNodeBase<Instruction> {
|
|||||||
// Spec constant.
|
// Spec constant.
|
||||||
inline bool IsConstant() const;
|
inline bool IsConstant() const;
|
||||||
|
|
||||||
|
// Returns true if |this| is an instruction with an opcode safe to move
|
||||||
|
bool IsOpcodeCodeMotionSafe() const;
|
||||||
|
|
||||||
// Pretty-prints |inst|.
|
// Pretty-prints |inst|.
|
||||||
//
|
//
|
||||||
// Provides the disassembly of a specific instruction. Utilizes |inst|'s
|
// 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,
|
Loop::Loop(IRContext* context, opt::DominatorAnalysis* dom_analysis,
|
||||||
BasicBlock* header, BasicBlock* continue_target,
|
BasicBlock* header, BasicBlock* continue_target,
|
||||||
BasicBlock* merge_target)
|
BasicBlock* merge_target)
|
||||||
: loop_header_(header),
|
: context_(context),
|
||||||
|
loop_header_(header),
|
||||||
loop_continue_(continue_target),
|
loop_continue_(continue_target),
|
||||||
loop_merge_(merge_target),
|
loop_merge_(merge_target),
|
||||||
loop_preheader_(nullptr),
|
loop_preheader_(nullptr),
|
||||||
parent_(nullptr) {
|
parent_(nullptr) {
|
||||||
assert(context);
|
assert(context);
|
||||||
assert(dom_analysis);
|
assert(dom_analysis);
|
||||||
loop_preheader_ = FindLoopPreheader(context, dom_analysis);
|
loop_preheader_ = FindLoopPreheader(dom_analysis);
|
||||||
AddBasicBlockToLoop(header);
|
AddBasicBlockToLoop(header);
|
||||||
AddBasicBlockToLoop(continue_target);
|
AddBasicBlockToLoop(continue_target);
|
||||||
}
|
}
|
||||||
|
|
||||||
BasicBlock* Loop::FindLoopPreheader(IRContext* ir_context,
|
BasicBlock* Loop::FindLoopPreheader(opt::DominatorAnalysis* dom_analysis) {
|
||||||
opt::DominatorAnalysis* dom_analysis) {
|
CFG* cfg = context_->cfg();
|
||||||
CFG* cfg = ir_context->cfg();
|
|
||||||
opt::DominatorTree& dom_tree = dom_analysis->GetDomTree();
|
opt::DominatorTree& dom_tree = dom_analysis->GetDomTree();
|
||||||
opt::DominatorTreeNode* header_node = dom_tree.GetTreeNode(loop_header_);
|
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 {
|
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;
|
if (!parent_block) return false;
|
||||||
return IsInsideLoop(parent_block);
|
return IsInsideLoop(parent_block);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Loop::IsBasicBlockInLoopSlow(const BasicBlock* bb) {
|
bool Loop::IsBasicBlockInLoopSlow(const BasicBlock* bb) {
|
||||||
assert(bb->GetParent() && "The basic block does not belong to a function");
|
assert(bb->GetParent() && "The basic block does not belong to a function");
|
||||||
IRContext* context = bb->GetParent()->GetParent()->context();
|
|
||||||
|
|
||||||
opt::DominatorAnalysis* dom_analysis =
|
opt::DominatorAnalysis* dom_analysis =
|
||||||
context->GetDominatorAnalysis(bb->GetParent(), *context->cfg());
|
context_->GetDominatorAnalysis(bb->GetParent(), *context_->cfg());
|
||||||
if (!dom_analysis->Dominates(GetHeaderBlock(), bb)) return false;
|
if (!dom_analysis->Dominates(GetHeaderBlock(), bb)) return false;
|
||||||
|
|
||||||
opt::PostDominatorAnalysis* postdom_analysis =
|
opt::PostDominatorAnalysis* postdom_analysis =
|
||||||
context->GetPostDominatorAnalysis(bb->GetParent(), *context->cfg());
|
context_->GetPostDominatorAnalysis(bb->GetParent(), *context_->cfg());
|
||||||
if (!postdom_analysis->Dominates(GetMergeBlock(), bb)) return false;
|
if (!postdom_analysis->Dominates(GetMergeBlock(), bb)) return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
BasicBlock* Loop::GetOrCreatePreHeaderBlock(ir::IRContext* context) {
|
BasicBlock* Loop::GetOrCreatePreHeaderBlock() {
|
||||||
if (loop_preheader_) return loop_preheader_;
|
if (loop_preheader_) return loop_preheader_;
|
||||||
|
|
||||||
Function* fn = loop_header_->GetParent();
|
Function* fn = loop_header_->GetParent();
|
||||||
@ -117,7 +116,7 @@ BasicBlock* Loop::GetOrCreatePreHeaderBlock(ir::IRContext* context) {
|
|||||||
// Create the preheader basic block.
|
// Create the preheader basic block.
|
||||||
loop_preheader_ = &*header_it.InsertBefore(std::unique_ptr<ir::BasicBlock>(
|
loop_preheader_ = &*header_it.InsertBefore(std::unique_ptr<ir::BasicBlock>(
|
||||||
new ir::BasicBlock(std::unique_ptr<ir::Instruction>(new ir::Instruction(
|
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);
|
loop_preheader_->SetParent(fn);
|
||||||
uint32_t loop_preheader_id = loop_preheader_->id();
|
uint32_t loop_preheader_id = loop_preheader_->id();
|
||||||
|
|
||||||
@ -132,11 +131,11 @@ BasicBlock* Loop::GetOrCreatePreHeaderBlock(ir::IRContext* context) {
|
|||||||
// instruction;
|
// instruction;
|
||||||
// - Redirect all edges coming from outside the loop to the preheader.
|
// - Redirect all edges coming from outside the loop to the preheader.
|
||||||
opt::InstructionBuilder builder(
|
opt::InstructionBuilder builder(
|
||||||
context, loop_preheader_,
|
context_, loop_preheader_,
|
||||||
ir::IRContext::kAnalysisDefUse |
|
ir::IRContext::kAnalysisDefUse |
|
||||||
ir::IRContext::kAnalysisInstrToBlockMapping);
|
ir::IRContext::kAnalysisInstrToBlockMapping);
|
||||||
// Patch all the phi instructions.
|
// 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> preheader_phi_ops;
|
||||||
std::vector<uint32_t> header_phi_ops;
|
std::vector<uint32_t> header_phi_ops;
|
||||||
for (uint32_t i = 0; i < phi->NumInOperands(); i += 2) {
|
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);
|
preheader_insn_def = builder.AddPhi(phi->type_id(), preheader_phi_ops);
|
||||||
else
|
else
|
||||||
preheader_insn_def =
|
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.
|
// Build the new incoming edge.
|
||||||
header_phi_ops.push_back(preheader_insn_def->result_id());
|
header_phi_ops.push_back(preheader_insn_def->result_id());
|
||||||
header_phi_ops.push_back(loop_preheader_->id());
|
header_phi_ops.push_back(loop_preheader_->id());
|
||||||
@ -174,7 +173,7 @@ BasicBlock* Loop::GetOrCreatePreHeaderBlock(ir::IRContext* context) {
|
|||||||
builder.AddBranch(loop_header_->id());
|
builder.AddBranch(loop_header_->id());
|
||||||
|
|
||||||
// Redirect all out of loop branches to the header to the preheader.
|
// Redirect all out of loop branches to the header to the preheader.
|
||||||
CFG* cfg = context->cfg();
|
CFG* cfg = context_->cfg();
|
||||||
cfg->RegisterBlock(loop_preheader_);
|
cfg->RegisterBlock(loop_preheader_);
|
||||||
for (uint32_t pred_id : cfg->preds(loop_header_->id())) {
|
for (uint32_t pred_id : cfg->preds(loop_header_->id())) {
|
||||||
if (pred_id == loop_preheader_->id()) continue;
|
if (pred_id == loop_preheader_->id()) continue;
|
||||||
@ -190,11 +189,11 @@ BasicBlock* Loop::GetOrCreatePreHeaderBlock(ir::IRContext* context) {
|
|||||||
// Update the loop descriptors.
|
// Update the loop descriptors.
|
||||||
if (HasParent()) {
|
if (HasParent()) {
|
||||||
GetParent()->AddBasicBlock(loop_preheader_);
|
GetParent()->AddBasicBlock(loop_preheader_);
|
||||||
context->GetLoopDescriptor(fn)->SetBasicBlockToLoop(loop_preheader_->id(),
|
context_->GetLoopDescriptor(fn)->SetBasicBlockToLoop(loop_preheader_->id(),
|
||||||
GetParent());
|
GetParent());
|
||||||
}
|
}
|
||||||
|
|
||||||
context->InvalidateAnalysesExceptFor(
|
context_->InvalidateAnalysesExceptFor(
|
||||||
builder.GetPreservedAnalysis() |
|
builder.GetPreservedAnalysis() |
|
||||||
ir::IRContext::Analysis::kAnalysisLoopAnalysis |
|
ir::IRContext::Analysis::kAnalysisLoopAnalysis |
|
||||||
ir::IRContext::kAnalysisCFG);
|
ir::IRContext::kAnalysisCFG);
|
||||||
@ -226,8 +225,8 @@ void Loop::SetMergeBlock(BasicBlock* merge) {
|
|||||||
assert(IsInsideLoop(pred) &&
|
assert(IsInsideLoop(pred) &&
|
||||||
"A predecessor of the merge block does not belong to the loop");
|
"A predecessor of the merge block does not belong to the loop");
|
||||||
}
|
}
|
||||||
#endif // NDEBUG
|
|
||||||
assert(!IsInsideLoop(merge) && "The merge block is in the loop");
|
assert(!IsInsideLoop(merge) && "The merge block is in the loop");
|
||||||
|
#endif // NDEBUG
|
||||||
|
|
||||||
SetMergeBlockImpl(merge);
|
SetMergeBlockImpl(merge);
|
||||||
if (GetHeaderBlock()->GetLoopMergeInst()) {
|
if (GetHeaderBlock()->GetLoopMergeInst()) {
|
||||||
@ -235,9 +234,9 @@ void Loop::SetMergeBlock(BasicBlock* merge) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Loop::GetExitBlocks(IRContext* context,
|
void Loop::GetExitBlocks(std::unordered_set<uint32_t>* exit_blocks) const {
|
||||||
std::unordered_set<uint32_t>* exit_blocks) const {
|
ir::CFG* cfg = context_->cfg();
|
||||||
ir::CFG* cfg = context->cfg();
|
exit_blocks->clear();
|
||||||
|
|
||||||
for (uint32_t bb_id : GetBlocks()) {
|
for (uint32_t bb_id : GetBlocks()) {
|
||||||
const spvtools::ir::BasicBlock* bb = cfg->block(bb_id);
|
const spvtools::ir::BasicBlock* bb = cfg->block(bb_id);
|
||||||
@ -250,9 +249,10 @@ void Loop::GetExitBlocks(IRContext* context,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Loop::GetMergingBlocks(
|
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");
|
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;
|
std::stack<const ir::BasicBlock*> to_visit;
|
||||||
to_visit.push(GetMergeBlock());
|
to_visit.push(GetMergeBlock());
|
||||||
@ -269,12 +269,14 @@ void Loop::GetMergingBlocks(
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Loop::IsLCSSA() const {
|
bool Loop::IsLCSSA() const {
|
||||||
IRContext* context = GetHeaderBlock()->GetParent()->GetParent()->context();
|
ir::CFG* cfg = context_->cfg();
|
||||||
ir::CFG* cfg = context->cfg();
|
opt::analysis::DefUseManager* def_use_mgr = context_->get_def_use_mgr();
|
||||||
opt::analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
|
|
||||||
|
|
||||||
std::unordered_set<uint32_t> exit_blocks;
|
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 (uint32_t bb_id : GetBlocks()) {
|
||||||
for (Instruction& insn : *cfg->block(bb_id)) {
|
for (Instruction& insn : *cfg->block(bb_id)) {
|
||||||
@ -283,8 +285,8 @@ bool Loop::IsLCSSA() const {
|
|||||||
// - In an exit block and in a phi instruction.
|
// - In an exit block and in a phi instruction.
|
||||||
if (!def_use_mgr->WhileEachUser(
|
if (!def_use_mgr->WhileEachUser(
|
||||||
&insn,
|
&insn,
|
||||||
[&exit_blocks, context, this](ir::Instruction* use) -> bool {
|
[&exit_blocks, ir_context, this](ir::Instruction* use) -> bool {
|
||||||
BasicBlock* parent = context->get_instr_block(use);
|
BasicBlock* parent = ir_context->get_instr_block(use);
|
||||||
assert(parent && "Invalid analysis");
|
assert(parent && "Invalid analysis");
|
||||||
if (IsInsideLoop(parent)) return true;
|
if (IsInsideLoop(parent)) return true;
|
||||||
if (use->opcode() != SpvOpPhi) return false;
|
if (use->opcode() != SpvOpPhi) return false;
|
||||||
@ -296,6 +298,27 @@ bool Loop::IsLCSSA() const {
|
|||||||
return true;
|
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_() {
|
LoopDescriptor::LoopDescriptor(const Function* f) : loops_() {
|
||||||
PopulateList(f);
|
PopulateList(f);
|
||||||
}
|
}
|
||||||
@ -304,7 +327,6 @@ LoopDescriptor::~LoopDescriptor() { ClearLoops(); }
|
|||||||
|
|
||||||
void LoopDescriptor::PopulateList(const Function* f) {
|
void LoopDescriptor::PopulateList(const Function* f) {
|
||||||
IRContext* context = f->GetParent()->context();
|
IRContext* context = f->GetParent()->context();
|
||||||
|
|
||||||
opt::DominatorAnalysis* dom_analysis =
|
opt::DominatorAnalysis* dom_analysis =
|
||||||
context->GetDominatorAnalysis(f, *context->cfg());
|
context->GetDominatorAnalysis(f, *context->cfg());
|
||||||
|
|
||||||
|
@ -47,7 +47,8 @@ class Loop {
|
|||||||
using BasicBlockListTy = std::unordered_set<uint32_t>;
|
using BasicBlockListTy = std::unordered_set<uint32_t>;
|
||||||
|
|
||||||
Loop()
|
Loop()
|
||||||
: loop_header_(nullptr),
|
: context_(nullptr),
|
||||||
|
loop_header_(nullptr),
|
||||||
loop_continue_(nullptr),
|
loop_continue_(nullptr),
|
||||||
loop_merge_(nullptr),
|
loop_merge_(nullptr),
|
||||||
loop_preheader_(nullptr),
|
loop_preheader_(nullptr),
|
||||||
@ -115,22 +116,20 @@ class Loop {
|
|||||||
|
|
||||||
// Returns the loop pre-header, if there is no suitable preheader it will be
|
// Returns the loop pre-header, if there is no suitable preheader it will be
|
||||||
// created.
|
// created.
|
||||||
BasicBlock* GetOrCreatePreHeaderBlock(ir::IRContext* context);
|
BasicBlock* GetOrCreatePreHeaderBlock();
|
||||||
|
|
||||||
// Returns true if this loop contains any nested loops.
|
// Returns true if this loop contains any nested loops.
|
||||||
inline bool HasNestedLoops() const { return nested_loops_.size() != 0; }
|
inline bool HasNestedLoops() const { return nested_loops_.size() != 0; }
|
||||||
|
|
||||||
// Fills |exit_blocks| with all basic blocks that are not in the loop and has
|
// Clears and fills |exit_blocks| with all basic blocks that are not in the
|
||||||
// at least one predecessor in the loop.
|
// loop and has at least one predecessor in the loop.
|
||||||
void GetExitBlocks(IRContext* context,
|
void GetExitBlocks(std::unordered_set<uint32_t>* exit_blocks) const;
|
||||||
std::unordered_set<uint32_t>* exit_blocks) const;
|
|
||||||
|
|
||||||
// Fills |merging_blocks| with all basic blocks that are post-dominated by the
|
// Clears and fills |merging_blocks| with all basic blocks that are
|
||||||
// merge block. The merge block must exist.
|
// post-dominated by the merge block. The merge block must exist.
|
||||||
// The set |merging_blocks| will only contain the merge block if it is
|
// The set |merging_blocks| will only contain the merge block if it is
|
||||||
// unreachable.
|
// unreachable.
|
||||||
void GetMergingBlocks(IRContext* context,
|
void GetMergingBlocks(std::unordered_set<uint32_t>* merging_blocks) const;
|
||||||
std::unordered_set<uint32_t>* merging_blocks) const;
|
|
||||||
|
|
||||||
// Returns true if the loop is in a Loop Closed SSA form.
|
// 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
|
// 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.
|
// as a nested child loop.
|
||||||
inline void SetParent(Loop* parent) { parent_ = parent; }
|
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:
|
private:
|
||||||
|
IRContext* context_;
|
||||||
// The block which marks the start of the loop.
|
// The block which marks the start of the loop.
|
||||||
BasicBlock* loop_header_;
|
BasicBlock* loop_header_;
|
||||||
|
|
||||||
@ -229,8 +236,7 @@ class Loop {
|
|||||||
bool IsBasicBlockInLoopSlow(const BasicBlock* bb);
|
bool IsBasicBlockInLoopSlow(const BasicBlock* bb);
|
||||||
|
|
||||||
// Returns the loop preheader if it exists, returns nullptr otherwise.
|
// Returns the loop preheader if it exists, returns nullptr otherwise.
|
||||||
BasicBlock* FindLoopPreheader(IRContext* context,
|
BasicBlock* FindLoopPreheader(opt::DominatorAnalysis* dom_analysis);
|
||||||
opt::DominatorAnalysis* dom_analysis);
|
|
||||||
|
|
||||||
// Sets |latch| as the loop unique continue block. No checks are performed
|
// Sets |latch| as the loop unique continue block. No checks are performed
|
||||||
// here.
|
// here.
|
||||||
@ -266,6 +272,7 @@ class LoopDescriptor {
|
|||||||
other.loops_.clear();
|
other.loops_.clear();
|
||||||
basic_block_to_loop_ = std::move(other.basic_block_to_loop_);
|
basic_block_to_loop_ = std::move(other.basic_block_to_loop_);
|
||||||
other.basic_block_to_loop_.clear();
|
other.basic_block_to_loop_.clear();
|
||||||
|
dummy_top_loop_ = std::move(other.dummy_top_loop_);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Destructor
|
// Destructor
|
||||||
|
@ -331,7 +331,7 @@ void LoopUtils::CreateLoopDedicatedExits() {
|
|||||||
// Gathers the set of basic block that are not in this loop and have at least
|
// 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.
|
// one predecessor in the loop and one not in the loop.
|
||||||
std::unordered_set<uint32_t> exit_bb_set;
|
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;
|
std::unordered_set<ir::BasicBlock*> new_loop_exits;
|
||||||
bool made_change = false;
|
bool made_change = false;
|
||||||
@ -448,7 +448,7 @@ void LoopUtils::MakeLoopClosedSSA() {
|
|||||||
std::unordered_set<ir::BasicBlock*> exit_bb;
|
std::unordered_set<ir::BasicBlock*> exit_bb;
|
||||||
{
|
{
|
||||||
std::unordered_set<uint32_t> exit_bb_id;
|
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) {
|
for (uint32_t bb_id : exit_bb_id) {
|
||||||
exit_bb.insert(cfg.block(bb_id));
|
exit_bb.insert(cfg.block(bb_id));
|
||||||
}
|
}
|
||||||
@ -463,7 +463,7 @@ void LoopUtils::MakeLoopClosedSSA() {
|
|||||||
// further than the merge block.
|
// further than the merge block.
|
||||||
if (loop_->GetMergeBlock()) {
|
if (loop_->GetMergeBlock()) {
|
||||||
std::unordered_set<uint32_t> merging_bb_id;
|
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());
|
merging_bb_id.erase(loop_->GetMergeBlock()->id());
|
||||||
// Reset the exit set, now only the merge block is the exit.
|
// Reset the exit set, now only the merge block is the exit.
|
||||||
exit_bb.clear();
|
exit_bb.clear();
|
||||||
|
@ -30,8 +30,8 @@ class LoopUtils {
|
|||||||
LoopUtils(ir::IRContext* context, ir::Loop* loop)
|
LoopUtils(ir::IRContext* context, ir::Loop* loop)
|
||||||
: context_(context), loop_(loop) {}
|
: context_(context), loop_(loop) {}
|
||||||
|
|
||||||
// The make the current loop in the loop closed SSA form.
|
// The converts the current loop to loop closed SSA form.
|
||||||
// In the loop closed SSA, all loop exiting values goes through a dedicate SSA
|
// In the loop closed SSA, all loop exiting values go through a dedicated Phi
|
||||||
// instruction. For instance:
|
// instruction. For instance:
|
||||||
//
|
//
|
||||||
// for (...) {
|
// for (...) {
|
||||||
|
@ -342,6 +342,10 @@ Optimizer::PassToken CreateLocalRedundancyEliminationPass() {
|
|||||||
MakeUnique<opt::LocalRedundancyEliminationPass>());
|
MakeUnique<opt::LocalRedundancyEliminationPass>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Optimizer::PassToken CreateLoopInvariantCodeMotionPass() {
|
||||||
|
return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::LICMPass>());
|
||||||
|
}
|
||||||
|
|
||||||
Optimizer::PassToken CreateRedundancyEliminationPass() {
|
Optimizer::PassToken CreateRedundancyEliminationPass() {
|
||||||
return MakeUnique<Optimizer::PassToken::Impl>(
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
||||||
MakeUnique<opt::RedundancyEliminationPass>());
|
MakeUnique<opt::RedundancyEliminationPass>());
|
||||||
|
@ -35,6 +35,7 @@
|
|||||||
#include "inline_exhaustive_pass.h"
|
#include "inline_exhaustive_pass.h"
|
||||||
#include "inline_opaque_pass.h"
|
#include "inline_opaque_pass.h"
|
||||||
#include "insert_extract_elim.h"
|
#include "insert_extract_elim.h"
|
||||||
|
#include "licm_pass.h"
|
||||||
#include "local_access_chain_convert_pass.h"
|
#include "local_access_chain_convert_pass.h"
|
||||||
#include "local_redundancy_elimination.h"
|
#include "local_redundancy_elimination.h"
|
||||||
#include "local_single_block_elim_pass.h"
|
#include "local_single_block_elim_pass.h"
|
||||||
|
@ -30,3 +30,39 @@ add_spvtools_unittest(TARGET lcssa_test
|
|||||||
lcssa.cpp
|
lcssa.cpp
|
||||||
LIBS SPIRV-Tools-opt
|
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];
|
ir::Loop* loop = ld[27];
|
||||||
EXPECT_EQ(loop->GetPreHeaderBlock(), nullptr);
|
EXPECT_EQ(loop->GetPreHeaderBlock(), nullptr);
|
||||||
EXPECT_NE(loop->GetOrCreatePreHeaderBlock(context.get()), nullptr);
|
EXPECT_NE(loop->GetOrCreatePreHeaderBlock(), nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
@ -725,7 +725,7 @@ TEST_F(PassClassTest, CreatePreheaderTest) {
|
|||||||
{
|
{
|
||||||
ir::Loop& loop = *ld[33];
|
ir::Loop& loop = *ld[33];
|
||||||
EXPECT_EQ(loop.GetPreHeaderBlock(), nullptr);
|
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.
|
// Make sure the loop descriptor was properly updated.
|
||||||
EXPECT_EQ(ld[loop.GetPreHeaderBlock()], ld[16]);
|
EXPECT_EQ(ld[loop.GetPreHeaderBlock()], ld[16]);
|
||||||
{
|
{
|
||||||
@ -762,7 +762,7 @@ TEST_F(PassClassTest, CreatePreheaderTest) {
|
|||||||
{
|
{
|
||||||
ir::Loop& loop = *ld[41];
|
ir::Loop& loop = *ld[41];
|
||||||
EXPECT_EQ(loop.GetPreHeaderBlock(), nullptr);
|
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(ld[loop.GetPreHeaderBlock()], nullptr);
|
||||||
EXPECT_EQ(cfg->preds(loop.GetPreHeaderBlock()->id()).size(), 1u);
|
EXPECT_EQ(cfg->preds(loop.GetPreHeaderBlock()->id()).size(), 1u);
|
||||||
EXPECT_EQ(cfg->preds(loop.GetPreHeaderBlock()->id())[0], 25u);
|
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());
|
optimizer->RegisterPass(CreateCFGCleanupPass());
|
||||||
} else if (0 == strcmp(cur_arg, "--local-redundancy-elimination")) {
|
} else if (0 == strcmp(cur_arg, "--local-redundancy-elimination")) {
|
||||||
optimizer->RegisterPass(CreateLocalRedundancyEliminationPass());
|
optimizer->RegisterPass(CreateLocalRedundancyEliminationPass());
|
||||||
|
} else if (0 == strcmp(cur_arg, "--loop-invariant-code-motion")) {
|
||||||
|
optimizer->RegisterPass(CreateLoopInvariantCodeMotionPass());
|
||||||
} else if (0 == strcmp(cur_arg, "--redundancy-elimination")) {
|
} else if (0 == strcmp(cur_arg, "--redundancy-elimination")) {
|
||||||
optimizer->RegisterPass(CreateRedundancyEliminationPass());
|
optimizer->RegisterPass(CreateRedundancyEliminationPass());
|
||||||
} else if (0 == strcmp(cur_arg, "--private-to-local")) {
|
} else if (0 == strcmp(cur_arg, "--private-to-local")) {
|
||||||
|
Loading…
Reference in New Issue
Block a user