This is an archive of the discontinued LLVM Phabricator instance.

Prevent line 0 instructions from dividing a lexical block into ranges
AbandonedPublic

Authored by ykhatav on Feb 18 2023, 3:25 PM.

Details

Summary

Certain instructions with line 0 source correlation create discontiguous/split address ranges in lexical blocks. This affects Visual Studio's ability to print variables reliably because Codeview debug format used on Windows does not support describing a lexical block with multiple address ranges. As a result, LLVM compiler's Codeview emission code evaporates these lexical blocks and pushes variables to the enclosing routine scope, causing gaps in their location ranges and potential confusion with similarly named variables in the debugger. While this issue is also visible on Linux, the Dwarf debugging format can handle it without impacting debugging.

To prevent the line 0 instructions from breaking up a lexical block we fold the line zero machine instructions into the range when the switch "UnknownLocations" is not Enabled. This essentially removes the split address ranges and allows the lexical block to be treated as a contiguous range of instructions.

Diff Detail

Event Timeline

ykhatav created this revision.Feb 18 2023, 3:25 PM
Herald added a project: Restricted Project. · View Herald TranscriptFeb 18 2023, 3:25 PM
Herald added a subscriber: hiraditya. · View Herald Transcript
ykhatav requested review of this revision.Feb 18 2023, 3:25 PM
Herald added a project: Restricted Project. · View Herald TranscriptFeb 18 2023, 3:25 PM
skan added a subscriber: skan.Feb 18 2023, 5:26 PM
krisb added a subscriber: krisb.Feb 19 2023, 12:33 AM
ykhatav updated this revision to Diff 498829.Feb 20 2023, 6:33 AM

Nit:test-fix

ykhatav updated this revision to Diff 498842.Feb 20 2023, 6:57 AM

Fix metadata numbering

ykhatav edited the summary of this revision. (Show Details)Feb 21 2023, 8:36 AM

It seems strange to define the enum in a header for a module that uses it only once, and externalize it in a module that uses it several times.

I'd always thought this DefaultOnOff business should be generalized... Hmm I see boolOrDefault in CommandLine.h. Maybe we could convert UnknownLocations to use that, remove static from the definition, and then LexicalScopes.cpp can reference it as an extern.

(Converting the rest of the DwarfDebug.cpp options from DefaultOnOff to boolOrDefault should not be part of your task. But maybe add a FIXME on the enum definition.)

It seems strange to define the enum in a header for a module that uses it only once, and externalize it in a module that uses it several times.

I'd always thought this DefaultOnOff business should be generalized... Hmm I see boolOrDefault in CommandLine.h. Maybe we could convert UnknownLocations to use that, remove static from the definition, and then LexicalScopes.cpp can reference it as an extern.
I agree with you that UnknownLocations may be better defined with boolorDefault instead of DefaultOnOff. I can change that.

However, the reason I moved the definition of UnknownLocations to LexicalScopes.cpp was to avoid a link-time error. Defining UnknownLocations as extern in LexicalScopes.cpp gives a link-time error despite removing the "static" keyword from its definition in DwarfDebug.cpp. I see that the two object files are in different directories and while building the LexicalScopes.cpp file it is unable to find the definition of UnknownLocations. I am aware that it may not be the optimal approach so I would appreciate any feedback or consider alternative methods.

(Converting the rest of the DwarfDebug.cpp options from DefaultOnOff to boolOrDefault should not be part of your task. But maybe add a FIXME on the enum definition.)

Defining UnknownLocations as extern in LexicalScopes.cpp gives a link-time error despite removing the "static" keyword from its definition in DwarfDebug.cpp.

Ah, the layering is in the other direction than I thought (AsmPrinter depends on CodeGen, not vice versa). Okay, it has to be defined in LexicalScopes and made extern from DwarfDebug.

Please do change over to boolOrDefault for this one flag, which means you can leave the DefaultOnOff enum in DwarfDebug.cpp.

Does this need a flag? The patch describes doing this for CodeView? Could/should it be done unconditionally for CV?

Though I'll mention that there's a risk of messing up profiles with this - those zero line instructions do have scopes and other things attached to them, if they were of the appropriate scope, even with a zero line, then it wouldn't be fragmenting the scope. So it's because the location isn't attributed to the inline scope that it's causing fragmentation - but that means the instruction doesn't necessarily come from that scope, and so a sample based profile might incorrectly conclude something about reachability if it is instead attributed to this scope... maybe?

llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
68

