+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 2
; RUN: opt -opaque-pointers=0 < %s -passes=inline -S | FileCheck %s
-; RUN: opt -opaque-pointers=0 < %s -passes='cgscc(inline)' -S | FileCheck %s
; We have to apply the less restrictive TailCallKind of the call site being
; inlined and any call sites cloned into the caller.
; No tail marker after inlining, since test_capture_c captures an alloca.
-; CHECK: define void @test_capture_a(
-; CHECK-NOT: tail
-; CHECK: call void @test_capture_c(
-
declare void @test_capture_c(i32*)
define internal void @test_capture_b(i32* %P) {
tail call void @test_capture_c(i32* %P)
ret void
}
define void @test_capture_a() {
+; CHECK-LABEL: define void @test_capture_a() {
+; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4
+; CHECK-NEXT: call void @test_capture_c(i32* [[A]])
+; CHECK-NEXT: ret void
+;
%A = alloca i32 ; captured by test_capture_b
call void @test_capture_b(i32* %A)
ret void
}
; No musttail marker after inlining, since the prototypes don't match.
-; CHECK: define void @test_proto_mismatch_a(
-; CHECK-NOT: musttail
-; CHECK: call void @test_proto_mismatch_c(
-
declare void @test_proto_mismatch_c(i32*)
define internal void @test_proto_mismatch_b(i32* %p) {
musttail call void @test_proto_mismatch_c(i32* %p)
ret void
}
define void @test_proto_mismatch_a() {
+; CHECK-LABEL: define void @test_proto_mismatch_a() {
+; CHECK-NEXT: call void @test_proto_mismatch_c(i32* null)
+; CHECK-NEXT: ret void
+;
call void @test_proto_mismatch_b(i32* null)
ret void
}
; After inlining through a musttail call site, we need to keep musttail markers
; to prevent unbounded stack growth.
-; CHECK: define void @test_musttail_basic_a(
-; CHECK: musttail call void @test_musttail_basic_c(
-
declare void @test_musttail_basic_c(i32* %p)
define internal void @test_musttail_basic_b(i32* %p) {
musttail call void @test_musttail_basic_c(i32* %p)
ret void
}
define void @test_musttail_basic_a(i32* %p) {
+; CHECK-LABEL: define void @test_musttail_basic_a
+; CHECK-SAME: (i32* [[P:%.*]]) {
+; CHECK-NEXT: musttail call void @test_musttail_basic_c(i32* [[P]])
+; CHECK-NEXT: ret void
+;
musttail call void @test_musttail_basic_b(i32* %p)
ret void
}
; Don't insert lifetime end markers here, the lifetime is trivially over due
; the return.
-; CHECK: define void @test_byval_a(
-; CHECK: musttail call void @test_byval_c(
-; CHECK-NEXT: ret void
-
declare void @test_byval_c(i32* byval(i32) %p)
define internal void @test_byval_b(i32* byval(i32) %p) {
musttail call void @test_byval_c(i32* byval(i32) %p)
ret void
}
define void @test_byval_a(i32* byval(i32) %p) {
+; CHECK-LABEL: define void @test_byval_a
+; CHECK-SAME: (i32* byval(i32) [[P:%.*]]) {
+; CHECK-NEXT: [[P1:%.*]] = alloca i32, align 4
+; CHECK-NEXT: [[TMP1:%.*]] = bitcast i32* [[P1]] to i8*
+; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 4, i8* [[TMP1]])
+; CHECK-NEXT: [[TMP2:%.*]] = bitcast i32* [[P1]] to i8*
+; CHECK-NEXT: [[TMP3:%.*]] = bitcast i32* [[P]] to i8*
+; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 [[TMP2]], i8* align 1 [[TMP3]], i64 4, i1 false)
+; CHECK-NEXT: musttail call void @test_byval_c(i32* byval(i32) [[P1]])
+; CHECK-NEXT: ret void
+;
musttail call void @test_byval_b(i32* byval(i32) %p)
ret void
}
; Don't insert a stack restore, we're about to return.
-; CHECK: define void @test_dynalloca_a(
-; CHECK: call i8* @llvm.stacksave(
-; CHECK: alloca i8, i32 %n
-; CHECK: musttail call void @test_dynalloca_c(
-; CHECK-NEXT: ret void
-
declare void @escape(i8* %buf)
declare void @test_dynalloca_c(i32* byval(i32) %p, i32 %n)
define internal void @test_dynalloca_b(i32* byval(i32) %p, i32 %n) alwaysinline {
ret void
}
define void @test_dynalloca_a(i32* byval(i32) %p, i32 %n) {
+; CHECK-LABEL: define void @test_dynalloca_a
+; CHECK-SAME: (i32* byval(i32) [[P:%.*]], i32 [[N:%.*]]) {
+; CHECK-NEXT: [[P1:%.*]] = alloca i32, align 4
+; CHECK-NEXT: [[SAVEDSTACK:%.*]] = call i8* @llvm.stacksave()
+; CHECK-NEXT: [[TMP1:%.*]] = bitcast i32* [[P1]] to i8*
+; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 4, i8* [[TMP1]])
+; CHECK-NEXT: [[TMP2:%.*]] = bitcast i32* [[P1]] to i8*
+; CHECK-NEXT: [[TMP3:%.*]] = bitcast i32* [[P]] to i8*
+; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 [[TMP2]], i8* align 1 [[TMP3]], i64 4, i1 false)
+; CHECK-NEXT: [[BUF_I:%.*]] = alloca i8, i32 [[N]], align 1
+; CHECK-NEXT: call void @escape(i8* [[BUF_I]])
+; CHECK-NEXT: musttail call void @test_dynalloca_c(i32* byval(i32) [[P1]], i32 [[N]])
+; CHECK-NEXT: ret void
+;
musttail call void @test_dynalloca_b(i32* byval(i32) %p, i32 %n)
ret void
}
; We can't merge the returns.
-; CHECK: define void @test_multiret_a(
-; CHECK: musttail call void @test_multiret_c(
-; CHECK-NEXT: ret void
-; CHECK: musttail call void @test_multiret_d(
-; CHECK-NEXT: ret void
-
declare void @test_multiret_c(i1 zeroext %b)
declare void @test_multiret_d(i1 zeroext %b)
define internal void @test_multiret_b(i1 zeroext %b) {
ret void
}
define void @test_multiret_a(i1 zeroext %b) {
+; CHECK-LABEL: define void @test_multiret_a
+; CHECK-SAME: (i1 zeroext [[B:%.*]]) {
+; CHECK-NEXT: br i1 [[B]], label [[C_I:%.*]], label [[D_I:%.*]]
+; CHECK: c.i:
+; CHECK-NEXT: musttail call void @test_multiret_c(i1 zeroext [[B]])
+; CHECK-NEXT: ret void
+; CHECK: d.i:
+; CHECK-NEXT: musttail call void @test_multiret_d(i1 zeroext [[B]])
+; CHECK-NEXT: ret void
+;
musttail call void @test_multiret_b(i1 zeroext %b)
ret void
}
; We have to avoid bitcast chains.
-; CHECK: define i32* @test_retptr_a(
-; CHECK: musttail call i8* @test_retptr_c(
-; CHECK-NEXT: bitcast i8* {{.*}} to i32*
-; CHECK-NEXT: ret i32*
-
declare i8* @test_retptr_c()
define internal i16* @test_retptr_b() {
%rv = musttail call i8* @test_retptr_c()
ret i16* %v
}
define i32* @test_retptr_a() {
+; CHECK-LABEL: define i32* @test_retptr_a() {
+; CHECK-NEXT: [[RV_I:%.*]] = musttail call i8* @test_retptr_c()
+; CHECK-NEXT: [[TMP1:%.*]] = bitcast i8* [[RV_I]] to i32*
+; CHECK-NEXT: ret i32* [[TMP1]]
+;
%rv = musttail call i16* @test_retptr_b()
%v = bitcast i16* %rv to i32*
ret i32* %v
}
; Combine the last two cases: multiple returns with pointer bitcasts.
-; CHECK: define i32* @test_multiptrret_a(
-; CHECK: musttail call i8* @test_multiptrret_c(
-; CHECK-NEXT: bitcast i8* {{.*}} to i32*
-; CHECK-NEXT: ret i32*
-; CHECK: musttail call i8* @test_multiptrret_d(
-; CHECK-NEXT: bitcast i8* {{.*}} to i32*
-; CHECK-NEXT: ret i32*
-
declare i8* @test_multiptrret_c(i1 zeroext %b)
declare i8* @test_multiptrret_d(i1 zeroext %b)
define internal i16* @test_multiptrret_b(i1 zeroext %b) {
ret i16* %d_v
}
define i32* @test_multiptrret_a(i1 zeroext %b) {
+; CHECK-LABEL: define i32* @test_multiptrret_a
+; CHECK-SAME: (i1 zeroext [[B:%.*]]) {
+; CHECK-NEXT: br i1 [[B]], label [[C_I:%.*]], label [[D_I:%.*]]
+; CHECK: c.i:
+; CHECK-NEXT: [[C_RV_I:%.*]] = musttail call i8* @test_multiptrret_c(i1 zeroext [[B]])
+; CHECK-NEXT: [[TMP1:%.*]] = bitcast i8* [[C_RV_I]] to i32*
+; CHECK-NEXT: ret i32* [[TMP1]]
+; CHECK: d.i:
+; CHECK-NEXT: [[D_RV_I:%.*]] = musttail call i8* @test_multiptrret_d(i1 zeroext [[B]])
+; CHECK-NEXT: [[TMP2:%.*]] = bitcast i8* [[D_RV_I]] to i32*
+; CHECK-NEXT: ret i32* [[TMP2]]
+;
%rv = musttail call i16* @test_multiptrret_b(i1 zeroext %b)
%v = bitcast i16* %rv to i32*
ret i32* %v
}
; Inline a musttail call site which contains a normal return and a musttail call.
-; CHECK: define i32 @test_mixedret_a(
-; CHECK: br i1 %b
-; CHECK: musttail call i32 @test_mixedret_c(
-; CHECK-NEXT: ret i32
-; CHECK: call i32 @test_mixedret_d(i1 zeroext %b)
-; CHECK: add i32 1,
-; CHECK-NOT: br
-; CHECK: ret i32
-
declare i32 @test_mixedret_c(i1 zeroext %b)
declare i32 @test_mixedret_d(i1 zeroext %b)
define internal i32 @test_mixedret_b(i1 zeroext %b) {
ret i32 %d_rv1
}
define i32 @test_mixedret_a(i1 zeroext %b) {
+; CHECK-LABEL: define i32 @test_mixedret_a
+; CHECK-SAME: (i1 zeroext [[B:%.*]]) {
+; CHECK-NEXT: br i1 [[B]], label [[C_I:%.*]], label [[TEST_MIXEDRET_B_EXIT:%.*]]
+; CHECK: c.i:
+; CHECK-NEXT: [[C_RV_I:%.*]] = musttail call i32 @test_mixedret_c(i1 zeroext [[B]])
+; CHECK-NEXT: ret i32 [[C_RV_I]]
+; CHECK: test_mixedret_b.exit:
+; CHECK-NEXT: [[D_RV_I:%.*]] = call i32 @test_mixedret_d(i1 zeroext [[B]])
+; CHECK-NEXT: [[D_RV1_I:%.*]] = add i32 1, [[D_RV_I]]
+; CHECK-NEXT: ret i32 [[D_RV1_I]]
+;
%rv = musttail call i32 @test_mixedret_b(i1 zeroext %b)
ret i32 %rv
}
declare i32 @donttailcall()
define i32 @notail() {
+; CHECK-LABEL: define i32 @notail() {
+; CHECK-NEXT: [[RV:%.*]] = notail call i32 @donttailcall()
+; CHECK-NEXT: ret i32 [[RV]]
+;
%rv = notail call i32 @donttailcall()
ret i32 %rv
}
-; CHECK: @test_notail
-; CHECK: notail call i32 @donttailcall
-; CHECK: ret
define i32 @test_notail() {
+; CHECK-LABEL: define i32 @test_notail() {
+; CHECK-NEXT: [[RV_I:%.*]] = notail call i32 @donttailcall()
+; CHECK-NEXT: ret i32 [[RV_I]]
+;
%rv = tail call i32 @notail()
ret i32 %rv
}
declare void @do_ret(i32)
define void @test_notail_inline_musttail(i32 %a) {
+; CHECK-LABEL: define void @test_notail_inline_musttail
+; CHECK-SAME: (i32 [[A:%.*]]) {
+; CHECK-NEXT: call void @do_ret(i32 [[A]])
+; CHECK-NEXT: musttail call void @do_ret(i32 [[A]])
+; CHECK-NEXT: ret void
+;
notail call void @inline_musttail(i32 %a)
musttail call void @do_ret(i32 %a)
ret void
musttail call void @do_ret(i32 %a)
ret void
}
-
-; CHECK-LABEL: define void @test_notail_inline_musttail(i32 %a)
-; CHECK: {{^ *}}call void @do_ret(i32 %a)
-; CHECK: musttail call void @do_ret(i32 %a)
-; CHECK: ret void
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 2
; RUN: opt -opaque-pointers=0 -passes=inline -S < %s | FileCheck %s
-; RUN: opt -opaque-pointers=0 -passes='cgscc(inline)' -S < %s | FileCheck %s
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128"
declare void @llvm.lifetime.start.p0i8(i64, i8*)
declare void @llvm.lifetime.end.p0i8(i64, i8*)
define void @helper_both_markers() {
+; CHECK-LABEL: define void @helper_both_markers() {
+; CHECK-NEXT: [[A:%.*]] = alloca i8, align 1
+; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 2, i8* [[A]])
+; CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 2, i8* [[A]])
+; CHECK-NEXT: ret void
+;
%a = alloca i8
; Size in llvm.lifetime.start / llvm.lifetime.end differs from
; allocation size. We should use the former.
}
define void @test_both_markers() {
-; CHECK-LABEL: @test_both_markers(
-; CHECK: llvm.lifetime.start.p0i8(i64 2
-; CHECK-NEXT: llvm.lifetime.end.p0i8(i64 2
+; CHECK-LABEL: define void @test_both_markers() {
+; CHECK-NEXT: [[A_I1:%.*]] = alloca i8, align 1
+; CHECK-NEXT: [[A_I:%.*]] = alloca i8, align 1
+; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 2, i8* [[A_I]])
+; CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 2, i8* [[A_I]])
+; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 2, i8* [[A_I1]])
+; CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 2, i8* [[A_I1]])
+; CHECK-NEXT: ret void
+;
call void @helper_both_markers()
-; CHECK-NEXT: llvm.lifetime.start.p0i8(i64 2
-; CHECK-NEXT: llvm.lifetime.end.p0i8(i64 2
call void @helper_both_markers()
-; CHECK-NEXT: ret void
ret void
}
declare void @use(i8* %a)
define void @helper_no_markers() {
+; CHECK-LABEL: define void @helper_no_markers() {
+; CHECK-NEXT: [[A:%.*]] = alloca i8, align 1
+; CHECK-NEXT: call void @use(i8* [[A]])
+; CHECK-NEXT: ret void
+;
%a = alloca i8 ; Allocation size is 1 byte.
call void @use(i8* %a)
ret void
}
-;; We can't use CHECK-NEXT because there's an extra call void @use in between.
-;; Instead, we use CHECK-NOT to verify that there are no other lifetime calls.
define void @test_no_marker() {
-; CHECK-LABEL: @test_no_marker(
-; CHECK-NOT: lifetime
-; CHECK: llvm.lifetime.start.p0i8(i64 1
-; CHECK-NOT: lifetime
-; CHECK: llvm.lifetime.end.p0i8(i64 1
+; CHECK-LABEL: define void @test_no_marker() {
+; CHECK-NEXT: [[A_I1:%.*]] = alloca i8, align 1
+; CHECK-NEXT: [[A_I:%.*]] = alloca i8, align 1
+; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 1, i8* [[A_I]])
+; CHECK-NEXT: call void @use(i8* [[A_I]])
+; CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 1, i8* [[A_I]])
+; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 1, i8* [[A_I1]])
+; CHECK-NEXT: call void @use(i8* [[A_I1]])
+; CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 1, i8* [[A_I1]])
+; CHECK-NEXT: ret void
+;
call void @helper_no_markers()
-; CHECK-NOT: lifetime
-; CHECK: llvm.lifetime.start.p0i8(i64 1
-; CHECK-NOT: lifetime
-; CHECK: llvm.lifetime.end.p0i8(i64 1
call void @helper_no_markers()
-; CHECK-NOT: lifetime
-; CHECK: ret void
ret void
}
define void @helper_two_casts() {
+; CHECK-LABEL: define void @helper_two_casts() {
+; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4
+; CHECK-NEXT: [[B:%.*]] = bitcast i32* [[A]] to i8*
+; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 4, i8* [[B]])
+; CHECK-NEXT: [[C:%.*]] = bitcast i32* [[A]] to i8*
+; CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 4, i8* [[C]])
+; CHECK-NEXT: ret void
+;
%a = alloca i32
%b = bitcast i32* %a to i8*
call void @llvm.lifetime.start.p0i8(i64 4, i8* %b)
}
define void @test_two_casts() {
-; CHECK-LABEL: @test_two_casts(
-; CHECK-NOT: lifetime
-; CHECK: llvm.lifetime.start.p0i8(i64 4
-; CHECK-NOT: lifetime
-; CHECK: llvm.lifetime.end.p0i8(i64 4
+; CHECK-LABEL: define void @test_two_casts() {
+; CHECK-NEXT: [[A_I1:%.*]] = alloca i32, align 4
+; CHECK-NEXT: [[A_I:%.*]] = alloca i32, align 4
+; CHECK-NEXT: [[B_I:%.*]] = bitcast i32* [[A_I]] to i8*
+; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 4, i8* [[B_I]])
+; CHECK-NEXT: [[C_I:%.*]] = bitcast i32* [[A_I]] to i8*
+; CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 4, i8* [[C_I]])
+; CHECK-NEXT: [[B_I2:%.*]] = bitcast i32* [[A_I1]] to i8*
+; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 4, i8* [[B_I2]])
+; CHECK-NEXT: [[C_I3:%.*]] = bitcast i32* [[A_I1]] to i8*
+; CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 4, i8* [[C_I3]])
+; CHECK-NEXT: ret void
+;
call void @helper_two_casts()
-; CHECK-NOT: lifetime
-; CHECK: llvm.lifetime.start.p0i8(i64 4
-; CHECK-NOT: lifetime
-; CHECK: llvm.lifetime.end.p0i8(i64 4
call void @helper_two_casts()
-; CHECK-NOT: lifetime
-; CHECK: ret void
ret void
}
define void @helper_arrays_alloca() {
+; CHECK-LABEL: define void @helper_arrays_alloca() {
+; CHECK-NEXT: [[A:%.*]] = alloca [10 x i32], align 16
+; CHECK-NEXT: [[TMP1:%.*]] = bitcast [10 x i32]* [[A]] to i8*
+; CHECK-NEXT: call void @use(i8* [[TMP1]])
+; CHECK-NEXT: ret void
+;
%a = alloca [10 x i32], align 16
%1 = bitcast [10 x i32]* %a to i8*
call void @use(i8* %1)
}
define void @test_arrays_alloca() {
-; CHECK-LABEL: @test_arrays_alloca(
-; CHECK-NOT: lifetime
-; CHECK: llvm.lifetime.start.p0i8(i64 40,
-; CHECK-NOT: lifetime
-; CHECK: llvm.lifetime.end.p0i8(i64 40,
+; CHECK-LABEL: define void @test_arrays_alloca() {
+; CHECK-NEXT: [[A_I:%.*]] = alloca [10 x i32], align 16
+; CHECK-NEXT: [[TMP1:%.*]] = bitcast [10 x i32]* [[A_I]] to i8*
+; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 40, i8* [[TMP1]])
+; CHECK-NEXT: [[TMP2:%.*]] = bitcast [10 x i32]* [[A_I]] to i8*
+; CHECK-NEXT: call void @use(i8* [[TMP2]])
+; CHECK-NEXT: [[TMP3:%.*]] = bitcast [10 x i32]* [[A_I]] to i8*
+; CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 40, i8* [[TMP3]])
+; CHECK-NEXT: ret void
+;
call void @helper_arrays_alloca()
-; CHECK-NOT: lifetime
-; CHECK: ret void
ret void
}
%swift.error = type opaque
define void @helper_swifterror_alloca() {
+; CHECK-LABEL: define void @helper_swifterror_alloca() {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[SWIFTERROR:%.*]] = alloca swifterror %swift.error*, align 8
+; CHECK-NEXT: store %swift.error* null, %swift.error** [[SWIFTERROR]], align 8
+; CHECK-NEXT: ret void
+;
entry:
%swifterror = alloca swifterror %swift.error*, align 8
store %swift.error* null, %swift.error** %swifterror, align 8
}
define void @test_swifterror_alloca() {
-; CHECK-LABEL: @test_swifterror_alloca(
-; CHECK-NOT: lifetime
+; CHECK-LABEL: define void @test_swifterror_alloca() {
+; CHECK-NEXT: [[SWIFTERROR_I:%.*]] = alloca swifterror %swift.error*, align 8
+; CHECK-NEXT: store %swift.error* null, %swift.error** [[SWIFTERROR_I]], align 8
+; CHECK-NEXT: ret void
+;
call void @helper_swifterror_alloca()
-; CHECK: ret void
ret void
}