mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2025-01-02 07:32:52 +00:00
Teach the inliner to emit llvm.lifetime.start/end, to scope the local variables
of the inlinee to the code representing the original function. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@131838 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
9063b55f9b
commit
6d55f2269e
@ -28,6 +28,7 @@
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/ADT/StringExtras.h"
|
||||
#include "llvm/Support/CallSite.h"
|
||||
#include "llvm/Support/IRBuilder.h"
|
||||
using namespace llvm;
|
||||
|
||||
bool llvm::InlineFunction(CallInst *CI, InlineFunctionInfo &IFI) {
|
||||
@ -314,6 +315,41 @@ static Value *HandleByValArgument(Value *Arg, Instruction *TheCall,
|
||||
return NewAlloca;
|
||||
}
|
||||
|
||||
// isUsedByLifetimeMarker - Check whether this Value is used by a lifetime
|
||||
// intrinsic.
|
||||
static bool isUsedByLifetimeMarker(Value *V) {
|
||||
for (Value::use_iterator UI = V->use_begin(), UE = V->use_end(); UI != UE;
|
||||
++UI) {
|
||||
if (IntrinsicInst *II = dyn_cast<IntrinsicInst>(*UI)) {
|
||||
switch (II->getIntrinsicID()) {
|
||||
default: break;
|
||||
case Intrinsic::lifetime_start:
|
||||
case Intrinsic::lifetime_end:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// hasLifetimeMarkers - Check whether the given alloca already has
|
||||
// lifetime.start or lifetime.end intrinsics.
|
||||
static bool hasLifetimeMarkers(AllocaInst *AI) {
|
||||
const Type *Int8PtrTy = Type::getInt8PtrTy(AI->getType()->getContext());
|
||||
if (AI->getType() == Int8PtrTy)
|
||||
return isUsedByLifetimeMarker(AI);
|
||||
|
||||
// Do a scan to find all the bitcasts to i8*.
|
||||
for (Value::use_iterator I = AI->use_begin(), E = AI->use_end(); I != E;
|
||||
++I) {
|
||||
if (I->getType() != Int8PtrTy) continue;
|
||||
if (!isa<BitCastInst>(*I)) continue;
|
||||
if (isUsedByLifetimeMarker(*I))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// InlineFunction - This function inlines the called function into the basic
|
||||
// block of the caller. This returns false if it is not possible to inline this
|
||||
// call. The program is still in a well defined state if this occurs though.
|
||||
@ -460,6 +496,40 @@ bool llvm::InlineFunction(CallSite CS, InlineFunctionInfo &IFI) {
|
||||
}
|
||||
}
|
||||
|
||||
// Leave lifetime markers for the static alloca's, scoping them to the
|
||||
// function we just inlined.
|
||||
if (!IFI.StaticAllocas.empty()) {
|
||||
// Also preserve the call graph, if applicable.
|
||||
CallGraphNode *StartCGN = 0, *EndCGN = 0, *CallerNode = 0;
|
||||
if (CallGraph *CG = IFI.CG) {
|
||||
Function *Start = Intrinsic::getDeclaration(Caller->getParent(),
|
||||
Intrinsic::lifetime_start);
|
||||
Function *End = Intrinsic::getDeclaration(Caller->getParent(),
|
||||
Intrinsic::lifetime_end);
|
||||
StartCGN = CG->getOrInsertFunction(Start);
|
||||
EndCGN = CG->getOrInsertFunction(End);
|
||||
CallerNode = (*CG)[Caller];
|
||||
}
|
||||
|
||||
IRBuilder<> builder(FirstNewBlock->begin());
|
||||
for (unsigned ai = 0, ae = IFI.StaticAllocas.size(); ai != ae; ++ai) {
|
||||
AllocaInst *AI = IFI.StaticAllocas[ai];
|
||||
|
||||
// If the alloca is already scoped to something smaller than the whole
|
||||
// function then there's no need to add redundant, less accurate markers.
|
||||
if (hasLifetimeMarkers(AI))
|
||||
continue;
|
||||
|
||||
CallInst *StartCall = builder.CreateLifetimeStart(AI);
|
||||
if (IFI.CG) CallerNode->addCalledFunction(StartCall, StartCGN);
|
||||
for (unsigned ri = 0, re = Returns.size(); ri != re; ++ri) {
|
||||
IRBuilder<> builder(Returns[ri]);
|
||||
CallInst *EndCall = builder.CreateLifetimeEnd(AI);
|
||||
if (IFI.CG) CallerNode->addCalledFunction(EndCall, EndCGN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the inlined code contained dynamic alloca instructions, wrap the inlined
|
||||
// code with llvm.stacksave/llvm.stackrestore intrinsics.
|
||||
if (InlinedFunctionInfo.ContainsDynamicAllocas) {
|
||||
|
78
test/Transforms/Inline/lifetime.ll
Normal file
78
test/Transforms/Inline/lifetime.ll
Normal file
@ -0,0 +1,78 @@
|
||||
; RUN: opt -inline %s -S -o - | FileCheck %s
|
||||
|
||||
declare void @llvm.lifetime.start(i64, i8*)
|
||||
declare void @llvm.lifetime.end(i64, i8*)
|
||||
|
||||
define void @helper_both_markers() {
|
||||
%a = alloca i8
|
||||
call void @llvm.lifetime.start(i64 1, i8* %a)
|
||||
call void @llvm.lifetime.end(i64 1, i8* %a)
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @test_both_markers() {
|
||||
; CHECK: @test_both_markers
|
||||
; CHECK: llvm.lifetime.start(i64 1
|
||||
; CHECK-NEXT: llvm.lifetime.end(i64 1
|
||||
call void @helper_both_markers()
|
||||
; CHECK-NEXT: llvm.lifetime.start(i64 1
|
||||
; CHECK-NEXT: llvm.lifetime.end(i64 1
|
||||
call void @helper_both_markers()
|
||||
; CHECK-NEXT: ret void
|
||||
ret void
|
||||
}
|
||||
|
||||
;; Without this, the inliner will simplify out @test_no_marker before adding
|
||||
;; any lifetime markers.
|
||||
declare void @use(i8* %a)
|
||||
|
||||
define void @helper_no_markers() {
|
||||
%a = alloca i8
|
||||
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: @test_no_marker
|
||||
; CHECK-NOT: lifetime
|
||||
; CHECK: llvm.lifetime.start(i64 -1
|
||||
; CHECK-NOT: lifetime
|
||||
; CHECK: llvm.lifetime.end(i64 -1
|
||||
call void @helper_no_markers()
|
||||
; CHECK-NOT: lifetime
|
||||
; CHECK: llvm.lifetime.start(i64 -1
|
||||
; CHECK-NOT: lifetime
|
||||
; CHECK: llvm.lifetime.end(i64 -1
|
||||
call void @helper_no_markers()
|
||||
; CHECK-NOT: lifetime
|
||||
; CHECK: ret void
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @helper_two_casts() {
|
||||
%a = alloca i32
|
||||
%b = bitcast i32* %a to i8*
|
||||
call void @llvm.lifetime.start(i64 4, i8* %b)
|
||||
%c = bitcast i32* %a to i8*
|
||||
call void @llvm.lifetime.end(i64 4, i8* %c)
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @test_two_casts() {
|
||||
; CHECK: @test_two_casts
|
||||
; CHECK-NOT: lifetime
|
||||
; CHECK: llvm.lifetime.start(i64 4
|
||||
; CHECK-NOT: lifetime
|
||||
; CHECK: llvm.lifetime.end(i64 4
|
||||
call void @helper_two_casts()
|
||||
; CHECK-NOT: lifetime
|
||||
; CHECK: llvm.lifetime.start(i64 4
|
||||
; CHECK-NOT: lifetime
|
||||
; CHECK: llvm.lifetime.end(i64 4
|
||||
call void @helper_two_casts()
|
||||
; CHECK-NOT: lifetime
|
||||
; CHECK: ret void
|
||||
ret void
|
||||
}
|
Loading…
Reference in New Issue
Block a user