We shouldn't introduce declarations of foreign entities into .cpp files like this (there's a risk of the decl/def getting out of date/inconsistent with each other) - there should be some common header to declare this in. Perhaps that means it goes up in LexicalScopes.h?

ykhatav added a comment.EditedMar 8 2023, 3:18 PM

Does this need a flag? The patch describes doing this for CodeView? Could/should it be done unconditionally for CV?

When UnknownLocations is left at default, the line table includes line 0 entries, and the lexical scopes utilize them while computing the ranges. However, if the unknown locations are disabled, the line table already ignores line 0 entries. This patch modifies the lexical scope behavior to ignore line 0 entries when UnknownLocations is not enabled. If this change is applied unconditionally, the line table entries and lexical scope will not match, causing inconsistencies
Although the DWARF format has the ability to describe the split address ranges, it is possible that the lexical blocks with split address ranges can result in buggy behavior on Linux as well.

ykhatav added inline comments.Mar 8 2023, 3:22 PM
llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
68

Unfortunately, declaring it in LexicalScopes.h does not work due to dependency issue between AsmPrinter and Codegen.

ykhatav updated this revision to Diff 503539.Mar 8 2023, 3:25 PM

Change UnknownLocations to use boolorDefault

Could you describe why this relates to/depends on the "UnknownLocations" flag? I'm not following from the description - we could still describe these locations as being at line zero, while including them in lexical scopes to avoid fragmented ranges, I think?

llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
68

Could you describe the dependency issue in more detail? I think it's important that we figure out the layering & where things should be declared - not introduce multiple independent declarations of the same entity.

Could you describe why this relates to/depends on the "UnknownLocations" flag? I'm not following from the description - we could still describe these locations as being at line zero, while including them in lexical scopes to avoid fragmented ranges, I think?

The reason why we want to check for “UnknownLocations” flag is that this flag is used to check if we want to add line 0 entries in the line table (in DwarfDebug.cpp) and thereby controlling whether or not line zero instructions are represented in the debug information. If we don’t check for this flag and skip the line 0 entries while extracting lexical scopes there will be a mismatch in the line table entries and lexical scopes.

ykhatav updated this revision to Diff 504652.Mar 13 2023, 7:44 AM

Move UnknownLocations declaration to LexicalScopes.h

ykhatav added inline comments.Mar 13 2023, 7:46 AM
llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
68

Sorry, I misunderstood what you said.. I was able to move "UnknownLocations" to LexicalScope.h

Could you describe why this relates to/depends on the "UnknownLocations" flag? I'm not following from the description - we could still describe these locations as being at line zero, while including them in lexical scopes to avoid fragmented ranges, I think?

The reason why we want to check for “UnknownLocations” flag is that this flag is used to check if we want to add line 0 entries in the line table (in DwarfDebug.cpp) and thereby controlling whether or not line zero instructions are represented in the debug information. If we don’t check for this flag and skip the line 0 entries while extracting lexical scopes there will be a mismatch in the line table entries and lexical scopes.

Sorry, still not quite following. Could you walk me through an example of the bug you're trying to address with this patch? And an example of the bug you're trying to avoid introducing/that causes you to need this extra UnknownLocations checking?

Could you describe why this relates to/depends on the "UnknownLocations" flag? I'm not following from the description - we could still describe these locations as being at line zero, while including them in lexical scopes to avoid fragmented ranges, I think?

The reason why we want to check for “UnknownLocations” flag is that this flag is used to check if we want to add line 0 entries in the line table (in DwarfDebug.cpp) and thereby controlling whether or not line zero instructions are represented in the debug information. If we don’t check for this flag and skip the line 0 entries while extracting lexical scopes there will be a mismatch in the line table entries and lexical scopes.

Sorry, still not quite following. Could you walk me through an example of the bug you're trying to address with this patch? And an example of the bug you're trying to avoid introducing/that causes you to need this extra UnknownLocations checking?

If you consider the attached test case (llvm/test/DebugInfo/X86/contiguous-lexical-block.ll) you can see that the lexical block is fragmented into ranges due to the presence of an instruction having line number 0. This I believe is a bug and If the same test case is run on windows, Visual Studio' can't reliably print variable "i" because CodeView does not provide a mechanism for describing a lexical block with more than one address ranges. In order to prevent the line 0 instructions from breaking up a lexical block, in this patch, we fold the line zero machine instructions into the range.
I don't have a test case that demonstrates whether using or not using UnknownLocations could lead to a bug. It is an internal flag and is suitable for controlling whether or not line zero instructions are represented in the debug information and so, in my opinion, can be used to control the scope tree as well.. Do you have any other flags in mind that can be used instead of "UnknownLocations"?

