Index: include/clang/Driver/Options.td =================================================================== --- include/clang/Driver/Options.td +++ include/clang/Driver/Options.td @@ -1564,7 +1564,12 @@ Flags<[CC1Option]>, HelpText<"Place each data in its own section (ELF Only)">; def fno_data_sections : Flag <["-"], "fno-data-sections">, Group, Flags<[CC1Option]>; - +def fextend_this_ptr : Flag <["-"], "fextend-this-ptr">, Group, + HelpText<"Extend the lifetime of the 'this' pointer to improve visibility " + "in optimized debugging">, Flags<[CC1Option]>; +def fextend_lifetimes : Flag <["-"], "fextend-lifetimes">, Group, + HelpText<"Extend the lifetimes of local variables and parameters to improve " + "visibility in optimized debugging">, Flags<[CC1Option]>; def funique_section_names : Flag <["-"], "funique-section-names">, Group, Flags<[CC1Option]>, HelpText<"Use unique names for text and data sections (ELF Only)">; Index: include/clang/Frontend/CodeGenOptions.def =================================================================== --- include/clang/Frontend/CodeGenOptions.def +++ include/clang/Frontend/CodeGenOptions.def @@ -278,6 +278,12 @@ /// The default TLS model to use. ENUM_CODEGENOPT(DefaultTLSModel, TLSModel, 2, GeneralDynamicTLSModel) +/// Whether to extend the live range of the this ptr. +CODEGENOPT(ExtendThisPtr, 1, 0) + +/// Whether to extend the live range of all local variables. +CODEGENOPT(ExtendLifetimes, 1, 0) + /// Number of path components to strip when emitting checks. (0 == full /// filename) VALUE_CODEGENOPT(EmitCheckPathComponentsToStrip, 32, 0) Index: lib/CodeGen/CGDecl.cpp =================================================================== --- lib/CodeGen/CGDecl.cpp +++ lib/CodeGen/CGDecl.cpp @@ -955,6 +955,12 @@ C->setDoesNotThrow(); } +void CodeGenFunction::EmitFakeUse(Address Addr) { + llvm::Value *V = Builder.CreateLoad(Addr, "fake.use"); + llvm::CallInst *C = Builder.CreateCall(CGM.getLLVMFakeUseFn(), {V}); + C->setDoesNotThrow(); +} + /// EmitAutoVarAlloca - Emit the alloca and debug information for a /// local variable. Does not emit initialization or destruction. CodeGenFunction::AutoVarEmission @@ -1140,6 +1146,13 @@ emission.getAllocatedAddress(), emission.getSizeForLifetimeMarkers()); + // Analogous to lifetime markers, we use a 'cleanup' to emit fake.use + // calls for local variables. + if (CGM.getCodeGenOpts().ExtendLifetimes && + hasScalarEvaluationKind(D.getType())) { + EHStack.pushCleanup(NormalCleanup, emission.getAllocatedAddress()); + } + return emission; } @@ -1757,6 +1770,15 @@ return LifetimeEndFn; } +/// Lazily declare the @llvm.fake.use intrinsic. +llvm::Constant *CodeGenModule::getLLVMFakeUseFn() { + if (FakeUseFn) + return FakeUseFn; + FakeUseFn = + llvm::Intrinsic::getDeclaration(&getModule(), llvm::Intrinsic::fake_use); + return FakeUseFn; +} + namespace { /// A cleanup to perform a release of an object at the end of a /// function. This is used to balance out the incoming +1 of a @@ -1897,6 +1919,21 @@ setAddrOfLocalVar(&D, DeclPtr); + // Push a FakeUse 'cleanup' object onto the EHStack for the parameter, + // which may be the 'this' pointer. This causes the emission of a fake.use + // call with the parameter as argument at the end of the function. + if (CurFuncDecl && !CurFuncDecl->isImplicit() && + (&D == CXXABIThisDecl || !D.isImplicit())) { + if (CGM.getCodeGenOpts().ExtendLifetimes) { + if (IsScalar) + EHStack.pushCleanup(NormalCleanup, DeclPtr); + } else if (CGM.getCodeGenOpts().ExtendThisPtr) { + // Enter only the 'this' pointer for -fextend-this-ptr. + if (&D == CXXABIThisDecl) + EHStack.pushCleanup(NormalCleanup, DeclPtr); + } + } + // Emit debug info for param declaration. if (CGDebugInfo *DI = getDebugInfo()) { if (CGM.getCodeGenOpts().getDebugInfo() >= Index: lib/CodeGen/CodeGenFunction.h =================================================================== --- lib/CodeGen/CodeGenFunction.h +++ lib/CodeGen/CodeGenFunction.h @@ -421,6 +421,20 @@ } }; + // We are using objects of this 'cleanup' class to emit fake.use calls + // for -fextend-lifetimes. They are placed at the end of a variable's + // scope analogous to lifetime markers. + class FakeUse final : public EHScopeStack::Cleanup { + Address Addr; + + public: + FakeUse(Address addr) : Addr(addr) {} + + void Emit(CodeGenFunction &CGF, Flags flags) override { + CGF.EmitFakeUse(Addr); + } + }; + /// Header for data within LifetimeExtendedCleanupStack. struct LifetimeExtendedCleanupHeader { /// The size of the following cleanup object. @@ -3625,6 +3639,8 @@ RValue EmitAtomicExpr(AtomicExpr *E); + void EmitFakeUse(Address Addr); + //===--------------------------------------------------------------------===// // Annotations Emission //===--------------------------------------------------------------------===// Index: lib/CodeGen/CodeGenModule.h =================================================================== --- lib/CodeGen/CodeGenModule.h +++ lib/CodeGen/CodeGenModule.h @@ -484,6 +484,9 @@ /// void @llvm.lifetime.end(i64 %size, i8* nocapture ) llvm::Constant *LifetimeEndFn = nullptr; + /// void @llvm.fake.use(i8* nocapture ) + llvm::Constant *FakeUseFn = nullptr; + GlobalDecl initializedGlobalDecl; std::unique_ptr SanitizerMD; @@ -970,6 +973,7 @@ llvm::Constant *getLLVMLifetimeStartFn(); llvm::Constant *getLLVMLifetimeEndFn(); + llvm::Constant *getLLVMFakeUseFn(); // Make sure that this type is translated. void UpdateCompletedType(const TagDecl *TD); Index: lib/Driver/ToolChains/Clang.cpp =================================================================== --- lib/Driver/ToolChains/Clang.cpp +++ lib/Driver/ToolChains/Clang.cpp @@ -4434,6 +4434,11 @@ if (Args.hasArg(options::OPT_fretain_comments_from_system_headers)) CmdArgs.push_back("-fretain-comments-from-system-headers"); + if (Args.hasArg(options::OPT_fextend_this_ptr)) + CmdArgs.push_back("-fextend-this-ptr"); + if (Args.hasArg(options::OPT_fextend_lifetimes)) + CmdArgs.push_back("-fextend-lifetimes"); + // Forward -fcomment-block-commands to -cc1. Args.AddAllArgs(CmdArgs, options::OPT_fcomment_block_commands); // Forward -fparse-all-comments to -cc1. Index: lib/Frontend/CompilerInvocation.cpp =================================================================== --- lib/Frontend/CompilerInvocation.cpp +++ lib/Frontend/CompilerInvocation.cpp @@ -999,6 +999,11 @@ Opts.CudaGpuBinaryFileNames = Args.getAllArgValues(OPT_fcuda_include_gpubinary); + Opts.ExtendThisPtr = + Opts.OptimizationLevel > 0 && Args.hasArg(OPT_fextend_this_ptr); + Opts.ExtendLifetimes = + Opts.OptimizationLevel > 0 && Args.hasArg(OPT_fextend_lifetimes); + Opts.Backchain = Args.hasArg(OPT_mbackchain); Opts.EmitCheckPathComponentsToStrip = getLastArgIntValue( Index: test/CodeGen/fake-use.cpp =================================================================== --- test/CodeGen/fake-use.cpp +++ test/CodeGen/fake-use.cpp @@ -0,0 +1,44 @@ +// RUN: %clang_cc1 %s -O2 -emit-llvm -fextend-lifetimes -o - | FileCheck %s +// Check that we generate a fake_use call with the 'this' pointer as argument. +// The call should appear after the call to bar(). + +extern void bar(); + +class v +{ +public: + int x; + int y; + int z; + int w; + + v(int a, int b, int c, int d) : x(a), y(b), z(c), w(d) {} +}; + +class w +{ +public: + v test(int, int, int, int, int, int, int, int, int, int); + w(int in): a(in), b(1234) {} + +private: + int a; + int b; +}; + +v w::test(int q, int w, int e, int r, int t, int y, int u, int i, int o, int p) +{ +// CHECK: define{{.*}}test + int res = q*w + e - r*t + y*u*i*o*p; + int res2 = (w + e + r + t + y + o)*(p*q); + int res3 = res + res2; + int res4 = q*e + t*y*i + p*e*w * 6 * 4 * 3; + + v V(res, res2, res3, res4); + + bar(); +// CHECK: call{{.*}}bar +// CHECK: call void (...) @llvm.fake.use(%class.w* %this) + return V; +// CHECK: ret +}