diff options
Diffstat (limited to 'clang')
20 files changed, 483 insertions, 10 deletions
diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index e0cf6ca..f790e78 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -3581,15 +3581,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<llvm::BitCastInst>(&I)) continue; - if (auto *II = dyn_cast<llvm::IntrinsicInst>(&I)) + if (auto *II = dyn_cast<llvm::IntrinsicInst>(&I)) { if (II->getIntrinsicID() == llvm::Intrinsic::lifetime_end) continue; + if (II->getIntrinsicID() == llvm::Intrinsic::fake_use) { + LoadIntoFakeUse = dyn_cast<llvm::Instruction>(II->getArgOperand(0)); + continue; + } + } return GetStoreIfValid(&I); } return nullptr; diff --git a/clang/lib/CodeGen/CGCleanup.cpp b/clang/lib/CodeGen/CGCleanup.cpp index d9c0dbe..7e1c5b7 100644 --- a/clang/lib/CodeGen/CGCleanup.cpp +++ b/clang/lib/CodeGen/CGCleanup.cpp @@ -112,11 +112,15 @@ 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<EHCleanupScope>(&*it); - if (!cleanup || !cleanup->isLifetimeMarker()) + // If this is anything other than a lifetime marker or fake use cleanup, + // then the scope stack does not contain only noop cleanups. + if (!cleanup) + return false; + if (!cleanup->isLifetimeMarker() && !cleanup->isFakeUse()) return false; } @@ -154,6 +158,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 +181,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 c73c971..ba78e54 100644 --- a/clang/lib/CodeGen/CGCleanup.h +++ b/clang/lib/CodeGen/CGCleanup.h @@ -87,6 +87,10 @@ protected: LLVM_PREFERRED_TYPE(bool) unsigned IsLifetimeMarker : 1; + /// Whether this cleanup is a fake use + LLVM_PREFERRED_TYPE(bool) + unsigned IsFakeUse : 1; + /// Whether the normal cleanup should test the activation flag. LLVM_PREFERRED_TYPE(bool) unsigned TestFlagInNormalCleanup : 1; @@ -352,6 +356,7 @@ public: CleanupBits.IsEHCleanup = isEH; CleanupBits.IsActive = true; CleanupBits.IsLifetimeMarker = false; + CleanupBits.IsFakeUse = false; CleanupBits.TestFlagInNormalCleanup = false; CleanupBits.TestFlagInEHCleanup = false; CleanupBits.CleanupSize = cleanupSize; @@ -384,6 +389,9 @@ public: 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 60f67d4..ded905c 100644 --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -1355,6 +1355,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 @@ -1414,6 +1422,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 shouldExtendLifetime(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 or optnone functions. + if (FuncDecl->hasAttr<NoDebugAttr>() || FuncDecl->hasAttr<OptimizeNoneAttr>()) + return false; + return true; +} + /// EmitAutoVarAlloca - Emit the alloca and debug information for a /// local variable. Does not emit initialization or destruction. CodeGenFunction::AutoVarEmission @@ -1666,6 +1707,18 @@ 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. Larger + // non-scalars are often allocated in memory and may create unnecessary + // overhead. + if (CGM.getCodeGenOpts().getExtendVariableLiveness() == + CodeGenOptions::ExtendVariableLivenessKind::All) { + if (shouldExtendLifetime(getContext(), CurCodeDecl, D, CXXABIThisDecl)) + EHStack.pushCleanup<FakeUse>(NormalFakeUse, + emission.getAllocatedAddress()); + } + return emission; } @@ -2532,6 +2585,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 @@ -2725,6 +2786,18 @@ 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().getExtendVariableLiveness() == + CodeGenOptions::ExtendVariableLivenessKind::All || + (CGM.getCodeGenOpts().getExtendVariableLiveness() == + CodeGenOptions::ExtendVariableLivenessKind::This && + &D == CXXABIThisDecl)) { + if (shouldExtendLifetime(getContext(), CurCodeDecl, D, CXXABIThisDecl)) + EHStack.pushCleanup<FakeUse>(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 11fdddb..bbef277 100644 --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -404,9 +404,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<ApplyDebugLocation> OAL; if (HasCleanups) { diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index f70e73f..60f16a3 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -723,6 +723,20 @@ public: } }; + // 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. @@ -5075,6 +5089,8 @@ public: 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 1aa5d48..0956296 100644 --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -647,6 +647,9 @@ private: /// void @llvm.lifetime.end(i64 %size, i8* nocapture <ptr>) llvm::Function *LifetimeEndFn = nullptr; + /// void @llvm.fake.use(...) + llvm::Function *FakeUseFn = nullptr; + std::unique_ptr<SanitizerMetadata> SanitizerMD; llvm::MapVector<const Decl *, bool> DeferredEmptyCoverageMappingDecls; @@ -1326,6 +1329,7 @@ public: 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 0c667e8..ed11dc2 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 @@ public: 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/test/CodeGen/extend-variable-liveness-except.cpp b/clang/test/CodeGen/extend-variable-liveness-except.cpp new file mode 100644 index 0000000..13a5c2e --- /dev/null +++ b/clang/test/CodeGen/extend-variable-liveness-except.cpp @@ -0,0 +1,34 @@ +// RUN: %clang_cc1 %s -emit-llvm -fextend-variable-liveness -fcxx-exceptions -fexceptions -o - | FileCheck %s +// This test checks that the fake uses can be generated in exception handling +// blocks and that we can emit fake uses for the __int128 data type. + +extern int bar(); + +/// Try block: fake use ends at try-block scope. +// [[BAR_VAL::%[a-zA-Z0-9\.]+]] = invoke{{.*}} i32 @_Z3barv() +// store i32 %[[BAR_VAL]], ptr [[K_ALLOC_VAL:%[a-zA-Z0-9\.]+]], align 4 +// [[K_FAKE_USE:%[a-zA-Z0-9\.]+]] = load i32, ptr [[K_ALLOC_VAL]], align 4 +// call void (...) @llvm.fake.use(i32 [[K_FAKE_USE]]) #2 +// br label + +/// Catch block: fetching the caught value... +// CHECK: [[CATCH_PTR:%[a-zA-Z0-9\.]+]] = call ptr @__cxa_begin_catch( +// CHECK: [[L_VAL:%[a-zA-Z0-9\.]+]] = load i32, ptr [[CATCH_PTR]], align 4 + +/// Storing to allocas... +// CHECK-DAG: store i32 8, ptr [[M_ALLOC_VAL:%[a-zA-Z0-9\.]+]] +// CHECK-DAG: store i32 [[L_VAL]], ptr [[L_ALLOC_VAL:%[a-zA-Z0-9\.]+]], align 4 + +/// Load into fake uses - expect M to precede L. +// CHECK: [[M_FAKE_VAL:%[a-zA-Z0-9\.]+]] = load i32, ptr [[M_ALLOC_VAL]] +// CHECK: call void (...) @llvm.fake.use(i32 [[M_FAKE_VAL]]) +// CHECK: [[L_FAKE_VAL:%[a-zA-Z0-9\.]+]] = load i32, ptr [[L_ALLOC_VAL]] +// CHECK: call void (...) @llvm.fake.use(i32 [[L_FAKE_VAL]]) +void foo() { + try { + int k = bar(); + } catch (int l) { + /// The catch block contains a fake use for the local within its scope. + int m = 8; + } +} diff --git a/clang/test/CodeGen/extend-variable-liveness-wide-scalar.cpp b/clang/test/CodeGen/extend-variable-liveness-wide-scalar.cpp new file mode 100644 index 0000000..42b893c --- /dev/null +++ b/clang/test/CodeGen/extend-variable-liveness-wide-scalar.cpp @@ -0,0 +1,11 @@ +// RUN: %clang_cc1 %s -emit-llvm -fextend-variable-liveness -triple x86_64-unknown-linux -o - | FileCheck %s +// REQUIRES: x86-registered-target +// This test checks that the fake uses can be generated in exception handling +// blocks and that we can emit fake uses for the __int128 data type. + +void bar(); + +// CHECK: call void (...) @llvm.fake.use(i128 % +void foo(__int128 wide_int) { + bar(); +} diff --git a/clang/test/CodeGen/extend-variable-liveness.c b/clang/test/CodeGen/extend-variable-liveness.c new file mode 100644 index 0000000..0eae155 --- /dev/null +++ b/clang/test/CodeGen/extend-variable-liveness.c @@ -0,0 +1,29 @@ +// RUN: %clang_cc1 %s -emit-llvm -fextend-variable-liveness -o - | FileCheck %s --implicit-check-not=llvm.fake.use +// 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. + +int glob_i; +char glob_c; +float glob_f; + +int foo(int i) { + // CHECK-LABEL: define{{.*}}foo + if (i < 4) { + char j = i * 3; + if (glob_i > 3) { + float f = glob_f; + j = f; + glob_c = j; + // CHECK: call void (...) @llvm.fake.use(float % + // CHECK-NEXT: br label % + } + glob_i = j; + // CHECK: call void (...) @llvm.fake.use(i8 % + // CHECK-NEXT: br label % + } + // CHECK: call void (...) @llvm.fake.use(i32 % + // CHECK-NEXT: ret + return 4; +} + +// CHECK: declare void @llvm.fake.use(...) diff --git a/clang/test/CodeGen/fake-use-determinism.c b/clang/test/CodeGen/fake-use-determinism.c new file mode 100644 index 0000000..459d791 --- /dev/null +++ b/clang/test/CodeGen/fake-use-determinism.c @@ -0,0 +1,20 @@ +// RUN: %clang_cc1 -emit-llvm -fextend-variable-liveness %s -o - | FileCheck %s +// +// 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: [[K_FAKE_USE:%[a-zA-Z0-9\.]+]] = load i32, ptr %k.addr +// CHECK-NEXT: call void (...) @llvm.fake.use(i32 [[K_FAKE_USE]]) #2 +// CHECK-NEXT: [[J_FAKE_USE:%[a-zA-Z0-9\.]+]] = load i32, ptr %j.addr +// CHECK-NEXT: call void (...) @llvm.fake.use(i32 [[J_FAKE_USE]]) #2 +// CHECK-NEXT: [[I_FAKE_USE:%[a-zA-Z0-9\.]+]] = load i32, ptr %i.addr +// CHECK-NEXT: call void (...) @llvm.fake.use(i32 [[I_FAKE_USE]]) #2 + +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 0000000..aaf25ca --- /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-variable-liveness -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-LABEL: 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-LABEL: 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 0000000..ffaf397 --- /dev/null +++ b/clang/test/CodeGen/fake-use-landingpad.c @@ -0,0 +1,14 @@ +// RUN: %clang_cc1 %s -emit-llvm -fextend-variable-liveness -fexceptions -o - | FileCheck %s --implicit-check-not="landingpad {" + +// 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: call void (...) @llvm.fake.use diff --git a/clang/test/CodeGen/fake-use-noreturn.cpp b/clang/test/CodeGen/fake-use-noreturn.cpp new file mode 100644 index 0000000..6a3a07c --- /dev/null +++ b/clang/test/CodeGen/fake-use-noreturn.cpp @@ -0,0 +1,28 @@ +// RUN: %clang_cc1 %s -emit-llvm -fextend-variable-liveness -o - | FileCheck %s +// +// Check we can correctly produce fake uses for function-level variables even +// when we have a return in a nested conditional and there is no code at the end +// of the function. + +// CHECK-LABEL: define{{.*}}@_Z3fooi +// CHECK: [[I_FAKE_USE:%[a-zA-Z0-9\.]+]] = load i32, ptr %i.addr +// CHECK: call void (...) @llvm.fake.use(i32 [[I_FAKE_USE]]) +// CHECK-LABEL: define{{.*}}@_ZN1C3barEi +// CHECK: [[J_FAKE_USE:%[a-zA-Z0-9\.]+]] = load i32, ptr %j.addr +// CHECK: call void (...) @llvm.fake.use(i32 [[J_FAKE_USE]]) + +void foo(int i) { + while (0) + if (1) + return; +} + +class C { + void bar(int j); +}; + +void C::bar(int j) { + 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 0000000..50d5885 --- /dev/null +++ b/clang/test/CodeGen/fake-use-return-line.c @@ -0,0 +1,16 @@ +// RUN: %clang_cc1 -emit-llvm -debug-info-kind=limited -fextend-variable-liveness -o - %s | FileCheck %s + +// Clang adjusts the line numbers of returns based on the line numbers of +// dominating stores to %retval; we test that fake use intrinsics do not affect +// this, and the return is given the correct line. + +// CHECK: define{{.*}}@main +// CHECK: call void (...) @llvm.fake.use(i32 +// CHECK-NEXT: ret i32{{.*}}!dbg ![[MDINDEX:[0-9]*]] +// CHECK: ![[MDINDEX]] = !DILocation(line: [[# @LINE + 5]] +int main() +{ + volatile int a = 1; + int b = a + 2; + return b; +} diff --git a/clang/test/CodeGen/fake-use-sanitizer.cpp b/clang/test/CodeGen/fake-use-sanitizer.cpp new file mode 100644 index 0000000..4d33dcf --- /dev/null +++ b/clang/test/CodeGen/fake-use-sanitizer.cpp @@ -0,0 +1,61 @@ +// RUN: %clang_cc1 %s -emit-llvm -fextend-variable-liveness -fsanitize=null -fsanitize-trap=null -o - | FileCheck --check-prefixes=CHECK,NULL --implicit-check-not=ubsantrap %s +// RUN: %clang_cc1 %s -emit-llvm -fextend-variable-liveness -o - | FileCheck %s + +// With -fextend-lifetimes, the compiler previously generated a fake.use of any +// reference variable at the end of the scope in which its alloca exists. This +// caused two issues, where we would get fake uses for uninitialized variables +// if that variable was declared after an early-return, and UBSan's null checks +// would complain about this. +// This test verifies that UBSan does not produce null-checks for arguments to +// llvm.fake.use, and that fake uses are not emitted for a variable on paths +// it has not been declared. + +struct A { short s1, s2; }; +extern long& getA(); + +void foo() +{ + auto& va = getA(); + if (va < 5) + return; + + auto& vb = getA(); +} + +// CHECK-LABEL: define{{.*}}foo +// CHECK: [[VA_CALL:%.+]] = call{{.*}} ptr @_Z4getAv() + +/// We check here for the first UBSan check for "va". +// NULL: [[VA_ISNULL:%.+]] = icmp ne ptr [[VA_CALL]], null +// NULL: br i1 [[VA_ISNULL]], label %{{[^,]+}}, label %[[VA_TRAP:[^,]+]] +// NULL: [[VA_TRAP]]: +// NULL: call void @llvm.ubsantrap( + +// CHECK: [[VA_PTR:%.+]] = load ptr, ptr %va +// CHECK-NEXT: [[VA_CMP:%.+]] = load i64, ptr [[VA_PTR]] +// CHECK-NEXT: [[VA_CMP_RES:%.+]] = icmp slt i64 [[VA_CMP]], 5 +// CHECK-NEXT: br i1 [[VA_CMP_RES]], label %[[EARLY_EXIT:[^,]+]], label %[[NOT_EARLY_EXIT:[^,]+]] + +// CHECK: [[EARLY_EXIT]]: +// CHECK: br label %cleanup + +/// The fake use for "vb" only appears on the path where its declaration is +/// reached. +// CHECK: [[NOT_EARLY_EXIT]]: +// CHECK: [[VB_CALL:%.+]] = call{{.*}} ptr @_Z4getAv() + +/// We check here for the second UBSan check for "vb". +// NULL: [[VB_ISNULL:%.+]] = icmp ne ptr [[VB_CALL]], null +// NULL: br i1 [[VB_ISNULL]], label %{{[^,]+}}, label %[[VB_TRAP:[^,]+]] +// NULL: [[VB_TRAP]]: +// NULL: call void @llvm.ubsantrap( + +// CHECK: [[VB_FAKE_USE:%.+]] = load ptr, ptr %vb +// CHECK-NEXT: call void (...) @llvm.fake.use(ptr [[VB_FAKE_USE]]) +// CHECK: br label %cleanup + +// CHECK: cleanup: +// CHECK: [[VA_FAKE_USE:%.+]] = load ptr, ptr %va +// CHECK-NEXT: call void (...) @llvm.fake.use(ptr [[VA_FAKE_USE]]) + +// NULL: declare void @llvm.ubsantrap diff --git a/clang/test/CodeGen/fake-use-scalar.c b/clang/test/CodeGen/fake-use-scalar.c new file mode 100644 index 0000000..8514d57 --- /dev/null +++ b/clang/test/CodeGen/fake-use-scalar.c @@ -0,0 +1,41 @@ +// RUN: %clang_cc1 %s -emit-llvm -fextend-variable-liveness -o - | FileCheck %s --implicit-check-not=fake.use +// Make sure we don't generate fake.use for non-scalar variables, unless they +// are small enough that they may be represented as a scalar in LLVM IR. +// Make sure we don't generate fake.use for volatile variables +// and parameters even when they are scalar. + +struct BigAggr { + unsigned long t; + char c[1024]; + unsigned char r[32]; +}; + +struct SmallAggr { + int i; + int j; +}; + +int foo(volatile int vol_param, int param) +{ + struct BigAggr big; + struct SmallAggr small; + volatile int vol_local; + int local; + unsigned long_arr[5]; + unsigned short_arr[4]; + return 0; +} + +// CHECK: [[SMALL_ARR_FAKE_USE:%.+]] = load [4 x i[[#UINT_SIZE:]]], ptr %short_arr +// CHECK: call void (...) @llvm.fake.use([4 x i[[#UINT_SIZE]]] [[SMALL_ARR_FAKE_USE]]) + +// CHECK: [[LOCAL_FAKE_USE:%.+]] = load i32, ptr %local +// CHECK: call void (...) @llvm.fake.use(i32 [[LOCAL_FAKE_USE]]) + +// CHECK: [[SMALL_FAKE_USE:%.+]] = load %struct.SmallAggr, ptr %small +// CHECK: call void (...) @llvm.fake.use(%struct.SmallAggr [[SMALL_FAKE_USE]]) + +// CHECK: [[PARAM_FAKE_USE:%.+]] = load i32, ptr %param.addr +// CHECK: call void (...) @llvm.fake.use(i32 [[PARAM_FAKE_USE]]) + +// CHECK: declare void @llvm.fake.use diff --git a/clang/test/CodeGen/fake-use-this.cpp b/clang/test/CodeGen/fake-use-this.cpp new file mode 100644 index 0000000..2c49694 --- /dev/null +++ b/clang/test/CodeGen/fake-use-this.cpp @@ -0,0 +1,34 @@ +// RUN: %clang_cc1 %s -emit-llvm -fextend-this-ptr-liveness -o - | FileCheck %s --implicit-check-not=fake.use +// Check that we generate a fake_use call with the 'this' pointer as argument, +// and no other fake uses. +// The call should appear after the call to bar(). + +void bar(); + +class C +{ +public: + bool test(int p); + C(int v): v(v) {} + +private: + int v; +}; + +bool C::test(int p) +{ +// CHECK-LABEL: define{{.*}}_ZN1C4testEi(ptr{{[^,]*}} %this, i32{{.*}} %p) +// CHECK: %this.addr = alloca ptr +// CHECK: store ptr %this, ptr %this.addr + int res = p - v; + + bar(); +// CHECK: call{{.*}}bar + + return res != 0; +// CHECK: [[FAKE_USE:%.+]] = load ptr, ptr %this.addr +// CHECK-NEXT: call void (...) @llvm.fake.use(ptr{{.*}} [[FAKE_USE]]) +// CHECK-NEXT: ret +} + +// CHECK: declare void @llvm.fake.use diff --git a/clang/test/CodeGen/fake-use-while.c b/clang/test/CodeGen/fake-use-while.c new file mode 100644 index 0000000..a74887d --- /dev/null +++ b/clang/test/CodeGen/fake-use-while.c @@ -0,0 +1,18 @@ +// RUN: %clang_cc1 %s -emit-llvm -fextend-variable-liveness -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; + } + } +} |