Index: lib/CodeGen/CGDecl.cpp =================================================================== --- lib/CodeGen/CGDecl.cpp +++ lib/CodeGen/CGDecl.cpp @@ -889,8 +889,8 @@ /// Emit a lifetime.begin marker if some criteria are satisfied. /// \return a pointer to the temporary size Value if a marker was emitted, null /// otherwise -llvm::Value *CodeGenFunction::EmitLifetimeStart(uint64_t Size, - llvm::Value *Addr) { +llvm::CallInst *CodeGenFunction::EmitLifetimeStart(uint64_t Size, + llvm::Value *Addr) { if (!ShouldEmitLifetimeMarkers) return nullptr; @@ -899,7 +899,7 @@ llvm::CallInst *C = Builder.CreateCall(CGM.getLLVMLifetimeStartFn(), {SizeV, Addr}); C->setDoesNotThrow(); - return SizeV; + return C; } void CodeGenFunction::EmitLifetimeEnd(llvm::Value *Size, llvm::Value *Addr) { @@ -1024,7 +1024,7 @@ // them incorrectly placed. if (!Bypasses.IsBypassed(&D)) { uint64_t size = CGM.getDataLayout().getTypeAllocSize(allocaTy); - emission.SizeForLifetimeMarkers = + emission.LifetimeStart = EmitLifetimeStart(size, address.getPointer()); } } else { @@ -1373,8 +1373,7 @@ // *last*, so the cleanup needs to be pushed *first*. if (emission.useLifetimeMarkers()) EHStack.pushCleanup(NormalEHLifetimeMarker, - emission.getAllocatedAddress(), - emission.getSizeForLifetimeMarkers()); + emission.getLifetimeStartMarker()); // Check the type for a cleanup. if (QualType::DestructionKind dtorKind = D.getType().isDestructedType()) Index: lib/CodeGen/CGExpr.cpp =================================================================== --- lib/CodeGen/CGExpr.cpp +++ lib/CodeGen/CGExpr.cpp @@ -430,15 +430,15 @@ switch (M->getStorageDuration()) { case SD_Automatic: case SD_FullExpression: - if (auto *Size = EmitLifetimeStart( + if (auto *LifetimeStart = EmitLifetimeStart( CGM.getDataLayout().getTypeAllocSize(Object.getElementType()), Object.getPointer())) { if (M->getStorageDuration() == SD_Automatic) pushCleanupAfterFullExpr(NormalEHLifetimeMarker, - Object, Size); + LifetimeStart); else - pushFullExprCleanup(NormalEHLifetimeMarker, Object, - Size); + pushFullExprCleanup(NormalEHLifetimeMarker, + LifetimeStart); } break; default: Index: lib/CodeGen/CGStmt.cpp =================================================================== --- lib/CodeGen/CGStmt.cpp +++ lib/CodeGen/CGStmt.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "CodeGenFunction.h" +#include "CGCleanup.h" #include "CGDebugInfo.h" #include "CodeGenModule.h" #include "TargetInfo.h" @@ -558,6 +559,36 @@ } } +/// Move LifetimeStart to the beginning of lexical scope LS. +static void moveLifetimeStart(CodeGenFunction::CallLifetimeEnd *Cleanup, + CodeGenFunction::LexicalScope *LS, + CodeGenFunction *CGF) { + llvm::BasicBlock *EntryBlock = &CGF->CurFn->getEntryBlock(); + llvm::BasicBlock *BB = LS ? LS->getBasicBlock() : EntryBlock; + auto I = BB->begin(); + + // If BB is the entry block, move the lifetime marker to after the alloca + // insertion point. + if (BB == EntryBlock) + I = CGF->AllocaInsertPt->getIterator(); + + // Move the lifetime marker. + llvm::CallInst *LifetimeStart = Cleanup->getLifetimeStart(); + LifetimeStart->removeFromParent(); + BB->getInstList().insertAfter(I, LifetimeStart); + + // Move the bitcast instruction too. + auto *Ptr = cast(LifetimeStart->getOperand(1)); + if (isa(Ptr)) { + assert(isa(cast(Ptr)->getOperand(0)) && + "this should be a bitcast of an alloca"); + Ptr->removeFromParent(); + BB->getInstList().insertAfter(I, Ptr); + } + + Cleanup->setLifetimeStartMoved(); +} + void CodeGenFunction::EmitGotoStmt(const GotoStmt &S) { // If this code is reachable then emit a stop point (if generating // debug info). We have to do this ourselves because we are on the @@ -565,7 +596,43 @@ if (HaveInsertPoint()) EmitStopPoint(&S); - EmitBranchThroughCleanup(getJumpDestForLabel(S.getLabel())); + auto Dest = getJumpDestForLabel(S.getLabel()); + auto JumpDestIdx = Dest.getScopeDepth(); + + // If we are compiling for C and the goto's destination label has been seen, + // move the lifetime.start markers. + if (!getLangOpts().CPlusPlus && JumpDestIdx.isValid()) { + LexicalScope *LS = CurLexicalScope; + auto SI = EHStack.getInnermostNormalCleanup(); + + // Search for the lexical scope enclosing the destination label. Note that + // LS will be null if the function body is the enclosing lexical scope. + while (LS) { + if (LS->hasLabel(S.getLabel())) + break; + SI = LS->getCleanupStackDepth(); + LS = LS->getParentScope(); + } + + // Move all the lifetime.start markers of the scopes in range + // [SI, JumpDestIdx) to the beginning of the enclosing lexical scope. + while (SI != JumpDestIdx) { + auto *Scope = &cast(*EHStack.find(SI)); + SI = Scope->getEnclosingNormalCleanup(); + + if (!Scope->isLifetimeMarker()) + continue; + + auto *C = static_cast(Scope->getCleanup()); + + if (C->lifetimeStartMoved()) + continue; + + moveLifetimeStart(C, LS, this); + } + } + + EmitBranchThroughCleanup(Dest); } Index: lib/CodeGen/CodeGenFunction.h =================================================================== --- lib/CodeGen/CodeGenFunction.h +++ lib/CodeGen/CodeGenFunction.h @@ -346,15 +346,32 @@ llvm::Instruction *CurrentFuncletPad = nullptr; class CallLifetimeEnd final : public EHScopeStack::Cleanup { - llvm::Value *Addr; - llvm::Value *Size; + // lifetime.start intrinsic and a flag indicating whether it has been moved + // to the beginning of the lexical scope of the object associated with the + // intrinsic. + llvm::PointerIntPair LifetimeStart; public: - CallLifetimeEnd(Address addr, llvm::Value *size) - : Addr(addr.getPointer()), Size(size) {} + CallLifetimeEnd(llvm::CallInst *lifetimeStart) + : LifetimeStart(lifetimeStart) {} void Emit(CodeGenFunction &CGF, Flags flags) override { - CGF.EmitLifetimeEnd(Size, Addr); + auto *Addr = LifetimeStart.getPointer()->getOperand(1); + if (auto *BC = dyn_cast(Addr)) + Addr = BC->getOperand(0); + CGF.EmitLifetimeEnd(LifetimeStart.getPointer()->getOperand(0), Addr); + } + + llvm::CallInst *getLifetimeStart() { + return LifetimeStart.getPointer(); + } + + bool lifetimeStartMoved() const { + return LifetimeStart.getInt(); + } + + void setLifetimeStartMoved() { + LifetimeStart.setInt(true); } }; @@ -523,10 +540,10 @@ /// \brief Enters a new scope for capturing cleanups, all of which /// will be executed once the scope is exited. class RunCleanupsScope { - EHScopeStack::stable_iterator CleanupStackDepth; size_t LifetimeExtendedCleanupStackSize; bool OldDidCallStackSave; protected: + EHScopeStack::stable_iterator CleanupStackDepth; bool PerformCleanup; private: @@ -578,6 +595,7 @@ SourceRange Range; SmallVector Labels; LexicalScope *ParentScope; + llvm::BasicBlock *BB; LexicalScope(const LexicalScope &) = delete; void operator=(const LexicalScope &) = delete; @@ -589,6 +607,8 @@ CGF.CurLexicalScope = this; if (CGDebugInfo *DI = CGF.getDebugInfo()) DI->EmitLexicalBlockStart(CGF.Builder, Range.getBegin()); + CGF.EnsureInsertPoint(); + BB = CGF.Builder.GetInsertBlock(); } void addLabel(const LabelDecl *label) { @@ -596,6 +616,18 @@ Labels.push_back(label); } + EHScopeStack::stable_iterator getCleanupStackDepth() const { + return CleanupStackDepth; + } + + llvm::BasicBlock *getBasicBlock() const { + return BB; + } + + LexicalScope *getParentScope() const { + return ParentScope; + } + /// \brief Exit this cleanup scope, emitting any accumulated /// cleanups. ~LexicalScope() { @@ -620,6 +652,10 @@ rescopeLabels(); } + bool hasLabel(const LabelDecl *LD) const { + return llvm::is_contained(Labels, LD); + } + void rescopeLabels(); }; @@ -2184,7 +2220,7 @@ void EmitCXXTemporary(const CXXTemporary *Temporary, QualType TempType, Address Ptr); - llvm::Value *EmitLifetimeStart(uint64_t Size, llvm::Value *Addr); + llvm::CallInst *EmitLifetimeStart(uint64_t Size, llvm::Value *Addr); void EmitLifetimeEnd(llvm::Value *Size, llvm::Value *Addr); llvm::Value *EmitCXXNewExpr(const CXXNewExpr *E); @@ -2313,15 +2349,14 @@ bool IsConstantAggregate; /// Non-null if we should use lifetime annotations. - llvm::Value *SizeForLifetimeMarkers; + llvm::CallInst *LifetimeStart; struct Invalid {}; AutoVarEmission(Invalid) : Variable(nullptr), Addr(Address::invalid()) {} AutoVarEmission(const VarDecl &variable) : Variable(&variable), Addr(Address::invalid()), NRVOFlag(nullptr), - IsByRef(false), IsConstantAggregate(false), - SizeForLifetimeMarkers(nullptr) {} + IsByRef(false), IsConstantAggregate(false), LifetimeStart(nullptr) {} bool wasEmittedAsGlobal() const { return !Addr.isValid(); } @@ -2329,11 +2364,15 @@ static AutoVarEmission invalid() { return AutoVarEmission(Invalid()); } bool useLifetimeMarkers() const { - return SizeForLifetimeMarkers != nullptr; + return LifetimeStart != nullptr; } llvm::Value *getSizeForLifetimeMarkers() const { + return LifetimeStart->getOperand(0); + } + + llvm::CallInst *getLifetimeStartMarker() const { assert(useLifetimeMarkers()); - return SizeForLifetimeMarkers; + return LifetimeStart; } /// Returns the raw, allocated address, which is not necessarily @@ -3627,6 +3666,10 @@ // If it's not an instruction, we don't need to save. if (!isa(value)) return false; + // Return if type is void. + if (cast(value)->getType()->isVoidTy()) + return false; + // If it's an instruction in the entry block, we don't need to save. llvm::BasicBlock *block = cast(value)->getParent(); return (block != &block->getParent()->getEntryBlock()); Index: test/CodeGen/lifetime2.c =================================================================== --- test/CodeGen/lifetime2.c +++ test/CodeGen/lifetime2.c @@ -1,4 +1,4 @@ -// RUN: %clang -S -emit-llvm -o - -O2 %s | FileCheck %s -check-prefixes=CHECK,O2 +// RUN: %clang -S -emit-llvm -o - -O2 -mllvm -disable-llvm-optzns %s | FileCheck %s -check-prefixes=CHECK,O2 // RUN: %clang -S -emit-llvm -o - -O0 %s | FileCheck %s -check-prefixes=CHECK,O0 extern int bar(char *A, int n); @@ -19,11 +19,11 @@ // CHECK-LABEL: @no_goto_bypass void no_goto_bypass() { + // O2: @llvm.lifetime.start(i64 5 // O2: @llvm.lifetime.start(i64 1 char x; l1: bar(&x, 1); - // O2: @llvm.lifetime.start(i64 5 // O2: @llvm.lifetime.end(i64 5 char y[5]; bar(y, 5); @@ -89,3 +89,58 @@ L: bar(&x, 1); } + +// O2-LABEL: define i32 @move_lifetime_start +// O2: %i = alloca i32, align 4 +// O2: %[[BITCAST:[0-9]+]] = bitcast i32* %i to i8* +// O2: call void @llvm.lifetime.start(i64 4, i8* %[[BITCAST]]) +// O2: br label %[[DESTLABEL:[a-z0-9]+]] +// O2: [[DESTLABEL]]: +// O2: switch i32 %{{.*}}, label %{{.*}} [ +// O2-NEXT: i32 2, label %[[DESTLABEL]] + +extern void foo2(int p); + +int move_lifetime_start(int a) { + int *p = 0; +label1: + if (p) { + foo2(*p); + return 0; + } + + int i = 999; + if (a != 2) { + p = &i; + goto label1; + } + return -1; +} + +// O2-LABEL: define i32 @dont_move_lifetime_start +// O2: %i = alloca i32, align 4 +// O2: br label %[[DESTLABEL:[a-z0-9]+]] +// O2: [[DESTLABEL]]: +// O2: %[[BITCAST:[0-9]+]] = bitcast i32* %i to i8* +// O2: call void @llvm.lifetime.start(i64 4, i8* %[[BITCAST]]) +// O2: switch i32 %{{.*}}, label %{{.*}} [ +// O2-NEXT: i32 0, label %{{.*}} +// O2-NEXT: i32 2, label %[[DESTLABEL]] + +int dont_move_lifetime_start(int a) { + int *p = 0; +label1: + { + if (p) { + foo2(*p); + return 0; + } + + int i = 999; + if (a != 2) { + p = &i; + goto label1; + } + } + return -1; +}