dblaikie added a subscriber: hans.Mar 14 2023, 1:34 PM

If you consider the attached test case (llvm/test/DebugInfo/X86/contiguous-lexical-block.ll) you can see that the lexical block is fragmented into ranges due to the presence of an instruction having line number 0. This I believe is a bug and If the same test case is run on windows, Visual Studio' can't reliably print variable "i" because CodeView does not provide a mechanism for describing a lexical block with more than one address ranges. In order to prevent the line 0 instructions from breaking up a lexical block, in this patch, we fold the line zero machine instructions into the range.

Sure, I'm on-board there. I'll take your word for it that CV can't represent discontiguous scopes and making scopes contiguous is the better tradeoff than omitting them entirely.

I don't have a test case that demonstrates whether using or not using UnknownLocations could lead to a bug. It is an internal flag and is suitable for controlling whether or not line zero instructions are represented in the debug information and so, in my opinion, can be used to control the scope tree as well.. Do you have any other flags in mind that can be used instead of "UnknownLocations"?

I don't understand this bit - there can be zero locations for other reasons, apart from UnknownLocations - you could just check for the zero locations, without needing to check UnknownLocations too, right?

But maybe you're using UnknownLocations as a proxy for CV/MSVC debug info emission? That seems unfortunate/not suitable, and this behavior should be gated on/more directly test for CV output, I think?

@hans maybe you've got thoughts on this stuff

hans added a subscriber: zequanwu.Mar 15 2023, 6:41 AM

@hans maybe you've got thoughts on this stuff

Sorry, I don't know much about Codeview. @zequanwu knows more.

If you consider the attached test case (llvm/test/DebugInfo/X86/contiguous-lexical-block.ll) you can see that the lexical block is fragmented into ranges due to the presence of an instruction having line number 0. This I believe is a bug and If the same test case is run on windows, Visual Studio' can't reliably print variable "i" because CodeView does not provide a mechanism for describing a lexical block with more than one address ranges. In order to prevent the line 0 instructions from breaking up a lexical block, in this patch, we fold the line zero machine instructions into the range.

Sure, I'm on-board there. I'll take your word for it that CV can't represent discontiguous scopes and making scopes contiguous is the better tradeoff than omitting them entirely.

I don't have a test case that demonstrates whether using or not using UnknownLocations could lead to a bug. It is an internal flag and is suitable for controlling whether or not line zero instructions are represented in the debug information and so, in my opinion, can be used to control the scope tree as well.. Do you have any other flags in mind that can be used instead of "UnknownLocations"?

I don't understand this bit - there can be zero locations for other reasons, apart from UnknownLocations - you could just check for the zero locations, without needing to check UnknownLocations too, right?

But maybe you're using UnknownLocations as a proxy for CV/MSVC debug info emission? That seems unfortunate/not suitable, and this behavior should be gated on/more directly test for CV output, I think?

No, my intent was not to use UnknownLocations as a proxy for CV, but I think it is safe not to fold line 0 instructions into a scope range when UnknownLocations is enabled. AFAIK, when UnknownLocations is enabled line 0 entries are emitted in the debug information. In my opinion, Folding these instructions when the switch is enabled could break stuff.

@hans maybe you've got thoughts on this stuff

No, my intent was not to use UnknownLocations as a proxy for CV, but I think it is safe not to fold line 0 instructions into a scope range when UnknownLocations is enabled. AFAIK, when UnknownLocations is enabled line 0 entries are emitted in the debug information. In my opinion, Folding these instructions when the switch is enabled could break stuff.

Yeah, I'm still really not following this. UnknownLocations isn't the only source of zero lines (I think the most obvious/easy example is, if I recall correctly, line zero is used for merged function calls). And even if it was, I don't understand why it would need to be tested in LexicalScopes.cpp where the line can be tested for zero directly, right? So why do we need to test anything else other than that it's line zero?

(I think the most obvious/easy example is, if I recall correctly, line zero is used for merged function calls)

Actually, any merged instructions not originally from the same source location. (Because we don't have a way to represent multiple source locations for a single instruction.)

I agree with @dblaikie, for CV you can just eliminate the line-zero records without reference to UnknownLocations.

ykhatav abandoned this revision.Mar 27 2023, 3:33 PM

I will open a design discussion to discuss what an appropriate flag will be to control this behavior.