Reland "[clang][DebugInfo] Emit global variable definitions for static data members with constant initializers"#71780
Conversation
…c data members with constant initializers"
|
@llvm/pr-subscribers-lldb @llvm/pr-subscribers-clang Author: Michael Buch (Michael137) ChangesThis patch relands #70639 It was reverted because under certain conditions we triggered an assertion To fix this, the latest iteration of the patch moves Original commit message: To make it more convenient for debuggers to get to the value of inline static data The implementation keeps track of newly created static data members. The newly emitted This patch also drops the Dependent changes:
Full diff: https://github.com/llvm/llvm-project/pull/71780.diff 6 Files Affected:
|
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
adrian-prantl
left a comment
There was a problem hiding this comment.
Minor comments inside.
clang/lib/CodeGen/CGDebugInfo.cpp
Outdated
There was a problem hiding this comment.
is it intentional that we cache nullptr here if the next condition fails?
There was a problem hiding this comment.
Good catch, I'll err on the side of caution and not do that
clang/lib/CodeGen/CGDebugInfo.cpp
Outdated
There was a problem hiding this comment.
it's non-obvious why this skip exists, can you add a comment here?
There was a problem hiding this comment.
I'll update the EmitGlobalVariable docs to clarify that we need an initialiser, otherwise behaviour is undefined
3bdaa98 to
573f9f6
Compare
…e defintion if available" (#71800) This patch relands #71004 which was reverted because the clang change it depends on was reverted. In addition to the original patch, this PR includes a change to `SymbolFileDWARF::ParseVariableDIE` to support CU-level variable definitions that don't have locations, but represent a constant value. Previously, when debug-maps were available, we would assume that a variable with "static lifetime" (which in this case means "has a linkage name") has a valid address, which isn't the case for non-locationed constants. We could omit this additional change if we stopped attaching linkage names to global non-locationed constants. Original commit message: """ #71780 proposes moving the `DW_AT_const_value` on inline static members from the declaration DIE to the definition DIE. This patch makes sure the LLDB's expression evaluator can continue to support static initialisers even if the declaration doesn't have a `DW_AT_const_value` anymore. Previously the expression evaluator would find the constant for a VarDecl from its declaration `DW_TAG_member` DIE. In cases where the initialiser was specified out-of-class, LLDB could find it during symbol resolution. However, neither of those will work for constants, since we don't have a constant attribute on the declaration anymore and we don't have constants in the symbol table. """ Depends on: * #71780
|
We're seeing crashes after this. Here is a reproducer: https://bugs.chromium.org/p/chromium/issues/detail?id=1502489#c1 Please consider reverting if it can't be fixed quickly (I had a look, but it seems there are some dependent changes on top). |
Thanks for reporting, will check and revert if not obvious what's wrong |
Just based on the backtrace the only cause I see would be if we put a nullptr into |
|
Ok with ASAN I could trigger this quite consistently. Turns out we can't iterate over the |
This patch fixes an issue introduced in llvm#71780 where, if `EmitGlobalVariable` triggered a call to `CreateRecordStaticField` it would invalidate the `StaticDataMemberDefinitionsToEmit` iterator that is currently in-use. This fixes a crash reported in llvm#71780 (comment)
|
@zmodem Fix pushed in |
|
Thanks! |
|
Hi @Michael137, we are seeing a failure in one of our internal tests that I bisected back to this change. Consider the following code: struct X
{
static const int constant = 1;
int x;
X() { x = constant; }
};
const int X::constant;
int main()
{
X x;
x.x = X::constant;
x.x = X::constant;
x.x = X::constant;
x.x = X::constant;
x.x = X::constant;
return 0;
}Prior to your change, the compiler would generate the following DWARF for the constant value: After your change, the DW_AT_const_value is gone from this DW_TAG_member group, but doesn't appear anywhere else in the DWARF output which seems to indicate that it was dropped completely which does not seem to be correct. Is this intended or am I missing something? |
Right, we stopped putting the What's the nature of the failure? Would you instead be able to read the value out of the definition? CC @dwblaikie |
Our test is expecting to find a DW_AT_const_value somewhere in the dwarf output which it no longer seems to generate. From what I gather from your commit message, the compiler should now be emitting a new DW_TAG_variable section that it previously did not with the DW_AT_const_value attached? Or is my understanding incorrect? |
|
The member is const with an initializer in-class. How is the constant value not available for the definition? |
Right, it is available, we just don't attach it if we have a location for it. Don't see why we couldn't put it on the definition if we have the constant on hand |
So I guess what you are saying in this case is that it is expected and the value is at the location indicated by the DW_AT_location value? As long as the value is still available I suppose that is fine then and the test just needs updating. |
|
I think it is a valuable bit of information for the debugger, to know the constant value without having to read it from memory. I think we should emit both the location and the value. |
Yup that's exactly right. I'll go ahead with @pogo59's suggestion then and make sure we also emit the constant on the definition when we can. If updating your test to accommodate for only having the DW_AT_location is feasible for now that'd be great since I'll only be able to get to it tomorrow |
|
Hi, we're seeing some breakages, similar to the above in our debugger tests with this patch. A failing bot can be found here: https://luci-milo.appspot.com/ui/p/fuchsia/builders/ci/clang_toolchain.ci.core.x64-debug/b8764552260903625809/overview You can find a fuller discussion in our bugtracker: https://bugs.fuchsia.dev/p/fuchsia/issues/detail?id=136182. The problem in our test is that the |
Sorry for not updating the thread. I'm about to open a PR for said change |
|
Fantastic! |
|
@ilovepi To clarify the upcoming change, we are planning to attach the |
|
I'm unclear on the specifics of the check, but it's probably something we can adjust if that is the long-term solution. CC @petrhosek Since he was interested in getting this resolved soon. |
…c data members with constant initializers" (llvm#71780) This patch relands llvm#70639 It was reverted because under certain conditions we triggered an assertion in `DIBuilder`. Specifically, in the original patch we called `EmitGlobalVariable` at the end of `CGDebugInfo::finalize`, after all the temporary `DIType`s have been uniqued. With limited debug-info such temporary nodes would be created more frequently, leaving us with non-uniqued nodes by the time we got to `DIBuilder::finalize`; this violated its pre-condition and caused assertions to trigger. To fix this, the latest iteration of the patch moves `EmitGlobalVariable` to the beginning of `CGDebugInfo::finalize`. Now, when we create a temporary `DIType` node as a result of emitting a variable definition, it will get uniqued in time. A test-case was added for this scenario. We also now don't emit a linkage name for non-locationed constants since LLDB doesn't make use of it anyway. Original commit message: """ 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") ``` This patch also drops the `DW_AT_const_value` off of the declaration since we now always have it on the definition. This ensures that the `DWARFParallelLinker` can type-merge class with static members where we couldn't attach the constant on the declaration in some CUs. """ Dependent changes: * llvm#71800
…e defintion if available" (llvm#71800) This patch relands llvm#71004 which was reverted because the clang change it depends on was reverted. In addition to the original patch, this PR includes a change to `SymbolFileDWARF::ParseVariableDIE` to support CU-level variable definitions that don't have locations, but represent a constant value. Previously, when debug-maps were available, we would assume that a variable with "static lifetime" (which in this case means "has a linkage name") has a valid address, which isn't the case for non-locationed constants. We could omit this additional change if we stopped attaching linkage names to global non-locationed constants. Original commit message: """ llvm#71780 proposes moving the `DW_AT_const_value` on inline static members from the declaration DIE to the definition DIE. This patch makes sure the LLDB's expression evaluator can continue to support static initialisers even if the declaration doesn't have a `DW_AT_const_value` anymore. Previously the expression evaluator would find the constant for a VarDecl from its declaration `DW_TAG_member` DIE. In cases where the initialiser was specified out-of-class, LLDB could find it during symbol resolution. However, neither of those will work for constants, since we don't have a constant attribute on the declaration anymore and we don't have constants in the symbol table. """ Depends on: * llvm#71780
There's a few things left to iron out with #72730, which I hope to resolve by Friday. If this is blocking you urgently then we could add back the constant to the declaration until I land #72730 (instead of reverting the entire patch). |
|
Can we add back the constant to the declaration? It's been a week and the progress on #72730 seems to have stalled while our builders are still broken. |
Yup will submit a PR today, apologies for the noise |
…mber declarations In llvm#71780 we started emitting definitions for all static data-members with constant initialisers, even if they were constants (i.e., didn't have a location). We also dropped the DW_AT_const_value from the declaration to help resolve inconsistencies during type merging in the DWARFParallelLinker. However, for static data members that do have locations, we wouldn't emit a DW_AT_const_value on it, assuming that the consumer knows how to read the value using the location. This broke some consumers that really wanted to find a DW_AT_const_value. Ultimately we want to attach a DW_AT_const_value to definitions that have a location too. But to fix consumers broken by said change, this patch adds the constant back onto the declaration.
…mber declarations (#73626) In #71780 we started emitting definitions for all static data-members with constant initialisers, even if they were constants (i.e., didn't have a location). We also dropped the DW_AT_const_value from the declaration to [help resolve inconsistencies during type merging in the DWARFParallelLinker](#68721). However, for static data members that do have locations, we wouldn't emit a DW_AT_const_value on it, assuming that the consumer knows how to read the value using the location. This broke some consumers that really wanted to find a DW_AT_const_value. Ultimately we want to attach a DW_AT_const_value to definitions that have a location too. But to fix consumers broken by said change, this patch adds the constant back onto the declaration. This is what we used to do prior to #71780
… a static variable's initializer (#72974) This patch extracts the logic to evaluate a C++ static data-member's constant initializer. This logic will be re-used in an upcoming patch. It also makes the check for whether we are dealing with a constant initializer more robust/idiomatic, which revealed a bug in the `debug-info-static-inline-member` test (which existed since its introduction in #71780) **Test changes** * `debug-info-static-member.cpp`: * We added the check for `const_b` as part of the patch series in `638a8393615e911b729d5662096f60ef49f1c65e`. The check for `isUsableAsConstantExpression` added in the current patch doesn't support constant inline floats (since they are neither constexpr nor integrals). This isn't a regression since before said patch series we wouldn't ever emit the definition for `const_b` anyway. Now we just don't do it for `inline const float`s. This is consistent with GCC's behaviour starting with C++11. * `debug-info-static-inline-member`: * This was just a bug which is now fixed. We shouldn't emit a `DW_AT_const_value` for a non-const static.
…tialized static data-members" This commit reverts the changes in llvm#71780 and all of its follow-up patches. We got reports of the `.debug_names/.debug_gnu_pubnames/gdb_index/etc.` sections growing by a non-trivial amount for some large projects. While GCC does index definitions for static data member constants, they do so *only* for explicitly `constexpr` members. We were indexing *all* constant-initialized const-static members, which is likely where the significant size difference comes from. However, only emitting explicitly `constexpr` variables into the index doesn't seem like a good way forward, since from clang's perspective `const`-static integrals are `constexpr` too, and that shouldn't be any different in the debug-info component. Also, as new code moves to `constexpr` instead of `const` static for constants, such solution would just delay the growth of the Names index. To prevent the size regression we revert to not emitting definitions for static data-members that have no location. To support access to such constants from LLDB we'll most likely have to have to make LLDB find the constants by looking at the containing class first.
…static data-members" (#74580) This commit reverts the changes in #71780 and all of its follow-up patches. We got reports of the `.debug_names/.debug_gnu_pubnames/gdb_index/etc.` sections growing by a non-trivial amount for some large projects. While GCC emits definitions for static data member constants into the Names index, they do so *only* for explicitly `constexpr` members. We were indexing *all* constant-initialized const-static members, which is likely where the significant size difference comes from. However, only emitting explicitly `constexpr` variables into the index doesn't seem like a good way forward, since from clang's perspective `const`-static integrals are `constexpr` too, and that shouldn't be any different in the debug-info component. Also, as new code moves to `constexpr` instead of `const` static for constants, such solution would just delay the growth of the Names index. To prevent the size regression we revert to not emitting definitions for static data-members that have no location. To support access to such constants from LLDB we'll most likely have to have to make LLDB find the constants by looking at the containing class first.
This patch relands #70639
It was reverted because under certain conditions we triggered an assertion
in
DIBuilder. Specifically, in the original patch we calledEmitGlobalVariableat the end of
CGDebugInfo::finalize, after all the temporaryDITypes havebeen uniqued. With limited debug-info such temporary nodes would be created
more frequently, leaving us with non-uniqued nodes by the time we got to
DIBuilder::finalize; this violated its pre-condition and causedassertions to trigger.
To fix this, the latest iteration of the patch moves
EmitGlobalVariableto thebeginning of
CGDebugInfo::finalize. Now, when we create a temporaryDITypenode as a result of emitting a variable definition, it will get uniquedin time. A test-case was added for this scenario.
We also now don't emit a linkage name for non-locationed constants since
LLDB doesn't make use of it anyway.
Original commit message:
"""
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_memberthatusually holds the constant initializer. But we don't get a corresponding
CU-level
DW_TAG_variablelike 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 globalDW_TAG_variablewith aDW_AT_const_valuefor any of those declarations that didn't end up with adefinition in the
DeclCache.The newly emitted
DW_TAG_variablewill look as follows:This patch also drops the
DW_AT_const_valueoff of the declaration since wenow always have it on the definition. This ensures that the
DWARFParallelLinkercan type-merge class with static members where we couldn't attach the constant
on the declaration in some CUs.
"""
Dependent changes: