
Now that #149310 has restricted lifetime intrinsics to only work on allocas, we can also drop the explicit size argument. Instead, the size is implied by the alloca. This removes the ability to only mark a prefix of an alloca alive/dead. We never used that capability, so we should remove the need to handle that possibility everywhere (though many key places, including stack coloring, did not actually respect this).
141 lines
6.1 KiB
LLVM
141 lines
6.1 KiB
LLVM
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
|
|
; RUN: opt -S -passes='cgscc(inline)' -pass-remarks=inline -pass-remarks-missed=inline < %s 2> %t.err | FileCheck %s
|
|
; RUN: FileCheck -implicit-check-not=remark -check-prefix=REMARK %s < %t.err
|
|
|
|
; REMARK: remark: <unknown>:0:0: 'callee_with_gc' inlined into 'caller_no_gc'
|
|
; REMARK-NEXT: remark: <unknown>:0:0: 'callee_with_gc' inlined into 'caller_same_gc'
|
|
; REMARK-NEXT: remark: <unknown>:0:0: 'callee_with_gc' is not inlined into 'caller_incompatible_gc': incompatible GC
|
|
; REMARK-NEXT: remark: <unknown>:0:0: 'callee_with_gc' inlined into 'caller_inline_first_caller'
|
|
; REMARK-NEXT: remark: <unknown>:0:0: 'callee_with_other_gc' is not inlined into 'caller_inline_first_caller': incompatible GC
|
|
; REMARK-NEXT: remark: <unknown>:0:0: 'callee_with_gc' inlined into 'caller_inline_second_caller'
|
|
; REMARK-NEXT: remark: <unknown>:0:0: 'callee_with_other_gc' is not inlined into 'caller_inline_second_caller': incompatible GC
|
|
|
|
%IntArray = type { i32, [0 x ptr] }
|
|
|
|
; Callee gc propagates to the caller
|
|
define i32 @caller_no_gc() {
|
|
; CHECK-LABEL: define i32 @caller_no_gc() gc "example" {
|
|
; CHECK-NEXT: [[ROOT_I:%.*]] = alloca ptr, align 8
|
|
; CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr [[ROOT_I]])
|
|
; CHECK-NEXT: call void @llvm.gcroot(ptr [[ROOT_I]], ptr null)
|
|
; CHECK-NEXT: [[OBJ_I:%.*]] = call ptr @h()
|
|
; CHECK-NEXT: store ptr [[OBJ_I]], ptr [[ROOT_I]], align 8
|
|
; CHECK-NEXT: [[LENGTH_I:%.*]] = load i32, ptr [[OBJ_I]], align 4
|
|
; CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr [[ROOT_I]])
|
|
; CHECK-NEXT: ret i32 [[LENGTH_I]]
|
|
;
|
|
%x = call i32 @callee_with_gc()
|
|
ret i32 %x
|
|
}
|
|
|
|
; Inline of matching gc allowed.
|
|
define i32 @caller_same_gc() gc "example" {
|
|
; CHECK-LABEL: define i32 @caller_same_gc() gc "example" {
|
|
; CHECK-NEXT: [[ROOT_I:%.*]] = alloca ptr, align 8
|
|
; CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr [[ROOT_I]])
|
|
; CHECK-NEXT: call void @llvm.gcroot(ptr [[ROOT_I]], ptr null)
|
|
; CHECK-NEXT: [[OBJ_I:%.*]] = call ptr @h()
|
|
; CHECK-NEXT: store ptr [[OBJ_I]], ptr [[ROOT_I]], align 8
|
|
; CHECK-NEXT: [[LENGTH_I:%.*]] = load i32, ptr [[OBJ_I]], align 4
|
|
; CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr [[ROOT_I]])
|
|
; CHECK-NEXT: ret i32 [[LENGTH_I]]
|
|
;
|
|
%x = call i32 @callee_with_gc()
|
|
ret i32 %x
|
|
}
|
|
|
|
; Reject inline with mismatched gc
|
|
define i32 @caller_incompatible_gc() gc "incompatible" {
|
|
; CHECK-LABEL: define i32 @caller_incompatible_gc() gc "incompatible" {
|
|
; CHECK-NEXT: [[X:%.*]] = call i32 @callee_with_gc()
|
|
; CHECK-NEXT: ret i32 [[X]]
|
|
;
|
|
%x = call i32 @callee_with_gc()
|
|
ret i32 %x
|
|
}
|
|
|
|
define i32 @callee_with_gc() gc "example" {
|
|
; CHECK-LABEL: define i32 @callee_with_gc() gc "example" {
|
|
; CHECK-NEXT: [[ROOT:%.*]] = alloca ptr, align 8
|
|
; CHECK-NEXT: call void @llvm.gcroot(ptr [[ROOT]], ptr null)
|
|
; CHECK-NEXT: [[OBJ:%.*]] = call ptr @h()
|
|
; CHECK-NEXT: store ptr [[OBJ]], ptr [[ROOT]], align 8
|
|
; CHECK-NEXT: [[LENGTH_PTR:%.*]] = getelementptr [[INTARRAY:%.*]], ptr [[OBJ]], i32 0, i32 0
|
|
; CHECK-NEXT: [[LENGTH:%.*]] = load i32, ptr [[LENGTH_PTR]], align 4
|
|
; CHECK-NEXT: ret i32 [[LENGTH]]
|
|
;
|
|
%root = alloca ptr, align 8
|
|
call void @llvm.gcroot(ptr %root, ptr null)
|
|
%obj = call ptr @h()
|
|
store ptr %obj, ptr %root, align 8
|
|
%Length.ptr = getelementptr %IntArray, ptr %obj, i32 0, i32 0
|
|
%Length = load i32, ptr %Length.ptr, align 4
|
|
ret i32 %Length
|
|
}
|
|
|
|
define i32 @callee_with_other_gc() gc "other-example" {
|
|
; CHECK-LABEL: define i32 @callee_with_other_gc() gc "other-example" {
|
|
; CHECK-NEXT: [[ROOT:%.*]] = alloca ptr, align 8
|
|
; CHECK-NEXT: call void @llvm.gcroot(ptr [[ROOT]], ptr null)
|
|
; CHECK-NEXT: [[OBJ:%.*]] = call ptr @h()
|
|
; CHECK-NEXT: store ptr [[OBJ]], ptr [[ROOT]], align 8
|
|
; CHECK-NEXT: [[LENGTH_PTR:%.*]] = getelementptr [[INTARRAY:%.*]], ptr [[OBJ]], i32 0, i32 0
|
|
; CHECK-NEXT: [[LENGTH:%.*]] = load i32, ptr [[LENGTH_PTR]], align 4
|
|
; CHECK-NEXT: ret i32 [[LENGTH]]
|
|
;
|
|
%root = alloca ptr, align 8
|
|
call void @llvm.gcroot(ptr %root, ptr null)
|
|
%obj = call ptr @h()
|
|
store ptr %obj, ptr %root, align 8
|
|
%Length.ptr = getelementptr %IntArray, ptr %obj, i32 0, i32 0
|
|
%Length = load i32, ptr %Length.ptr, align 4
|
|
ret i32 %Length
|
|
}
|
|
|
|
; After inlining the first call, inline is blocked of the second call
|
|
; since the gc type propagates to the caller.
|
|
define i32 @caller_inline_first_caller() {
|
|
; CHECK-LABEL: define i32 @caller_inline_first_caller() gc "example" {
|
|
; CHECK-NEXT: [[ROOT_I:%.*]] = alloca ptr, align 8
|
|
; CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr [[ROOT_I]])
|
|
; CHECK-NEXT: call void @llvm.gcroot(ptr [[ROOT_I]], ptr null)
|
|
; CHECK-NEXT: [[OBJ_I:%.*]] = call ptr @h()
|
|
; CHECK-NEXT: store ptr [[OBJ_I]], ptr [[ROOT_I]], align 8
|
|
; CHECK-NEXT: [[LENGTH_I:%.*]] = load i32, ptr [[OBJ_I]], align 4
|
|
; CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr [[ROOT_I]])
|
|
; CHECK-NEXT: [[Y:%.*]] = call i32 @callee_with_other_gc()
|
|
; CHECK-NEXT: [[ADD:%.*]] = add i32 [[LENGTH_I]], [[Y]]
|
|
; CHECK-NEXT: ret i32 [[ADD]]
|
|
;
|
|
%x = call i32 @callee_with_gc()
|
|
%y = call i32 @callee_with_other_gc()
|
|
%add = add i32 %x, %y
|
|
ret i32 %add
|
|
}
|
|
|
|
; We can't inline the first call due to the incompatible gc, but can
|
|
; inline the second
|
|
define i32 @caller_inline_second_caller() gc "example" {
|
|
; CHECK-LABEL: define i32 @caller_inline_second_caller() gc "example" {
|
|
; CHECK-NEXT: [[ROOT_I:%.*]] = alloca ptr, align 8
|
|
; CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr [[ROOT_I]])
|
|
; CHECK-NEXT: call void @llvm.gcroot(ptr [[ROOT_I]], ptr null)
|
|
; CHECK-NEXT: [[OBJ_I:%.*]] = call ptr @h()
|
|
; CHECK-NEXT: store ptr [[OBJ_I]], ptr [[ROOT_I]], align 8
|
|
; CHECK-NEXT: [[LENGTH_I:%.*]] = load i32, ptr [[OBJ_I]], align 4
|
|
; CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr [[ROOT_I]])
|
|
; CHECK-NEXT: [[Y:%.*]] = call i32 @callee_with_other_gc()
|
|
; CHECK-NEXT: [[ADD:%.*]] = add i32 [[LENGTH_I]], [[Y]]
|
|
; CHECK-NEXT: ret i32 [[ADD]]
|
|
;
|
|
%x = call i32 @callee_with_gc()
|
|
%y = call i32 @callee_with_other_gc()
|
|
%add = add i32 %x, %y
|
|
ret i32 %add
|
|
}
|
|
|
|
declare ptr @h()
|
|
|
|
declare void @llvm.gcroot(ptr, ptr) #0
|
|
attributes #0 = { nounwind }
|