Fix RemoveUnstructuredLoopExits when an exiting edge jumps out multiple levels of loops. (#6668)
Before doing any major surgery on an exit from loop L, ensure that if an exit edge from L goes to block X, then X is in L's parent loop or no loop at all. Add test cases: - a reduced test case where the exiting block does not dominate its own loop latch. - a reduced test case where the exiting block is the latch for its own loop. This reproduces the assert triggered by the original HLSL. - the original HLSL that triggered this bug fix. - the intermediate module from the original HLSL, taken just before the attempt to remove unstructured loop exits.
This commit is contained in:
Родитель
8408ae8829
Коммит
8206fbdc7f
|
@ -130,10 +130,12 @@
|
|||
#include "llvm/IR/Constant.h"
|
||||
#include "llvm/IR/Instructions.h"
|
||||
#include "llvm/IR/IntrinsicInst.h"
|
||||
#include "llvm/IR/Module.h"
|
||||
#include "llvm/IR/Verifier.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include "llvm/Transforms/Scalar.h"
|
||||
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
|
||||
#include "llvm/Transforms/Utils/Local.h"
|
||||
#include "llvm/Transforms/Utils/LoopUtils.h"
|
||||
|
||||
|
@ -456,6 +458,79 @@ static SmallVector<Value_Info, 8> CollectExitValues(Value *new_exit_cond,
|
|||
return exit_values;
|
||||
}
|
||||
|
||||
// Ensures the branch from exiting_block to outside L escapes exactly one
|
||||
// level of loop nesting, and does not immediately jump into an otherwise
|
||||
// unrelated loop. Creates a downstream block as needed. If the exiting edge is
|
||||
// critical, it will be split. Updates dominator tree and loop info. Returns
|
||||
// true if any changes were made.
|
||||
static bool EnsureSingleLevelExit(Loop *L, LoopInfo *LI, DominatorTree *DT,
|
||||
BasicBlock *exiting_block) {
|
||||
BasicBlock *exit_block = GetExitBlockForExitingBlock(L, exiting_block);
|
||||
|
||||
Loop *exit_loop = LI->getLoopFor(exit_block);
|
||||
assert(L != exit_loop);
|
||||
|
||||
Loop *parent_loop = L->getParentLoop();
|
||||
if (parent_loop != exit_loop) {
|
||||
// Split the edge between the blocks, returning the newly created block.
|
||||
BasicBlock *new_bb = SplitEdge(exiting_block, exit_block, DT, LI);
|
||||
// The new block might be in the middle or at the end.
|
||||
BasicBlock *middle_bb;
|
||||
if (new_bb->getSingleSuccessor() == exit_block) {
|
||||
middle_bb = new_bb;
|
||||
} else {
|
||||
middle_bb = exit_block;
|
||||
exit_block = new_bb;
|
||||
}
|
||||
|
||||
// What loop does middle_bb end up in? SplitEdge has these cases:
|
||||
// If the edge was critical:
|
||||
// if source block is not in a loop: ruled out already
|
||||
// if dest block is not in a loop --> not in any loop.
|
||||
// if going from outer loop to inner loop: ruled out already
|
||||
// if going from inner loop to outer loop --> outer loop
|
||||
// if loops unrelated by containment -> the parent loop of the
|
||||
// destination block (which must be a loop header because we
|
||||
// assume irreducible loops).
|
||||
// If the edge was non-critcial:
|
||||
// If the exit block only had one incominge edge --> same loop as
|
||||
// destination block.
|
||||
// otherwise the exiting block had a single successor.
|
||||
// This is ruled out because the the exiting block ends with a
|
||||
// conditional branch, and so has two successors.
|
||||
|
||||
// Move the middle_block to the parent loop, if it exists.
|
||||
// If all goes well, the latch exit block will branch to it.
|
||||
// If the algorithm bails early, then there is no harm in putting
|
||||
// it in L's parent loop. At worst it will be an exiting block for
|
||||
// the parent loop.
|
||||
LI->removeBlock(middle_bb);
|
||||
if (parent_loop) {
|
||||
parent_loop->addBasicBlockToLoop(middle_bb, *LI);
|
||||
|
||||
// middle_bb block is now an exiting block, going from parent_loop to
|
||||
// exit_loop, which we know are different. Make sure it ends in a
|
||||
// in a conditional branch, as expected by the rest of the algorithm.
|
||||
auto *br = cast<BranchInst>(middle_bb->getTerminator());
|
||||
assert(!br->isConditional());
|
||||
auto *true_val = ConstantInt::getTrue(br->getContext());
|
||||
br->eraseFromParent();
|
||||
BasicBlock *parent_latch = parent_loop->getLoopLatch();
|
||||
BranchInst::Create(exit_block, parent_latch, true_val, middle_bb);
|
||||
// Fix phis in parent_latch
|
||||
for (Instruction &inst : *parent_latch) {
|
||||
PHINode *phi = dyn_cast<PHINode>(&inst);
|
||||
if (!phi)
|
||||
break;
|
||||
// We don't care about the values. The path is never taken.
|
||||
phi->addIncoming(GetDefaultValue(phi->getType()), middle_bb);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Restructures exiting_block so its work, including its exit branch, is moved
|
||||
// to a block B that dominates the latch block. Let's call B the
|
||||
// newly-exiting-block.
|
||||
|
@ -465,6 +540,15 @@ static bool RemoveUnstructuredLoopExitsIteration(BasicBlock *exiting_block,
|
|||
Loop *L, LoopInfo *LI,
|
||||
DominatorTree *DT) {
|
||||
BasicBlock *latch = L->getLoopLatch();
|
||||
|
||||
if (EnsureSingleLevelExit(L, LI, DT, latch)) {
|
||||
// Exit early so we're forced to recompute exit blocks.
|
||||
return true;
|
||||
}
|
||||
if (EnsureSingleLevelExit(L, LI, DT, exiting_block)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
BasicBlock *latch_exit = GetExitBlockForExitingBlock(L, latch);
|
||||
BasicBlock *exit_block = GetExitBlockForExitingBlock(L, exiting_block);
|
||||
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
; RUN: opt %s -analyze -loops | FileCheck -check-prefix=LOOPBEFORE %s
|
||||
; RUN: opt %s -dxil-remove-unstructured-loop-exits -o %t.bc
|
||||
; RUN: opt %t.bc -S | FileCheck %s
|
||||
; RUN: opt %t.bc -analyze -loops | FileCheck -check-prefix=LOOPAFTER %s
|
||||
|
||||
; The exiting edge goes to the header of a completely unrelated loop.
|
||||
; That edge is 'critical' in CFG terms, and will be split before attempting
|
||||
; to restructure the exit.
|
||||
|
||||
|
||||
; entry
|
||||
; | +---------+
|
||||
; v v |
|
||||
; header.1 --> if.1 -----> header.u2 -+
|
||||
; ^ | |
|
||||
; | | |
|
||||
; | +-------- endif.1 end.u2
|
||||
; | |
|
||||
; | v
|
||||
; latch.1
|
||||
; |
|
||||
; v
|
||||
; end
|
||||
;
|
||||
|
||||
; LOOPBEFORE-DAG: Loop at depth 1 containing: %header.u2<header><latch><exiting>
|
||||
; LOOPBEFORE-DAG: Loop at depth 1 containing: %header.1<header>,%if.1<exiting>,%endif.1,%latch.1<latch><exiting>
|
||||
; LOOPBEFORE-NOT: Loop at depth
|
||||
|
||||
; LOOPAFTER-DAG: Loop at depth 1 containing: %header.u2<header><latch><exiting>
|
||||
; LOOPAFTER-DAG: Loop at depth 1 containing: %header.1<header>,%if.1<exiting>,%endif.1,%latch.1<latch><exiting>
|
||||
; LOOPAFTER-NOT: Loop at depth
|
||||
|
||||
|
||||
target datalayout = "e-m:e-p:32:32-i1:32-i8:32-i16:32-i32:32-i64:64-f16:32-f32:32-f64:64-n8:16:32:64"
|
||||
target triple = "dxil-ms-dx"
|
||||
|
||||
|
||||
define void @main(i1 %cond) {
|
||||
entry:
|
||||
br label %header.1
|
||||
|
||||
header.1:
|
||||
br label %if.1
|
||||
|
||||
if.1:
|
||||
br i1 %cond, label %header.u2, label %endif.1
|
||||
|
||||
endif.1:
|
||||
br label %latch.1
|
||||
|
||||
latch.1:
|
||||
br i1 %cond, label %end, label %header.1
|
||||
|
||||
end:
|
||||
ret void
|
||||
|
||||
header.u2:
|
||||
br i1 %cond, label %end.u2, label %header.u2
|
||||
|
||||
end.u2:
|
||||
ret void
|
||||
}
|
||||
|
||||
|
||||
; CHECK: define void @main
|
||||
; CHECK: entry:
|
||||
; CHECK: br label %header.1
|
||||
|
||||
; CHECK: header.1:
|
||||
; CHECK: br label %if.1
|
||||
|
||||
; CHECK: if.1:
|
||||
; CHECK: br i1 %cond, label %if.1.header.u2_crit_edge, label %endif.1
|
||||
|
||||
; CHECK: if.1.header.u2_crit_edge:
|
||||
; CHECK: br label %header.u2
|
||||
|
||||
; CHECK: endif.1:
|
||||
; CHECK: br label %latch.1
|
||||
|
||||
; CHECK: latch.1:
|
||||
; CHECK: br i1 %cond, label %end, label %header.1
|
||||
|
||||
; CHECK: end:
|
||||
; CHECK: ret void
|
||||
|
||||
; CHECK: header.u2:
|
||||
; CHECK: br i1 %cond, label %end.u2, label %header.u2
|
||||
|
||||
; CHECK: end.u2:
|
||||
; CHECK: ret void
|
||||
; CHECK: }
|
|
@ -0,0 +1,166 @@
|
|||
; RUN: opt %s -analyze -loops | FileCheck -check-prefix=LOOPBEFORE %s
|
||||
; RUN: opt %s -dxil-remove-unstructured-loop-exits -o %t.bc
|
||||
; RUN: opt %t.bc -S | FileCheck %s
|
||||
; RUN: opt %t.bc -analyze -loops | FileCheck -check-prefix=LOOPAFTER %s
|
||||
|
||||
; The exiting edge from the latch block of the loop at depth 3 exits to the loop at depth 1.
|
||||
; This reproduces the original bug.
|
||||
;
|
||||
; Loop exits are 'dedicated', one of the LoopSimplifyForm criteria.
|
||||
|
||||
;
|
||||
; entry
|
||||
; |
|
||||
; v
|
||||
; header.1 --> header.2 --> header.3 --> if.3 -----> exiting.3
|
||||
; ^ ^ ^ | | |
|
||||
; | | | v | |
|
||||
; | | latch.3 <--- endif.3 <----+ |
|
||||
; | | | |
|
||||
; | | | v
|
||||
; | latch.2 <----------------------------- exit.3.to.2
|
||||
; | | |
|
||||
; | +-------- latch.2.exit |
|
||||
; | | |
|
||||
; | | v
|
||||
; | | latch.3.exit
|
||||
; | | |
|
||||
; | v |
|
||||
; latch.1 <-----------------+
|
||||
; |
|
||||
; v
|
||||
; end
|
||||
;
|
||||
|
||||
|
||||
; LOOPBEFORE: Loop at depth 1 containing: %header.1<header>,%header.2,%header.3,%if.3,%exiting.3,%endif.3,%latch.3,%latch.3.exit,%exit.3.to.2,%latch.2,%latch.2.exit,%latch.1<latch><exiting>
|
||||
; LOOPBEFORE-NEXT: Loop at depth 2 containing: %header.2<header>,%header.3,%if.3,%exiting.3,%endif.3,%latch.3<exiting>,%exit.3.to.2,%latch.2<latch><exiting>
|
||||
; LOOPBEFORE-NEXT: Loop at depth 3 containing: %header.3<header>,%if.3,%exiting.3<exiting>,%endif.3,%latch.3<latch><exiting>
|
||||
; no more loops expected
|
||||
; LOOPBEFORE-NOT: Loop at depth
|
||||
|
||||
; LOOPAFTER: Loop at depth 1 containing: %header.1<header>,%header.2,%header.3,%if.3,%exiting.3,%dx.struct_exit.new_exiting,%endif.3,%latch.3,%latch.3.exit,%0,%exit.3.to.2,%dx.struct_exit.new_exiting4,%latch.2,%latch.2.exit,%1,%latch.3.exit.split,%latch.1<latch><exiting>
|
||||
; LOOPAFTER-NEXT: Loop at depth 2 containing: %header.2<header>,%header.3,%if.3,%exiting.3,%dx.struct_exit.new_exiting,%endif.3,%latch.3,%latch.3.exit,%0,%exit.3.to.2,%dx.struct_exit.new_exiting4<exiting>,%latch.2<latch><exiting>
|
||||
; LOOPAFTER-NEXT: Loop at depth 3 containing: %header.3<header>,%if.3,%exiting.3,%dx.struct_exit.new_exiting<exiting>,%endif.3,%latch.3<latch><exiting>
|
||||
; no more loops expected
|
||||
; LOOPAFTER-NOT: Loop at depth
|
||||
|
||||
|
||||
target datalayout = "e-m:e-p:32:32-i1:32-i8:32-i16:32-i32:32-i64:64-f16:32-f32:32-f64:64-n8:16:32:64"
|
||||
target triple = "dxil-ms-dx"
|
||||
|
||||
|
||||
define void @main(i1 %cond) {
|
||||
entry:
|
||||
br label %header.1
|
||||
|
||||
header.1:
|
||||
br label %header.2
|
||||
|
||||
header.2:
|
||||
br label %header.3
|
||||
|
||||
header.3:
|
||||
br label %if.3
|
||||
|
||||
if.3:
|
||||
br i1 %cond, label %exiting.3, label %endif.3
|
||||
|
||||
exiting.3:
|
||||
%x3val = add i32 0, 0
|
||||
br i1 %cond, label %exit.3.to.2, label %endif.3
|
||||
|
||||
endif.3:
|
||||
br label %latch.3
|
||||
|
||||
latch.3:
|
||||
br i1 %cond, label %latch.3.exit, label %header.3
|
||||
|
||||
latch.3.exit:
|
||||
br label %latch.1
|
||||
|
||||
latch.2:
|
||||
%l2val = phi i32 [ %x3val, %exit.3.to.2 ]
|
||||
br i1 %cond, label %latch.2.exit, label %header.2
|
||||
|
||||
latch.2.exit:
|
||||
br label %latch.1
|
||||
|
||||
exit.3.to.2:
|
||||
br label %latch.2
|
||||
|
||||
latch.1:
|
||||
br i1 %cond, label %end, label %header.1
|
||||
|
||||
end:
|
||||
ret void
|
||||
}
|
||||
|
||||
|
||||
; CHECK: define void @main(i1 %cond) {
|
||||
; CHECK: entry:
|
||||
; CHECK: br label %header.1
|
||||
|
||||
; CHECK: header.1:
|
||||
; CHECK: br label %header.2
|
||||
|
||||
; CHECK: header.2:
|
||||
; CHECK: br label %header.3
|
||||
|
||||
; CHECK: header.3:
|
||||
; CHECK: br label %if.3
|
||||
|
||||
; CHECK: if.3:
|
||||
; CHECK: br i1 %cond, label %exiting.3, label %dx.struct_exit.new_exiting
|
||||
|
||||
; CHECK: exiting.3:
|
||||
; CHECK: %x3val = add i32 0, 0
|
||||
; CHECK: br label %dx.struct_exit.new_exiting
|
||||
|
||||
; CHECK: dx.struct_exit.new_exiting:
|
||||
; CHECK: %dx.struct_exit.prop1 = phi i1 [ %cond, %exiting.3 ], [ false, %if.3 ]
|
||||
; CHECK: %dx.struct_exit.prop = phi i32 [ %x3val, %exiting.3 ], [ 0, %if.3 ]
|
||||
; CHECK: br i1 %dx.struct_exit.prop1, label %latch.3.exit, label %endif.3
|
||||
|
||||
; CHECK: endif.3:
|
||||
; CHECK: br label %latch.3
|
||||
|
||||
; CHECK: latch.3:
|
||||
; CHECK: br i1 %cond, label %latch.3.exit, label %header.3
|
||||
|
||||
; CHECK: latch.3.exit:
|
||||
; CHECK: %dx.struct_exit.exit_cond_lcssa = phi i1 [ %dx.struct_exit.prop1, %dx.struct_exit.new_exiting ], [ false, %latch.3 ]
|
||||
; CHECK: %dx.struct_exit.val_lcssa = phi i32 [ %dx.struct_exit.prop, %dx.struct_exit.new_exiting ], [ 0, %latch.3 ]
|
||||
; CHECK: br i1 %dx.struct_exit.exit_cond_lcssa, label %exit.3.to.2, label %0
|
||||
|
||||
; CHECK: <label>:0
|
||||
; CHECK: br label %dx.struct_exit.new_exiting4
|
||||
|
||||
; CHECK: latch.3.exit.split:
|
||||
; CHECK: br label %latch.1
|
||||
|
||||
; CHECK: dx.struct_exit.new_exiting4:
|
||||
; CHECK: %dx.struct_exit.prop3 = phi i1 [ true, %0 ], [ false, %exit.3.to.2 ]
|
||||
; CHECK: %l2val = phi i32 [ %x3val.lcssa, %exit.3.to.2 ], [ 0, %0 ]
|
||||
; CHECK: br i1 %dx.struct_exit.prop3, label %latch.2.exit, label %latch.2
|
||||
|
||||
; CHECK: latch.2:
|
||||
; CHECK: br i1 %cond, label %latch.2.exit, label %header.2
|
||||
|
||||
; CHECK: latch.2.exit:
|
||||
; CHECK: %dx.struct_exit.exit_cond_lcssa6 = phi i1 [ %dx.struct_exit.prop3, %dx.struct_exit.new_exiting4 ], [ false, %latch.2 ]
|
||||
; CHECK: br i1 %dx.struct_exit.exit_cond_lcssa6, label %latch.3.exit.split, label %1
|
||||
|
||||
; CHECK: <label>:1
|
||||
; CHECK: br label %latch.1
|
||||
|
||||
; CHECK: exit.3.to.2:
|
||||
; CHECK: %x3val.lcssa = phi i32 [ %dx.struct_exit.val_lcssa, %latch.3.exit ]
|
||||
; CHECK: br label %dx.struct_exit.new_exiting4
|
||||
|
||||
; CHECK: latch.1:
|
||||
; CHECK: br i1 %cond, label %end, label %header.1
|
||||
|
||||
; CHECK: end:
|
||||
; CHECK: ret void
|
||||
; CHECK: }
|
|
@ -0,0 +1,104 @@
|
|||
; RUN: opt %s -analyze -loops | FileCheck -check-prefix=LOOPBEFORE %s
|
||||
; RUN: opt %s -dxil-remove-unstructured-loop-exits -o %t.bc
|
||||
|
||||
; This is the test case from ../../../../tools/clang/test/HLSLFileCheck/hlsl/control_flow/loops/multi_level_exit_regression.hlsl
|
||||
; but just before the attempt to remove unstructured loop exits.
|
||||
; There used to be a bug where the algorithm did not handle the branch that goes
|
||||
; directly from: while.body.7.i.backege (in loop at depth 3)
|
||||
; to: while.body.i.loopexit (in loop at depth 1)
|
||||
|
||||
|
||||
; LOOPBEFORE: Loop at depth 1 containing: %while.body.i<header>,%while.body.7.i.preheader.preheader,%while.body.7.i.preheader,%while.body.i.loopexit.5,%if.end.i.preheader,%if.end.i,%if.end.15.i,%if.then.11.i,%while.body.7.i.backedge,%while.body.i.loopexit,%while.body.2.i.loopexit,%if.end.19.i.loopexit,%if.end.19.i,%while.end.22.i,%while.body.i.backedge<latch>
|
||||
; LOOPBEFORE-NEXT: Loop at depth 2 containing: %while.body.7.i.preheader<header><exiting>,%if.end.i.preheader,%if.end.i,%if.end.15.i,%if.then.11.i,%while.body.7.i.backedge<exiting>,%while.body.2.i.loopexit<latch><exiting>
|
||||
; LOOPBEFORE-NEXT: Loop at depth 3 containing: %if.end.i<header>,%if.end.15.i<exiting>,%if.then.11.i<exiting>,%while.body.7.i.backedge<latch><exiting>
|
||||
; no more loops expected
|
||||
; LOOPBEFORE-NOT: Loop at depth
|
||||
|
||||
; ModuleID = 'standalone.hlsl'
|
||||
target datalayout = "e-m:e-p:32:32-i1:32-i8:32-i16:32-i32:32-i64:64-f16:32-f32:32-f64:64-n8:16:32:64"
|
||||
target triple = "dxil-ms-dx"
|
||||
|
||||
|
||||
%struct.RWByteAddressBuffer = type { i32 }
|
||||
%dx.types.Handle = type { i8* }
|
||||
%dx.types.ResourceProperties = type { i32, i32 }
|
||||
|
||||
@"\01?g_1@@3URWByteAddressBuffer@@A" = external global %struct.RWByteAddressBuffer, align 4
|
||||
@llvm.used = appending global [1 x i8*] [i8* bitcast (%struct.RWByteAddressBuffer* @"\01?g_1@@3URWByteAddressBuffer@@A" to i8*)], section "llvm.metadata"
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define void @main() {
|
||||
entry:
|
||||
%0 = load %struct.RWByteAddressBuffer, %struct.RWByteAddressBuffer* @"\01?g_1@@3URWByteAddressBuffer@@A"
|
||||
br label %while.body.i
|
||||
|
||||
while.body.i.loopexit: ; preds = %while.body.7.i.backedge
|
||||
br label %while.body.i.backedge
|
||||
|
||||
while.body.i.loopexit.5: ; preds = %while.body.7.i.preheader
|
||||
br label %while.body.i.backedge
|
||||
|
||||
while.body.i: ; preds = %while.body.i.backedge, %entry
|
||||
%l.i.0 = phi i32 [ 0, %entry ], [ %l.i.0.be, %while.body.i.backedge ]
|
||||
%cmp.i = icmp ult i32 %l.i.0, 512
|
||||
%cmp3.i.3 = icmp ne i32 %l.i.0, 9
|
||||
br i1 %cmp3.i.3, label %while.body.7.i.preheader.preheader, label %if.end.19.i
|
||||
|
||||
while.body.7.i.preheader.preheader: ; preds = %while.body.i
|
||||
br label %while.body.7.i.preheader
|
||||
|
||||
while.body.2.i.loopexit: ; preds = %if.then.11.i, %if.end.15.i
|
||||
%cmp3.i = icmp ne i32 %l.i.0, 9
|
||||
br i1 %cmp3.i, label %while.body.7.i.preheader, label %if.end.19.i.loopexit
|
||||
|
||||
while.body.7.i.preheader: ; preds = %while.body.7.i.preheader.preheader, %while.body.2.i.loopexit
|
||||
br i1 %cmp.i, label %if.end.i.preheader, label %while.body.i.loopexit.5
|
||||
|
||||
if.end.i.preheader: ; preds = %while.body.7.i.preheader
|
||||
br label %if.end.i
|
||||
|
||||
if.end.i: ; preds = %if.end.i.preheader, %while.body.7.i.backedge
|
||||
br i1 true, label %if.then.11.i, label %if.end.15.i
|
||||
|
||||
if.then.11.i: ; preds = %if.end.i
|
||||
br i1 %cmp.i, label %while.body.2.i.loopexit, label %while.body.7.i.backedge
|
||||
|
||||
while.body.7.i.backedge: ; preds = %if.then.11.i, %if.end.15.i
|
||||
br i1 false, label %if.end.i, label %while.body.i.loopexit
|
||||
|
||||
if.end.15.i: ; preds = %if.end.i
|
||||
br i1 %cmp.i, label %while.body.2.i.loopexit, label %while.body.7.i.backedge
|
||||
|
||||
if.end.19.i.loopexit: ; preds = %while.body.2.i.loopexit
|
||||
br label %if.end.19.i
|
||||
|
||||
if.end.19.i: ; preds = %if.end.19.i.loopexit, %while.body.i
|
||||
br i1 %cmp.i, label %while.end.22.i, label %while.body.i.backedge
|
||||
|
||||
while.end.22.i: ; preds = %if.end.19.i
|
||||
%mul.i = mul i32 4, %l.i.0
|
||||
%1 = call %dx.types.Handle @dx.op.createHandleForLib.struct.RWByteAddressBuffer(i32 160, %struct.RWByteAddressBuffer %0)
|
||||
%2 = call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle %1, %dx.types.ResourceProperties { i32 4107, i32 0 })
|
||||
call void @dx.op.rawBufferStore.i32(i32 140, %dx.types.Handle %2, i32 %mul.i, i32 undef, i32 %l.i.0, i32 undef, i32 undef, i32 undef, i8 1, i32 4)
|
||||
%add.i = add i32 %l.i.0, 1
|
||||
br label %while.body.i.backedge
|
||||
|
||||
while.body.i.backedge: ; preds = %while.end.22.i, %if.end.19.i, %while.body.i.loopexit, %while.body.i.loopexit.5
|
||||
%l.i.0.be = phi i32 [ %add.i, %while.end.22.i ], [ 0, %if.end.19.i ], [ 0, %while.body.i.loopexit ], [ 0, %while.body.i.loopexit.5 ]
|
||||
br label %while.body.i
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind readnone
|
||||
declare %dx.types.Handle @"dx.hl.createhandle..%dx.types.Handle (i32, %struct.RWByteAddressBuffer)"(i32, %struct.RWByteAddressBuffer)
|
||||
|
||||
; Function Attrs: nounwind readnone
|
||||
declare %dx.types.Handle @"dx.hl.annotatehandle..%dx.types.Handle (i32, %dx.types.Handle, %dx.types.ResourceProperties, %struct.RWByteAddressBuffer)"(i32, %dx.types.Handle, %dx.types.ResourceProperties, %struct.RWByteAddressBuffer)
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare void @dx.op.rawBufferStore.i32(i32, %dx.types.Handle, i32, i32, i32, i32, i32, i32, i8, i32)
|
||||
|
||||
; Function Attrs: nounwind readonly
|
||||
declare %dx.types.Handle @dx.op.createHandleForLib.struct.RWByteAddressBuffer(i32, %struct.RWByteAddressBuffer)
|
||||
|
||||
; Function Attrs: nounwind readnone
|
||||
declare %dx.types.Handle @dx.op.annotateHandle(i32, %dx.types.Handle, %dx.types.ResourceProperties)
|
|
@ -0,0 +1,167 @@
|
|||
; RUN: opt %s -analyze -loops | FileCheck -check-prefix=LOOPBEFORE %s
|
||||
; RUN: opt %s -dxil-remove-unstructured-loop-exits -o %t.bc
|
||||
; RUN: opt %t.bc -S | FileCheck %s
|
||||
; RUN: opt %t.bc -analyze -loops | FileCheck -check-prefix=LOOPAFTER %s
|
||||
|
||||
; The exiting edge from %exiting.3 jumps from inner loop at depth 3
|
||||
; directly to a block in the loop at depth 1 (and not in any other loop).
|
||||
; Block %exiting.3 does not dominate the latch for its own loop.
|
||||
;
|
||||
; Loop exits are 'dedicated', one of the LoopSimplifyForm criteria.
|
||||
|
||||
;
|
||||
; entry
|
||||
; |
|
||||
; v
|
||||
; header.1 --> header.2 --> header.3 --> if.3 -----> exiting.3
|
||||
; ^ ^ ^ | | |
|
||||
; | | | v | |
|
||||
; | | latch.3 <--- endif.3 <----+ |
|
||||
; | | | |
|
||||
; | | v |
|
||||
; | | +-------- latch.3.exit |
|
||||
; | | | |
|
||||
; | | v v
|
||||
; | latch.2 exit.3.to.1
|
||||
; | | |
|
||||
; | v |
|
||||
; | +-------- latch.2.exit |
|
||||
; | | |
|
||||
; | v |
|
||||
; latch.1 <---------------------------------------------+
|
||||
; |
|
||||
; v
|
||||
; end
|
||||
;
|
||||
|
||||
|
||||
; LOOPBEFORE: Loop at depth 1 containing: %header.1<header>,%header.2,%header.3,%if.3,%exiting.3,%endif.3,%latch.3,%latch.3.exit,%latch.2,%latch.2.exit,%exit.3.to.1,%latch.1<latch><exiting>
|
||||
; LOOPBEFORE-NEXT: Loop at depth 2 containing: %header.2<header>,%header.3,%if.3,%exiting.3<exiting>,%endif.3,%latch.3,%latch.3.exit,%latch.2<latch><exiting>
|
||||
; LOOPBEFORE-NEXT: Loop at depth 3 containing: %header.3<header>,%if.3,%exiting.3<exiting>,%endif.3,%latch.3<latch><exiting>
|
||||
; no more loops expected
|
||||
; LOOPBEFORE-NOT: Loop at depth
|
||||
|
||||
; LOOPAFTER: Loop at depth 1 containing: %header.1<header>,%header.2,%header.3,%if.3,%exiting.3,%dx.struct_exit.new_exiting,%endif.3,%latch.3,%latch.3.exit,%0,%exit.3.to.1,%dx.struct_exit.new_exiting2,%latch.2,%latch.2.exit,%1,%exit.3.to.1.split,%latch.1<latch><exiting>
|
||||
; LOOPAFTER-NEXT: Loop at depth 2 containing: %header.2<header>,%header.3,%if.3,%exiting.3,%dx.struct_exit.new_exiting,%endif.3,%latch.3,%latch.3.exit,%0,%exit.3.to.1,%dx.struct_exit.new_exiting2<exiting>,%latch.2<latch><exiting>
|
||||
; LOOPAFTER-NEXT: Loop at depth 3 containing: %header.3<header>,%if.3,%exiting.3,%dx.struct_exit.new_exiting<exiting>,%endif.3,%latch.3<latch><exiting>
|
||||
; no more loops expected
|
||||
; LOOPAFTER-NOT: Loop at depth
|
||||
|
||||
|
||||
|
||||
target datalayout = "e-m:e-p:32:32-i1:32-i8:32-i16:32-i32:32-i64:64-f16:32-f32:32-f64:64-n8:16:32:64"
|
||||
target triple = "dxil-ms-dx"
|
||||
|
||||
|
||||
define void @main(i1 %cond) {
|
||||
entry:
|
||||
br label %header.1
|
||||
|
||||
header.1:
|
||||
br label %header.2
|
||||
|
||||
header.2:
|
||||
br label %header.3
|
||||
|
||||
header.3:
|
||||
br label %if.3
|
||||
|
||||
if.3:
|
||||
br i1 %cond, label %exiting.3, label %endif.3
|
||||
|
||||
exiting.3:
|
||||
br i1 %cond, label %exit.3.to.1, label %endif.3
|
||||
|
||||
endif.3:
|
||||
%e3val = add i32 0, 0
|
||||
br label %latch.3
|
||||
|
||||
latch.3:
|
||||
br i1 %cond, label %latch.3.exit, label %header.3
|
||||
|
||||
latch.3.exit:
|
||||
br label %latch.2
|
||||
|
||||
latch.2:
|
||||
%l2val = phi i32 [ %e3val, %latch.3.exit ]
|
||||
br i1 %cond, label %latch.2.exit, label %header.2
|
||||
|
||||
latch.2.exit:
|
||||
br label %latch.1
|
||||
|
||||
exit.3.to.1:
|
||||
br label %latch.1
|
||||
|
||||
latch.1:
|
||||
br i1 %cond, label %end, label %header.1
|
||||
|
||||
end:
|
||||
ret void
|
||||
}
|
||||
|
||||
|
||||
;CHECK:define void @main
|
||||
;CHECK:entry:
|
||||
;CHECK: br label %header.1
|
||||
|
||||
;CHECK:header.1: ; preds = %latch.1, %entry
|
||||
;CHECK: br label %header.2
|
||||
|
||||
;CHECK:header.2: ; preds = %latch.2, %header.1
|
||||
;CHECK: br label %header.3
|
||||
|
||||
;CHECK:header.3: ; preds = %latch.3, %header.2
|
||||
;CHECK: br label %if.3
|
||||
|
||||
;CHECK:if.3: ; preds = %header.3
|
||||
;CHECK: br i1 %cond, label %exiting.3, label %dx.struct_exit.new_exiting
|
||||
|
||||
;CHECK:exiting.3: ; preds = %if.3
|
||||
;CHECK: br label %dx.struct_exit.new_exiting
|
||||
|
||||
;CHECK:dx.struct_exit.new_exiting: ; preds = %exiting.3, %if.3
|
||||
;CHECK: %dx.struct_exit.prop = phi i1 [ %cond, %exiting.3 ], [ false, %if.3 ]
|
||||
;CHECK: br i1 %dx.struct_exit.prop, label %latch.3.exit, label %endif.3
|
||||
|
||||
;CHECK:endif.3: ; preds = %dx.struct_exit.new_exiting
|
||||
;CHECK: %e3val = add i32 0, 0
|
||||
;CHECK: br label %latch.3
|
||||
|
||||
;CHECK:latch.3: ; preds = %endif.3
|
||||
;CHECK: br i1 %cond, label %latch.3.exit, label %header.3
|
||||
|
||||
;CHECK:latch.3.exit: ; preds = %dx.struct_exit.new_exiting, %latch.3
|
||||
;CHECK: %dx.struct_exit.exit_cond_lcssa = phi i1 [ %dx.struct_exit.prop, %dx.struct_exit.new_exiting ], [ false, %latch.3 ]
|
||||
;CHECK: %e3val.lcssa = phi i32 [ %e3val, %latch.3 ], [ 0, %dx.struct_exit.new_exiting ]
|
||||
;CHECK: br i1 %dx.struct_exit.exit_cond_lcssa, label %exit.3.to.1, label %0
|
||||
|
||||
;CHECK:; <label>:0 ; preds = %latch.3.exit
|
||||
;CHECK: br label %dx.struct_exit.new_exiting2
|
||||
|
||||
;CHECK:dx.struct_exit.new_exiting2: ; preds = %exit.3.to.1, %0
|
||||
;CHECK: %dx.struct_exit.prop1 = phi i1 [ true, %exit.3.to.1 ], [ false, %0 ]
|
||||
;CHECK: %l2val = phi i32 [ %e3val.lcssa, %0 ], [ 0, %exit.3.to.1 ]
|
||||
;CHECK: br i1 %dx.struct_exit.prop1, label %latch.2.exit, label %latch.2
|
||||
|
||||
;CHECK:latch.2: ; preds = %dx.struct_exit.new_exiting2
|
||||
;CHECK: br i1 %cond, label %latch.2.exit, label %header.2
|
||||
|
||||
;CHECK:latch.2.exit: ; preds = %dx.struct_exit.new_exiting2, %latch.2
|
||||
;CHECK: %dx.struct_exit.exit_cond_lcssa3 = phi i1 [ %dx.struct_exit.prop1, %dx.struct_exit.new_exiting2 ], [ false, %latch.2 ]
|
||||
;CHECK: br i1 %dx.struct_exit.exit_cond_lcssa3, label %exit.3.to.1.split, label %1
|
||||
|
||||
;CHECK:; <label>:1 ; preds = %latch.2.exit
|
||||
;CHECK: br label %latch.1
|
||||
|
||||
;CHECK:exit.3.to.1: ; preds = %latch.3.exit
|
||||
;CHECK: br label %dx.struct_exit.new_exiting2
|
||||
|
||||
;CHECK:exit.3.to.1.split: ; preds = %latch.2.exit
|
||||
;CHECK: br label %latch.1
|
||||
|
||||
;CHECK:latch.1: ; preds = %exit.3.to.1.split, %1
|
||||
;CHECK: br i1 %cond, label %end, label %header.1
|
||||
|
||||
;CHECK:end: ; preds = %latch.1
|
||||
;CHECK: ret void
|
||||
;CHECK:}
|
|
@ -0,0 +1,49 @@
|
|||
// RUN: %dxc -E main -T cs_6_0 %s | FileCheck %s
|
||||
// CHECK: error: Loop must have break
|
||||
|
||||
RWByteAddressBuffer g_1 : register(u0);
|
||||
|
||||
void f() {
|
||||
uint l = 0u;
|
||||
while (true) {
|
||||
bool _e10 = (l < 512u);
|
||||
while (true) {
|
||||
if ((l != 9u)) {
|
||||
while (true) {
|
||||
if (!(_e10)) {
|
||||
return;
|
||||
}
|
||||
if (_e10) {
|
||||
{
|
||||
if (_e10) { break; }
|
||||
}
|
||||
continue;
|
||||
}
|
||||
{
|
||||
if (_e10) { break; }
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (_e10) {
|
||||
break;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
uint _e22 = dot(g_1.Load3(0u), g_1.Load3(0u));
|
||||
g_1.Store((4u * l), asuint(l));
|
||||
l = (l + 1u);
|
||||
}
|
||||
}
|
||||
|
||||
[numthreads(1, 1, 1)]
|
||||
void main() {
|
||||
while (true) {
|
||||
f();
|
||||
{
|
||||
if (false) { break; }
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
Загрузка…
Ссылка в новой задаче