Hongyu Chen 22a02b63e8
[DFAJumpThreading] Unify equivalent states (#162447)
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.
2025-10-10 13:44:06 +08:00

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
}