Responding to a feature request from the Rust community: https://github.com/rust-lang/rust/issues/80630 void foo(X) { for (...) switch (X) case A X = B case B X = C } Even though the initial switch value is non-constant, the switch statement can still be threaded: the initial value will hit the switch statement but the rest of the state changes will proceed by jumping unconditionally. The early predictability check is relaxed to allow unpredictable values anywhere, but later, after the paths through the switch statement have been enumerated, no non-constant state values are allowed along the paths. Any state value not along a path will be an initial switch value, which can be safely ignored. Differential Revision: https://reviews.llvm.org/D124394
266 lines
6.4 KiB
LLVM
266 lines
6.4 KiB
LLVM
; RUN: opt -dfa-jump-threading -dfa-cost-threshold=25 -pass-remarks-missed='dfa-jump-threading' -pass-remarks-output=%t -disable-output %s
|
|
; RUN: FileCheck --input-file %t --check-prefix=REMARK %s
|
|
; RUN: opt -S -dfa-jump-threading %s | FileCheck %s
|
|
|
|
; This negative test case checks that the optimization doesn't trigger
|
|
; when the code size cost is too high.
|
|
define i32 @negative1(i32 %num) {
|
|
; REMARK: NotProfitable
|
|
; REMARK-NEXT: negative1
|
|
entry:
|
|
br label %for.body
|
|
|
|
for.body:
|
|
%count = phi i32 [ 0, %entry ], [ %inc, %for.inc ]
|
|
%state = phi i32 [ 1, %entry ], [ %state.next, %for.inc ]
|
|
switch i32 %state, label %for.inc [
|
|
i32 1, label %case1
|
|
i32 2, label %case2
|
|
]
|
|
|
|
case1:
|
|
br label %for.inc
|
|
|
|
case2:
|
|
%cmp = icmp eq i32 %count, 50
|
|
%sel = select i1 %cmp, i32 1, i32 2
|
|
br label %for.inc
|
|
|
|
for.inc:
|
|
%state.next = phi i32 [ %sel, %case2 ], [ 1, %for.body ], [ 2, %case1 ]
|
|
%add1 = add i32 %num, %num
|
|
%add2 = add i32 %add1, %add1
|
|
%add3 = add i32 %add2, %add2
|
|
%add4 = add i32 %add3, %add3
|
|
%add5 = add i32 %add4, %add4
|
|
%add6 = add i32 %add5, %add5
|
|
%add7 = add i32 %add6, %add6
|
|
%add8 = add i32 %add7, %add7
|
|
%add9 = add i32 %add8, %add8
|
|
%add10 = add i32 %add9, %add9
|
|
%add11 = add i32 %add10, %add10
|
|
%add12 = add i32 %add11, %add11
|
|
%add13 = add i32 %add12, %add12
|
|
%add14 = add i32 %add13, %add13
|
|
%add15 = add i32 %add14, %add14
|
|
%add16 = add i32 %add15, %add15
|
|
%add17 = add i32 %add16, %add16
|
|
%add18 = add i32 %add17, %add17
|
|
%add19 = add i32 %add18, %add18
|
|
%add20 = add i32 %add19, %add19
|
|
%add21 = add i32 %add20, %add20
|
|
%add22 = add i32 %add21, %add21
|
|
%inc = add nsw i32 %count, 1
|
|
%cmp.exit = icmp slt i32 %inc, %num
|
|
br i1 %cmp.exit, label %for.body, label %for.end
|
|
|
|
for.end:
|
|
ret i32 %add22
|
|
}
|
|
|
|
declare void @func()
|
|
|
|
define i32 @negative2(i32 %num) {
|
|
; REMARK: NonDuplicatableInst
|
|
; REMARK-NEXT: negative2
|
|
entry:
|
|
br label %for.body
|
|
|
|
for.body:
|
|
%count = phi i32 [ 0, %entry ], [ %inc, %for.inc ]
|
|
%state = phi i32 [ 1, %entry ], [ %state.next, %for.inc ]
|
|
switch i32 %state, label %for.inc [
|
|
i32 1, label %case1
|
|
i32 2, label %case2
|
|
]
|
|
|
|
case1:
|
|
br label %for.inc
|
|
|
|
case2:
|
|
%cmp = icmp eq i32 %count, 50
|
|
%sel = select i1 %cmp, i32 1, i32 2
|
|
br label %for.inc
|
|
|
|
for.inc:
|
|
%state.next = phi i32 [ %sel, %case2 ], [ 1, %for.body ], [ 2, %case1 ]
|
|
call void @func() noduplicate
|
|
%inc = add nsw i32 %count, 1
|
|
%cmp.exit = icmp slt i32 %inc, %num
|
|
br i1 %cmp.exit, label %for.body, label %for.end
|
|
|
|
for.end:
|
|
ret i32 0
|
|
}
|
|
|
|
define i32 @negative3(i32 %num) {
|
|
; REMARK: ConvergentInst
|
|
; REMARK-NEXT: negative3
|
|
entry:
|
|
br label %for.body
|
|
|
|
for.body:
|
|
%count = phi i32 [ 0, %entry ], [ %inc, %for.inc ]
|
|
%state = phi i32 [ 1, %entry ], [ %state.next, %for.inc ]
|
|
switch i32 %state, label %for.inc [
|
|
i32 1, label %case1
|
|
i32 2, label %case2
|
|
]
|
|
|
|
case1:
|
|
br label %for.inc
|
|
|
|
case2:
|
|
%cmp = icmp eq i32 %count, 50
|
|
%sel = select i1 %cmp, i32 1, i32 2
|
|
br label %for.inc
|
|
|
|
for.inc:
|
|
%state.next = phi i32 [ %sel, %case2 ], [ 1, %for.body ], [ 2, %case1 ]
|
|
call void @func() convergent
|
|
%inc = add nsw i32 %count, 1
|
|
%cmp.exit = icmp slt i32 %inc, %num
|
|
br i1 %cmp.exit, label %for.body, label %for.end
|
|
|
|
for.end:
|
|
ret i32 0
|
|
}
|
|
|
|
define i32 @negative4(i32 %num) {
|
|
; REMARK: SwitchNotPredictable
|
|
; REMARK-NEXT: negative4
|
|
entry:
|
|
br label %for.body
|
|
|
|
for.body:
|
|
%count = phi i32 [ 0, %entry ], [ %inc, %for.inc ]
|
|
%state = phi i32 [ 1, %entry ], [ %state.next, %for.inc ]
|
|
switch i32 %state, label %for.inc [
|
|
i32 1, label %case1
|
|
i32 2, label %case2
|
|
]
|
|
|
|
case1:
|
|
br label %for.inc
|
|
|
|
case2:
|
|
%cmp = icmp eq i32 %count, 50
|
|
%sel = select i1 %cmp, i32 1, i32 2
|
|
br label %for.inc
|
|
|
|
for.inc:
|
|
; the switch variable is not predictable since the exit value for %case1
|
|
; is defined through a non-instruction (function argument).
|
|
%state.next = phi i32 [ %sel, %case2 ], [ 1, %for.body ], [ %num, %case1 ]
|
|
%inc = add nsw i32 %count, 1
|
|
%cmp.exit = icmp slt i32 %inc, %num
|
|
br i1 %cmp.exit, label %for.body, label %for.end
|
|
|
|
for.end:
|
|
ret i32 0
|
|
}
|
|
|
|
; Do not optimize if marked minsize.
|
|
define i32 @negative5(i32 %num) minsize {
|
|
; CHECK-LABEL: @negative5(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br label [[FOR_BODY:%.*]]
|
|
; CHECK: for.body:
|
|
; CHECK-NEXT: [[COUNT:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC:%.*]], [[FOR_INC:%.*]] ]
|
|
; CHECK-NEXT: [[STATE:%.*]] = phi i32 [ 1, [[ENTRY]] ], [ [[STATE_NEXT:%.*]], [[FOR_INC]] ]
|
|
; CHECK-NEXT: switch i32 [[STATE]], label [[FOR_INC]] [
|
|
; CHECK-NEXT: i32 1, label [[CASE1:%.*]]
|
|
; CHECK-NEXT: i32 2, label [[CASE2:%.*]]
|
|
; CHECK-NEXT: ]
|
|
; CHECK: case1:
|
|
; CHECK-NEXT: br label [[FOR_INC]]
|
|
; CHECK: case2:
|
|
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[COUNT]], 50
|
|
; CHECK-NEXT: [[SEL:%.*]] = select i1 [[CMP]], i32 1, i32 2
|
|
; CHECK-NEXT: br label [[FOR_INC]]
|
|
; CHECK: for.inc:
|
|
; CHECK-NEXT: [[STATE_NEXT]] = phi i32 [ [[SEL]], [[CASE2]] ], [ 1, [[FOR_BODY]] ], [ 2, [[CASE1]] ]
|
|
; CHECK-NEXT: [[INC]] = add nsw i32 [[COUNT]], 1
|
|
; CHECK-NEXT: [[CMP_EXIT:%.*]] = icmp slt i32 [[INC]], [[NUM:%.*]]
|
|
; CHECK-NEXT: br i1 [[CMP_EXIT]], label [[FOR_BODY]], label [[FOR_END:%.*]]
|
|
; CHECK: for.end:
|
|
; CHECK-NEXT: ret i32 0
|
|
;
|
|
entry:
|
|
br label %for.body
|
|
|
|
for.body:
|
|
%count = phi i32 [ 0, %entry ], [ %inc, %for.inc ]
|
|
%state = phi i32 [ 1, %entry ], [ %state.next, %for.inc ]
|
|
switch i32 %state, label %for.inc [
|
|
i32 1, label %case1
|
|
i32 2, label %case2
|
|
]
|
|
|
|
case1:
|
|
br label %for.inc
|
|
|
|
case2:
|
|
%cmp = icmp eq i32 %count, 50
|
|
%sel = select i1 %cmp, i32 1, i32 2
|
|
br label %for.inc
|
|
|
|
for.inc:
|
|
%state.next = phi i32 [ %sel, %case2 ], [ 1, %for.body ], [ 2, %case1 ]
|
|
%inc = add nsw i32 %count, 1
|
|
%cmp.exit = icmp slt i32 %inc, %num
|
|
br i1 %cmp.exit, label %for.body, label %for.end
|
|
|
|
for.end:
|
|
ret i32 0
|
|
}
|
|
|
|
declare i32 @arbitrary_function()
|
|
|
|
; Don't confuse %state.2 for the initial switch value.
|
|
define i32 @negative6(i32 %init) {
|
|
; REMARK: SwitchNotPredictable
|
|
; REMARK-NEXT: negative6
|
|
entry:
|
|
%cmp = icmp eq i32 %init, 0
|
|
br label %loop.2
|
|
|
|
loop.2:
|
|
%state.2 = call i32 @arbitrary_function()
|
|
br label %loop.3
|
|
|
|
loop.3:
|
|
%state = phi i32 [ %state.2, %loop.2 ], [ 3, %case2 ]
|
|
switch i32 %state, label %infloop.i [
|
|
i32 2, label %case2
|
|
i32 3, label %case3
|
|
i32 4, label %case4
|
|
i32 0, label %case0
|
|
i32 1, label %case1
|
|
]
|
|
|
|
case2:
|
|
br label %loop.3
|
|
|
|
case3:
|
|
br i1 %cmp, label %loop.2.backedge, label %case4
|
|
|
|
case4:
|
|
br label %loop.2.backedge
|
|
|
|
loop.2.backedge:
|
|
br label %loop.2
|
|
|
|
case0:
|
|
br label %exit
|
|
|
|
case1:
|
|
br label %exit
|
|
|
|
infloop.i:
|
|
br label %infloop.i
|
|
|
|
exit:
|
|
ret i32 0
|
|
}
|