
Instructions in unreachable basic blocks are removed, but terminators are not. In this case, even instructions that are only referenced by a terminator, such as a return instruction, cannot be processed properly. This patch changes the operand of a return instruction in an unreachable basic block to poison if it refers to the instruction, allowing the instruction to be properly processed. Fixes #65107.
603 lines
13 KiB
LLVM
603 lines
13 KiB
LLVM
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 2
|
|
; RUN: opt -S -passes=instcombine < %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
|
|
}
|
|
|
|
declare void @invoke(ptr)
|
|
declare i32 @__gxx_personality_v0(...)
|
|
define void @test(i1 %x) personality ptr @__gxx_personality_v0 {
|
|
; CHECK-LABEL: define void @test
|
|
; CHECK-SAME: (i1 [[X:%.*]]) personality ptr @__gxx_personality_v0 {
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br i1 [[X]], label [[IF_ELSE:%.*]], label [[CLEAN1:%.*]]
|
|
; CHECK: if.else:
|
|
; CHECK-NEXT: store i32 1, ptr undef, align 4
|
|
; CHECK-NEXT: invoke void @invoke(ptr poison)
|
|
; CHECK-NEXT: to label [[CONT:%.*]] unwind label [[LPAD5:%.*]]
|
|
; CHECK: cont:
|
|
; CHECK-NEXT: invoke void @invoke(ptr poison)
|
|
; CHECK-NEXT: to label [[CLEAN1]] unwind label [[LPAD6:%.*]]
|
|
; CHECK: lpad5:
|
|
; CHECK-NEXT: [[TMP0:%.*]] = landingpad { ptr, i32 }
|
|
; CHECK-NEXT: cleanup
|
|
; CHECK-NEXT: br label [[CLEAN1]]
|
|
; CHECK: lpad6:
|
|
; CHECK-NEXT: [[TMP1:%.*]] = landingpad { ptr, i32 }
|
|
; CHECK-NEXT: cleanup
|
|
; CHECK-NEXT: br label [[CLEAN2:%.*]]
|
|
; CHECK: clean1:
|
|
; CHECK-NEXT: ret void
|
|
; CHECK: clean2:
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
%ref = alloca ptr
|
|
br i1 %x, label %if.else, label %clean1
|
|
|
|
if.else:
|
|
store i32 1, ptr undef
|
|
invoke void @invoke(ptr %ref)
|
|
to label %cont unwind label %lpad5
|
|
|
|
cont:
|
|
invoke void @invoke(ptr %ref)
|
|
to label %clean1 unwind label %lpad6
|
|
|
|
lpad5:
|
|
%13 = landingpad { ptr, i32 }
|
|
cleanup
|
|
br label %clean1
|
|
|
|
lpad6:
|
|
%14 = landingpad { ptr, i32 }
|
|
cleanup
|
|
br label %clean2
|
|
|
|
clean1:
|
|
ret void
|
|
|
|
clean2:
|
|
ret void
|
|
}
|
|
|
|
;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
|
|
; DEFAULT_ITER: {{.*}}
|
|
; MAX1: {{.*}}
|