Skip to content

Commit

Permalink
(New) Add option to generate additional debug info for expression der…
Browse files Browse the repository at this point in the history
…eferencing pointer to pointers (llvm#95298)

This is a different implementation to llvm#94100, which has been reverted.

When -fdebug-info-for-profiling is specified, for any Load expression if
the pointer operand is not a declared variable, clang will emit debug
info describing the type of the pointer operand (which can be an
intermediate expr)
  • Loading branch information
huangjd authored Jun 15, 2024
1 parent cddb9ce commit 675d8d6
Show file tree
Hide file tree
Showing 4 changed files with 183 additions and 1 deletion.
42 changes: 42 additions & 0 deletions clang/lib/CodeGen/CGDebugInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5746,6 +5746,48 @@ void CGDebugInfo::EmitExternalVariable(llvm::GlobalVariable *Var,
Var->addDebugInfo(GVE);
}

void CGDebugInfo::EmitPseudoVariable(CGBuilderTy &Builder,
llvm::Instruction *Value, QualType Ty) {
// Only when -g2 or above is specified, debug info for variables will be
// generated.
if (CGM.getCodeGenOpts().getDebugInfo() <=
llvm::codegenoptions::DebugLineTablesOnly)
return;

llvm::DILocation *DIL = Value->getDebugLoc().get();
if (!DIL)
return;

llvm::DIFile *Unit = DIL->getFile();
llvm::DIType *Type = getOrCreateType(Ty, Unit);

// Check if Value is already a declared variable and has debug info, in this
// case we have nothing to do. Clang emits a declared variable as alloca, and
// it is loaded upon use, so we identify such pattern here.
if (llvm::LoadInst *Load = dyn_cast<llvm::LoadInst>(Value)) {
llvm::Value *Var = Load->getPointerOperand();
// There can be implicit type cast applied on a variable if it is an opaque
// ptr, in this case its debug info may not match the actual type of object
// being used as in the next instruction, so we will need to emit a pseudo
// variable for type-casted value.
auto DeclareTypeMatches = [&](auto *DbgDeclare) {
return DbgDeclare->getVariable()->getType() == Type;
};
if (any_of(llvm::findDbgDeclares(Var), DeclareTypeMatches) ||
any_of(llvm::findDVRDeclares(Var), DeclareTypeMatches))
return;
}

llvm::DILocalVariable *D =
DBuilder.createAutoVariable(LexicalBlockStack.back(), "", nullptr, 0,
Type, false, llvm::DINode::FlagArtificial);

if (auto InsertPoint = Value->getInsertionPointAfterDef()) {
DBuilder.insertDbgValueIntrinsic(Value, D, DBuilder.createExpression(), DIL,
&**InsertPoint);
}
}

void CGDebugInfo::EmitGlobalAlias(const llvm::GlobalValue *GV,
const GlobalDecl GD) {

Expand Down
6 changes: 6 additions & 0 deletions clang/lib/CodeGen/CGDebugInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,12 @@ class CGDebugInfo {
/// Emit information about an external variable.
void EmitExternalVariable(llvm::GlobalVariable *GV, const VarDecl *Decl);

/// Emit a pseudo variable and debug info for an intermediate value if it does
/// not correspond to a variable in the source code, so that a profiler can
/// track more accurate usage of certain instructions of interest.
void EmitPseudoVariable(CGBuilderTy &Builder, llvm::Instruction *Value,
QualType Ty);

/// Emit information about global variable alias.
void EmitGlobalAlias(const llvm::GlobalValue *GV, const GlobalDecl Decl);

Expand Down
21 changes: 20 additions & 1 deletion clang/lib/CodeGen/CGExprScalar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1937,7 +1937,26 @@ Value *ScalarExprEmitter::VisitMemberExpr(MemberExpr *E) {
}
}

return EmitLoadOfLValue(E);
llvm::Value *Result = EmitLoadOfLValue(E);

// If -fdebug-info-for-profiling is specified, emit a pseudo variable and its
// debug info for the pointer, even if there is no variable associated with
// the pointer's expression.
if (CGF.CGM.getCodeGenOpts().DebugInfoForProfiling && CGF.getDebugInfo()) {
if (llvm::LoadInst *Load = dyn_cast<llvm::LoadInst>(Result)) {
if (llvm::GetElementPtrInst *GEP =
dyn_cast<llvm::GetElementPtrInst>(Load->getPointerOperand())) {
if (llvm::Instruction *Pointer =
dyn_cast<llvm::Instruction>(GEP->getPointerOperand())) {
QualType Ty = E->getBase()->getType();
if (!E->isArrow())
Ty = CGF.getContext().getPointerType(Ty);
CGF.getDebugInfo()->EmitPseudoVariable(Builder, Pointer, Ty);
}
}
}
}
return Result;
}

Value *ScalarExprEmitter::VisitArraySubscriptExpr(ArraySubscriptExpr *E) {
Expand Down
115 changes: 115 additions & 0 deletions clang/test/CodeGenCXX/debug-info-ptr-to-ptr.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// Test debug info for intermediate value of a chained pointer deferencing
// expression when the flag -fdebug-info-for-pointer-type is enabled.
// RUN: %clang_cc1 -emit-llvm -triple x86_64-linux-gnu %s -fdebug-info-for-profiling -debug-info-kind=constructor -o - | FileCheck %s

class A {
public:
int i;
char c;
void *p;
int arr[3];
};

class B {
public:
A* a;
};

class C {
public:
B* b;
A* a;
A arr[10];
};

// CHECK-LABEL: define dso_local noundef i32 @{{.*}}func1{{.*}}(
// CHECK: [[A_ADDR:%.*]] = getelementptr inbounds %class.B, ptr {{%.*}}, i32 0, i32 0, !dbg [[DBG1:![0-9]+]]
// CHECK-NEXT: [[A:%.*]] = load ptr, ptr [[A_ADDR]], align {{.*}}, !dbg [[DBG1]]
// CHECK-NEXT: call void @llvm.dbg.value(metadata ptr [[A]], metadata [[META1:![0-9]+]], metadata !DIExpression()), !dbg [[DBG1]]
// CHECK-NEXT: {{%.*}} = getelementptr inbounds %class.A, ptr [[A]], i32 0, i32 0,
int func1(B *b) {
return b->a->i;
}

// Should generate a pseudo variable when pointer is type-casted.
// CHECK-LABEL: define dso_local noundef ptr @{{.*}}func2{{.*}}(
// CHECK: call void @llvm.dbg.declare(metadata ptr [[B_ADDR:%.*]], metadata [[META2:![0-9]+]], metadata !DIExpression())
// CHECK-NEXT: [[B:%.*]] = load ptr, ptr [[B_ADDR]],
// CHECK-NEXT: call void @llvm.dbg.value(metadata ptr [[B]], metadata [[META3:![0-9]+]], metadata !DIExpression())
// CHECK-NEXT: {{%.*}} = getelementptr inbounds %class.B, ptr [[B]], i32 0,
A* func2(void *b) {
return ((B*)b)->a;
}

// Should not generate pseudo variable in this case.
// CHECK-LABEL: define dso_local noundef i32 @{{.*}}func3{{.*}}(
// CHECK: call void @llvm.dbg.declare(metadata ptr [[B_ADDR:%.*]], metadata [[META4:![0-9]+]], metadata !DIExpression())
// CHECK: call void @llvm.dbg.declare(metadata ptr [[LOCAL1:%.*]], metadata [[META5:![0-9]+]], metadata !DIExpression())
// CHECK-NOT: call void @llvm.dbg.value(metadata ptr
int func3(B *b) {
A *local1 = b->a;
return local1->i;
}

// CHECK-LABEL: define dso_local noundef signext i8 @{{.*}}func4{{.*}}(
// CHECK: [[A_ADDR:%.*]] = getelementptr inbounds %class.C, ptr {{%.*}}, i32 0, i32 1
// CHECK-NEXT: [[A:%.*]] = load ptr, ptr [[A_ADDR]],
// CHECK-NEXT: call void @llvm.dbg.value(metadata ptr [[A]], metadata [[META6:![0-9]+]], metadata !DIExpression())
// CHECK-NEXT: {{%.*}} = getelementptr inbounds %class.A, ptr [[A]], i32 0, i32 0,
// CHECK: [[CALL:%.*]] = call noundef ptr @{{.*}}foo{{.*}}(
// CHECK-NEXT: call void @llvm.dbg.value(metadata ptr [[CALL]], metadata [[META6]], metadata !DIExpression())
// CHECK-NEXT: [[I1:%.*]] = getelementptr inbounds %class.A, ptr [[CALL]], i32 0, i32 1
char func4(C *c) {
extern A* foo(int x);
return foo(c->a->i)->c;
}

// CHECK-LABEL: define dso_local noundef signext i8 @{{.*}}func5{{.*}}(
// CHECK: call void @llvm.dbg.declare(metadata ptr {{%.*}}, metadata [[META7:![0-9]+]], metadata !DIExpression())
// CHECK: call void @llvm.dbg.declare(metadata ptr {{%.*}}, metadata [[META8:![0-9]+]], metadata !DIExpression())
// CHECK: [[A_ADDR:%.*]] = getelementptr inbounds %class.A, ptr {{%.*}}, i64 {{%.*}},
// CHECK-NEXT: call void @llvm.dbg.value(metadata ptr [[A_ADDR]], metadata [[META9:![0-9]+]], metadata !DIExpression())
// CHECK-NEXT: {{%.*}} = getelementptr inbounds %class.A, ptr [[A_ADDR]], i32 0, i32 1,
char func5(void *arr, int n) {
return ((A*)arr)[n].c;
}

// CHECK-LABEL: define dso_local noundef i32 @{{.*}}func6{{.*}}(
// CHECK: call void @llvm.dbg.declare(metadata ptr {{%.*}}, metadata [[META10:![0-9]+]], metadata !DIExpression())
// CHECK: call void @llvm.dbg.value(metadata ptr {{%.*}}, metadata [[META11:![0-9]+]], metadata !DIExpression())
int func6(B &b) {
return reinterpret_cast<A&>(b).i;
}

// CHECK-LABEL: define dso_local noundef i32 @{{.*}}global{{.*}}(
// CHECK: [[GA:%.*]] = load ptr, ptr @ga
// CHECK-NEXT: call void @llvm.dbg.value(metadata ptr [[GA]], metadata [[META12:![0-9]+]], metadata !DIExpression())
A *ga;
int global() {
return ga->i;
}


// CHECK-DAG: [[META_A:![0-9]+]] = distinct !DICompositeType(tag: DW_TAG_class_type, name: "A",
// CHECK-DAG: [[META_AP:![0-9]+]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: [[META_A]],
// CHECK-DAG: [[META_B:![0-9]+]] = distinct !DICompositeType(tag: DW_TAG_class_type, name: "B",
// CHECK-DAG: [[META_BP:![0-9]+]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: [[META_B]],
// CHECK-DAG: [[META_C:![0-9]+]] = distinct !DICompositeType(tag: DW_TAG_class_type, name: "C",
// CHECK-DAG: [[META_CP:![0-9]+]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: [[META_C]],
// CHECK-DAG: [[META_VP:![0-9]+]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null,
// CHECK-DAG: [[META_I32:![0-9]+]] = !DIBasicType(name: "int", size: 32,
// CHECK-DAG: [[META_BR:![0-9]+]] = !DIDerivedType(tag: DW_TAG_reference_type, baseType: [[META_B]],

// CHECK-DAG: [[DBG1]] = !DILocation(line: 31, column: 13,
// CHECK-DAG: [[META1]] = !DILocalVariable(scope: {{.*}}, type: [[META_AP]], flags: DIFlagArtificial)
// CHECK-DAG: [[META2]] = !DILocalVariable(name: "b", arg: 1, scope: {{.*}}, file: {{.*}}, line: 40, type: [[META_VP]])
// CHECK-DAG: [[META3]] = !DILocalVariable(scope: {{.*}}, type: [[META_BP]], flags: DIFlagArtificial)
// CHECK-DAG: [[META4]] = !DILocalVariable(name: "b", arg: 1, scope: {{.*}}, file: {{.*}}, line: 49, type: [[META_BP]])
// CHECK-DAG: [[META5]] = !DILocalVariable(name: "local1", scope: {{.*}}, file: {{.*}}, line: 50, type: [[META_AP]])
// CHECK-DAG: [[META6]] = !DILocalVariable(scope: {{.*}}, type: [[META_AP]], flags: DIFlagArtificial)
// CHECK-DAG: [[META7]] = !DILocalVariable(name: "arr", arg: 1, scope: {{.*}}, file: {{.*}}, line: 73, type: [[META_VP]])
// CHECK-DAG: [[META8]] = !DILocalVariable(name: "n", arg: 2, scope: {{.*}}, file: {{.*}}, line: 73, type: [[META_I32]])
// CHECK-DAG: [[META9]] = !DILocalVariable(scope: {{.*}}, type: [[META_AP]], flags: DIFlagArtificial)
// CHECK-DAG: [[META10]] = !DILocalVariable(name: "b", arg: 1, scope: {{.*}}, file: {{.*}}, line: 80, type: [[META_BR]])
// CHECK-DAG: [[META11]] = !DILocalVariable(scope: {{.*}}, type: [[META_AP]], flags: DIFlagArtificial)
// CHECK-DAG: [[META12]] = !DILocalVariable(scope: {{.*}}, type: [[META_AP]], flags: DIFlagArtificial)

0 comments on commit 675d8d6

Please sign in to comment.