Skip to content

Commit 5cb0078

Browse files
authored
Add option to generate additional debug info for expression dereferencing pointer to pointers. (#94100)
This is another attempt to land #81545, which was reverted. Fixed test case by adding a target triple so that clang generates the same IR for all platforms
1 parent 930c2d9 commit 5cb0078

File tree

4 files changed

+230
-1
lines changed

4 files changed

+230
-1
lines changed

clang/lib/CodeGen/CGDebugInfo.cpp

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5737,6 +5737,90 @@ void CGDebugInfo::EmitExternalVariable(llvm::GlobalVariable *Var,
57375737
Var->addDebugInfo(GVE);
57385738
}
57395739

5740+
void CGDebugInfo::EmitPseudoVariable(CGBuilderTy &Builder,
5741+
llvm::Instruction *Value, QualType Ty) {
5742+
// Only when -g2 or above is specified, debug info for variables will be
5743+
// generated.
5744+
if (CGM.getCodeGenOpts().getDebugInfo() <=
5745+
llvm::codegenoptions::DebugLineTablesOnly)
5746+
return;
5747+
5748+
llvm::DebugLoc SaveDebugLoc = Builder.getCurrentDebugLocation();
5749+
if (!SaveDebugLoc.get())
5750+
return;
5751+
5752+
llvm::DIFile *Unit = SaveDebugLoc->getFile();
5753+
llvm::DIType *Type = getOrCreateType(Ty, Unit);
5754+
5755+
// Check if Value is already a declared variable and has debug info, in this
5756+
// case we have nothing to do. Clang emits declared variable as alloca, and
5757+
// it is loaded upon use, so we identify such pattern here.
5758+
if (llvm::LoadInst *Load = dyn_cast<llvm::LoadInst>(Value)) {
5759+
llvm::Value *Var = Load->getPointerOperand();
5760+
if (llvm::Metadata *MDValue = llvm::ValueAsMetadata::getIfExists(Var)) {
5761+
if (llvm::Value *DbgValue = llvm::MetadataAsValue::getIfExists(
5762+
CGM.getLLVMContext(), MDValue)) {
5763+
for (llvm::User *U : DbgValue->users()) {
5764+
if (llvm::CallInst *DbgDeclare = dyn_cast<llvm::CallInst>(U)) {
5765+
if (DbgDeclare->getCalledFunction()->getIntrinsicID() ==
5766+
llvm::Intrinsic::dbg_declare &&
5767+
DbgDeclare->getArgOperand(0) == DbgValue) {
5768+
// There can be implicit type cast applied on a variable if it is
5769+
// an opaque ptr, in this case its debug info may not match the
5770+
// actual type of object being used as in the next instruction, so
5771+
// we will need to emit a pseudo variable for type-casted value.
5772+
llvm::DILocalVariable *MDNode = cast<llvm::DILocalVariable>(
5773+
cast<llvm::MetadataAsValue>(DbgDeclare->getOperand(1))
5774+
->getMetadata());
5775+
if (MDNode->getType() == Type)
5776+
return;
5777+
}
5778+
}
5779+
}
5780+
}
5781+
}
5782+
}
5783+
5784+
// Find the correct location to insert a sequence of instructions to
5785+
// materialize Value on the stack.
5786+
auto SaveInsertionPoint = Builder.saveIP();
5787+
if (llvm::InvokeInst *Invoke = dyn_cast<llvm::InvokeInst>(Value))
5788+
Builder.SetInsertPoint(Invoke->getNormalDest()->begin());
5789+
else if (llvm::Instruction *Next = Value->getIterator()->getNextNode())
5790+
Builder.SetInsertPoint(Next);
5791+
else
5792+
Builder.SetInsertPoint(Value->getParent());
5793+
llvm::DebugLoc DL = Value->getDebugLoc();
5794+
if (DL.get())
5795+
Builder.SetCurrentDebugLocation(DL);
5796+
else if (!Builder.getCurrentDebugLocation().get())
5797+
Builder.SetCurrentDebugLocation(SaveDebugLoc);
5798+
5799+
llvm::AllocaInst *PseudoVar = Builder.CreateAlloca(Value->getType());
5800+
Address PseudoVarAddr(PseudoVar, Value->getType(),
5801+
CharUnits::fromQuantity(PseudoVar->getAlign()));
5802+
llvm::LoadInst *Load = Builder.CreateLoad(PseudoVarAddr);
5803+
Value->replaceAllUsesWith(Load);
5804+
Builder.SetInsertPoint(Load);
5805+
Builder.CreateStore(Value, PseudoVarAddr);
5806+
5807+
// Emit debug info for materialized Value.
5808+
unsigned Line = Builder.getCurrentDebugLocation().getLine();
5809+
unsigned Column = Builder.getCurrentDebugLocation().getCol();
5810+
llvm::DILocalVariable *D = DBuilder.createAutoVariable(
5811+
LexicalBlockStack.back(), "", nullptr, 0, Type, false,
5812+
llvm::DINode::FlagArtificial);
5813+
llvm::DILocation *DIL =
5814+
llvm::DILocation::get(CGM.getLLVMContext(), Line, Column,
5815+
LexicalBlockStack.back(), CurInlinedAt);
5816+
SmallVector<uint64_t> Expr;
5817+
DBuilder.insertDeclare(PseudoVar, D, DBuilder.createExpression(Expr), DIL,
5818+
Load);
5819+
5820+
Builder.restoreIP(SaveInsertionPoint);
5821+
Builder.SetCurrentDebugLocation(SaveDebugLoc);
5822+
}
5823+
57405824
void CGDebugInfo::EmitGlobalAlias(const llvm::GlobalValue *GV,
57415825
const GlobalDecl GD) {
57425826

clang/lib/CodeGen/CGDebugInfo.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,12 @@ class CGDebugInfo {
530530
/// Emit information about an external variable.
531531
void EmitExternalVariable(llvm::GlobalVariable *GV, const VarDecl *Decl);
532532

533+
/// Emit a pseudo variable and debug info for an intermediate value if it does
534+
/// not correspond to a variable in the source code, so that a profiler can
535+
/// track more accurate usage of certain instructions of interest.
536+
void EmitPseudoVariable(CGBuilderTy &Builder, llvm::Instruction *Value,
537+
QualType Ty);
538+
533539
/// Emit information about global variable alias.
534540
void EmitGlobalAlias(const llvm::GlobalValue *GV, const GlobalDecl Decl);
535541

clang/lib/CodeGen/CGExprScalar.cpp

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1937,7 +1937,26 @@ Value *ScalarExprEmitter::VisitMemberExpr(MemberExpr *E) {
19371937
}
19381938
}
19391939

1940-
return EmitLoadOfLValue(E);
1940+
llvm::Value *Result = EmitLoadOfLValue(E);
1941+
1942+
// If -fdebug-info-for-profiling is specified, emit a pseudo variable and its
1943+
// debug info for the pointer, even if there is no variable associated with
1944+
// the pointer's expression.
1945+
if (CGF.CGM.getCodeGenOpts().DebugInfoForProfiling && CGF.getDebugInfo()) {
1946+
if (llvm::LoadInst *Load = dyn_cast<llvm::LoadInst>(Result)) {
1947+
if (llvm::GetElementPtrInst *GEP =
1948+
dyn_cast<llvm::GetElementPtrInst>(Load->getPointerOperand())) {
1949+
if (llvm::Instruction *Pointer =
1950+
dyn_cast<llvm::Instruction>(GEP->getPointerOperand())) {
1951+
QualType Ty = E->getBase()->getType();
1952+
if (!E->isArrow())
1953+
Ty = CGF.getContext().getPointerType(Ty);
1954+
CGF.getDebugInfo()->EmitPseudoVariable(Builder, Pointer, Ty);
1955+
}
1956+
}
1957+
}
1958+
}
1959+
return Result;
19411960
}
19421961

19431962
Value *ScalarExprEmitter::VisitArraySubscriptExpr(ArraySubscriptExpr *E) {
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
// Test debug info for intermediate value of a chained pointer deferencing
2+
// expression when the flag -fdebug-info-for-pointer-type is enabled.
3+
// RUN: %clang_cc1 -emit-llvm -triple x86_64-linux-gnu %s -fdebug-info-for-profiling -debug-info-kind=constructor -o - | FileCheck %s
4+
5+
class A {
6+
public:
7+
int i;
8+
char c;
9+
void *p;
10+
int arr[3];
11+
};
12+
13+
class B {
14+
public:
15+
A* a;
16+
};
17+
18+
class C {
19+
public:
20+
B* b;
21+
A* a;
22+
A arr[10];
23+
};
24+
25+
// CHECK-LABEL: define dso_local noundef i32 @{{.*}}func1{{.*}}(
26+
// CHECK: [[A_ADDR:%.*]] = getelementptr inbounds %class.B, ptr {{%.*}}, i32 0, i32 0, !dbg [[DBG1:![0-9]+]]
27+
// CHECK-NEXT: [[A:%.*]] = load ptr, ptr [[A_ADDR]], align {{.*}}, !dbg [[DBG1]]
28+
// CHECK-NEXT: [[PSEUDO1:%.*]] = alloca ptr, align {{.*}}, !dbg [[DBG1]]
29+
// CHECK-NEXT: store ptr [[A]], ptr [[PSEUDO1]], align {{.*}}, !dbg [[DBG1]]
30+
// CHECK-NEXT: call void @llvm.dbg.declare(metadata ptr [[PSEUDO1]], metadata [[META1:![0-9]+]], metadata !DIExpression()), !dbg [[DBG1]]
31+
// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[PSEUDO1]], align {{.*}}, !dbg [[DBG1]]
32+
// CHECK-NEXT: {{%.*}} = getelementptr inbounds %class.A, ptr [[TMP1]], i32 0, i32 0,
33+
int func1(B *b) {
34+
return b->a->i;
35+
}
36+
37+
// Should generate a pseudo variable when pointer is type-casted.
38+
// CHECK-LABEL: define dso_local noundef ptr @{{.*}}func2{{.*}}(
39+
// CHECK: call void @llvm.dbg.declare(metadata ptr [[B_ADDR:%.*]], metadata [[META2:![0-9]+]], metadata !DIExpression())
40+
// CHECK-NEXT: [[B:%.*]] = load ptr, ptr [[B_ADDR]],
41+
// CHECK-NEXT: [[PSEUDO1:%.*]] = alloca ptr,
42+
// CHECK-NEXT: store ptr [[B]], ptr [[PSEUDO1]],
43+
// CHECK-NEXT: call void @llvm.dbg.declare(metadata ptr [[PSEUDO1]], metadata [[META3:![0-9]+]], metadata !DIExpression())
44+
// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[PSEUDO1]],
45+
// CHECK-NEXT: {{%.*}} = getelementptr inbounds %class.B, ptr [[TMP1]], i32 0,
46+
A* func2(void *b) {
47+
return ((B*)b)->a;
48+
}
49+
50+
// Should not generate pseudo variable in this case.
51+
// CHECK-LABEL: define dso_local noundef i32 @{{.*}}func3{{.*}}(
52+
// CHECK: call void @llvm.dbg.declare(metadata ptr [[B_ADDR:%.*]], metadata [[META4:![0-9]+]], metadata !DIExpression())
53+
// CHECK: call void @llvm.dbg.declare(metadata ptr [[LOCAL1:%.*]], metadata [[META5:![0-9]+]], metadata !DIExpression())
54+
// CHECK-NOT: call void @llvm.dbg.declare(metadata ptr
55+
int func3(B *b) {
56+
A *local1 = b->a;
57+
return local1->i;
58+
}
59+
60+
// CHECK-LABEL: define dso_local noundef signext i8 @{{.*}}func4{{.*}}(
61+
// CHECK: [[A_ADDR:%.*]] = getelementptr inbounds %class.C, ptr {{%.*}}, i32 0, i32 1
62+
// CHECK-NEXT: [[A:%.*]] = load ptr, ptr [[A_ADDR]],
63+
// CHECK-NEXT: [[PSEUDO1:%.*]] = alloca ptr,
64+
// CHECK-NEXT: store ptr [[A]], ptr [[PSEUDO1]],
65+
// CHECK-NEXT: call void @llvm.dbg.declare(metadata ptr [[PSEUDO1]], metadata [[META6:![0-9]+]], metadata !DIExpression())
66+
// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[PSEUDO1]],
67+
// CHECK-NEXT: {{%.*}} = getelementptr inbounds %class.A, ptr [[TMP1]], i32 0, i32 0,
68+
// CHECK: [[CALL:%.*]] = call noundef ptr @{{.*}}foo{{.*}}(
69+
// CHECK-NEXT: [[PSEUDO2:%.*]] = alloca ptr,
70+
// CHECK-NEXT: store ptr [[CALL]], ptr [[PSEUDO2]]
71+
// CHECK-NEXT: call void @llvm.dbg.declare(metadata ptr [[PSEUDO2]], metadata [[META6]], metadata !DIExpression())
72+
// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[PSEUDO2]]
73+
// CHECK-NEXT: [[I1:%.*]] = getelementptr inbounds %class.A, ptr [[TMP2]], i32 0, i32 1
74+
char func4(C *c) {
75+
extern A* foo(int x);
76+
return foo(c->a->i)->c;
77+
}
78+
79+
// CHECK-LABEL: define dso_local noundef signext i8 @{{.*}}func5{{.*}}(
80+
// CHECK: call void @llvm.dbg.declare(metadata ptr {{%.*}}, metadata [[META7:![0-9]+]], metadata !DIExpression())
81+
// CHECK: call void @llvm.dbg.declare(metadata ptr {{%.*}}, metadata [[META8:![0-9]+]], metadata !DIExpression())
82+
// CHECK: [[A_ADDR:%.*]] = getelementptr inbounds %class.A, ptr {{%.*}}, i64 {{%.*}},
83+
// CHECK-NEXT: [[PSEUDO1:%.*]] = alloca ptr,
84+
// CHECK-NEXT: store ptr [[A_ADDR]], ptr [[PSEUDO1]],
85+
// CHECK-NEXT: call void @llvm.dbg.declare(metadata ptr [[PSEUDO1]], metadata [[META9:![0-9]+]], metadata !DIExpression())
86+
// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[PSEUDO1]],
87+
// CHECK-NEXT: {{%.*}} = getelementptr inbounds %class.A, ptr [[TMP1]], i32 0, i32 1,
88+
char func5(void *arr, int n) {
89+
return ((A*)arr)[n].c;
90+
}
91+
92+
// CHECK-LABEL: define dso_local noundef i32 @{{.*}}func6{{.*}}(
93+
// CHECK: call void @llvm.dbg.declare(metadata ptr {{%.*}}, metadata [[META10:![0-9]+]], metadata !DIExpression())
94+
// CHECK: call void @llvm.dbg.declare(metadata ptr {{%.*}}, metadata [[META11:![0-9]+]], metadata !DIExpression())
95+
int func6(B &b) {
96+
return reinterpret_cast<A&>(b).i;
97+
}
98+
99+
// CHECK-DAG: [[META_A:![0-9]+]] = distinct !DICompositeType(tag: DW_TAG_class_type, name: "A",
100+
// CHECK-DAG: [[META_AP:![0-9]+]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: [[META_A]],
101+
// CHECK-DAG: [[META_B:![0-9]+]] = distinct !DICompositeType(tag: DW_TAG_class_type, name: "B",
102+
// CHECK-DAG: [[META_BP:![0-9]+]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: [[META_B]],
103+
// CHECK-DAG: [[META_C:![0-9]+]] = distinct !DICompositeType(tag: DW_TAG_class_type, name: "C",
104+
// CHECK-DAG: [[META_CP:![0-9]+]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: [[META_C]],
105+
// CHECK-DAG: [[META_VP:![0-9]+]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null,
106+
// CHECK-DAG: [[META_I32:![0-9]+]] = !DIBasicType(name: "int", size: 32,
107+
// CHECK-DAG: [[META_BR:![0-9]+]] = !DIDerivedType(tag: DW_TAG_reference_type, baseType: [[META_B]],
108+
109+
// CHECK-DAG: [[DBG1]] = !DILocation(line: 34, column: 13,
110+
// CHECK-DAG: [[META1]] = !DILocalVariable(scope: {{.*}}, type: [[META_AP]], flags: DIFlagArtificial)
111+
// CHECK-DAG: [[META2]] = !DILocalVariable(name: "b", arg: 1, scope: {{.*}}, file: {{.*}}, line: 46, type: [[META_VP]])
112+
// CHECK-DAG: [[META3]] = !DILocalVariable(scope: {{.*}}, type: [[META_BP]], flags: DIFlagArtificial)
113+
// CHECK-DAG: [[META4]] = !DILocalVariable(name: "b", arg: 1, scope: {{.*}}, file: {{.*}}, line: 55, type: [[META_BP]])
114+
// CHECK-DAG: [[META5]] = !DILocalVariable(name: "local1", scope: {{.*}}, file: {{.*}}, line: 56, type: [[META_AP]])
115+
// CHECK-DAG: [[META6]] = !DILocalVariable(scope: {{.*}}, type: [[META_AP]], flags: DIFlagArtificial)
116+
// CHECK-DAG: [[META7]] = !DILocalVariable(name: "arr", arg: 1, scope: {{.*}}, file: {{.*}}, line: 88, type: [[META_VP]])
117+
// CHECK-DAG: [[META8]] = !DILocalVariable(name: "n", arg: 2, scope: {{.*}}, file: {{.*}}, line: 88, type: [[META_I32]])
118+
// CHECK-DAG: [[META9]] = !DILocalVariable(scope: {{.*}}, type: [[META_AP]], flags: DIFlagArtificial)
119+
// CHECK-DAG: [[META10]] = !DILocalVariable(name: "b", arg: 1, scope: {{.*}}, file: {{.*}}, line: 95, type: [[META_BR]])
120+
// CHECK-DAG: [[META11]] = !DILocalVariable(scope: {{.*}}, type: [[META_AP]], flags: DIFlagArtificial)

0 commit comments

Comments
 (0)