[Lldb-commits] [lldb] [clang][DebugInfo] Emit global variable definitions for static data members with constant initializers (PR #70639)
via lldb-commits
lldb-commits at lists.llvm.org
Mon Oct 30 02:59:26 PDT 2023
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Michael Buch (Michael137)
<details>
<summary>Changes</summary>
When an LLDB user asks for the value of a static data member, LLDB starts by
searching the Names accelerator table for the corresponding variable definition
DIE. For static data members with out-of-class definitions that works fine,
because those get represented as global variables with a location and making them
eligible to be added to the Names table. However, in-class definitions won’t get
indexed because we usually don't emit global variables for them. So in DWARF
we end up with a single `DW_TAG_member` that usually holds the constant initializer.
But we don't get a corresponding CU-level `DW_TAG_variable` like we do for
out-of-class definitions.
To make it more convenient for debuggers to get to the value of inline static data members,
this patch makes sure we emit definitions for static variables with constant initializers
the same way we do for other static variables. This also aligns Clang closer to GCC, which
produces CU-level definitions for inline statics and also emits these into `.debug_pubnames`.
The implementation keeps track of newly created static data members. Then in
`CGDebugInfo::finalize`, we emit a global `DW_TAG_variable` with a `DW_AT_const_value` for
any of those declarations that didn't end up with a definition in the `DeclCache`.
The newly emitted `DW_TAG_variable` will look as follows:
```
0x0000007b: DW_TAG_structure_type
DW_AT_calling_convention (DW_CC_pass_by_value)
DW_AT_name ("Foo")
...
0x0000008d: DW_TAG_member
DW_AT_name ("i")
DW_AT_type (0x00000062 "const int")
DW_AT_external (true)
DW_AT_declaration (true)
DW_AT_const_value (4)
Newly added
vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
0x0000009a: DW_TAG_variable
DW_AT_specification (0x0000008d "i")
DW_AT_const_value (4)
DW_AT_linkage_name ("_ZN2t2IiE1iIfEE")
```
---
Full diff: https://github.com/llvm/llvm-project/pull/70639.diff
5 Files Affected:
- (modified) clang/lib/CodeGen/CGDebugInfo.cpp (+72-18)
- (modified) clang/lib/CodeGen/CGDebugInfo.h (+11)
- (modified) clang/test/CodeGenCXX/debug-info-class.cpp (+9-4)
- (added) clang/test/CodeGenCXX/debug-info-static-inline-member.cpp (+79)
- (modified) lldb/test/API/lang/cpp/const_static_integral_member/TestConstStaticIntegralMember.py (+4-3)
``````````diff
diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp
index 0aaf678bf287c6e..8b85fb03c05f6e7 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -1693,6 +1693,7 @@ CGDebugInfo::CreateRecordStaticField(const VarDecl *Var, llvm::DIType *RecordTy,
llvm::DIDerivedType *GV = DBuilder.createStaticMemberType(
RecordTy, VName, VUnit, LineNumber, VTy, Flags, C, Align);
StaticDataMemberCache[Var->getCanonicalDecl()].reset(GV);
+ StaticDataMemberDefinitionsToEmit.push_back(Var->getCanonicalDecl());
return GV;
}
@@ -5580,25 +5581,8 @@ void CGDebugInfo::EmitGlobalVariable(const ValueDecl *VD, const APValue &Init) {
auto &GV = DeclCache[VD];
if (GV)
return;
- llvm::DIExpression *InitExpr = nullptr;
- if (CGM.getContext().getTypeSize(VD->getType()) <= 64) {
- // FIXME: Add a representation for integer constants wider than 64 bits.
- if (Init.isInt()) {
- const llvm::APSInt &InitInt = Init.getInt();
- std::optional<uint64_t> InitIntOpt;
- if (InitInt.isUnsigned())
- InitIntOpt = InitInt.tryZExtValue();
- else if (auto tmp = InitInt.trySExtValue(); tmp.has_value())
- // Transform a signed optional to unsigned optional. When cpp 23 comes,
- // use std::optional::transform
- InitIntOpt = (uint64_t)tmp.value();
- if (InitIntOpt)
- InitExpr = DBuilder.createConstantValueExpression(InitIntOpt.value());
- } else if (Init.isFloat())
- InitExpr = DBuilder.createConstantValueExpression(
- Init.getFloat().bitcastToAPInt().getZExtValue());
- }
+ llvm::DIExpression *InitExpr = createConstantValueExpression(VD, Init);
llvm::MDTuple *TemplateParameters = nullptr;
if (isa<VarTemplateSpecializationDecl>(VD))
@@ -5613,6 +5597,39 @@ void CGDebugInfo::EmitGlobalVariable(const ValueDecl *VD, const APValue &Init) {
TemplateParameters, Align));
}
+void CGDebugInfo::EmitGlobalVariable(const VarDecl *VD) {
+ assert(VD->hasInit());
+ assert(CGM.getCodeGenOpts().hasReducedDebugInfo());
+ if (VD->hasAttr<NoDebugAttr>())
+ return;
+
+ auto &GV = DeclCache[VD];
+ if (GV)
+ return;
+
+ auto const *InitVal = VD->evaluateValue();
+ if (!InitVal)
+ return;
+
+ llvm::DIFile *Unit = nullptr;
+ llvm::DIScope *DContext = nullptr;
+ unsigned LineNo;
+ StringRef DeclName, LinkageName;
+ QualType T;
+ llvm::MDTuple *TemplateParameters = nullptr;
+ collectVarDeclProps(VD, Unit, LineNo, T, DeclName, LinkageName,
+ TemplateParameters, DContext);
+
+ auto Align = getDeclAlignIfRequired(VD, CGM.getContext());
+ llvm::DINodeArray Annotations = CollectBTFDeclTagAnnotations(VD);
+ llvm::DIExpression *InitExpr = createConstantValueExpression(VD, *InitVal);
+
+ GV.reset(DBuilder.createGlobalVariableExpression(
+ TheCU, DeclName, LinkageName, Unit, LineNo, getOrCreateType(T, Unit),
+ true, true, InitExpr, getOrCreateStaticDataMemberDeclarationOrNull(VD),
+ TemplateParameters, Align, Annotations));
+}
+
void CGDebugInfo::EmitExternalVariable(llvm::GlobalVariable *Var,
const VarDecl *D) {
assert(CGM.getCodeGenOpts().hasReducedDebugInfo());
@@ -5883,6 +5900,18 @@ void CGDebugInfo::finalize() {
DBuilder.replaceTemporary(std::move(FwdDecl), cast<llvm::MDNode>(Repl));
}
+ for (auto const *VD : StaticDataMemberDefinitionsToEmit) {
+ assert (VD->isStaticDataMember());
+
+ if (auto It = DeclCache.find(VD); It != DeclCache.end())
+ continue;
+
+ if (!VD->hasInit())
+ continue;
+
+ EmitGlobalVariable(VD);
+ }
+
// We keep our own list of retained types, because we need to look
// up the final type in the type cache.
for (auto &RT : RetainedTypes)
@@ -5935,3 +5964,28 @@ llvm::DINode::DIFlags CGDebugInfo::getCallSiteRelatedAttrs() const {
return llvm::DINode::FlagAllCallsDescribed;
}
+
+llvm::DIExpression *
+CGDebugInfo::createConstantValueExpression(clang::ValueDecl const *VD,
+ const APValue &Val) {
+ llvm::DIExpression *ValExpr = nullptr;
+ if (CGM.getContext().getTypeSize(VD->getType()) <= 64) {
+ // FIXME: Add a representation for integer constants wider than 64 bits.
+ if (Val.isInt()) {
+ const llvm::APSInt &ValInt = Val.getInt();
+ std::optional<uint64_t> ValIntOpt;
+ if (ValInt.isUnsigned())
+ ValIntOpt = ValInt.tryZExtValue();
+ else if (auto tmp = ValInt.trySExtValue(); tmp.has_value())
+ // Transform a signed optional to unsigned optional. When cpp 23 comes,
+ // use std::optional::transform
+ ValIntOpt = (uint64_t)tmp.value();
+ if (ValIntOpt)
+ ValExpr = DBuilder.createConstantValueExpression(ValIntOpt.value());
+ } else if (Val.isFloat())
+ ValExpr = DBuilder.createConstantValueExpression(
+ Val.getFloat().bitcastToAPInt().getZExtValue());
+ }
+
+ return ValExpr;
+}
diff --git a/clang/lib/CodeGen/CGDebugInfo.h b/clang/lib/CodeGen/CGDebugInfo.h
index ae12485850ca775..3e4c133b7f2b9f1 100644
--- a/clang/lib/CodeGen/CGDebugInfo.h
+++ b/clang/lib/CodeGen/CGDebugInfo.h
@@ -161,6 +161,9 @@ class CGDebugInfo {
llvm::DenseMap<const Decl *, llvm::TypedTrackingMDRef<llvm::DIDerivedType>>
StaticDataMemberCache;
+ /// Keeps track of static data members for which we should emit a definition.
+ std::vector<const VarDecl *> StaticDataMemberDefinitionsToEmit;
+
using ParamDecl2StmtTy = llvm::DenseMap<const ParmVarDecl *, const Stmt *>;
using Param2DILocTy =
llvm::DenseMap<const ParmVarDecl *, llvm::DILocalVariable *>;
@@ -526,6 +529,9 @@ class CGDebugInfo {
/// Emit a constant global variable's debug info.
void EmitGlobalVariable(const ValueDecl *VD, const APValue &Init);
+ /// Emit debug-info for a variable with a constant initializer.
+ void EmitGlobalVariable(const VarDecl *VD);
+
/// Emit information about an external variable.
void EmitExternalVariable(llvm::GlobalVariable *GV, const VarDecl *Decl);
@@ -800,6 +806,11 @@ class CGDebugInfo {
llvm::MDTuple *&TemplateParameters,
llvm::DIScope *&VDContext);
+ /// Create a DIExpression representing the constant corresponding
+ /// to the specified 'Val'. Returns nullptr on failure.
+ llvm::DIExpression *createConstantValueExpression(const clang::ValueDecl *VD,
+ const APValue &Val);
+
/// Allocate a copy of \p A using the DebugInfoNames allocator
/// and return a reference to it. If multiple arguments are given the strings
/// are concatenated.
diff --git a/clang/test/CodeGenCXX/debug-info-class.cpp b/clang/test/CodeGenCXX/debug-info-class.cpp
index ef84b91b67821a9..a3111cd7c3640a0 100644
--- a/clang/test/CodeGenCXX/debug-info-class.cpp
+++ b/clang/test/CodeGenCXX/debug-info-class.cpp
@@ -116,11 +116,19 @@ int main(int argc, char **argv) {
// CHECK-SAME: DIFlagFwdDecl
// CHECK-NOT: identifier:
// CHECK-SAME: ){{$}}
+
+// CHECK: !DIGlobalVariableExpression(var: ![[HDR_VAR:[0-9]+]], expr: !DIExpression(DW_OP_constu, 52, DW_OP_stack_value))
+// CHECK: ![[HDR_VAR]] = distinct !DIGlobalVariable(name: "HdrSize",
+// CHECK-SAME: isLocal: true, isDefinition: true, declaration: ![[HDR_VAR_DECL:[0-9]+]])
+// CHECK: ![[INT:[0-9]+]] = !DIBasicType(name: "int"
+// CHECK: ![[HDR_VAR_DECL]] = !DIDerivedType(tag: DW_TAG_member, name: "HdrSize"
+
+// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "A"
+
// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "I"
// CHECK-NOT: DIFlagFwdDecl
// CHECK-SAME: ){{$}}
-// CHECK: ![[INT:[0-9]+]] = !DIBasicType(name: "int"
// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "foo"
// CHECK: !DICompositeType(tag: DW_TAG_class_type, name: "bar"
// CHECK: !DICompositeType(tag: DW_TAG_union_type, name: "baz"
@@ -186,8 +194,5 @@ int main(int argc, char **argv) {
// CHECK: [[G_INNER_I]] = !DIDerivedType(tag: DW_TAG_member, name: "j"
// CHECK-SAME: baseType: ![[INT]]
-// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "A"
-// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "HdrSize"
-//
// CHECK: ![[EXCEPTLOC]] = !DILocation(line: 100,
// CHECK: ![[RETLOC]] = !DILocation(line: 99,
diff --git a/clang/test/CodeGenCXX/debug-info-static-inline-member.cpp b/clang/test/CodeGenCXX/debug-info-static-inline-member.cpp
new file mode 100644
index 000000000000000..917e101714846d7
--- /dev/null
+++ b/clang/test/CodeGenCXX/debug-info-static-inline-member.cpp
@@ -0,0 +1,79 @@
+// RUN: %clangxx -target arm64-apple-macosx11.0.0 -g %s -emit-llvm -S -o - | FileCheck --check-prefixes=CHECK %s
+
+enum class Enum : int {
+ VAL = -1
+};
+
+struct Empty {};
+
+constexpr auto func() { return 25; }
+
+struct Foo {
+ static constexpr int cexpr_int = func();
+ static constexpr int cexpr_int2 = func() + 1;
+ static constexpr float cexpr_float = 2.0 + 1.0;
+ static constexpr Enum cexpr_enum = Enum::VAL;
+ static constexpr Empty cexpr_empty{};
+
+ template<typename T>
+ static constexpr T cexpr_template{};
+};
+
+int main() {
+ Foo f;
+
+ // Force global variable definitions to be emitted.
+ (void)&Foo::cexpr_int;
+ (void)&Foo::cexpr_empty;
+
+ return Foo::cexpr_int + Foo::cexpr_float
+ + (int)Foo::cexpr_enum + Foo::cexpr_template<short>;
+}
+
+// CHECK: @{{.*}}cexpr_int{{.*}} =
+// CHECK-SAME: !dbg ![[INT_GLOBAL:[0-9]+]]
+
+// CHECK: @{{.*}}cexpr_empty{{.*}} =
+// CHECK-SAME !dbg ![[EMPTY_GLOBAL:[0-9]+]]
+
+// CHECK: !DIGlobalVariableExpression(var: ![[INT_VAR:[0-9]+]], expr: !DIExpression())
+// CHECK: ![[INT_VAR]] = distinct !DIGlobalVariable(name: "cexpr_int", linkageName:
+// CHECK-SAME: isLocal: false, isDefinition: true, declaration: ![[INT_DECL:[0-9]+]])
+
+// CHECK: ![[INT_DECL]] = !DIDerivedType(tag: DW_TAG_member, name: "cexpr_int",
+// CHECK-SAME: flags: DIFlagStaticMember, extraData: i32 25)
+
+// CHECK: ![[INT_DECL2:[0-9]+]] = !DIDerivedType(tag: DW_TAG_member, name: "cexpr_int2",
+// CHECK-SAME: flags: DIFlagStaticMember, extraData: i32 26)
+
+// CHECK: ![[FLOAT_DECL:[0-9]+]] = !DIDerivedType(tag: DW_TAG_member, name: "cexpr_float",
+// CHECK-SAME: flags: DIFlagStaticMember, extraData: float
+
+// CHECK: ![[ENUM_DECL:[0-9]+]] = !DIDerivedType(tag: DW_TAG_member, name: "cexpr_enum",
+// CHECK-SAME: flags: DIFlagStaticMember, extraData: i32 -1)
+
+// CHECK: ![[EMPTY_DECL:[0-9]+]] = !DIDerivedType(tag: DW_TAG_member, name: "cexpr_empty",
+// CHECK-SAME: flags: DIFlagStaticMember)
+
+// CHECK: ![[TEMPLATE_DECL:[0-9]+]] = !DIDerivedType(tag: DW_TAG_member, name: "cexpr_template",
+// CHECK-SAME: flags: DIFlagStaticMember, extraData: i16 0)
+
+// CHECK: !DIGlobalVariableExpression(var: ![[EMPTY_VAR:[0-9]+]], expr: !DIExpression())
+// CHECK: ![[EMPTY_VAR]] = distinct !DIGlobalVariable(name: "cexpr_empty", linkageName:
+// CHECK-SAME: isLocal: false, isDefinition: true, declaration: ![[EMPTY_DECL]])
+
+// CHECK: !DIGlobalVariableExpression(var: ![[INT_VAR2:[0-9]+]], expr: !DIExpression(DW_OP_constu, 26, DW_OP_stack_value))
+// CHECK: ![[INT_VAR2]] = distinct !DIGlobalVariable(name: "cexpr_int2", linkageName:
+// CHECK-SAME: isLocal: true, isDefinition: true, declaration: ![[INT_DECL2]])
+
+// CHECK: !DIGlobalVariableExpression(var: ![[FLOAT_VAR:[0-9]+]], expr: !DIExpression(DW_OP_constu, {{.*}}, DW_OP_stack_value))
+// CHECK: ![[FLOAT_VAR]] = distinct !DIGlobalVariable(name: "cexpr_float", linkageName:
+// CHECK-SAME: isLocal: true, isDefinition: true, declaration: ![[FLOAT_DECL]])
+
+// CHECK: !DIGlobalVariableExpression(var: ![[ENUM_VAR:[0-9]+]], expr: !DIExpression(DW_OP_constu, {{.*}}, DW_OP_stack_value))
+// CHECK: ![[ENUM_VAR]] = distinct !DIGlobalVariable(name: "cexpr_enum", linkageName:
+// CHECK-SAME: isLocal: true, isDefinition: true, declaration: ![[ENUM_DECL]])
+
+// CHECK: !DIGlobalVariableExpression(var: ![[TEMPLATE_VAR:[0-9]+]], expr: !DIExpression(DW_OP_constu, 0, DW_OP_stack_value))
+// CHECK: ![[TEMPLATE_VAR]] = distinct !DIGlobalVariable(name: "cexpr_template", linkageName:
+// CHECK-SAME: isLocal: true, isDefinition: true, declaration: ![[TEMPLATE_DECL]], templateParams: ![[TEMPLATE_PARMS:[0-9]+]])
diff --git a/lldb/test/API/lang/cpp/const_static_integral_member/TestConstStaticIntegralMember.py b/lldb/test/API/lang/cpp/const_static_integral_member/TestConstStaticIntegralMember.py
index 530191e8a37ba1b..78ea23ac8f70610 100644
--- a/lldb/test/API/lang/cpp/const_static_integral_member/TestConstStaticIntegralMember.py
+++ b/lldb/test/API/lang/cpp/const_static_integral_member/TestConstStaticIntegralMember.py
@@ -102,9 +102,10 @@ def test(self):
# it does not crash.
self.expect("image lookup -t A")
- # dsymutil strips the debug info for classes that only have const static
- # data members without a definition namespace scope.
- @expectedFailureAll(debug_info=["dsym"])
+ # For debug-info produced by older versions of clang, dsymutil strips the
+ # debug info for classes that only have const static data members without
+ # definitions.
+ @expectedFailureAll(compiler=["clang"], compiler_version=["<", "18.0"])
def test_class_with_only_const_static(self):
self.build()
lldbutil.run_to_source_breakpoint(
``````````
</details>
https://github.com/llvm/llvm-project/pull/70639
More information about the lldb-commits
mailing list