-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[Clang] Add fake use emission to Clang with -fextend-lifetimes #110102
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
fdfa695
cde7206
124ada4
e8a38f0
804d966
17ff158
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1352,6 +1352,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 | ||
|
@@ -1411,6 +1419,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 | ||
|
@@ -1663,6 +1704,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()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see the nearby EHStack cleanup code uses getOriginalAllocatedAddress -- should we be using that instead? Related comments talk about there being a difference the address and the object; dunno what to make of that, but it's worth checking. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll look into it - I'm not familiar enough to know, and a quick test indicates it's nothing obviously wrong, but I'll try to verify if it should change or not. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks like the difference is about allocas in a non-default address space - essentially we'll get either:
In practice, both of them result in extended variable lifetimes (the fake use will continue to track |
||
} | ||
|
||
return emission; | ||
} | ||
|
||
|
@@ -2524,6 +2577,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 | ||
|
@@ -2717,6 +2778,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 && | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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(); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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(...) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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(); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could be nitpicking now; but we don't test that it's actually a load, right? So technically a legitimate instruction that's followed by a fake use might be ignored? (Perhaps this is a situation that never occurs).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's looking at the operand, not the preceding instruction.
CodeGenFunction::EmitFakeUse
generates the operand as a load, and that's the only place fake uses get created IIUC. You could have an assert here that the operand is a load.Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All fake uses at this stage have a load (which should be the immediately preceding instruction) as their operand, since fake uses are implemented as cleanups for stack variables. A check that it's a load could be added here, but if anything I'd say an assert would be preferred, since if the above fact ever changes this will need to be revisited.
Edit: Beaten to the punch! But it seems there's agreement that an assertion makes sense.