
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).
216 lines
7.0 KiB
LLVM
216 lines
7.0 KiB
LLVM
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature
|
|
; RUN: opt -passes=inline < %s -S -o - | FileCheck %s
|
|
target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
|
|
target triple = "x86_64-apple-macosx10.15.0"
|
|
|
|
define void @caller1(ptr %p1, i1 %b) {
|
|
; CHECK-LABEL: define {{[^@]+}}@caller1
|
|
; CHECK-SAME: (ptr [[P1:%.*]], i1 [[B:%.*]]) {
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[COND:%.*]] = icmp eq i1 [[B]], true
|
|
; CHECK-NEXT: br i1 [[COND]], label [[EXIT:%.*]], label [[SPLIT:%.*]]
|
|
; CHECK: split:
|
|
; CHECK-NEXT: call void @callee(ptr [[P1]], i32 0, i32 -1)
|
|
; CHECK-NEXT: br label [[EXIT]]
|
|
; CHECK: exit:
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
%cond = icmp eq i1 %b, true
|
|
br i1 %cond, label %exit, label %split
|
|
|
|
split:
|
|
; This path may be generated from CS splitting and never taken at runtime.
|
|
call void @callee(ptr %p1, i32 0, i32 -1)
|
|
br label %exit
|
|
|
|
exit:
|
|
ret void
|
|
}
|
|
|
|
define void @callee(ptr %p1, i32 %l1, i32 %l2) {
|
|
; CHECK-LABEL: define {{[^@]+}}@callee
|
|
; CHECK-SAME: (ptr [[P1:%.*]], i32 [[L1:%.*]], i32 [[L2:%.*]]) {
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[EXT:%.*]] = zext i32 [[L2]] to i64
|
|
; CHECK-NEXT: [[VLA:%.*]] = alloca float, i64 [[EXT]], align 16
|
|
; CHECK-NEXT: call void @extern_call(ptr nonnull [[VLA]]) #[[ATTR3:[0-9]+]]
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
%ext = zext i32 %l2 to i64
|
|
%vla = alloca float, i64 %ext, align 16
|
|
call void @extern_call(ptr nonnull %vla) #3
|
|
ret void
|
|
}
|
|
|
|
|
|
define void @caller2_below_threshold(ptr %p1, i1 %b) {
|
|
; CHECK-LABEL: define {{[^@]+}}@caller2_below_threshold
|
|
; CHECK-SAME: (ptr [[P1:%.*]], i1 [[B:%.*]]) {
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[VLA_I:%.*]] = alloca float, i64 15000, align 16
|
|
; CHECK-NEXT: [[COND:%.*]] = icmp eq i1 [[B]], true
|
|
; CHECK-NEXT: br i1 [[COND]], label [[EXIT:%.*]], label [[SPLIT:%.*]]
|
|
; CHECK: split:
|
|
; CHECK-NEXT: [[SAVEDSTACK:%.*]] = call ptr @llvm.stacksave.p0()
|
|
; CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr [[VLA_I]])
|
|
; CHECK-NEXT: call void @extern_call(ptr nonnull [[VLA_I]]) #[[ATTR3]]
|
|
; CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr [[VLA_I]])
|
|
; CHECK-NEXT: call void @llvm.stackrestore.p0(ptr [[SAVEDSTACK]])
|
|
; CHECK-NEXT: br label [[EXIT]]
|
|
; CHECK: exit:
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
%cond = icmp eq i1 %b, true
|
|
br i1 %cond, label %exit, label %split
|
|
|
|
split:
|
|
call void @callee(ptr %p1, i32 0, i32 15000)
|
|
br label %exit
|
|
|
|
exit:
|
|
ret void
|
|
}
|
|
|
|
define void @callee2_not_in_entry(ptr %p1, i32 %l1, i32 %l2) {
|
|
; CHECK-LABEL: define {{[^@]+}}@callee2_not_in_entry
|
|
; CHECK-SAME: (ptr [[P1:%.*]], i32 [[L1:%.*]], i32 [[L2:%.*]]) {
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[EXT:%.*]] = zext i32 [[L2]] to i64
|
|
; CHECK-NEXT: [[C:%.*]] = icmp eq i32 [[L1]], 42
|
|
; CHECK-NEXT: br i1 [[C]], label [[BB2:%.*]], label [[BB3:%.*]]
|
|
; CHECK: bb2:
|
|
; CHECK-NEXT: [[VLA:%.*]] = alloca float, i64 [[EXT]], align 16
|
|
; CHECK-NEXT: call void @extern_call(ptr nonnull [[VLA]]) #[[ATTR3]]
|
|
; CHECK-NEXT: ret void
|
|
; CHECK: bb3:
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
%ext = zext i32 %l2 to i64
|
|
%c = icmp eq i32 %l1, 42
|
|
br i1 %c, label %bb2, label %bb3
|
|
bb2:
|
|
%vla = alloca float, i64 %ext, align 16
|
|
call void @extern_call(ptr nonnull %vla) #3
|
|
ret void
|
|
bb3:
|
|
ret void
|
|
}
|
|
|
|
define void @caller3_alloca_not_in_entry(ptr %p1, i1 %b) {
|
|
; CHECK-LABEL: define {{[^@]+}}@caller3_alloca_not_in_entry
|
|
; CHECK-SAME: (ptr [[P1:%.*]], i1 [[B:%.*]]) {
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[COND:%.*]] = icmp eq i1 [[B]], true
|
|
; CHECK-NEXT: br i1 [[COND]], label [[EXIT:%.*]], label [[SPLIT:%.*]]
|
|
; CHECK: split:
|
|
; CHECK-NEXT: br label [[EXIT]]
|
|
; CHECK: exit:
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
%cond = icmp eq i1 %b, true
|
|
br i1 %cond, label %exit, label %split
|
|
|
|
split:
|
|
call void @callee2_not_in_entry(ptr %p1, i32 0, i32 -1)
|
|
br label %exit
|
|
|
|
exit:
|
|
ret void
|
|
}
|
|
|
|
define void @caller4_over_threshold(ptr %p1, i1 %b, i32 %len) {
|
|
; CHECK-LABEL: define {{[^@]+}}@caller4_over_threshold
|
|
; CHECK-SAME: (ptr [[P1:%.*]], i1 [[B:%.*]], i32 [[LEN:%.*]]) {
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[COND:%.*]] = icmp eq i1 [[B]], true
|
|
; CHECK-NEXT: br i1 [[COND]], label [[EXIT:%.*]], label [[SPLIT:%.*]]
|
|
; CHECK: split:
|
|
; CHECK-NEXT: call void @callee(ptr [[P1]], i32 0, i32 16500)
|
|
; CHECK-NEXT: br label [[EXIT]]
|
|
; CHECK: exit:
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
%cond = icmp eq i1 %b, true
|
|
br i1 %cond, label %exit, label %split
|
|
|
|
split:
|
|
call void @callee(ptr %p1, i32 0, i32 16500)
|
|
br label %exit
|
|
|
|
exit:
|
|
ret void
|
|
}
|
|
|
|
declare noalias ptr @malloc(i64)
|
|
define ptr @stack_allocate(i32 %size) #2 {
|
|
; CHECK-LABEL: define {{[^@]+}}@stack_allocate
|
|
; CHECK-SAME: (i32 [[SIZE:%.*]]) #[[ATTR0:[0-9]+]] {
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[SIZE]], 100
|
|
; CHECK-NEXT: [[CONV:%.*]] = zext i32 [[SIZE]] to i64
|
|
; CHECK-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_END:%.*]]
|
|
; CHECK: if.then:
|
|
; CHECK-NEXT: [[TMP0:%.*]] = alloca i8, i64 [[CONV]], align 8
|
|
; CHECK-NEXT: br label [[RETURN:%.*]]
|
|
; CHECK: if.end:
|
|
; CHECK-NEXT: [[CALL:%.*]] = tail call ptr @malloc(i64 [[CONV]]) #[[ATTR3]]
|
|
; CHECK-NEXT: br label [[RETURN]]
|
|
; CHECK: return:
|
|
; CHECK-NEXT: [[RETVAL_0:%.*]] = phi ptr [ [[TMP0]], [[IF_THEN]] ], [ [[CALL]], [[IF_END]] ]
|
|
; CHECK-NEXT: ret ptr [[RETVAL_0]]
|
|
;
|
|
entry:
|
|
%cmp = icmp ult i32 %size, 100
|
|
%conv = zext i32 %size to i64
|
|
br i1 %cmp, label %if.then, label %if.end
|
|
|
|
if.then: ; preds = %entry
|
|
%0 = alloca i8, i64 %conv, align 8
|
|
br label %return
|
|
|
|
if.end: ; preds = %entry
|
|
%call = tail call ptr @malloc(i64 %conv) #3
|
|
br label %return
|
|
|
|
return: ; preds = %if.end, %if.then
|
|
%retval.0 = phi ptr [ %0, %if.then ], [ %call, %if.end ]
|
|
ret ptr %retval.0
|
|
}
|
|
|
|
define ptr @test_stack_allocate_always(i32 %size) {
|
|
; CHECK-LABEL: define {{[^@]+}}@test_stack_allocate_always
|
|
; CHECK-SAME: (i32 [[SIZE:%.*]]) {
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[SAVEDSTACK:%.*]] = call ptr @llvm.stacksave.p0()
|
|
; CHECK-NEXT: [[CMP_I:%.*]] = icmp ult i32 [[SIZE]], 100
|
|
; CHECK-NEXT: [[CONV_I:%.*]] = zext i32 [[SIZE]] to i64
|
|
; CHECK-NEXT: br i1 [[CMP_I]], label [[IF_THEN_I:%.*]], label [[IF_END_I:%.*]]
|
|
; CHECK: if.then.i:
|
|
; CHECK-NEXT: [[TMP0:%.*]] = alloca i8, i64 [[CONV_I]], align 8
|
|
; CHECK-NEXT: br label [[STACK_ALLOCATE_EXIT:%.*]]
|
|
; CHECK: if.end.i:
|
|
; CHECK-NEXT: [[CALL_I:%.*]] = tail call ptr @malloc(i64 [[CONV_I]]) #[[ATTR3]]
|
|
; CHECK-NEXT: br label [[STACK_ALLOCATE_EXIT]]
|
|
; CHECK: stack_allocate.exit:
|
|
; CHECK-NEXT: [[RETVAL_0_I:%.*]] = phi ptr [ [[TMP0]], [[IF_THEN_I]] ], [ [[CALL_I]], [[IF_END_I]] ]
|
|
; CHECK-NEXT: call void @llvm.stackrestore.p0(ptr [[SAVEDSTACK]])
|
|
; CHECK-NEXT: ret ptr [[RETVAL_0_I]]
|
|
;
|
|
entry:
|
|
%call = tail call ptr @stack_allocate(i32 %size)
|
|
ret ptr %call
|
|
}
|
|
|
|
declare void @extern_call(ptr)
|
|
|
|
attributes #1 = { argmemonly nounwind willreturn writeonly }
|
|
attributes #2 = { alwaysinline }
|
|
attributes #3 = { nounwind }
|
|
|