blob: aa074e443269503b2bf7f71e2eee89a4d9286521 [file] [log] [blame]
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt -simplifycfg -simplifycfg-require-and-preserve-domtree=1 -sink-common-insts -S < %s | FileCheck %s
; Test that we tail merge noreturn call blocks and phi constants properly.
declare void @abort()
declare void @assert_fail_1(i32)
declare void @assert_fail_1_alt(i32)
define void @merge_simple() {
; CHECK-LABEL: @merge_simple(
; CHECK-NEXT: [[C1:%.*]] = call i1 @foo()
; CHECK-NEXT: br i1 [[C1]], label [[CONT1:%.*]], label [[A1:%.*]]
; CHECK: a1:
; CHECK-NEXT: call void @assert_fail_1(i32 0)
; CHECK-NEXT: unreachable
; CHECK: cont1:
; CHECK-NEXT: [[C2:%.*]] = call i1 @foo()
; CHECK-NEXT: br i1 [[C2]], label [[CONT2:%.*]], label [[A2:%.*]]
; CHECK: a2:
; CHECK-NEXT: call void @assert_fail_1(i32 0)
; CHECK-NEXT: unreachable
; CHECK: cont2:
; CHECK-NEXT: [[C3:%.*]] = call i1 @foo()
; CHECK-NEXT: br i1 [[C3]], label [[CONT3:%.*]], label [[A3:%.*]]
; CHECK: a3:
; CHECK-NEXT: call void @assert_fail_1(i32 0)
; CHECK-NEXT: unreachable
; CHECK: cont3:
; CHECK-NEXT: ret void
;
%c1 = call i1 @foo()
br i1 %c1, label %cont1, label %a1
a1:
call void @assert_fail_1(i32 0)
unreachable
cont1:
%c2 = call i1 @foo()
br i1 %c2, label %cont2, label %a2
a2:
call void @assert_fail_1(i32 0)
unreachable
cont2:
%c3 = call i1 @foo()
br i1 %c3, label %cont3, label %a3
a3:
call void @assert_fail_1(i32 0)
unreachable
cont3:
ret void
}
define void @phi_three_constants() {
; CHECK-LABEL: @phi_three_constants(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[C1:%.*]] = call i1 @foo()
; CHECK-NEXT: br i1 [[C1]], label [[CONT1:%.*]], label [[A1:%.*]]
; CHECK: a1:
; CHECK-NEXT: call void @assert_fail_1(i32 0)
; CHECK-NEXT: unreachable
; CHECK: cont1:
; CHECK-NEXT: [[C2:%.*]] = call i1 @foo()
; CHECK-NEXT: br i1 [[C2]], label [[CONT2:%.*]], label [[A2:%.*]]
; CHECK: a2:
; CHECK-NEXT: call void @assert_fail_1(i32 1)
; CHECK-NEXT: unreachable
; CHECK: cont2:
; CHECK-NEXT: [[C3:%.*]] = call i1 @foo()
; CHECK-NEXT: br i1 [[C3]], label [[CONT3:%.*]], label [[A3:%.*]]
; CHECK: a3:
; CHECK-NEXT: call void @assert_fail_1(i32 2)
; CHECK-NEXT: unreachable
; CHECK: cont3:
; CHECK-NEXT: ret void
;
entry:
%c1 = call i1 @foo()
br i1 %c1, label %cont1, label %a1
a1:
call void @assert_fail_1(i32 0)
unreachable
cont1:
%c2 = call i1 @foo()
br i1 %c2, label %cont2, label %a2
a2:
call void @assert_fail_1(i32 1)
unreachable
cont2:
%c3 = call i1 @foo()
br i1 %c3, label %cont3, label %a3
a3:
call void @assert_fail_1(i32 2)
unreachable
cont3:
ret void
}
define void @dont_phi_values(i32 %x, i32 %y) {
; CHECK-LABEL: @dont_phi_values(
; CHECK-NEXT: [[C1:%.*]] = call i1 @foo()
; CHECK-NEXT: br i1 [[C1]], label [[CONT1:%.*]], label [[A1:%.*]]
; CHECK: a1:
; CHECK-NEXT: call void @assert_fail_1(i32 [[X:%.*]])
; CHECK-NEXT: unreachable
; CHECK: cont1:
; CHECK-NEXT: [[C2:%.*]] = call i1 @foo()
; CHECK-NEXT: br i1 [[C2]], label [[CONT2:%.*]], label [[A2:%.*]]
; CHECK: a2:
; CHECK-NEXT: call void @assert_fail_1(i32 [[Y:%.*]])
; CHECK-NEXT: unreachable
; CHECK: cont2:
; CHECK-NEXT: ret void
;
%c1 = call i1 @foo()
br i1 %c1, label %cont1, label %a1
a1:
call void @assert_fail_1(i32 %x)
unreachable
cont1:
%c2 = call i1 @foo()
br i1 %c2, label %cont2, label %a2
a2:
call void @assert_fail_1(i32 %y)
unreachable
cont2:
ret void
}
define void @dont_phi_callees() {
; CHECK-LABEL: @dont_phi_callees(
; CHECK-NEXT: [[C1:%.*]] = call i1 @foo()
; CHECK-NEXT: br i1 [[C1]], label [[CONT1:%.*]], label [[A1:%.*]]
; CHECK: cont1:
; CHECK-NEXT: [[C2:%.*]] = call i1 @foo()
; CHECK-NEXT: br i1 [[C2]], label [[CONT2:%.*]], label [[A2:%.*]]
; CHECK: cont2:
; CHECK-NEXT: ret void
; CHECK: a1:
; CHECK-NEXT: call void @assert_fail_1(i32 0)
; CHECK-NEXT: unreachable
; CHECK: a2:
; CHECK-NEXT: call void @assert_fail_1_alt(i32 0)
; CHECK-NEXT: unreachable
;
%c1 = call i1 @foo()
br i1 %c1, label %cont1, label %a1
cont1:
%c2 = call i1 @foo()
br i1 %c2, label %cont2, label %a2
cont2:
ret void
a1:
call void @assert_fail_1(i32 0)
unreachable
a2:
call void @assert_fail_1_alt(i32 0)
unreachable
}
declare i1 @foo()
declare i1 @bar()
define void @unmergeable_phis(i32 %v, i1 %c) {
; CHECK-LABEL: @unmergeable_phis(
; CHECK-NEXT: entry:
; CHECK-NEXT: br i1 [[C:%.*]], label [[S1:%.*]], label [[S2:%.*]]
; CHECK: s1:
; CHECK-NEXT: [[C1:%.*]] = call i1 @foo()
; CHECK-NEXT: br i1 [[C1]], label [[A1:%.*]], label [[A2:%.*]]
; CHECK: s2:
; CHECK-NEXT: [[C2:%.*]] = call i1 @bar()
; CHECK-NEXT: br i1 [[C2]], label [[A1]], label [[A2]]
; CHECK: a1:
; CHECK-NEXT: [[L1:%.*]] = phi i32 [ 0, [[S1]] ], [ 1, [[S2]] ]
; CHECK-NEXT: call void @assert_fail_1(i32 [[L1]])
; CHECK-NEXT: unreachable
; CHECK: a2:
; CHECK-NEXT: [[L2:%.*]] = phi i32 [ 2, [[S1]] ], [ 3, [[S2]] ]
; CHECK-NEXT: call void @assert_fail_1(i32 [[L2]])
; CHECK-NEXT: unreachable
;
entry:
br i1 %c, label %s1, label %s2
s1:
%c1 = call i1 @foo()
br i1 %c1, label %a1, label %a2
s2:
%c2 = call i1 @bar()
br i1 %c2, label %a1, label %a2
a1:
%l1 = phi i32 [ 0, %s1 ], [ 1, %s2 ]
call void @assert_fail_1(i32 %l1)
unreachable
a2:
%l2 = phi i32 [ 2, %s1 ], [ 3, %s2 ]
call void @assert_fail_1(i32 %l2)
unreachable
}
define void @tail_merge_switch(i32 %v) {
; CHECK-LABEL: @tail_merge_switch(
; CHECK-NEXT: entry:
; CHECK-NEXT: switch i32 [[V:%.*]], label [[RET:%.*]] [
; CHECK-NEXT: i32 0, label [[A1:%.*]]
; CHECK-NEXT: i32 13, label [[A2:%.*]]
; CHECK-NEXT: i32 42, label [[A3:%.*]]
; CHECK-NEXT: ]
; CHECK: a1:
; CHECK-NEXT: call void @assert_fail_1(i32 0)
; CHECK-NEXT: unreachable
; CHECK: a2:
; CHECK-NEXT: call void @assert_fail_1(i32 1)
; CHECK-NEXT: unreachable
; CHECK: a3:
; CHECK-NEXT: call void @assert_fail_1(i32 2)
; CHECK-NEXT: unreachable
; CHECK: ret:
; CHECK-NEXT: ret void
;
entry:
switch i32 %v, label %ret [
i32 0, label %a1
i32 13, label %a2
i32 42, label %a3
]
a1:
call void @assert_fail_1(i32 0)
unreachable
a2:
call void @assert_fail_1(i32 1)
unreachable
a3:
call void @assert_fail_1(i32 2)
unreachable
ret:
ret void
}
define void @need_to_add_bb2_preds(i1 %c1) {
; CHECK-LABEL: @need_to_add_bb2_preds(
; CHECK-NEXT: bb1:
; CHECK-NEXT: br i1 [[C1:%.*]], label [[BB2:%.*]], label [[A1:%.*]]
; CHECK: bb2:
; CHECK-NEXT: [[C2:%.*]] = call i1 @bar()
; CHECK-NEXT: br i1 [[C2]], label [[A2:%.*]], label [[A3:%.*]]
; CHECK: a1:
; CHECK-NEXT: call void @assert_fail_1(i32 0)
; CHECK-NEXT: unreachable
; CHECK: a2:
; CHECK-NEXT: call void @assert_fail_1(i32 1)
; CHECK-NEXT: unreachable
; CHECK: a3:
; CHECK-NEXT: call void @assert_fail_1(i32 2)
; CHECK-NEXT: unreachable
;
bb1:
br i1 %c1, label %bb2, label %a1
bb2:
%c2 = call i1 @bar()
br i1 %c2, label %a2, label %a3
a1:
call void @assert_fail_1(i32 0)
unreachable
a2:
call void @assert_fail_1(i32 1)
unreachable
a3:
call void @assert_fail_1(i32 2)
unreachable
}
define void @phi_in_bb2() {
; CHECK-LABEL: @phi_in_bb2(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[C1:%.*]] = call i1 @foo()
; CHECK-NEXT: br i1 [[C1]], label [[CONT1:%.*]], label [[A1:%.*]]
; CHECK: a1:
; CHECK-NEXT: call void @assert_fail_1(i32 0)
; CHECK-NEXT: unreachable
; CHECK: cont1:
; CHECK-NEXT: [[C2:%.*]] = call i1 @foo()
; CHECK-NEXT: br i1 [[C2]], label [[CONT2:%.*]], label [[A2:%.*]]
; CHECK: a2:
; CHECK-NEXT: [[P2:%.*]] = phi i32 [ 1, [[CONT1]] ], [ 2, [[CONT2]] ]
; CHECK-NEXT: call void @assert_fail_1(i32 [[P2]])
; CHECK-NEXT: unreachable
; CHECK: cont2:
; CHECK-NEXT: [[C3:%.*]] = call i1 @foo()
; CHECK-NEXT: br i1 [[C3]], label [[CONT3:%.*]], label [[A2]]
; CHECK: cont3:
; CHECK-NEXT: ret void
;
entry:
%c1 = call i1 @foo()
br i1 %c1, label %cont1, label %a1
a1:
call void @assert_fail_1(i32 0)
unreachable
cont1:
%c2 = call i1 @foo()
br i1 %c2, label %cont2, label %a2
a2:
%p2 = phi i32 [ 1, %cont1 ], [ 2, %cont2 ]
call void @assert_fail_1(i32 %p2)
unreachable
cont2:
%c3 = call i1 @foo()
br i1 %c3, label %cont3, label %a2
cont3:
ret void
}
; Don't tail merge these noreturn blocks using lifetime end. It prevents us
; from sharing stack slots for x and y.
declare void @escape_i32_ptr(i32*)
declare void @llvm.lifetime.start(i64, i8* nocapture)
declare void @llvm.lifetime.end(i64, i8* nocapture)
define void @dont_merge_lifetimes(i32 %c1, i32 %c2) {
; CHECK-LABEL: @dont_merge_lifetimes(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[X:%.*]] = alloca i32, align 4
; CHECK-NEXT: [[Y:%.*]] = alloca i32, align 4
; CHECK-NEXT: switch i32 [[C1:%.*]], label [[IF_END9:%.*]] [
; CHECK-NEXT: i32 13, label [[IF_THEN:%.*]]
; CHECK-NEXT: i32 42, label [[IF_THEN3:%.*]]
; CHECK-NEXT: ]
; CHECK: if.then:
; CHECK-NEXT: [[TMP0:%.*]] = bitcast i32* [[X]] to i8*
; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull [[TMP0]])
; CHECK-NEXT: store i32 0, i32* [[X]], align 4
; CHECK-NEXT: [[TOBOOL:%.*]] = icmp eq i32 [[C2:%.*]], 0
; CHECK-NEXT: br i1 [[TOBOOL]], label [[IF_END:%.*]], label [[IF_THEN1:%.*]]
; CHECK: if.then1:
; CHECK-NEXT: call void @escape_i32_ptr(i32* nonnull [[X]])
; CHECK-NEXT: br label [[IF_END]]
; CHECK: if.end:
; CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull [[TMP0]])
; CHECK-NEXT: call void @abort()
; CHECK-NEXT: unreachable
; CHECK: if.then3:
; CHECK-NEXT: [[TMP1:%.*]] = bitcast i32* [[Y]] to i8*
; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull [[TMP1]])
; CHECK-NEXT: store i32 0, i32* [[Y]], align 4
; CHECK-NEXT: [[TOBOOL5:%.*]] = icmp eq i32 [[C2]], 0
; CHECK-NEXT: br i1 [[TOBOOL5]], label [[IF_END7:%.*]], label [[IF_THEN6:%.*]]
; CHECK: if.then6:
; CHECK-NEXT: call void @escape_i32_ptr(i32* nonnull [[Y]])
; CHECK-NEXT: br label [[IF_END7]]
; CHECK: if.end7:
; CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull [[TMP1]])
; CHECK-NEXT: call void @abort()
; CHECK-NEXT: unreachable
; CHECK: if.end9:
; CHECK-NEXT: ret void
;
entry:
%x = alloca i32, align 4
%y = alloca i32, align 4
switch i32 %c1, label %if.end9 [
i32 13, label %if.then
i32 42, label %if.then3
]
if.then: ; preds = %entry
%0 = bitcast i32* %x to i8*
call void @llvm.lifetime.start(i64 4, i8* nonnull %0)
store i32 0, i32* %x, align 4
%tobool = icmp eq i32 %c2, 0
br i1 %tobool, label %if.end, label %if.then1
if.then1: ; preds = %if.then
call void @escape_i32_ptr(i32* nonnull %x)
br label %if.end
if.end: ; preds = %if.then1, %if.then
call void @llvm.lifetime.end(i64 4, i8* nonnull %0)
call void @abort()
unreachable
if.then3: ; preds = %entry
%1 = bitcast i32* %y to i8*
call void @llvm.lifetime.start(i64 4, i8* nonnull %1)
store i32 0, i32* %y, align 4
%tobool5 = icmp eq i32 %c2, 0
br i1 %tobool5, label %if.end7, label %if.then6
if.then6: ; preds = %if.then3
call void @escape_i32_ptr(i32* nonnull %y)
br label %if.end7
if.end7: ; preds = %if.then6, %if.then3
call void @llvm.lifetime.end(i64 4, i8* nonnull %1)
call void @abort()
unreachable
if.end9: ; preds = %entry
ret void
}
; Dead phis in the block need to be handled.
declare void @llvm.dbg.value(metadata, i64, metadata, metadata)
define void @dead_phi() {
; CHECK-LABEL: @dead_phi(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[C1:%.*]] = call i1 @foo()
; CHECK-NEXT: br i1 [[C1]], label [[CONT1:%.*]], label [[A1:%.*]]
; CHECK: a1:
; CHECK-NEXT: [[DEAD:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ 1, [[CONT1]] ]
; CHECK-NEXT: call void @assert_fail_1(i32 0)
; CHECK-NEXT: unreachable
; CHECK: cont1:
; CHECK-NEXT: [[C2:%.*]] = call i1 @foo()
; CHECK-NEXT: br i1 [[C2]], label [[CONT2:%.*]], label [[A1]]
; CHECK: cont2:
; CHECK-NEXT: [[C3:%.*]] = call i1 @foo()
; CHECK-NEXT: br i1 [[C3]], label [[CONT3:%.*]], label [[A3:%.*]]
; CHECK: a3:
; CHECK-NEXT: call void @assert_fail_1(i32 0)
; CHECK-NEXT: unreachable
; CHECK: cont3:
; CHECK-NEXT: ret void
;
entry:
%c1 = call i1 @foo()
br i1 %c1, label %cont1, label %a1
a1:
%dead = phi i32 [ 0, %entry ], [ 1, %cont1 ]
call void @assert_fail_1(i32 0)
unreachable
cont1:
%c2 = call i1 @foo()
br i1 %c2, label %cont2, label %a1
cont2:
%c3 = call i1 @foo()
br i1 %c3, label %cont3, label %a3
a3:
call void @assert_fail_1(i32 0)
unreachable
cont3:
ret void
}
define void @strip_dbg_value(i32 %c) {
; CHECK-LABEL: @strip_dbg_value(
; CHECK-NEXT: entry:
; CHECK-NEXT: call void @llvm.dbg.value(metadata i32 [[C:%.*]], metadata [[META5:![0-9]+]], metadata !DIExpression()), !dbg [[DBG7:![0-9]+]]
; CHECK-NEXT: switch i32 [[C]], label [[SW_EPILOG:%.*]] [
; CHECK-NEXT: i32 13, label [[SW_BB:%.*]]
; CHECK-NEXT: i32 42, label [[SW_BB1:%.*]]
; CHECK-NEXT: ]
; CHECK: sw.bb:
; CHECK-NEXT: call void @llvm.dbg.value(metadata i32 55, metadata [[META5]], metadata !DIExpression()), !dbg [[DBG7]]
; CHECK-NEXT: tail call void @abort()
; CHECK-NEXT: unreachable
; CHECK: sw.bb1:
; CHECK-NEXT: call void @llvm.dbg.value(metadata i32 67, metadata [[META5]], metadata !DIExpression()), !dbg [[DBG7]]
; CHECK-NEXT: tail call void @abort()
; CHECK-NEXT: unreachable
; CHECK: sw.epilog:
; CHECK-NEXT: ret void
;
entry:
call void @llvm.dbg.value(metadata i32 %c, i64 0, metadata !12, metadata !13), !dbg !14
switch i32 %c, label %sw.epilog [
i32 13, label %sw.bb
i32 42, label %sw.bb1
]
sw.bb: ; preds = %entry
call void @llvm.dbg.value(metadata i32 55, i64 0, metadata !12, metadata !13), !dbg !14
tail call void @abort()
unreachable
sw.bb1: ; preds = %entry
call void @llvm.dbg.value(metadata i32 67, i64 0, metadata !12, metadata !13), !dbg !14
tail call void @abort()
unreachable
sw.epilog: ; preds = %entry
ret void
}
define void @dead_phi_and_dbg(i32 %c) {
; CHECK-LABEL: @dead_phi_and_dbg(
; CHECK-NEXT: entry:
; CHECK-NEXT: call void @llvm.dbg.value(metadata i32 [[C:%.*]], metadata [[META5]], metadata !DIExpression()), !dbg [[DBG7]]
; CHECK-NEXT: switch i32 [[C]], label [[SW_EPILOG:%.*]] [
; CHECK-NEXT: i32 13, label [[SW_BB:%.*]]
; CHECK-NEXT: i32 42, label [[SW_BB1:%.*]]
; CHECK-NEXT: i32 53, label [[SW_BB2:%.*]]
; CHECK-NEXT: ]
; CHECK: sw.bb:
; CHECK-NEXT: [[C_1:%.*]] = phi i32 [ 55, [[ENTRY:%.*]] ], [ 67, [[SW_BB1]] ]
; CHECK-NEXT: call void @llvm.dbg.value(metadata i32 [[C_1]], metadata [[META5]], metadata !DIExpression()), !dbg [[DBG7]]
; CHECK-NEXT: tail call void @abort()
; CHECK-NEXT: unreachable
; CHECK: sw.bb1:
; CHECK-NEXT: br label [[SW_BB]]
; CHECK: sw.bb2:
; CHECK-NEXT: call void @llvm.dbg.value(metadata i32 84, metadata [[META5]], metadata !DIExpression()), !dbg [[DBG7]]
; CHECK-NEXT: tail call void @abort()
; CHECK-NEXT: unreachable
; CHECK: sw.epilog:
; CHECK-NEXT: ret void
;
entry:
call void @llvm.dbg.value(metadata i32 %c, i64 0, metadata !12, metadata !13), !dbg !14
switch i32 %c, label %sw.epilog [
i32 13, label %sw.bb
i32 42, label %sw.bb1
i32 53, label %sw.bb2
]
sw.bb: ; preds = %entry
%c.1 = phi i32 [ 55, %entry], [ 67, %sw.bb1 ]
call void @llvm.dbg.value(metadata i32 %c.1, i64 0, metadata !12, metadata !13), !dbg !14
tail call void @abort()
unreachable
sw.bb1:
br label %sw.bb
sw.bb2: ; preds = %entry
call void @llvm.dbg.value(metadata i32 84, i64 0, metadata !12, metadata !13), !dbg !14
tail call void @abort()
unreachable
sw.epilog: ; preds = %entry
ret void
}
!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!3, !4, !5}
!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, runtimeVersion: 0, emissionKind: FullDebug)
!1 = !DIFile(filename: "t.c", directory: "asdf")
!3 = !{i32 2, !"Dwarf Version", i32 4}
!4 = !{i32 2, !"Debug Info Version", i32 3}
!5 = !{i32 1, !"PIC Level", i32 2}
!7 = distinct !DISubprogram(name: "f", scope: !1, file: !1, line: 2, isLocal: false, isDefinition: true, scopeLine: 2, flags: DIFlagPrototyped, isOptimized: true, unit: !0)
!12 = !DILocalVariable(name: "c", scope: !7)
!13 = !DIExpression()
!14 = !DILocation(line: 2, column: 12, scope: !7)