Skip to content

Commit 4242b57

Browse files
authored
Fixed crash in loop unroll caused by bug in structurize loop exits (microsoft#6548)
Fixed a bug in `hlsl::RemoveUnstructuredLoopExits` where when a new exiting block is created from splitting, it was added to the current loop being processed, when it could also part of an inner loop. Not adding the new block to inner loops that it's part of makes the inner loops malformed, and causes crash. This fix adds the new block to the inner most loop that it should be part of. Also adds the `StructurizeLoopExits` option to `loop-unroll` pass, which was missing before.
1 parent 7cf175f commit 4242b57

File tree

4 files changed

+99
-1
lines changed

4 files changed

+99
-1
lines changed

lib/Transforms/Scalar/DxilRemoveUnstructuredLoopExits.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -447,7 +447,12 @@ static bool RemoveUnstructuredLoopExitsIteration(BasicBlock *exiting_block,
447447
new_exiting_block->splitBasicBlock(new_exiting_block->getFirstNonPHI());
448448
new_exiting_block->setName("dx.struct_exit.new_exiting");
449449
new_not_exiting_block->setName(old_name);
450-
L->addBasicBlockToLoop(new_not_exiting_block, *LI);
450+
// Query for new_exiting_block's own loop to add new_not_exiting_block to.
451+
// It's possible that new_exiting_block is part of another inner loop
452+
// separate from L. If added directly to L, the inner loop(s) will not
453+
// contain new_not_exiting_block, making them malformed.
454+
Loop *inner_loop_of_exiting_block = LI->getLoopFor(new_exiting_block);
455+
inner_loop_of_exiting_block->addBasicBlockToLoop(new_not_exiting_block, *LI);
451456

452457
// Branch to latch_exit
453458
new_exiting_block->getTerminator()->eraseFromParent();

lib/Transforms/Scalar/LoopUnrollPass.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,18 @@ namespace {
155155
bool UserAllowPartial;
156156
bool UserRuntime;
157157

158+
// HLSL Change - begin
159+
// Function overrides that resolve options when used for DxOpt
160+
void applyOptions(PassOptions O) override {
161+
GetPassOptionBool(O, "StructurizeLoopExits", &StructurizeLoopExits,
162+
false);
163+
}
164+
void dumpConfig(raw_ostream &OS) override {
165+
LoopPass::dumpConfig(OS);
166+
OS << ",StructurizeLoopExits=" << StructurizeLoopExits;
167+
}
168+
// HLSL Change - end
169+
158170
bool runOnLoop(Loop *L, LPPassManager &LPM) override;
159171

160172
/// This transformation requires natural loop information & requires that
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
; RUN: %dxopt %s -S -loop-unroll,StructurizeLoopExits=1 | FileCheck %s
2+
; RUN: %dxopt %s -S -dxil-loop-unroll,StructurizeLoopExits=1 | FileCheck %s
3+
4+
; CHECK: mul nsw i32
5+
; CHECK: mul nsw i32
6+
; CHECK-NOT: mul nsw i32
7+
8+
; This is a regression test for a crash in loop unroll. When there are multiple
9+
; exits, the compiler will run hlsl::RemoveUnstructuredLoopExits to try to
10+
; avoid unstructured code.
11+
;
12+
; In this test, the compiler will try to unroll the middle loop. The exit edge
13+
; from %land.lhs.true to %if.then will be removed, and %if.end will be split at
14+
; the beginning, and branch to %if.end instead.
15+
;
16+
; Since the new split block at %if.end becomes the new latch of the inner-most
17+
; loop, it needs to be added to the Loop analysis structure of the inner loop.
18+
; However, it was only added to the current middle loop that is being unrolled.
19+
20+
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"
21+
target triple = "dxil-ms-dx"
22+
23+
; Function Attrs: nounwind
24+
define void @main(i32 *%arg0, i32 *%arg1, i32 *%arg2) #0 {
25+
entry:
26+
br label %while.body.3.preheader.lr.ph
27+
28+
while.body.3.preheader.lr.ph.loopexit: ; preds = %for.inc
29+
br label %while.body.3.preheader.lr.ph
30+
31+
while.body.3.preheader.lr.ph: ; preds = %while.body.3.preheader.lr.ph.loopexit, %entry
32+
br label %while.body.3.preheader
33+
34+
while.body.3.preheader: ; preds = %while.body.3.preheader.lr.ph, %for.inc
35+
%i.0 = phi i32 [ 0, %while.body.3.preheader.lr.ph ], [ %inc, %for.inc ]
36+
br label %while.body.3
37+
38+
while.body.3: ; preds = %while.body.3.preheader, %if.end
39+
%load_arg0 = load i32, i32* %arg0
40+
%cmp4 = icmp sgt i32 %load_arg0, 0
41+
br i1 %cmp4, label %land.lhs.true, label %if.end
42+
43+
land.lhs.true: ; preds = %while.body.3
44+
%load_arg1 = load i32, i32* %arg1
45+
%load_arg2 = load i32, i32* %arg2
46+
%mul = mul nsw i32 %load_arg2, %load_arg1
47+
%cmp7 = icmp eq i32 %mul, 10
48+
br i1 %cmp7, label %if.then, label %if.end
49+
50+
if.then: ; preds = %land.lhs.true
51+
ret void
52+
53+
if.end: ; preds = %land.lhs.true, %while.body.3
54+
%cmp10 = icmp sle i32 %i.0, 4
55+
br i1 %cmp10, label %for.inc, label %while.body.3
56+
57+
for.inc: ; preds = %if.end
58+
%inc = add nsw i32 %i.0, 1
59+
%cmp = icmp slt i32 %inc, 2
60+
br i1 %cmp, label %while.body.3.preheader, label %while.body.3.preheader.lr.ph.loopexit, !llvm.loop !3
61+
}
62+
63+
attributes #0 = { nounwind }
64+
attributes #1 = { nounwind readnone }
65+
attributes #2 = { nounwind readonly }
66+
67+
!llvm.module.flags = !{!0}
68+
!pauseresume = !{!1}
69+
!llvm.ident = !{!2}
70+
71+
!0 = !{i32 2, !"Debug Info Version", i32 3}
72+
!1 = !{!"hlsl-dxilemit", !"hlsl-dxilload"}
73+
!2 = !{!"dxc(private) 1.8.0.14563 (main, 07ce88034-dirty)"}
74+
!3 = distinct !{!3, !4}
75+
!4 = !{!"llvm.loop.unroll.full"}

utils/hct/hctdb.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6680,6 +6680,12 @@ def add_pass(name, type_name, doc, opts):
66806680
"t": "unsigned",
66816681
"d": "Unrolled size limit for loops with an unroll(full) or unroll_count pragma.",
66826682
},
6683+
{
6684+
"n": "StructurizeLoopExits",
6685+
"t": "bool",
6686+
"c": 1,
6687+
"d": "Whether the unroller should try to structurize loop exits first.",
6688+
},
66836689
],
66846690
)
66856691
add_pass("mldst-motion", "MergedLoadStoreMotion", "MergedLoadStoreMotion", [])

0 commit comments

Comments
 (0)