diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 6df8bc64f1c7d..869754628f35e 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -179,6 +179,16 @@ New Compiler Flags only for thread-local variables, and none (which corresponds to the existing ``-fno-c++-static-destructors`` flag) skips all static destructors registration. +- The ``-fextend-lifetimes`` and ``-fextend-this-ptr`` flags have been added to + allow for improved debugging of optimized code. Using ``-fextend-lifetimes`` + will cause Clang to generate code that tries to preserve the lifetimes of + source variables, meaning that variables will typically be visible in a + debugger more often. The ``-fextend-this-ptr`` flag has the same behaviour, + but applies only to the ``this`` variable in C++ class member functions. Note + that this flag modifies the optimizations that Clang performs, which may + result in reduced performance in generated code. Also, for performance + reasons, we do not extend the lifetimes of variables of types that are larger + than ``4 * sizeof(unsigned int)``. Deprecated Compiler Flags ------------------------- diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def index b600198998d85..5bf5c664b46d5 100644 --- a/clang/include/clang/Basic/CodeGenOptions.def +++ b/clang/include/clang/Basic/CodeGenOptions.def @@ -387,6 +387,12 @@ CODEGENOPT(EnableTLSDESC, 1, 0) /// Bit size of immediate TLS offsets (0 == use the default). VALUE_CODEGENOPT(TLSSize, 8, 0) +/// Whether to extend the live range of the `this` pointer. +CODEGENOPT(ExtendThisPtr, 1, 0) + +/// Whether to extend the live ranges of all local variables. +CODEGENOPT(ExtendLifetimes, 1, 0) + /// The default stack protector guard offset to use. VALUE_CODEGENOPT(StackProtectorGuardOffset, 32, INT_MAX) diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 83cf753e82484..281bdffec4c6e 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -4243,6 +4243,15 @@ def stack_usage_file : Separate<["-"], "stack-usage-file">, Visibility<[CC1Option]>, HelpText<"Filename (or -) to write stack usage output to">, MarshallingInfoString>; +def fextend_this_ptr : Flag <["-"], "fextend-this-ptr">, Group, + MarshallingInfoFlag>, + HelpText<"Extend the lifetime of the 'this' pointer to improve visibility " + "in optimized debugging">, Visibility<[ClangOption, CC1Option]>; +def fextend_lifetimes : Flag <["-"], "fextend-lifetimes">, Group, + MarshallingInfoFlag>, + HelpText<"Extend the lifetimes of local variables and parameters to improve " + "visibility in optimized debugging">, + Visibility<[ClangOption, CC1Option]>; defm unique_basic_block_section_names : BoolFOption<"unique-basic-block-section-names", CodeGenOpts<"UniqueBasicBlockSectionNames">, DefaultFalse, diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index ca2c79b51ac96..13a9630468f1f 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -2543,6 +2543,10 @@ void CodeGenModule::ConstructAttributeList(StringRef Name, if (shouldDisableTailCalls()) FuncAttrs.addAttribute("disable-tail-calls", "true"); + // Suppress the machine instruction scheduler when -fextend-lifetimes is on. + if (CodeGenOpts.ExtendLifetimes || CodeGenOpts.ExtendThisPtr) + FuncAttrs.addAttribute(llvm::Attribute::OptimizeForDebugging); + // CPU/feature overrides. addDefaultFunctionDefinitionAttributes // handles these separately to set them based on the global defaults. GetCPUAndFeaturesAttributes(CalleeInfo.getCalleeDecl(), FuncAttrs); @@ -3558,15 +3562,26 @@ static llvm::StoreInst *findDominatingStoreToReturnValue(CodeGenFunction &CGF) { llvm::BasicBlock *IP = CGF.Builder.GetInsertBlock(); if (IP->empty()) return nullptr; - // Look at directly preceding instruction, skipping bitcasts and lifetime - // markers. + // Look at directly preceding instruction, skipping bitcasts, lifetime + // markers, and fake uses and their operands. + const llvm::Instruction *LoadIntoFakeUse = nullptr; for (llvm::Instruction &I : make_range(IP->rbegin(), IP->rend())) { + // Ignore instructions that are just loads for fake uses; the load should + // immediately precede the fake use, so we only need to remember the + // operand for the last fake use seen. + if (LoadIntoFakeUse == &I) + continue; if (isa(&I)) continue; - if (auto *II = dyn_cast(&I)) + if (auto *II = dyn_cast(&I)) { if (II->getIntrinsicID() == llvm::Intrinsic::lifetime_end) continue; + if (II->getIntrinsicID() == llvm::Intrinsic::fake_use) { + LoadIntoFakeUse = dyn_cast(II->getArgOperand(0)); + continue; + } + } return GetStoreIfValid(&I); } return nullptr; diff --git a/clang/lib/CodeGen/CGCleanup.cpp b/clang/lib/CodeGen/CGCleanup.cpp index 5d253c92a38a8..82532e182bebb 100644 --- a/clang/lib/CodeGen/CGCleanup.cpp +++ b/clang/lib/CodeGen/CGCleanup.cpp @@ -112,11 +112,11 @@ void EHScopeStack::deallocate(size_t Size) { StartOfData += llvm::alignTo(Size, ScopeStackAlignment); } -bool EHScopeStack::containsOnlyLifetimeMarkers( +bool EHScopeStack::containsOnlyNoopCleanups( EHScopeStack::stable_iterator Old) const { for (EHScopeStack::iterator it = begin(); stabilize(it) != Old; it++) { EHCleanupScope *cleanup = dyn_cast(&*it); - if (!cleanup || !cleanup->isLifetimeMarker()) + if (!cleanup || !(cleanup->isLifetimeMarker() || cleanup->isFakeUse())) return false; } @@ -154,6 +154,7 @@ void *EHScopeStack::pushCleanup(CleanupKind Kind, size_t Size) { bool IsNormalCleanup = Kind & NormalCleanup; bool IsEHCleanup = Kind & EHCleanup; bool IsLifetimeMarker = Kind & LifetimeMarker; + bool IsFakeUse = Kind & FakeUse; // Per C++ [except.terminate], it is implementation-defined whether none, // some, or all cleanups are called before std::terminate. Thus, when @@ -176,6 +177,8 @@ void *EHScopeStack::pushCleanup(CleanupKind Kind, size_t Size) { InnermostEHScope = stable_begin(); if (IsLifetimeMarker) Scope->setLifetimeMarker(); + if (IsFakeUse) + Scope->setFakeUse(); // With Windows -EHa, Invoke llvm.seh.scope.begin() for EHCleanup // If exceptions are disabled/ignored and SEH is not in use, then there is no diff --git a/clang/lib/CodeGen/CGCleanup.h b/clang/lib/CodeGen/CGCleanup.h index c73c97146abc4..a2800442002a5 100644 --- a/clang/lib/CodeGen/CGCleanup.h +++ b/clang/lib/CodeGen/CGCleanup.h @@ -87,6 +87,9 @@ class EHScope { LLVM_PREFERRED_TYPE(bool) unsigned IsLifetimeMarker : 1; + /// Whether this cleanup is a fake use + unsigned IsFakeUse : 1; + /// Whether the normal cleanup should test the activation flag. LLVM_PREFERRED_TYPE(bool) unsigned TestFlagInNormalCleanup : 1; @@ -352,6 +355,7 @@ class alignas(8) EHCleanupScope : public EHScope { CleanupBits.IsEHCleanup = isEH; CleanupBits.IsActive = true; CleanupBits.IsLifetimeMarker = false; + CleanupBits.IsFakeUse = false; CleanupBits.TestFlagInNormalCleanup = false; CleanupBits.TestFlagInEHCleanup = false; CleanupBits.CleanupSize = cleanupSize; @@ -384,6 +388,9 @@ class alignas(8) EHCleanupScope : public EHScope { bool isLifetimeMarker() const { return CleanupBits.IsLifetimeMarker; } void setLifetimeMarker() { CleanupBits.IsLifetimeMarker = true; } + bool isFakeUse() const { return CleanupBits.IsFakeUse; } + void setFakeUse() { CleanupBits.IsFakeUse = true; } + bool hasActiveFlag() const { return ActiveFlag.isValid(); } Address getActiveFlag() const { return ActiveFlag; diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp index 563f728e29d78..7fdf18f3ca2e7 100644 --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -1353,6 +1353,14 @@ void CodeGenFunction::EmitLifetimeEnd(llvm::Value *Size, llvm::Value *Addr) { C->setDoesNotThrow(); } +void CodeGenFunction::EmitFakeUse(Address Addr) { + auto NL = ApplyDebugLocation::CreateEmpty(*this); + llvm::Value *V = Builder.CreateLoad(Addr, "fake.use"); + llvm::CallInst *C = Builder.CreateCall(CGM.getLLVMFakeUseFn(), {V}); + C->setDoesNotThrow(); + C->setTailCallKind(llvm::CallInst::TCK_NoTail); +} + void CodeGenFunction::EmitAndRegisterVariableArrayDimensions( CGDebugInfo *DI, const VarDecl &D, bool EmitDebugInfo) { // For each dimension stores its QualType and corresponding @@ -1412,6 +1420,39 @@ void CodeGenFunction::EmitAndRegisterVariableArrayDimensions( } } +/// Return the maximum size of an aggregate for which we generate a fake use +/// intrinsic when -fextend-lifetimes is in effect. +static uint64_t maxFakeUseAggregateSize(const ASTContext &C) { + return 4 * C.getTypeSize(C.UnsignedIntTy); +} + +// Helper function to determine whether a variable's or parameter's lifetime +// should be extended. +static bool extendLifetime(const ASTContext &Context, const Decl *FuncDecl, + const VarDecl &D, + ImplicitParamDecl *CXXABIThisDecl) { + // When we're not inside a valid function it is unlikely that any + // lifetime extension is useful. + if (!FuncDecl) + return false; + if (FuncDecl->isImplicit()) + return false; + // Do not extend compiler-created variables except for the this pointer. + if (D.isImplicit() && &D != CXXABIThisDecl) + return false; + QualType Ty = D.getType(); + // No need to extend volatiles, they have a memory location. + if (Ty.isVolatileQualified()) + return false; + // Don't extend variables that exceed a certain size. + if (Context.getTypeSize(Ty) > maxFakeUseAggregateSize(Context)) + return false; + // Do not extend variables in nodebug functions. + if (FuncDecl->hasAttr()) + return false; + return true; +} + /// EmitAutoVarAlloca - Emit the alloca and debug information for a /// local variable. Does not emit initialization or destruction. CodeGenFunction::AutoVarEmission @@ -1664,6 +1705,17 @@ CodeGenFunction::EmitAutoVarAlloca(const VarDecl &D) { emission.getOriginalAllocatedAddress(), emission.getSizeForLifetimeMarkers()); + // Analogous to lifetime markers, we use a 'cleanup' to emit fake.use + // calls for local variables. We are exempting volatile variables and + // non-scalars larger than 4 times the size of an unsigned int (32 bytes). + // Larger non-scalars are often allocated in memory and may create unnecessary + // overhead. + if (CGM.getCodeGenOpts().ExtendLifetimes) { + if (extendLifetime(getContext(), CurCodeDecl, D, CXXABIThisDecl)) + EHStack.pushCleanup(NormalFakeUse, + emission.getAllocatedAddress()); + } + return emission; } @@ -2523,6 +2575,14 @@ llvm::Function *CodeGenModule::getLLVMLifetimeEndFn() { return LifetimeEndFn; } +/// Lazily declare the @llvm.fake.use intrinsic. +llvm::Function *CodeGenModule::getLLVMFakeUseFn() { + if (!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 @@ -2716,6 +2776,15 @@ void CodeGenFunction::EmitParmDecl(const VarDecl &D, ParamValue Arg, 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 (CGM.getCodeGenOpts().ExtendLifetimes || + (CGM.getCodeGenOpts().ExtendThisPtr && &D == CXXABIThisDecl)) { + if (extendLifetime(getContext(), CurCodeDecl, D, CXXABIThisDecl)) + EHStack.pushCleanup(NormalFakeUse, DeclPtr); + } + // Emit debug info for param declarations in non-thunk functions. if (CGDebugInfo *DI = getDebugInfo()) { if (CGM.getCodeGenOpts().hasReducedDebugInfo() && !CurFuncIsThunk && diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp index a5747283e9805..4769c0ab22af6 100644 --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -403,9 +403,9 @@ void CodeGenFunction::FinishFunction(SourceLocation EndLoc) { // important to do this before we enter the return block or return // edges will be *really* confused. bool HasCleanups = EHStack.stable_begin() != PrologueCleanupDepth; - bool HasOnlyLifetimeMarkers = - HasCleanups && EHStack.containsOnlyLifetimeMarkers(PrologueCleanupDepth); - bool EmitRetDbgLoc = !HasCleanups || HasOnlyLifetimeMarkers; + bool HasOnlyNoopCleanups = + HasCleanups && EHStack.containsOnlyNoopCleanups(PrologueCleanupDepth); + bool EmitRetDbgLoc = !HasCleanups || HasOnlyNoopCleanups; std::optional OAL; if (HasCleanups) { diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index 05f85f8b95bfa..8bcd2027a3917 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -718,6 +718,20 @@ class CodeGenFunction : public CodeGenTypeCache { } }; + // We are using objects of this 'cleanup' class to emit fake.use calls + // for -fextend-lifetimes and -fextend-this-ptr. 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. @@ -4966,6 +4980,8 @@ class CodeGenFunction : public CodeGenTypeCache { RValue EmitAtomicExpr(AtomicExpr *E); + void EmitFakeUse(Address Addr); + //===--------------------------------------------------------------------===// // Annotations Emission //===--------------------------------------------------------------------===// diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h index c58bb88035ca8..77d83b1ba7d21 100644 --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -601,6 +601,9 @@ class CodeGenModule : public CodeGenTypeCache { /// void @llvm.lifetime.end(i64 %size, i8* nocapture ) llvm::Function *LifetimeEndFn = nullptr; + /// void @llvm.fake.use(i8* nocapture ) + llvm::Function *FakeUseFn = nullptr; + std::unique_ptr SanitizerMD; llvm::MapVector DeferredEmptyCoverageMappingDecls; @@ -1268,6 +1271,7 @@ class CodeGenModule : public CodeGenTypeCache { llvm::Function *getLLVMLifetimeStartFn(); llvm::Function *getLLVMLifetimeEndFn(); + llvm::Function *getLLVMFakeUseFn(); // Make sure that this type is translated. void UpdateCompletedType(const TagDecl *TD); diff --git a/clang/lib/CodeGen/EHScopeStack.h b/clang/lib/CodeGen/EHScopeStack.h index 0c667e80bb6d8..ed11dc2bb05d7 100644 --- a/clang/lib/CodeGen/EHScopeStack.h +++ b/clang/lib/CodeGen/EHScopeStack.h @@ -87,6 +87,11 @@ enum CleanupKind : unsigned { LifetimeMarker = 0x8, NormalEHLifetimeMarker = LifetimeMarker | NormalAndEHCleanup, + + // FakeUse needs to be recognized as a special cleanup similar to lifetime + // markers chiefly to be ignored in most contexts. + FakeUse = 0x10, + NormalFakeUse = FakeUse | NormalCleanup, }; /// A stack of scopes which respond to exceptions, including cleanups @@ -352,8 +357,8 @@ class EHScopeStack { void popTerminate(); // Returns true iff the current scope is either empty or contains only - // lifetime markers, i.e. no real cleanup code - bool containsOnlyLifetimeMarkers(stable_iterator Old) const; + // noop cleanups, i.e. lifetime markers and fake uses. + bool containsOnlyNoopCleanups(stable_iterator Old) const; /// Determines whether the exception-scopes stack is empty. bool empty() const { return StartOfData == EndOfBuffer; } diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index df86941950e46..e5020271ba95d 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -7581,6 +7581,11 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, 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. diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index 32628c5e84332..c2925ce461dc8 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -2219,6 +2219,11 @@ bool CompilerInvocation::ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, Args.getAllArgValues(OPT_fsanitize_trap_EQ), Diags, Opts.SanitizeTrap); + Opts.ExtendThisPtr = + Opts.OptimizationLevel > 0 && Args.hasArg(OPT_fextend_this_ptr); + Opts.ExtendLifetimes = + Opts.OptimizationLevel > 0 && Args.hasArg(OPT_fextend_lifetimes); + Opts.EmitVersionIdentMetadata = Args.hasFlag(OPT_Qy, OPT_Qn, true); if (!LangOpts->CUDAIsDevice) diff --git a/clang/test/CodeGen/extend-lifetimes-optdebug.c b/clang/test/CodeGen/extend-lifetimes-optdebug.c new file mode 100644 index 0000000000000..74da738d3ed6f --- /dev/null +++ b/clang/test/CodeGen/extend-lifetimes-optdebug.c @@ -0,0 +1,8 @@ +// RUN: %clang_cc1 %s -emit-llvm -O2 -fextend-lifetimes -o - | FileCheck %s + +// Emit the function attribute disable-post-ra when +// -fextend-lifetimes is on. + +// CHECK: attributes #0 = {{{.*}}optdebug + +void foo() {} diff --git a/clang/test/CodeGen/extend-liveness1.c b/clang/test/CodeGen/extend-liveness1.c new file mode 100644 index 0000000000000..ef2d00eb6be31 --- /dev/null +++ b/clang/test/CodeGen/extend-liveness1.c @@ -0,0 +1,29 @@ +// RUN: %clang_cc1 %s -O2 -emit-llvm -fextend-lifetimes -o - | FileCheck %s +// Check that fake use calls are emitted at the correct locations, i.e. +// at the end of lexical blocks and at the end of the function. + +extern int use(int); +int glob1; +int glob2; +float globf; + +int foo(int i) { + // CHECK: define{{.*}}foo + if (i < 4) { + int j = i * 3; + if (glob1 > 3) { + float f = globf; + // CHECK: [[SSAVAL:%[a-z0-9]*]] = load float{{.*}}globf + j = f; + glob2 = j; + // CHECK: store{{.*}}glob2 + // CHECK-NEXT: call void (...) @llvm.fake.use(float [[SSAVAL]]) + } + glob1 = j; + // CHECK: store{{.*}}glob1 + // CHECK-NEXT: call void (...) @llvm.fake.use(i32 %j. + } + // CHECK: call void (...) @llvm.fake.use(i32 %i) + // CHECK-NEXT: ret + return 4; +} diff --git a/clang/test/CodeGen/extend-liveness2.cpp b/clang/test/CodeGen/extend-liveness2.cpp new file mode 100644 index 0000000000000..119c783c63480 --- /dev/null +++ b/clang/test/CodeGen/extend-liveness2.cpp @@ -0,0 +1,34 @@ +// RUN: %clang_cc1 %s -O2 -emit-llvm -fextend-lifetimes -fcxx-exceptions -fexceptions -o - | FileCheck %s +// REQUIRES: x86-registered-target +// This test checks that the fake_use concept works with exception handling and that we +// can handle the __int128 data type. + +class A { +public: + A(int i) : m_i(i) {} + void func(__int128 i128); + + int m_i; +}; + +extern int bar(); +extern void foo(); +int glob; + +void A::func(__int128 i128) { + int j = 4; + try { + int k = bar(); + foo(); + // CHECK: [[SSAVAL:%[a-z0-9]*]] = invoke{{.*}}bar + glob = 0; + // CHECK: store{{.*}}glob + // CHECK-NEXT: call void (...) @llvm.fake.use(i32 [[SSAVAL]]) + } catch (...) { + foo(); + } + // CHECK-LABEL: try.cont: + // CHECK-DAG: call void (...) @llvm.fake.use({{.*%this}}) + // CHECK-DAG: call void (...) @llvm.fake.use(i128 %i128.sroa.0.0.insert.insert) + // CHECK: ret void +} diff --git a/clang/test/CodeGen/fake-use-determinism.c b/clang/test/CodeGen/fake-use-determinism.c new file mode 100644 index 0000000000000..d62efbb4efe7e --- /dev/null +++ b/clang/test/CodeGen/fake-use-determinism.c @@ -0,0 +1,18 @@ +// RUN: %clang -S -O2 -emit-llvm -fextend-lifetimes %s -o - | FileCheck %s +// REQUIRES: asserts +// +// We are checking that the fake.use calls for i, j and k appear +// in a particular order. It is not the order itself that is important +// but that it remains the same between different test runs. + +// CHECK: call {{.*}}void (...) @llvm.fake.use(i32 %k) +// CHECK-NEXT: call {{.*}}void (...) @llvm.fake.use(i32 %j) +// CHECK-NEXT: call {{.*}}void (...) @llvm.fake.use(i32 %i) + +extern void bar(); +void foo(int i, int j, int k) +{ + for (int l = 0; l < i; l++) { + bar(); + } +} diff --git a/clang/test/CodeGen/fake-use-lambda.cpp b/clang/test/CodeGen/fake-use-lambda.cpp new file mode 100644 index 0000000000000..fd4881b9bcd53 --- /dev/null +++ b/clang/test/CodeGen/fake-use-lambda.cpp @@ -0,0 +1,43 @@ +// RUN: %clang_cc1 %s -triple=%itanium_abi_triple -O1 -emit-llvm -fextend-lifetimes -o - | FileCheck %s +// Make sure we don't crash compiling a lambda that is not nested in a function. +// We also check that fake uses are properly issued in lambdas. + +int glob; + +extern int foo(); + +struct S { + static const int a; +}; + +const int S::a = [](int b) __attribute__((noinline)) { + return b * foo(); +} +(glob); + +int func(int param) { + return ([=](int lambdaparm) __attribute__((noinline))->int { + int lambdalocal = lambdaparm * 2; + return lambdalocal; + }(glob)); +} + +// We are looking for the first lambda's call operator, which should contain +// 2 fake uses, one for 'b' and one for its 'this' pointer (in that order). +// The mangled function name contains a $_0, followed by 'cl'. +// This lambda is an orphaned lambda, i.e. one without lexical parent. +// +// CHECK: define internal {{.+\"_Z.+\$_0.*cl.*\"}} +// CHECK-NOT: ret +// CHECK: fake.use(i32 +// CHECK-NOT: ret +// CHECK: fake.use(ptr + +// The second lambda. We are looking for 3 fake uses. +// CHECK: define internal {{.+\"_Z.+\$_0.*cl.*\"}} +// CHECK-NOT: ret +// CHECK: fake.use(i32 +// CHECK-NOT: ret +// CHECK: fake.use(i32 +// CHECK-NOT: ret +// CHECK: fake.use(ptr diff --git a/clang/test/CodeGen/fake-use-landingpad.c b/clang/test/CodeGen/fake-use-landingpad.c new file mode 100644 index 0000000000000..7f49aab7d1865 --- /dev/null +++ b/clang/test/CodeGen/fake-use-landingpad.c @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 %s -O3 -emit-llvm -fextend-lifetimes -fexceptions -o - | FileCheck %s + +// Check that fake uses do not mistakenly cause a landing pad to be generated when +// exceptions are enabled. + +extern void bar(int); +void foo(int p) { + int a = 17; + bar(a); +} + +// CHECK: define {{.*}} @foo +// CHECK-NOT: personality +// CHECK: entry: +// CHECK-NOT: landingpad diff --git a/clang/test/CodeGen/fake-use-noreturn.c b/clang/test/CodeGen/fake-use-noreturn.c new file mode 100644 index 0000000000000..31166d26e5ee0 --- /dev/null +++ b/clang/test/CodeGen/fake-use-noreturn.c @@ -0,0 +1,13 @@ +// RUN: %clang_cc1 %s -emit-llvm -fextend-lifetimes -o %t.ll +// +// Check we don't assert when we have a return in a nested conditional and +// there is no code at the end of the function. + +// CHECK: define{{.*}}main +// CHECK: call{{.*}}llvm.fake.use + +void foo(int i) { + while (0) + if (1) + return; +} diff --git a/clang/test/CodeGen/fake-use-return-line.c b/clang/test/CodeGen/fake-use-return-line.c new file mode 100644 index 0000000000000..b14e0fa405dbd --- /dev/null +++ b/clang/test/CodeGen/fake-use-return-line.c @@ -0,0 +1,10 @@ +// RUN: %clang_cc1 -emit-llvm -O2 -debug-info-kind=limited -fextend-lifetimes -o - %s | FileCheck %s +int main() +{ + volatile int a = 1; + int b = a + 2; + return b; +} +// CHECK: define{{.*}}@main +// CHECK: ret i32{{.*}}!dbg ![[MDINDEX:[0-9]*]] +// CHECK: ![[MDINDEX]] = !DILocation(line: 6 diff --git a/clang/test/CodeGen/fake-use-sanitizer.cpp b/clang/test/CodeGen/fake-use-sanitizer.cpp new file mode 100644 index 0000000000000..915924a2b3102 --- /dev/null +++ b/clang/test/CodeGen/fake-use-sanitizer.cpp @@ -0,0 +1,37 @@ +// RUN: %clang_cc1 %s -O1 -emit-llvm -fextend-lifetimes -fsanitize=null -fsanitize-trap=null -o - | FileCheck --check-prefix TRAP %s +// RUN: %clang_cc1 %s -O1 -emit-llvm -fextend-lifetimes -o - | FileCheck --check-prefix FAKEUSE %s + +// With -fextend-lifetimes the compiler generated a fake.use of a +// reference variable at the end of the function, in the cleanup block. This prompted the +// address sanitizer to verify that the variable has been allocated properly, even when +// the function returns early. +// We check that sanitizers are disabled for code generated for the benefit of fake.use +// intrinsics, as well as that the fake.use is no longer generated in the cleanup block. +// It should be generated in the block preceding the cleanup block instead. + +struct A { short s1, s2; }; +extern A& getA(); + +void foo() +{ + auto& va = getA(); + short s = va.s1 & ~va.s2; + if (s == 0) + return; + + auto& vb = getA(); +} + +// TRAP: define{{.*}}foo +// TRAP: [[COMPARE:%[^\s]*]] = icmp eq +// TRAP-NOT: br i1 [[COMPARE]]{{.*}}trap +// TRAP: br i1 [[COMPARE]]{{.*}}%if.end +// TRAP-NOT: trap: +// TRAP: if.end: +// TRAP-NOT: call{{.*}}llvm.trap + +// FAKEUSE: if.end: +// FAKEUSE-NEXT: [[CALL:%[^\s]*]] = {{.*}}call{{.*}}getA +// FAKEUSE-NOT: br{{.*}}cleanup +// FAKEUSE: call{{.*}}fake.use({{.*}}[[CALL]]) +// FAKEUSE: cleanup: diff --git a/clang/test/CodeGen/fake-use-scalar.c b/clang/test/CodeGen/fake-use-scalar.c new file mode 100644 index 0000000000000..e42216c4a7fed --- /dev/null +++ b/clang/test/CodeGen/fake-use-scalar.c @@ -0,0 +1,22 @@ +// RUN: %clang_cc1 %s -O2 -emit-llvm -fextend-lifetimes -o - | FileCheck %s +// Make sure we don't generate fake.use for non-scalar variables. +// Make sure we don't generate fake.use for volatile variables +// and parameters even when they are scalar. + +struct A { + unsigned long t; + char c[1024]; + unsigned char r[32]; +}; + + +int foo(volatile int param) +{ + struct A s; + volatile int vloc; + struct A v[128]; + char c[33]; + return 0; +} + +// CHECK-NOT: fake.use diff --git a/clang/test/CodeGen/fake-use-small-aggs.c b/clang/test/CodeGen/fake-use-small-aggs.c new file mode 100644 index 0000000000000..236e3a08c533e --- /dev/null +++ b/clang/test/CodeGen/fake-use-small-aggs.c @@ -0,0 +1,24 @@ +// RUN: %clang_cc1 %s -O2 -emit-llvm -fextend-lifetimes -disable-llvm-passes -o - | FileCheck %s +// Check that we generate a fake_use call for small aggregate types. + +// CHECK-DAG: %[[FAKEUSE1:[^ ]+]] = load{{.*}} %loc, +// CHECK-DAG: call{{.*}}llvm.fake.use({{.*}}%[[FAKEUSE1]]) +// CHECK-DAG: %[[FAKEUSE2:[^ ]+]] = load{{.*}} %arr, +// CHECK-DAG: call{{.*}}llvm.fake.use({{.*}}%[[FAKEUSE2]]) +// CHECK-DAG: %[[FAKEUSE3:[^ ]+]] = load{{.*}} %S, +// CHECK-DAG: call{{.*}}llvm.fake.use({{.*}}%[[FAKEUSE3]]) + +struct s { + int i; + int j; +}; + +extern void inita(int *); +extern struct s inits(); +void foo(struct s S) +{ + int arr[4]; + inita (arr); + struct s loc = inits(); +} + diff --git a/clang/test/CodeGen/fake-use-while.c b/clang/test/CodeGen/fake-use-while.c new file mode 100644 index 0000000000000..6055a73636f6c --- /dev/null +++ b/clang/test/CodeGen/fake-use-while.c @@ -0,0 +1,18 @@ +// RUN: %clang_cc1 %s -O2 -emit-llvm -fextend-lifetimes -o - | FileCheck %s +// +// Check we don't assert when there is no more code after a while statement +// and the body of the while statement ends in a return, i.e. no insertion point +// is available. + +// CHECK: define{{.*}}foo +// CHECK: call{{.*}}llvm.fake.use + +void foo() { + { + while (1) { + int ret; + if (1) + return; + } + } +} diff --git a/clang/test/CodeGen/fake-use.cpp b/clang/test/CodeGen/fake-use.cpp new file mode 100644 index 0000000000000..b58ba3ea60c4a --- /dev/null +++ b/clang/test/CodeGen/fake-use.cpp @@ -0,0 +1,44 @@ +// RUN: %clang_cc1 %s -O2 -emit-llvm -fextend-this-ptr -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(ptr nonnull %this) + return V; +// CHECK: ret +} diff --git a/clang/test/CodeGen/no-fake-use-O0.cpp b/clang/test/CodeGen/no-fake-use-O0.cpp new file mode 100644 index 0000000000000..67b9fac9b1bff --- /dev/null +++ b/clang/test/CodeGen/no-fake-use-O0.cpp @@ -0,0 +1,50 @@ +// RUN: %clang_cc1 %s -O0 -emit-llvm -fextend-this-ptr -o - | FileCheck %s \ +// RUN: --implicit-check-not="call void (...) @llvm.fake.use" +// RUN: %clang_cc1 %s -O0 -emit-llvm -fextend-lifetimes -o - | FileCheck %s \ +// RUN: --implicit-check-not="call void (...) @llvm.fake.use" +// RUN: %clang_cc1 %s -O1 -emit-llvm -fextend-this-ptr -o - | FileCheck -check-prefix=OPT %s +// RUN: %clang_cc1 %s -O1 -emit-llvm -fextend-lifetimes -o - | FileCheck -check-prefix=OPT %s +// RUN: %clang_cc1 %s -Os -emit-llvm -fextend-this-ptr -o - | FileCheck -check-prefix=OPT %s +// RUN: %clang_cc1 %s -Os -emit-llvm -fextend-lifetimes -o - | FileCheck -check-prefix=OPT %s +// RUN: %clang_cc1 %s -Oz -emit-llvm -fextend-this-ptr -o - | FileCheck -check-prefix=OPT %s +// RUN: %clang_cc1 %s -Oz -emit-llvm -fextend-lifetimes -o - | FileCheck -check-prefix=OPT %s +// Check that we do not generate a fake_use call when we are not optimizing. + +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(); +// OPT: call void (...) @llvm.fake.use + return V; +}