Set phi inputs to poison whenever we find a dead edge (either during initial worklist population or the main InstCombine run), instead of only doing this for successors of dead blocks. This means that the phi operand is set to poison even if for critical edges without an intermediate block. There are quite a few test changes, because the pattern is fairly common in vectorizer output, for cases where we know the vectorized loop will be entered.
546 lines
12 KiB
LLVM
546 lines
12 KiB
LLVM
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 2
|
|
; RUN: opt -S -passes=instcombine -instcombine-infinite-loop-threshold=2 < %s | FileCheck %s --check-prefixes=CHECK,DEFAULT_ITER
|
|
; RUN: opt -S -passes='instcombine<max-iterations=1>' < %s | FileCheck %s --check-prefixes=CHECK,MAX1
|
|
|
|
declare void @dummy()
|
|
declare void @llvm.assume(i1)
|
|
|
|
define i32 @br_true(i1 %x) {
|
|
; CHECK-LABEL: define i32 @br_true
|
|
; CHECK-SAME: (i1 [[X:%.*]]) {
|
|
; CHECK-NEXT: br i1 true, label [[IF:%.*]], label [[ELSE:%.*]]
|
|
; CHECK: if:
|
|
; CHECK-NEXT: call void @dummy()
|
|
; CHECK-NEXT: br label [[JOIN:%.*]]
|
|
; CHECK: else:
|
|
; CHECK-NEXT: br label [[JOIN]]
|
|
; CHECK: join:
|
|
; CHECK-NEXT: ret i32 1
|
|
;
|
|
%c = or i1 %x, true
|
|
br i1 %c, label %if, label %else
|
|
|
|
if:
|
|
call void @dummy()
|
|
br label %join
|
|
|
|
else:
|
|
call void @dummy()
|
|
br label %join
|
|
|
|
join:
|
|
%phi = phi i32 [ 1, %if ], [ 2, %else ]
|
|
ret i32 %phi
|
|
}
|
|
|
|
define i32 @br_false(i1 %x) {
|
|
; CHECK-LABEL: define i32 @br_false
|
|
; CHECK-SAME: (i1 [[X:%.*]]) {
|
|
; CHECK-NEXT: br i1 false, label [[IF:%.*]], label [[ELSE:%.*]]
|
|
; CHECK: if:
|
|
; CHECK-NEXT: br label [[JOIN:%.*]]
|
|
; CHECK: else:
|
|
; CHECK-NEXT: call void @dummy()
|
|
; CHECK-NEXT: br label [[JOIN]]
|
|
; CHECK: join:
|
|
; CHECK-NEXT: ret i32 2
|
|
;
|
|
%c = and i1 %x, false
|
|
br i1 %c, label %if, label %else
|
|
|
|
if:
|
|
call void @dummy()
|
|
br label %join
|
|
|
|
else:
|
|
call void @dummy()
|
|
br label %join
|
|
|
|
join:
|
|
%phi = phi i32 [ 1, %if ], [ 2, %else ]
|
|
ret i32 %phi
|
|
}
|
|
|
|
define i32 @br_undef(i1 %x) {
|
|
; CHECK-LABEL: define i32 @br_undef
|
|
; CHECK-SAME: (i1 [[X:%.*]]) {
|
|
; CHECK-NEXT: br i1 undef, label [[IF:%.*]], label [[ELSE:%.*]]
|
|
; CHECK: if:
|
|
; CHECK-NEXT: br label [[JOIN:%.*]]
|
|
; CHECK: else:
|
|
; CHECK-NEXT: br label [[JOIN]]
|
|
; CHECK: join:
|
|
; CHECK-NEXT: ret i32 poison
|
|
;
|
|
%c = xor i1 %x, undef
|
|
br i1 %c, label %if, label %else
|
|
|
|
if:
|
|
call void @dummy()
|
|
br label %join
|
|
|
|
else:
|
|
call void @dummy()
|
|
br label %join
|
|
|
|
join:
|
|
%phi = phi i32 [ 1, %if ], [ 2, %else ]
|
|
ret i32 %phi
|
|
}
|
|
|
|
define i32 @br_true_phi_with_repeated_preds(i1 %x) {
|
|
; CHECK-LABEL: define i32 @br_true_phi_with_repeated_preds
|
|
; CHECK-SAME: (i1 [[X:%.*]]) {
|
|
; CHECK-NEXT: br i1 true, label [[IF:%.*]], label [[ELSE:%.*]]
|
|
; CHECK: if:
|
|
; CHECK-NEXT: call void @dummy()
|
|
; CHECK-NEXT: br label [[JOIN:%.*]]
|
|
; CHECK: else:
|
|
; CHECK-NEXT: br i1 false, label [[JOIN]], label [[JOIN]]
|
|
; CHECK: join:
|
|
; CHECK-NEXT: ret i32 1
|
|
;
|
|
%c = or i1 %x, true
|
|
br i1 %c, label %if, label %else
|
|
|
|
if:
|
|
call void @dummy()
|
|
br label %join
|
|
|
|
else:
|
|
br i1 false, label %join, label %join
|
|
|
|
join:
|
|
%phi = phi i32 [ 1, %if ], [ 2, %else ], [ 2, %else ]
|
|
ret i32 %phi
|
|
}
|
|
|
|
define i32 @br_true_const_phi_direct_edge(i1 %x) {
|
|
; CHECK-LABEL: define i32 @br_true_const_phi_direct_edge
|
|
; CHECK-SAME: (i1 [[X:%.*]]) {
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br i1 true, label [[IF:%.*]], label [[JOIN:%.*]]
|
|
; CHECK: if:
|
|
; CHECK-NEXT: call void @dummy()
|
|
; CHECK-NEXT: br label [[JOIN]]
|
|
; CHECK: join:
|
|
; CHECK-NEXT: ret i32 2
|
|
;
|
|
entry:
|
|
br i1 true, label %if, label %join
|
|
|
|
if:
|
|
call void @dummy()
|
|
br label %join
|
|
|
|
join:
|
|
%phi = phi i32 [ 1, %entry ], [ 2, %if ]
|
|
ret i32 %phi
|
|
}
|
|
|
|
define i32 @br_true_var_phi_direct_edge(i1 %x) {
|
|
; CHECK-LABEL: define i32 @br_true_var_phi_direct_edge
|
|
; CHECK-SAME: (i1 [[X:%.*]]) {
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br i1 true, label [[IF:%.*]], label [[JOIN:%.*]]
|
|
; CHECK: if:
|
|
; CHECK-NEXT: call void @dummy()
|
|
; CHECK-NEXT: br label [[JOIN]]
|
|
; CHECK: join:
|
|
; CHECK-NEXT: ret i32 2
|
|
;
|
|
entry:
|
|
%c = or i1 %x, true
|
|
br i1 %c, label %if, label %join
|
|
|
|
if:
|
|
call void @dummy()
|
|
br label %join
|
|
|
|
join:
|
|
%phi = phi i32 [ 1, %entry ], [ 2, %if ]
|
|
ret i32 %phi
|
|
}
|
|
|
|
define void @switch_case(i32 %x) {
|
|
; CHECK-LABEL: define void @switch_case
|
|
; CHECK-SAME: (i32 [[X:%.*]]) {
|
|
; CHECK-NEXT: switch i32 0, label [[DEFAULT:%.*]] [
|
|
; CHECK-NEXT: i32 0, label [[CASE0:%.*]]
|
|
; CHECK-NEXT: ]
|
|
; CHECK: case0:
|
|
; CHECK-NEXT: call void @dummy()
|
|
; CHECK-NEXT: ret void
|
|
; CHECK: default:
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%v = and i32 %x, 0
|
|
switch i32 %v, label %default [
|
|
i32 0, label %case0
|
|
]
|
|
|
|
case0:
|
|
call void @dummy()
|
|
ret void
|
|
|
|
default:
|
|
call void @dummy()
|
|
ret void
|
|
}
|
|
|
|
define void @switch_default(i32 %x) {
|
|
; CHECK-LABEL: define void @switch_default
|
|
; CHECK-SAME: (i32 [[X:%.*]]) {
|
|
; CHECK-NEXT: switch i32 -1, label [[DEFAULT:%.*]] [
|
|
; CHECK-NEXT: i32 0, label [[CASE0:%.*]]
|
|
; CHECK-NEXT: ]
|
|
; CHECK: case0:
|
|
; CHECK-NEXT: ret void
|
|
; CHECK: default:
|
|
; CHECK-NEXT: call void @dummy()
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%v = or i32 %x, -1
|
|
switch i32 %v, label %default [
|
|
i32 0, label %case0
|
|
]
|
|
|
|
case0:
|
|
call void @dummy()
|
|
ret void
|
|
|
|
default:
|
|
call void @dummy()
|
|
ret void
|
|
}
|
|
|
|
define void @switch_undef(i32 %x) {
|
|
; CHECK-LABEL: define void @switch_undef
|
|
; CHECK-SAME: (i32 [[X:%.*]]) {
|
|
; CHECK-NEXT: switch i32 undef, label [[DEFAULT:%.*]] [
|
|
; CHECK-NEXT: i32 0, label [[CASE0:%.*]]
|
|
; CHECK-NEXT: ]
|
|
; CHECK: case0:
|
|
; CHECK-NEXT: ret void
|
|
; CHECK: default:
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%v = xor i32 %x, undef
|
|
switch i32 %v, label %default [
|
|
i32 0, label %case0
|
|
]
|
|
|
|
case0:
|
|
call void @dummy()
|
|
ret void
|
|
|
|
default:
|
|
call void @dummy()
|
|
ret void
|
|
}
|
|
|
|
define void @non_term_unreachable() {
|
|
; CHECK-LABEL: define void @non_term_unreachable() {
|
|
; CHECK-NEXT: call void @dummy()
|
|
; CHECK-NEXT: store i1 true, ptr poison, align 1
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
call void @dummy()
|
|
call void @dummy() nounwind willreturn
|
|
store i1 true, ptr poison
|
|
call void @dummy()
|
|
ret void
|
|
}
|
|
|
|
define i32 @non_term_unreachable_phi(i1 %c) {
|
|
; CHECK-LABEL: define i32 @non_term_unreachable_phi
|
|
; CHECK-SAME: (i1 [[C:%.*]]) {
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br i1 [[C]], label [[IF:%.*]], label [[JOIN:%.*]]
|
|
; CHECK: if:
|
|
; CHECK-NEXT: store i1 true, ptr poison, align 1
|
|
; CHECK-NEXT: br label [[JOIN]]
|
|
; CHECK: join:
|
|
; CHECK-NEXT: ret i32 2
|
|
;
|
|
entry:
|
|
br i1 %c, label %if, label %join
|
|
|
|
if:
|
|
store i1 true, ptr poison
|
|
call void @dummy()
|
|
br label %join
|
|
|
|
join:
|
|
%phi = phi i32 [ 1, %if], [ 2, %entry ]
|
|
ret i32 %phi
|
|
}
|
|
|
|
define void @non_term_unreachable_following_blocks() {
|
|
; CHECK-LABEL: define void @non_term_unreachable_following_blocks() {
|
|
; CHECK-NEXT: call void @dummy()
|
|
; CHECK-NEXT: store i1 true, ptr poison, align 1
|
|
; CHECK-NEXT: br label [[SPLIT:%.*]]
|
|
; CHECK: split:
|
|
; CHECK-NEXT: br label [[LOOP:%.*]]
|
|
; CHECK: loop:
|
|
; CHECK-NEXT: br label [[LOOP]]
|
|
;
|
|
call void @dummy()
|
|
store i1 true, ptr poison
|
|
call void @dummy()
|
|
br label %split
|
|
|
|
split:
|
|
call void @dummy()
|
|
br label %loop
|
|
|
|
loop:
|
|
call void @dummy()
|
|
br label %loop
|
|
}
|
|
|
|
define void @br_not_into_loop(i1 %x) {
|
|
; CHECK-LABEL: define void @br_not_into_loop
|
|
; CHECK-SAME: (i1 [[X:%.*]]) {
|
|
; CHECK-NEXT: br i1 true, label [[EXIT:%.*]], label [[LOOP:%.*]]
|
|
; CHECK: loop:
|
|
; CHECK-NEXT: br label [[LOOP]]
|
|
; CHECK: exit:
|
|
; CHECK-NEXT: call void @dummy()
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%c = or i1 %x, true
|
|
br i1 %c, label %exit, label %loop
|
|
|
|
loop:
|
|
call void @dummy()
|
|
br label %loop
|
|
|
|
exit:
|
|
call void @dummy()
|
|
ret void
|
|
}
|
|
|
|
define void @br_into_loop(i1 %x) {
|
|
; CHECK-LABEL: define void @br_into_loop
|
|
; CHECK-SAME: (i1 [[X:%.*]]) {
|
|
; CHECK-NEXT: br i1 true, label [[LOOP:%.*]], label [[EXIT:%.*]]
|
|
; CHECK: loop:
|
|
; CHECK-NEXT: call void @dummy()
|
|
; CHECK-NEXT: br label [[LOOP]]
|
|
; CHECK: exit:
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%c = or i1 %x, true
|
|
br i1 %c, label %loop, label %exit
|
|
|
|
loop:
|
|
call void @dummy()
|
|
br label %loop
|
|
|
|
exit:
|
|
call void @dummy()
|
|
ret void
|
|
}
|
|
|
|
define void @two_br_not_into_loop(i1 %x) {
|
|
; CHECK-LABEL: define void @two_br_not_into_loop
|
|
; CHECK-SAME: (i1 [[X:%.*]]) {
|
|
; CHECK-NEXT: br i1 true, label [[BB2:%.*]], label [[LOOP:%.*]]
|
|
; CHECK: bb2:
|
|
; CHECK-NEXT: br i1 true, label [[EXIT:%.*]], label [[LOOP]]
|
|
; CHECK: loop:
|
|
; CHECK-NEXT: br label [[LOOP]]
|
|
; CHECK: exit:
|
|
; CHECK-NEXT: call void @dummy()
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%c = or i1 %x, true
|
|
br i1 %c, label %bb2, label %loop
|
|
|
|
bb2:
|
|
%c2 = or i1 %x, true
|
|
br i1 %c2, label %exit, label %loop
|
|
|
|
loop:
|
|
call void @dummy()
|
|
br label %loop
|
|
|
|
exit:
|
|
call void @dummy()
|
|
ret void
|
|
}
|
|
|
|
define void @one_br_into_loop_one_not(i1 %x, i1 %c2) {
|
|
; CHECK-LABEL: define void @one_br_into_loop_one_not
|
|
; CHECK-SAME: (i1 [[X:%.*]], i1 [[C2:%.*]]) {
|
|
; CHECK-NEXT: br i1 true, label [[BB2:%.*]], label [[LOOP:%.*]]
|
|
; CHECK: bb2:
|
|
; CHECK-NEXT: br i1 [[C2]], label [[EXIT:%.*]], label [[LOOP]]
|
|
; CHECK: loop:
|
|
; CHECK-NEXT: call void @dummy()
|
|
; CHECK-NEXT: br label [[LOOP]]
|
|
; CHECK: exit:
|
|
; CHECK-NEXT: call void @dummy()
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%c = or i1 %x, true
|
|
br i1 %c, label %bb2, label %loop
|
|
|
|
bb2:
|
|
br i1 %c2, label %exit, label %loop
|
|
|
|
loop:
|
|
call void @dummy()
|
|
br label %loop
|
|
|
|
exit:
|
|
call void @dummy()
|
|
ret void
|
|
}
|
|
|
|
define void @two_br_not_into_loop_with_split(i1 %x) {
|
|
; CHECK-LABEL: define void @two_br_not_into_loop_with_split
|
|
; CHECK-SAME: (i1 [[X:%.*]]) {
|
|
; CHECK-NEXT: br i1 true, label [[BB2:%.*]], label [[SPLIT1:%.*]]
|
|
; CHECK: bb2:
|
|
; CHECK-NEXT: br i1 true, label [[EXIT:%.*]], label [[SPLIT2:%.*]]
|
|
; CHECK: split1:
|
|
; CHECK-NEXT: br label [[LOOP:%.*]]
|
|
; CHECK: split2:
|
|
; CHECK-NEXT: br label [[LOOP]]
|
|
; CHECK: loop:
|
|
; CHECK-NEXT: br label [[LOOP]]
|
|
; CHECK: exit:
|
|
; CHECK-NEXT: call void @dummy()
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%c = or i1 %x, true
|
|
br i1 %c, label %bb2, label %split1
|
|
|
|
bb2:
|
|
%c2 = or i1 %x, true
|
|
br i1 %c2, label %exit, label %split2
|
|
|
|
split1:
|
|
call void @dummy()
|
|
br label %loop
|
|
|
|
split2:
|
|
call void @dummy()
|
|
br label %loop
|
|
|
|
loop:
|
|
call void @dummy()
|
|
br label %loop
|
|
|
|
exit:
|
|
call void @dummy()
|
|
ret void
|
|
}
|
|
|
|
define void @irreducible() {
|
|
; CHECK-LABEL: define void @irreducible() {
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br i1 false, label [[LOOP2:%.*]], label [[LOOP1:%.*]]
|
|
; CHECK: loop1:
|
|
; CHECK-NEXT: call void @dummy()
|
|
; CHECK-NEXT: br label [[LOOP2]]
|
|
; CHECK: loop2:
|
|
; CHECK-NEXT: call void @dummy()
|
|
; CHECK-NEXT: br i1 true, label [[EXIT:%.*]], label [[LOOP1]]
|
|
; CHECK: exit:
|
|
; CHECK-NEXT: call void @dummy()
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
br i1 false, label %loop2, label %loop1
|
|
|
|
loop1:
|
|
call void @dummy()
|
|
br label %loop2
|
|
|
|
loop2:
|
|
call void @dummy()
|
|
br i1 true, label %exit, label %loop1
|
|
|
|
exit:
|
|
call void @dummy()
|
|
ret void
|
|
}
|
|
|
|
define void @really_unreachable() {
|
|
; CHECK-LABEL: define void @really_unreachable() {
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: ret void
|
|
; CHECK: unreachable:
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
ret void
|
|
|
|
unreachable:
|
|
call void @dummy()
|
|
ret void
|
|
}
|
|
|
|
define void @really_unreachable_predecessor() {
|
|
; CHECK-LABEL: define void @really_unreachable_predecessor() {
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br i1 false, label [[BB:%.*]], label [[EXIT:%.*]]
|
|
; CHECK: unreachable:
|
|
; CHECK-NEXT: br label [[BB]]
|
|
; CHECK: bb:
|
|
; CHECK-NEXT: ret void
|
|
; CHECK: exit:
|
|
; CHECK-NEXT: call void @dummy()
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
br i1 false, label %bb, label %exit
|
|
|
|
unreachable:
|
|
call void @dummy()
|
|
br label %bb
|
|
|
|
bb:
|
|
call void @dummy()
|
|
ret void
|
|
|
|
exit:
|
|
call void @dummy()
|
|
ret void
|
|
}
|
|
|
|
define i32 @pr64235() {
|
|
; CHECK-LABEL: define i32 @pr64235() {
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br i1 false, label [[BB:%.*]], label [[BB3:%.*]]
|
|
; CHECK: bb3:
|
|
; CHECK-NEXT: store i1 true, ptr poison, align 1
|
|
; CHECK-NEXT: br label [[BB2:%.*]]
|
|
; CHECK: bb:
|
|
; CHECK-NEXT: br label [[BB2]]
|
|
; CHECK: bb2:
|
|
; CHECK-NEXT: br label [[BB]]
|
|
;
|
|
entry:
|
|
br i1 false, label %bb, label %bb3
|
|
|
|
bb3:
|
|
call void @llvm.assume(i1 false)
|
|
br label %bb2
|
|
|
|
bb:
|
|
br label %bb2
|
|
|
|
bb2:
|
|
call void @llvm.assume(i1 false)
|
|
br label %bb
|
|
}
|
|
|
|
;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
|
|
; DEFAULT_ITER: {{.*}}
|
|
; MAX1: {{.*}}
|