From 6d55f2269e20298a1d6a683be72d9552482156a9 Mon Sep 17 00:00:00 2001 From: Nick Lewycky Date: Sun, 22 May 2011 05:22:10 +0000 Subject: [PATCH] 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 --- lib/Transforms/Utils/InlineFunction.cpp | 70 ++++++++++++++++++++++ test/Transforms/Inline/lifetime.ll | 78 +++++++++++++++++++++++++ 2 files changed, 148 insertions(+) create mode 100644 test/Transforms/Inline/lifetime.ll diff --git a/lib/Transforms/Utils/InlineFunction.cpp b/lib/Transforms/Utils/InlineFunction.cpp index 7d179092c06..bcdb72e23e7 100644 --- a/lib/Transforms/Utils/InlineFunction.cpp +++ b/lib/Transforms/Utils/InlineFunction.cpp @@ -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(*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(*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) { diff --git a/test/Transforms/Inline/lifetime.ll b/test/Transforms/Inline/lifetime.ll new file mode 100644 index 00000000000..a95c836b77d --- /dev/null +++ b/test/Transforms/Inline/lifetime.ll @@ -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 +} -- 2.34.1