mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-10-18 19:20:05 +00:00
Have PredicateBlocks jump the existing merge blocks. (#1849)
* Refactor PredicateBlocks Refactor PredicateBlocks so that we know which constructs a return is contained in. Will be used later. * Have PredicateBlocks jump the existing merge blocks. In PredicateBlocks, we currently skip instructions with side effects, but it still follows the same control flow (sort-of). This causes a problem, when we are trying to predicate code in a loop. We skip all of the code with side effects (IV increment), but still follow the same control flow (jump back the start of the loop). This creates an infinite loop because the code will keep jumping back to the start of the loop without changing the values that effect the exit condition. This is a large change to merge-return. When predicating a block that is in a loop or merge construct, it will jump to the merge block of the construct. Once out of all constructs we will generate code as we did before.
This commit is contained in:
parent
197b4597a0
commit
19264ef42c
@ -87,8 +87,33 @@ void MergeReturnPass::ProcessStructured(
|
||||
}
|
||||
}
|
||||
|
||||
// Predicate successors of the original return blocks as necessary.
|
||||
PredicateBlocks(return_blocks);
|
||||
state_.clear();
|
||||
state_.emplace_back(nullptr, nullptr);
|
||||
std::unordered_set<BasicBlock*> predicated;
|
||||
for (auto block : order) {
|
||||
if (cfg()->IsPseudoEntryBlock(block) || cfg()->IsPseudoExitBlock(block)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto blockId = block->GetLabelInst()->result_id();
|
||||
if (blockId == CurrentState().CurrentMergeId()) {
|
||||
// Pop the current state as we've hit the merge
|
||||
state_.pop_back();
|
||||
}
|
||||
|
||||
// Predicate successors of the original return blocks as necessary.
|
||||
if (std::find(return_blocks.begin(), return_blocks.end(), block) !=
|
||||
return_blocks.end()) {
|
||||
PredicateBlocks(block, &predicated);
|
||||
}
|
||||
|
||||
// Generate state for next block
|
||||
if (Instruction* mergeInst = block->GetMergeInst()) {
|
||||
Instruction* loopMergeInst = block->GetLoopMergeInst();
|
||||
if (!loopMergeInst) loopMergeInst = state_.back().LoopMergeInst();
|
||||
state_.emplace_back(loopMergeInst, mergeInst);
|
||||
}
|
||||
}
|
||||
|
||||
// We have not kept the dominator tree up-to-date.
|
||||
// Invalidate it at this point to make sure it will be rebuilt.
|
||||
@ -165,24 +190,8 @@ void MergeReturnPass::BranchToBlock(BasicBlock* block, uint32_t target) {
|
||||
RecordReturned(block);
|
||||
RecordReturnValue(block);
|
||||
}
|
||||
|
||||
// Fix up existing phi nodes.
|
||||
//
|
||||
// A new edge is being added from |block| to |target|, so go through
|
||||
// |target|'s phi nodes add an undef incoming value for |block|.
|
||||
BasicBlock* target_block = context()->get_instr_block(target);
|
||||
target_block->ForEachPhiInst([this, block](Instruction* inst) {
|
||||
uint32_t undefId = Type2Undef(inst->type_id());
|
||||
inst->AddOperand({SPV_OPERAND_TYPE_ID, {undefId}});
|
||||
inst->AddOperand({SPV_OPERAND_TYPE_ID, {block->id()}});
|
||||
context()->UpdateDefUse(inst);
|
||||
});
|
||||
|
||||
const auto& target_pred = cfg()->preds(target);
|
||||
if (target_pred.size() == 1) {
|
||||
MarkForNewPhiNodes(target_block,
|
||||
context()->get_instr_block(target_pred[0]));
|
||||
}
|
||||
UpdatePhiNodes(block, target_block);
|
||||
|
||||
Instruction* return_inst = block->terminator();
|
||||
return_inst->SetOpcode(SpvOpBranch);
|
||||
@ -191,6 +200,23 @@ void MergeReturnPass::BranchToBlock(BasicBlock* block, uint32_t target) {
|
||||
cfg()->AddEdge(block->id(), target);
|
||||
}
|
||||
|
||||
void MergeReturnPass::UpdatePhiNodes(BasicBlock* new_source,
|
||||
BasicBlock* target) {
|
||||
// A new edge is being added from |new_source| to |target|, so go through
|
||||
// |target|'s phi nodes add an undef incoming value for |new_source|.
|
||||
target->ForEachPhiInst([this, new_source](Instruction* inst) {
|
||||
uint32_t undefId = Type2Undef(inst->type_id());
|
||||
inst->AddOperand({SPV_OPERAND_TYPE_ID, {undefId}});
|
||||
inst->AddOperand({SPV_OPERAND_TYPE_ID, {new_source->id()}});
|
||||
context()->UpdateDefUse(inst);
|
||||
});
|
||||
|
||||
const auto& target_pred = cfg()->preds(target->id());
|
||||
if (target_pred.size() == 1) {
|
||||
MarkForNewPhiNodes(target, context()->get_instr_block(target_pred[0]));
|
||||
}
|
||||
}
|
||||
|
||||
void MergeReturnPass::CreatePhiNodesForInst(BasicBlock* merge_block,
|
||||
uint32_t predecessor,
|
||||
Instruction& inst) {
|
||||
@ -251,42 +277,72 @@ void MergeReturnPass::CreatePhiNodesForInst(BasicBlock* merge_block,
|
||||
}
|
||||
|
||||
void MergeReturnPass::PredicateBlocks(
|
||||
const std::vector<BasicBlock*>& return_blocks) {
|
||||
BasicBlock* return_block, std::unordered_set<BasicBlock*>* predicated) {
|
||||
// The CFG is being modified as the function proceeds so avoid caching
|
||||
// successors.
|
||||
std::vector<BasicBlock*> stack;
|
||||
auto add_successors = [this, &stack](BasicBlock* block) {
|
||||
const BasicBlock* const_block = const_cast<const BasicBlock*>(block);
|
||||
const_block->ForEachSuccessorLabel([this, &stack](const uint32_t idx) {
|
||||
stack.push_back(context()->get_instr_block(idx));
|
||||
});
|
||||
};
|
||||
|
||||
if (predicated->count(return_block)) {
|
||||
return;
|
||||
}
|
||||
|
||||
BasicBlock* block = nullptr;
|
||||
const BasicBlock* const_block = const_cast<const BasicBlock*>(return_block);
|
||||
const_block->ForEachSuccessorLabel([this, &block](const uint32_t idx) {
|
||||
BasicBlock* succ_block = context()->get_instr_block(idx);
|
||||
assert(block == nullptr);
|
||||
block = succ_block;
|
||||
});
|
||||
assert(block &&
|
||||
"Return blocks should have returns already replaced by a single "
|
||||
"unconditional branch.");
|
||||
|
||||
auto state = state_.rbegin();
|
||||
std::unordered_set<BasicBlock*> seen;
|
||||
std::unordered_set<BasicBlock*> predicated;
|
||||
for (auto b : return_blocks) {
|
||||
seen.clear();
|
||||
add_successors(b);
|
||||
|
||||
while (!stack.empty()) {
|
||||
BasicBlock* block = stack.back();
|
||||
assert(block);
|
||||
stack.pop_back();
|
||||
|
||||
if (block == b) continue;
|
||||
if (block == final_return_block_) continue;
|
||||
if (!seen.insert(block).second) continue;
|
||||
if (!predicated.insert(block).second) continue;
|
||||
|
||||
// Skip structured subgraphs.
|
||||
BasicBlock* next = block;
|
||||
while (next->GetMergeInst()) {
|
||||
next = context()->get_instr_block(next->MergeBlockIdIfAny());
|
||||
}
|
||||
add_successors(next);
|
||||
PredicateBlock(block, next, &predicated);
|
||||
if (block->id() == state->CurrentMergeId()) {
|
||||
state++;
|
||||
} else if (block->id() == state->LoopMergeId()) {
|
||||
while (state->LoopMergeId() == block->id()) {
|
||||
state++;
|
||||
}
|
||||
}
|
||||
|
||||
while (block != nullptr && block != final_return_block_) {
|
||||
if (!predicated->insert(block).second) break;
|
||||
|
||||
// Skip structured subgraphs.
|
||||
BasicBlock* next = nullptr;
|
||||
if (state->InLoop()) {
|
||||
next = context()->get_instr_block(state->LoopMergeId());
|
||||
while (state->LoopMergeId() == next->id()) {
|
||||
state++;
|
||||
}
|
||||
BreakFromConstruct(block, next, predicated);
|
||||
} else if (state->InStructuredFlow()) {
|
||||
next = context()->get_instr_block(state->CurrentMergeId());
|
||||
state++;
|
||||
BreakFromConstruct(block, next, predicated);
|
||||
} else {
|
||||
BasicBlock* tail = block;
|
||||
while (tail->GetMergeInst()) {
|
||||
tail = context()->get_instr_block(tail->MergeBlockIdIfAny());
|
||||
}
|
||||
|
||||
// Must find |next| (the successor of |tail|) before predicating the
|
||||
// block because, if |block| == |tail|, then |tail| will have multiple
|
||||
// successors.
|
||||
next = nullptr;
|
||||
tail->ForEachSuccessorLabel([this, &next](const uint32_t idx) {
|
||||
BasicBlock* succ_block = context()->get_instr_block(idx);
|
||||
assert(
|
||||
next == nullptr &&
|
||||
"Found block with multiple successors and no merge instruction.");
|
||||
next = succ_block;
|
||||
});
|
||||
|
||||
PredicateBlock(block, tail, predicated);
|
||||
}
|
||||
block = next;
|
||||
}
|
||||
}
|
||||
|
||||
bool MergeReturnPass::RequiresPredication(const BasicBlock* block,
|
||||
@ -312,18 +368,18 @@ void MergeReturnPass::PredicateBlock(
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure the cfg is build here. If we don't then it becomes very hard to
|
||||
// know which new blocks need to be updated.
|
||||
// Make sure the cfg is build here. If we don't then it becomes very hard
|
||||
// to know which new blocks need to be updated.
|
||||
context()->BuildInvalidAnalyses(IRContext::kAnalysisCFG);
|
||||
|
||||
// When predicating, be aware of whether this block is a header block, a merge
|
||||
// block or both.
|
||||
// When predicating, be aware of whether this block is a header block, a
|
||||
// merge block or both.
|
||||
//
|
||||
// If this block is a merge block, ensure the appropriate header stays
|
||||
// up-to-date with any changes (i.e. points to the pre-header).
|
||||
//
|
||||
// If this block is a header block, predicate the entire structured subgraph.
|
||||
// This can act recursively.
|
||||
// If this block is a header block, predicate the entire structured
|
||||
// subgraph. This can act recursively.
|
||||
|
||||
// If |block| is a loop header, then the back edge must jump to the original
|
||||
// code, not the new header.
|
||||
@ -385,8 +441,8 @@ void MergeReturnPass::PredicateBlock(
|
||||
get_def_use_mgr()->AnalyzeInstUse(new_merge->terminator());
|
||||
context()->set_instr_block(new_merge->terminator(), new_merge);
|
||||
|
||||
// Add a branch to the new merge. If we jumped multiple blocks, the branch is
|
||||
// added to tail_block, otherwise the branch belongs in old_body.
|
||||
// Add a branch to the new merge. If we jumped multiple blocks, the branch
|
||||
// is added to tail_block, otherwise the branch belongs in old_body.
|
||||
tail_block->AddInstruction(
|
||||
MakeUnique<Instruction>(context(), SpvOpBranch, 0, 0,
|
||||
std::initializer_list<Operand>{
|
||||
@ -443,6 +499,88 @@ void MergeReturnPass::PredicateBlock(
|
||||
MarkForNewPhiNodes(new_merge, tail_block);
|
||||
}
|
||||
|
||||
void MergeReturnPass::BreakFromConstruct(
|
||||
BasicBlock* block, BasicBlock* merge_block,
|
||||
std::unordered_set<BasicBlock*>* predicated) {
|
||||
// Make sure the cfg is build here. If we don't then it becomes very hard
|
||||
// to know which new blocks need to be updated.
|
||||
context()->BuildInvalidAnalyses(IRContext::kAnalysisCFG);
|
||||
|
||||
// When predicating, be aware of whether this block is a header block, a
|
||||
// merge block or both.
|
||||
//
|
||||
// If this block is a merge block, ensure the appropriate header stays
|
||||
// up-to-date with any changes (i.e. points to the pre-header).
|
||||
//
|
||||
// If this block is a header block, predicate the entire structured
|
||||
// subgraph. This can act recursively.
|
||||
|
||||
// If |block| is a loop header, then the back edge must jump to the original
|
||||
// code, not the new header.
|
||||
if (block->GetLoopMergeInst()) {
|
||||
cfg()->SplitLoopHeader(block);
|
||||
}
|
||||
|
||||
// Leave the phi instructions behind.
|
||||
auto iter = block->begin();
|
||||
while (iter->opcode() == SpvOpPhi) {
|
||||
++iter;
|
||||
}
|
||||
|
||||
// Forget about the edges leaving block. They will be removed.
|
||||
cfg()->RemoveSuccessorEdges(block);
|
||||
|
||||
std::unique_ptr<BasicBlock> new_block(
|
||||
block->SplitBasicBlock(context(), TakeNextId(), iter));
|
||||
BasicBlock* old_body =
|
||||
function_->InsertBasicBlockAfter(std::move(new_block), block);
|
||||
predicated->insert(old_body);
|
||||
|
||||
// Within the new header we need the following:
|
||||
// 1. Load of the return status flag
|
||||
// 2. Branch to new merge (true) or old body (false)
|
||||
// 3. Update OpPhi instructions in |merge_block|.
|
||||
//
|
||||
// Sine we are branching to the merge block of the current construct, there is
|
||||
// no need for an OpSelectionMerge.
|
||||
|
||||
// 1. Load of the return status flag
|
||||
analysis::Bool bool_type;
|
||||
uint32_t bool_id = context()->get_type_mgr()->GetId(&bool_type);
|
||||
assert(bool_id != 0);
|
||||
uint32_t load_id = TakeNextId();
|
||||
block->AddInstruction(MakeUnique<Instruction>(
|
||||
context(), SpvOpLoad, bool_id, load_id,
|
||||
std::initializer_list<Operand>{
|
||||
{SPV_OPERAND_TYPE_ID, {return_flag_->result_id()}}}));
|
||||
get_def_use_mgr()->AnalyzeInstDefUse(block->terminator());
|
||||
context()->set_instr_block(block->terminator(), block);
|
||||
|
||||
// 2. Branch to |merge_block| (true) or |old_body| (false)
|
||||
block->AddInstruction(MakeUnique<Instruction>(
|
||||
context(), SpvOpBranchConditional, 0, 0,
|
||||
std::initializer_list<Operand>{{SPV_OPERAND_TYPE_ID, {load_id}},
|
||||
{SPV_OPERAND_TYPE_ID, {merge_block->id()}},
|
||||
{SPV_OPERAND_TYPE_ID, {old_body->id()}}}));
|
||||
get_def_use_mgr()->AnalyzeInstUse(block->terminator());
|
||||
context()->set_instr_block(block->terminator(), block);
|
||||
|
||||
// Update the cfg
|
||||
cfg()->AddEdges(block);
|
||||
cfg()->RegisterBlock(old_body);
|
||||
|
||||
// 3. Update OpPhi instructions in |merge_block|.
|
||||
BasicBlock* merge_original_pred = MarkedSinglePred(merge_block);
|
||||
if (merge_original_pred == nullptr) {
|
||||
UpdatePhiNodes(block, merge_block);
|
||||
} else if (merge_original_pred == block) {
|
||||
MarkForNewPhiNodes(merge_block, old_body);
|
||||
}
|
||||
|
||||
assert(old_body->begin() != old_body->end());
|
||||
assert(block->begin() != block->end());
|
||||
}
|
||||
|
||||
void MergeReturnPass::RecordReturned(BasicBlock* block) {
|
||||
if (block->tail()->opcode() != SpvOpReturn &&
|
||||
block->tail()->opcode() != SpvOpReturnValue)
|
||||
@ -637,8 +775,8 @@ void MergeReturnPass::AddNewPhiNodes() {
|
||||
void MergeReturnPass::AddNewPhiNodes(BasicBlock* bb, BasicBlock* pred,
|
||||
uint32_t header_id) {
|
||||
DominatorAnalysis* dom_tree = context()->GetDominatorAnalysis(function_);
|
||||
// Insert as a stopping point. We do not have to add anything in the block or
|
||||
// above because the header dominates |bb|.
|
||||
// Insert as a stopping point. We do not have to add anything in the block
|
||||
// or above because the header dominates |bb|.
|
||||
|
||||
BasicBlock* current_bb = pred;
|
||||
while (current_bb != nullptr && current_bb->id() != header_id) {
|
||||
|
@ -220,7 +220,8 @@ class MergeReturnPass : public MemPass {
|
||||
// not be executed because the original code would have already returned. This
|
||||
// involves adding new selections constructs to jump around these
|
||||
// instructions.
|
||||
void PredicateBlocks(const std::vector<BasicBlock*>& return_blocks);
|
||||
void PredicateBlocks(BasicBlock* return_block,
|
||||
std::unordered_set<BasicBlock*>* pSet);
|
||||
|
||||
// Add the predication code (see |PredicateBlocks|) to |tail_block| if it
|
||||
// requires predication. |tail_block| and any new blocks that are known to
|
||||
@ -298,6 +299,9 @@ class MergeReturnPass : public MemPass {
|
||||
// it is mapped to it original single predcessor. It is assumed there are no
|
||||
// values that will need a phi on the new edges.
|
||||
std::unordered_map<BasicBlock*, BasicBlock*> new_merge_nodes_;
|
||||
void BreakFromConstruct(BasicBlock* block, BasicBlock* merge_block,
|
||||
std::unordered_set<BasicBlock*>* predicated);
|
||||
void UpdatePhiNodes(BasicBlock* new_source, BasicBlock* target);
|
||||
};
|
||||
|
||||
} // namespace opt
|
||||
|
@ -296,6 +296,7 @@ OpUnreachable
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
||||
SinglePassRunAndMatch<MergeReturnPass>(before, false);
|
||||
}
|
||||
|
||||
@ -344,6 +345,7 @@ OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
||||
SinglePassRunAndMatch<MergeReturnPass>(before, false);
|
||||
}
|
||||
|
||||
@ -593,16 +595,16 @@ TEST_F(MergeReturnPassTest, NestedSelectionMerge) {
|
||||
OpReturn
|
||||
%11 = OpLabel
|
||||
OpSelectionMerge %12 None
|
||||
OpBranchConditional %false %14 %15
|
||||
%14 = OpLabel
|
||||
%16 = OpIAdd %uint %uint_0 %uint_0
|
||||
OpBranchConditional %false %13 %14
|
||||
%13 = OpLabel
|
||||
%15 = OpIAdd %uint %uint_0 %uint_0
|
||||
OpBranch %12
|
||||
%15 = OpLabel
|
||||
%14 = OpLabel
|
||||
OpReturn
|
||||
%12 = OpLabel
|
||||
OpBranch %9
|
||||
%9 = OpLabel
|
||||
%17 = OpIAdd %uint %16 %16
|
||||
%16 = OpIAdd %uint %15 %15
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
@ -621,7 +623,7 @@ OpEntryPoint GLCompute %1 "simple_shader"
|
||||
%7 = OpTypeFunction %void
|
||||
%_ptr_Function_bool = OpTypePointer Function %bool
|
||||
%true = OpConstantTrue %bool
|
||||
%24 = OpUndef %uint
|
||||
%26 = OpUndef %uint
|
||||
%1 = OpFunction %void None %7
|
||||
%8 = OpLabel
|
||||
%19 = OpVariable %_ptr_Function_bool Function %false
|
||||
@ -640,24 +642,28 @@ OpBranch %12
|
||||
OpStore %19 %true
|
||||
OpBranch %12
|
||||
%12 = OpLabel
|
||||
%25 = OpPhi %uint %15 %13 %24 %14
|
||||
%27 = OpPhi %uint %15 %13 %26 %14
|
||||
%22 = OpLoad %bool %19
|
||||
OpBranchConditional %22 %9 %21
|
||||
%21 = OpLabel
|
||||
OpBranch %9
|
||||
%9 = OpLabel
|
||||
%26 = OpPhi %uint %25 %12 %24 %10
|
||||
%23 = OpLoad %bool %19
|
||||
OpSelectionMerge %22 None
|
||||
OpBranchConditional %23 %22 %21
|
||||
%21 = OpLabel
|
||||
%16 = OpIAdd %uint %26 %26
|
||||
%28 = OpPhi %uint %27 %21 %26 %10 %26 %12
|
||||
%25 = OpLoad %bool %19
|
||||
OpSelectionMerge %24 None
|
||||
OpBranchConditional %25 %24 %23
|
||||
%23 = OpLabel
|
||||
%16 = OpIAdd %uint %28 %28
|
||||
OpStore %19 %true
|
||||
OpBranch %22
|
||||
%22 = OpLabel
|
||||
OpBranch %24
|
||||
%24 = OpLabel
|
||||
OpBranch %17
|
||||
%17 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
||||
SinglePassRunAndCheck<MergeReturnPass>(before, after, false, true);
|
||||
}
|
||||
|
||||
@ -666,8 +672,7 @@ OpFunctionEnd
|
||||
// work even if the order of the traversals change.
|
||||
TEST_F(MergeReturnPassTest, NestedSelectionMerge2) {
|
||||
const std::string before =
|
||||
R"(
|
||||
OpCapability Addresses
|
||||
R"( OpCapability Addresses
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
@ -686,16 +691,16 @@ TEST_F(MergeReturnPassTest, NestedSelectionMerge2) {
|
||||
OpReturn
|
||||
%10 = OpLabel
|
||||
OpSelectionMerge %12 None
|
||||
OpBranchConditional %false %14 %15
|
||||
%14 = OpLabel
|
||||
%16 = OpIAdd %uint %uint_0 %uint_0
|
||||
OpBranchConditional %false %13 %14
|
||||
%13 = OpLabel
|
||||
%15 = OpIAdd %uint %uint_0 %uint_0
|
||||
OpBranch %12
|
||||
%15 = OpLabel
|
||||
%14 = OpLabel
|
||||
OpReturn
|
||||
%12 = OpLabel
|
||||
OpBranch %9
|
||||
%9 = OpLabel
|
||||
%17 = OpIAdd %uint %16 %16
|
||||
%16 = OpIAdd %uint %15 %15
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
@ -714,7 +719,7 @@ OpEntryPoint GLCompute %1 "simple_shader"
|
||||
%7 = OpTypeFunction %void
|
||||
%_ptr_Function_bool = OpTypePointer Function %bool
|
||||
%true = OpConstantTrue %bool
|
||||
%24 = OpUndef %uint
|
||||
%26 = OpUndef %uint
|
||||
%1 = OpFunction %void None %7
|
||||
%8 = OpLabel
|
||||
%19 = OpVariable %_ptr_Function_bool Function %false
|
||||
@ -733,15 +738,18 @@ OpBranch %12
|
||||
OpStore %19 %true
|
||||
OpBranch %12
|
||||
%12 = OpLabel
|
||||
%25 = OpPhi %uint %15 %13 %24 %14
|
||||
%27 = OpPhi %uint %15 %13 %26 %14
|
||||
%25 = OpLoad %bool %19
|
||||
OpBranchConditional %25 %9 %24
|
||||
%24 = OpLabel
|
||||
OpBranch %9
|
||||
%9 = OpLabel
|
||||
%26 = OpPhi %uint %25 %12 %24 %11
|
||||
%28 = OpPhi %uint %27 %24 %26 %11 %26 %12
|
||||
%23 = OpLoad %bool %19
|
||||
OpSelectionMerge %22 None
|
||||
OpBranchConditional %23 %22 %21
|
||||
%21 = OpLabel
|
||||
%16 = OpIAdd %uint %26 %26
|
||||
%16 = OpIAdd %uint %28 %28
|
||||
OpStore %19 %true
|
||||
OpBranch %22
|
||||
%22 = OpLabel
|
||||
@ -756,8 +764,7 @@ OpFunctionEnd
|
||||
|
||||
TEST_F(MergeReturnPassTest, NestedSelectionMerge3) {
|
||||
const std::string before =
|
||||
R"(
|
||||
OpCapability Addresses
|
||||
R"( OpCapability Addresses
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
@ -775,17 +782,17 @@ TEST_F(MergeReturnPassTest, NestedSelectionMerge3) {
|
||||
%11 = OpLabel
|
||||
OpReturn
|
||||
%10 = OpLabel
|
||||
%16 = OpIAdd %uint %uint_0 %uint_0
|
||||
OpSelectionMerge %12 None
|
||||
%12 = OpIAdd %uint %uint_0 %uint_0
|
||||
OpSelectionMerge %13 None
|
||||
OpBranchConditional %false %14 %15
|
||||
%14 = OpLabel
|
||||
OpBranch %12
|
||||
OpBranch %13
|
||||
%15 = OpLabel
|
||||
OpReturn
|
||||
%12 = OpLabel
|
||||
%13 = OpLabel
|
||||
OpBranch %9
|
||||
%9 = OpLabel
|
||||
%17 = OpIAdd %uint %16 %16
|
||||
%16 = OpIAdd %uint %12 %12
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
@ -804,7 +811,7 @@ OpEntryPoint GLCompute %1 "simple_shader"
|
||||
%7 = OpTypeFunction %void
|
||||
%_ptr_Function_bool = OpTypePointer Function %bool
|
||||
%true = OpConstantTrue %bool
|
||||
%24 = OpUndef %uint
|
||||
%26 = OpUndef %uint
|
||||
%1 = OpFunction %void None %7
|
||||
%8 = OpLabel
|
||||
%19 = OpVariable %_ptr_Function_bool Function %false
|
||||
@ -823,14 +830,17 @@ OpBranch %13
|
||||
OpStore %19 %true
|
||||
OpBranch %13
|
||||
%13 = OpLabel
|
||||
%25 = OpLoad %bool %19
|
||||
OpBranchConditional %25 %9 %24
|
||||
%24 = OpLabel
|
||||
OpBranch %9
|
||||
%9 = OpLabel
|
||||
%25 = OpPhi %uint %12 %13 %24 %11
|
||||
%27 = OpPhi %uint %12 %24 %26 %11 %26 %13
|
||||
%23 = OpLoad %bool %19
|
||||
OpSelectionMerge %22 None
|
||||
OpBranchConditional %23 %22 %21
|
||||
%21 = OpLabel
|
||||
%16 = OpIAdd %uint %25 %25
|
||||
%16 = OpIAdd %uint %27 %27
|
||||
OpStore %19 %true
|
||||
OpBranch %22
|
||||
%22 = OpLabel
|
||||
@ -843,6 +853,158 @@ OpFunctionEnd
|
||||
SinglePassRunAndCheck<MergeReturnPass>(before, after, false, true);
|
||||
}
|
||||
|
||||
TEST_F(MergeReturnPassTest, NestedLoopMerge) {
|
||||
const std::string before =
|
||||
R"( OpCapability SampledBuffer
|
||||
OpCapability StorageImageExtendedFormats
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %2 "CS"
|
||||
OpExecutionMode %2 LocalSize 8 8 1
|
||||
OpSource HLSL 600
|
||||
OpName %function "function"
|
||||
%uint = OpTypeInt 32 0
|
||||
%void = OpTypeVoid
|
||||
%6 = OpTypeFunction %void
|
||||
%uint_0 = OpConstant %uint 0
|
||||
%uint_1 = OpConstant %uint 1
|
||||
%v3uint = OpTypeVector %uint 3
|
||||
%bool = OpTypeBool
|
||||
%true = OpConstantTrue %bool
|
||||
%_ptr_Function_uint = OpTypePointer Function %uint
|
||||
%_struct_13 = OpTypeStruct %v3uint %v3uint %v3uint %uint %uint %uint %uint %uint %uint
|
||||
%2 = OpFunction %void None %6
|
||||
%14 = OpLabel
|
||||
%15 = OpFunctionCall %void %function
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
%function = OpFunction %void None %6
|
||||
%16 = OpLabel
|
||||
%17 = OpVariable %_ptr_Function_uint Function
|
||||
%18 = OpVariable %_ptr_Function_uint Function
|
||||
OpStore %17 %uint_0
|
||||
OpBranch %19
|
||||
%19 = OpLabel
|
||||
%20 = OpLoad %uint %17
|
||||
%21 = OpULessThan %bool %20 %uint_1
|
||||
OpLoopMerge %22 %23 DontUnroll
|
||||
OpBranchConditional %21 %24 %22
|
||||
%24 = OpLabel
|
||||
OpStore %18 %uint_1
|
||||
OpBranch %25
|
||||
%25 = OpLabel
|
||||
%26 = OpLoad %uint %18
|
||||
%27 = OpINotEqual %bool %26 %uint_0
|
||||
OpLoopMerge %28 %29 DontUnroll
|
||||
OpBranchConditional %27 %30 %28
|
||||
%30 = OpLabel
|
||||
OpSelectionMerge %31 None
|
||||
OpBranchConditional %true %32 %31
|
||||
%32 = OpLabel
|
||||
OpReturn
|
||||
%31 = OpLabel
|
||||
OpStore %18 %uint_1
|
||||
OpBranch %29
|
||||
%29 = OpLabel
|
||||
OpBranch %25
|
||||
%28 = OpLabel
|
||||
OpBranch %23
|
||||
%23 = OpLabel
|
||||
%33 = OpLoad %uint %17
|
||||
%34 = OpIAdd %uint %33 %uint_1
|
||||
OpStore %17 %34
|
||||
OpBranch %19
|
||||
%22 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const std::string after =
|
||||
R"(OpCapability SampledBuffer
|
||||
OpCapability StorageImageExtendedFormats
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %2 "CS"
|
||||
OpExecutionMode %2 LocalSize 8 8 1
|
||||
OpSource HLSL 600
|
||||
OpName %function "function"
|
||||
%uint = OpTypeInt 32 0
|
||||
%void = OpTypeVoid
|
||||
%6 = OpTypeFunction %void
|
||||
%uint_0 = OpConstant %uint 0
|
||||
%uint_1 = OpConstant %uint 1
|
||||
%v3uint = OpTypeVector %uint 3
|
||||
%bool = OpTypeBool
|
||||
%true = OpConstantTrue %bool
|
||||
%_ptr_Function_uint = OpTypePointer Function %uint
|
||||
%_struct_13 = OpTypeStruct %v3uint %v3uint %v3uint %uint %uint %uint %uint %uint %uint
|
||||
%false = OpConstantFalse %bool
|
||||
%_ptr_Function_bool = OpTypePointer Function %bool
|
||||
%2 = OpFunction %void None %6
|
||||
%14 = OpLabel
|
||||
%15 = OpFunctionCall %void %function
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
%function = OpFunction %void None %6
|
||||
%16 = OpLabel
|
||||
%38 = OpVariable %_ptr_Function_bool Function %false
|
||||
%17 = OpVariable %_ptr_Function_uint Function
|
||||
%18 = OpVariable %_ptr_Function_uint Function
|
||||
OpStore %17 %uint_0
|
||||
OpBranch %19
|
||||
%19 = OpLabel
|
||||
%20 = OpLoad %uint %17
|
||||
%21 = OpULessThan %bool %20 %uint_1
|
||||
OpLoopMerge %22 %23 DontUnroll
|
||||
OpBranchConditional %21 %24 %22
|
||||
%24 = OpLabel
|
||||
OpStore %18 %uint_1
|
||||
OpBranch %25
|
||||
%25 = OpLabel
|
||||
%26 = OpLoad %uint %18
|
||||
%27 = OpINotEqual %bool %26 %uint_0
|
||||
OpLoopMerge %28 %29 DontUnroll
|
||||
OpBranchConditional %27 %30 %28
|
||||
%30 = OpLabel
|
||||
OpSelectionMerge %31 None
|
||||
OpBranchConditional %true %32 %31
|
||||
%32 = OpLabel
|
||||
OpStore %38 %true
|
||||
OpBranch %28
|
||||
%31 = OpLabel
|
||||
OpStore %18 %uint_1
|
||||
OpBranch %29
|
||||
%29 = OpLabel
|
||||
OpBranch %25
|
||||
%28 = OpLabel
|
||||
%40 = OpLoad %bool %38
|
||||
OpBranchConditional %40 %22 %39
|
||||
%39 = OpLabel
|
||||
OpBranch %23
|
||||
%23 = OpLabel
|
||||
%33 = OpLoad %uint %17
|
||||
%34 = OpIAdd %uint %33 %uint_1
|
||||
OpStore %17 %34
|
||||
OpBranch %19
|
||||
%22 = OpLabel
|
||||
%43 = OpLoad %bool %38
|
||||
OpSelectionMerge %42 None
|
||||
OpBranchConditional %43 %42 %41
|
||||
%41 = OpLabel
|
||||
OpStore %38 %true
|
||||
OpBranch %42
|
||||
%42 = OpLabel
|
||||
OpBranch %35
|
||||
%35 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SinglePassRunAndCheck<MergeReturnPass>(before, after, false, true);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
Loading…
Reference in New Issue
Block a user