DFAJumpThreading determines the switch destination of a threadable path based on its next state and cannot reuse cloned blocks if their destinations differ. However, different states may lead to the same destination. This patch unifies equivalent states, thereby avoiding redundant duplication of cloned blocks.
282 lines
8.7 KiB
LLVM
282 lines
8.7 KiB
LLVM
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 3
|
|
; RUN: opt -S -passes=dfa-jump-threading %s | FileCheck %s
|
|
|
|
declare void @do_something()
|
|
declare void @user(i32)
|
|
|
|
define void @equivalent_on_default(i1 %c1) {
|
|
; CHECK-LABEL: define void @equivalent_on_default(
|
|
; CHECK-SAME: i1 [[C1:%.*]]) {
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br label [[SWITCH_BB:%.*]]
|
|
; CHECK: switch_bb:
|
|
; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ poison, [[CASE2END:%.*]] ]
|
|
; CHECK-NEXT: switch i32 [[PHI]], label [[DEFAULT_DEST:%.*]] [
|
|
; CHECK-NEXT: i32 0, label [[CASE1:%.*]]
|
|
; CHECK-NEXT: i32 1, label [[CASE2:%.*]]
|
|
; CHECK-NEXT: ]
|
|
; CHECK: switch_bb.jt2:
|
|
; CHECK-NEXT: [[PHI_JT2:%.*]] = phi i32 [ [[PHI_CASE2_JT2:%.*]], [[CASE2END_JT2:%.*]] ]
|
|
; CHECK-NEXT: br label [[DEFAULT_DEST]]
|
|
; CHECK: switch_bb.jt1:
|
|
; CHECK-NEXT: [[PHI_JT1:%.*]] = phi i32 [ 1, [[CASE1]] ]
|
|
; CHECK-NEXT: br label [[CASE2]]
|
|
; CHECK: case1:
|
|
; CHECK-NEXT: br label [[SWITCH_BB_JT1:%.*]]
|
|
; CHECK: case2:
|
|
; CHECK-NEXT: br i1 [[C1]], label [[CASE2THEN:%.*]], label [[CASE2END_JT2]]
|
|
; CHECK: case2then:
|
|
; CHECK-NEXT: br label [[CASE2END_JT2]]
|
|
; CHECK: case2end:
|
|
; CHECK-NEXT: call void @do_something()
|
|
; CHECK-NEXT: br label [[SWITCH_BB]]
|
|
; CHECK: case2end.jt2:
|
|
; CHECK-NEXT: [[PHI_CASE2_JT2]] = phi i32 [ 2, [[CASE2]] ], [ 3, [[CASE2THEN]] ]
|
|
; CHECK-NEXT: call void @do_something()
|
|
; CHECK-NEXT: br label [[SWITCH_BB_JT2:%.*]]
|
|
; CHECK: default_dest:
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
br label %switch_bb
|
|
|
|
switch_bb:
|
|
%phi = phi i32 [ 0, %entry ], [ 1, %case1 ], [ %phi_case2, %case2end ]
|
|
switch i32 %phi, label %default_dest [
|
|
i32 0, label %case1
|
|
i32 1, label %case2
|
|
]
|
|
|
|
case1:
|
|
br label %switch_bb
|
|
|
|
case2:
|
|
br i1 %c1, label %case2then, label %case2end
|
|
|
|
case2then:
|
|
br label %case2end
|
|
|
|
case2end:
|
|
%phi_case2 = phi i32 [ 2, %case2 ] , [ 3, %case2then ]
|
|
call void @do_something()
|
|
br label %switch_bb
|
|
|
|
default_dest:
|
|
ret void
|
|
}
|
|
|
|
define void @equivalent_on_default_user(i1 %c1) {
|
|
; CHECK-LABEL: define void @equivalent_on_default_user(
|
|
; CHECK-SAME: i1 [[C1:%.*]]) {
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br label [[SWITCH_BB:%.*]]
|
|
; CHECK: switch_bb:
|
|
; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ poison, [[CASE2END:%.*]] ]
|
|
; CHECK-NEXT: switch i32 [[PHI]], label [[DEFAULT_DEST:%.*]] [
|
|
; CHECK-NEXT: i32 0, label [[CASE1:%.*]]
|
|
; CHECK-NEXT: i32 1, label [[CASE2:%.*]]
|
|
; CHECK-NEXT: ]
|
|
; CHECK: switch_bb.jt2:
|
|
; CHECK-NEXT: [[PHI_JT2:%.*]] = phi i32 [ [[PHI_CASE2_JT2:%.*]], [[CASE2END_JT2:%.*]] ]
|
|
; CHECK-NEXT: br label [[DEFAULT_DEST]]
|
|
; CHECK: switch_bb.jt1:
|
|
; CHECK-NEXT: [[PHI_JT1:%.*]] = phi i32 [ 1, [[CASE1]] ]
|
|
; CHECK-NEXT: br label [[CASE2]]
|
|
; CHECK: case1:
|
|
; CHECK-NEXT: br label [[SWITCH_BB_JT1:%.*]]
|
|
; CHECK: case2:
|
|
; CHECK-NEXT: br i1 [[C1]], label [[CASE2THEN:%.*]], label [[CASE2END_JT2]]
|
|
; CHECK: case2then:
|
|
; CHECK-NEXT: br label [[CASE2END_JT2]]
|
|
; CHECK: case2end:
|
|
; CHECK-NEXT: call void @do_something()
|
|
; CHECK-NEXT: call void @user(i32 poison)
|
|
; CHECK-NEXT: br label [[SWITCH_BB]]
|
|
; CHECK: case2end.jt2:
|
|
; CHECK-NEXT: [[PHI_CASE2_JT2]] = phi i32 [ 2, [[CASE2]] ], [ 3, [[CASE2THEN]] ]
|
|
; CHECK-NEXT: call void @do_something()
|
|
; CHECK-NEXT: call void @user(i32 [[PHI_CASE2_JT2]])
|
|
; CHECK-NEXT: br label [[SWITCH_BB_JT2:%.*]]
|
|
; CHECK: default_dest:
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
br label %switch_bb
|
|
|
|
switch_bb:
|
|
%phi = phi i32 [ 0, %entry ], [ 1, %case1 ], [ %phi_case2, %case2end ]
|
|
switch i32 %phi, label %default_dest [
|
|
i32 0, label %case1
|
|
i32 1, label %case2
|
|
]
|
|
|
|
case1:
|
|
br label %switch_bb
|
|
|
|
case2:
|
|
br i1 %c1, label %case2then, label %case2end
|
|
|
|
case2then:
|
|
br label %case2end
|
|
|
|
case2end:
|
|
%phi_case2 = phi i32 [ 2, %case2 ] , [ 3, %case2then ]
|
|
call void @do_something()
|
|
call void @user(i32 %phi_case2)
|
|
br label %switch_bb
|
|
|
|
default_dest:
|
|
ret void
|
|
}
|
|
|
|
define void @equivalent_only_cases(i1 %c1) {
|
|
; CHECK-LABEL: define void @equivalent_only_cases(
|
|
; CHECK-SAME: i1 [[C1:%.*]]) {
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br label [[SWITCH_BB:%.*]]
|
|
; CHECK: switch_bb:
|
|
; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ poison, [[CASE2END:%.*]] ]
|
|
; CHECK-NEXT: switch i32 [[PHI]], label [[DEFAULT_DEST:%.*]] [
|
|
; CHECK-NEXT: i32 0, label [[CASE1:%.*]]
|
|
; CHECK-NEXT: i32 1, label [[CASE2:%.*]]
|
|
; CHECK-NEXT: i32 2, label [[CASE1]]
|
|
; CHECK-NEXT: i32 3, label [[CASE1]]
|
|
; CHECK-NEXT: ]
|
|
; CHECK: switch_bb.jt2:
|
|
; CHECK-NEXT: [[PHI_JT2:%.*]] = phi i32 [ [[PHI_CASE2_JT2:%.*]], [[CASE2END_JT2:%.*]] ]
|
|
; CHECK-NEXT: br label [[CASE1]]
|
|
; CHECK: switch_bb.jt1:
|
|
; CHECK-NEXT: [[PHI_JT1:%.*]] = phi i32 [ 1, [[CASE1]] ]
|
|
; CHECK-NEXT: br label [[CASE2]]
|
|
; CHECK: case1:
|
|
; CHECK-NEXT: call void @do_something()
|
|
; CHECK-NEXT: br label [[SWITCH_BB_JT1:%.*]]
|
|
; CHECK: case2:
|
|
; CHECK-NEXT: br i1 [[C1]], label [[CASE2THEN:%.*]], label [[CASE2END_JT2]]
|
|
; CHECK: case2then:
|
|
; CHECK-NEXT: br label [[CASE2END_JT2]]
|
|
; CHECK: case2end:
|
|
; CHECK-NEXT: call void @do_something()
|
|
; CHECK-NEXT: br label [[SWITCH_BB]]
|
|
; CHECK: case2end.jt2:
|
|
; CHECK-NEXT: [[PHI_CASE2_JT2]] = phi i32 [ 2, [[CASE2]] ], [ 3, [[CASE2THEN]] ]
|
|
; CHECK-NEXT: call void @do_something()
|
|
; CHECK-NEXT: br label [[SWITCH_BB_JT2:%.*]]
|
|
; CHECK: default_dest:
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
br label %switch_bb
|
|
|
|
switch_bb:
|
|
%phi = phi i32 [ 0, %entry ], [ 1, %case1 ], [ %phi_case2, %case2end ]
|
|
switch i32 %phi, label %default_dest [
|
|
i32 0, label %case1
|
|
i32 1, label %case2
|
|
i32 2, label %case1
|
|
i32 3, label %case1
|
|
]
|
|
|
|
case1:
|
|
call void @do_something()
|
|
br label %switch_bb
|
|
|
|
case2:
|
|
br i1 %c1, label %case2then, label %case2end
|
|
|
|
case2then:
|
|
br label %case2end
|
|
|
|
case2end:
|
|
%phi_case2 = phi i32 [ 2, %case2 ] , [ 3, %case2then ]
|
|
call void @do_something()
|
|
br label %switch_bb
|
|
|
|
default_dest:
|
|
ret void
|
|
}
|
|
|
|
define void @equivalent_both_case_and_default(i1 %c1, i1 %c2) {
|
|
; CHECK-LABEL: define void @equivalent_both_case_and_default(
|
|
; CHECK-SAME: i1 [[C1:%.*]], i1 [[C2:%.*]]) {
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br label [[SWITCH_BB:%.*]]
|
|
; CHECK: switch_bb:
|
|
; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ poison, [[CASE2END:%.*]] ]
|
|
; CHECK-NEXT: switch i32 [[PHI]], label [[DEFAULT_DEST:%.*]] [
|
|
; CHECK-NEXT: i32 0, label [[CASE1:%.*]]
|
|
; CHECK-NEXT: i32 1, label [[CASE2:%.*]]
|
|
; CHECK-NEXT: i32 2, label [[CASE1]]
|
|
; CHECK-NEXT: i32 3, label [[CASE1]]
|
|
; CHECK-NEXT: ]
|
|
; CHECK: switch_bb.jt4:
|
|
; CHECK-NEXT: [[PHI_JT3:%.*]] = phi i32 [ [[PHI_CASE2_JT3:%.*]], [[CASE2END_JT3:%.*]] ]
|
|
; CHECK-NEXT: br label [[DEFAULT_DEST]]
|
|
; CHECK: switch_bb.jt2:
|
|
; CHECK-NEXT: [[PHI_JT2:%.*]] = phi i32 [ [[PHI_CASE2_JT2:%.*]], [[CASE2END_JT2:%.*]] ]
|
|
; CHECK-NEXT: br label [[CASE1]]
|
|
; CHECK: switch_bb.jt1:
|
|
; CHECK-NEXT: [[PHI_JT1:%.*]] = phi i32 [ 1, [[CASE1]] ]
|
|
; CHECK-NEXT: br label [[CASE2]]
|
|
; CHECK: case1:
|
|
; CHECK-NEXT: call void @do_something()
|
|
; CHECK-NEXT: br label [[SWITCH_BB_JT1:%.*]]
|
|
; CHECK: case2:
|
|
; CHECK-NEXT: br i1 [[C1]], label [[CASE2THEN:%.*]], label [[CASE2END_JT2]]
|
|
; CHECK: case2then:
|
|
; CHECK-NEXT: br i1 [[C2]], label [[CASE2THEN2:%.*]], label [[CASE2END_JT2]]
|
|
; CHECK: case2then2:
|
|
; CHECK-NEXT: br i1 [[C2]], label [[CASE2THEN3:%.*]], label [[CASE2END_JT3]]
|
|
; CHECK: case2then3:
|
|
; CHECK-NEXT: br label [[CASE2END_JT3]]
|
|
; CHECK: case2end:
|
|
; CHECK-NEXT: call void @do_something()
|
|
; CHECK-NEXT: br label [[SWITCH_BB]]
|
|
; CHECK: case2end.jt4:
|
|
; CHECK-NEXT: [[PHI_CASE2_JT3]] = phi i32 [ 4, [[CASE2THEN2]] ], [ 5, [[CASE2THEN3]] ]
|
|
; CHECK-NEXT: call void @do_something()
|
|
; CHECK-NEXT: br label [[SWITCH_BB_JT3:%.*]]
|
|
; CHECK: case2end.jt2:
|
|
; CHECK-NEXT: [[PHI_CASE2_JT2]] = phi i32 [ 2, [[CASE2]] ], [ 3, [[CASE2THEN]] ]
|
|
; CHECK-NEXT: call void @do_something()
|
|
; CHECK-NEXT: br label [[SWITCH_BB_JT2:%.*]]
|
|
; CHECK: default_dest:
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
br label %switch_bb
|
|
|
|
switch_bb:
|
|
%phi = phi i32 [ 0, %entry ], [ 1, %case1 ], [ %phi_case2, %case2end ]
|
|
switch i32 %phi, label %default_dest [
|
|
i32 0, label %case1
|
|
i32 1, label %case2
|
|
i32 2, label %case1
|
|
i32 3, label %case1
|
|
]
|
|
|
|
case1:
|
|
call void @do_something()
|
|
br label %switch_bb
|
|
|
|
case2:
|
|
br i1 %c1, label %case2then, label %case2end
|
|
|
|
case2then:
|
|
br i1 %c2, label %case2then2, label %case2end
|
|
|
|
case2then2:
|
|
br i1 %c2, label %case2then3, label %case2end
|
|
|
|
case2then3:
|
|
br label %case2end
|
|
|
|
case2end:
|
|
%phi_case2 = phi i32 [ 2, %case2 ], [ 3, %case2then ], [ 4, %case2then2 ], [ 5, %case2then3 ]
|
|
call void @do_something()
|
|
br label %switch_bb
|
|
|
|
default_dest:
|
|
ret void
|
|
}
|