This is an archive of the discontinued LLVM Phabricator instance.

Fixing line numbers of inlined allocas
ClosedPublic

Authored by wolfgangp on Sep 18 2014, 10:50 AM.

Details

Reviewers
dblaikie
echristo
Summary

When a function is inlined (for example, as a result of it carrying the attribute alwaysinline), the cloned instructions receive the call site’s line number information (per r210459). After cloning, the cloned alloca instructions are moved to the caller’s entry block, retaining the call site’s line number information.
When, subsequently, new instructions are generated at the start of the caller’s entry block (such as instructions generated by the stack-protector pass), they pick up the line number information from the entry block’s first instruction, which may be one of the cloned alloca instructions.

This can cause incorrect line number information being generated for the start of the calling function, with an inlined function’s call site being associated with the first stack-protector instruction, for example. Since the stack-protector instruction is likely deemed to be the end of the function prologue, breakpoints that are set using the function name will be set at the wrong location.

Example:

int attribute((always_inline)) foo()
{

int arr[10];
arr[0] = 5;
int sum = 4;
return sum;

}

extern void bar();

int main()
{

bar();
int i = foo();   <== breaking at ‘main’ will stop here because main’s function prologue gets foo’s call site’s line number.
return i;

}

The proposed fix is to modify the cloned alloca instructions’ debug information when they are moved to the caller’s entry block. Instead of the call site’s information they would get the same information as the instructions already at the start of the caller’s entry block, i.e. they place where they’re being moved to.

Note to the test case: the sspstrong attribute on the 'main' function is not necessary to make the test case pass, but if one were to generate a .o file the incorrect line numbers for the function prologue would not appear without the stack-protector instructions being generated.

Diff Detail

Event Timeline

wolfgangp updated this revision to Diff 13813.Sep 18 2014, 10:50 AM
wolfgangp retitled this revision from to Fixing line numbers of inlined allocas.
wolfgangp updated this object.
wolfgangp edited the test plan for this revision. (Show Details)
wolfgangp added a subscriber: Unknown Object (MLST).

Wouldn't it be a better fix to use the proper location for the alloca and rather fix the backend to follow the inlinedAt references in the alloca's debug location when deciding the end of the prologue?

dblaikie edited edge metadata.Oct 15 2014, 10:34 AM

So currently if you have an instruction with no DebugLoc that's inlined
into a function with debug info, that instruction appears to get the
DebugLoc of the call site, yes? (if I'm understanding this correctly)

We could, maybe, ish, fix this by describing the instruction as inlined -
but our DebugLoc schema doesn't really have a way to describe "no specific
location, but it's inlined within this scope" (line/col 0, but with a
scope, maybe? Is line/col 0 the same as no DebugLoc? I guess it probably
is, but I'm not sure).

& what this patch is proposing is to special case static allocas with no
DebugLoc and assign them the same DebugLoc as other static allocas? (what
if there aren't any?) which should be the null DebugLoc. I suspect the more
general solution than this is to just ensure that instructions with no
DebugLoc continue to have no DebugLoc when they're inlined (they certainly
shouldn't end up with the call sites DebugLoc - without inlining info, etc).

But, yes, potentially from a source fidelity perspective we could see if a
0 line/col(/file) DebugLoc with scoping information could be used to
produce reasonable output, and then special case the prologue handling code
to be a bit smarter about instructions with DebugLocs... but I don't know
how smart that would end up being.

In D5401#8, @dblaikie wrote:

So currently if you have an instruction with no DebugLoc that's inlined
into a function with debug info, that instruction appears to get the
DebugLoc of the call site, yes? (if I'm understanding this correctly)

We could, maybe, ish, fix this by describing the instruction as inlined -
but our DebugLoc schema doesn't really have a way to describe "no specific
location, but it's inlined within this scope" (line/col 0, but with a
scope, maybe? Is line/col 0 the same as no DebugLoc? I guess it probably
is, but I'm not sure).

It isn't. No DebugLoc means reuse the location of the previous insn, or, if there was no previous insn, it means we are in the function prologue. Line 0 on the other hand is actually emitted in the line table and carries the special meaning: this is compiler-generated code with no source that could be associated with it. LLDB, for instance, treats line 0 as "always skip over this, the developer would never want to stop here". Clang uses this for ObjC retain/release calls inserted by the compiler, for instance.

& what this patch is proposing is to special case static allocas with no
DebugLoc and assign them the same DebugLoc as other static allocas? (what
if there aren't any?) which should be the null DebugLoc. I suspect the more
general solution than this is to just ensure that instructions with no
DebugLoc continue to have no DebugLoc when they're inlined (they certainly
shouldn't end up with the call sites DebugLoc - without inlining info, etc).

But, yes, potentially from a source fidelity perspective we could see if a
0 line/col(/file) DebugLoc with scoping information could be used to
produce reasonable output, and then special case the prologue handling code
to be a bit smarter about instructions with DebugLocs... but I don't know
how smart that would end up being.

echristo edited edge metadata.Oct 15 2014, 10:46 AM

if there aren't any?) which should be the null DebugLoc. I suspect the more
general solution than this is to just ensure that instructions with no
DebugLoc continue to have no DebugLoc when they're inlined (they certainly
shouldn't end up with the call sites DebugLoc - without inlining info, etc).

This is likely the right solution.

In D5401#10, @echristo wrote:

if there aren't any?) which should be the null DebugLoc. I suspect the more
general solution than this is to just ensure that instructions with no
DebugLoc continue to have no DebugLoc when they're inlined (they certainly
shouldn't end up with the call sites DebugLoc - without inlining info, etc).

This is likely the right solution.

Agreed, especially since we want the alloca in this example to be counted towards the prologue.

  • adrian
In D5401#11, @aprantl wrote:
In D5401#10, @echristo wrote:

if there aren't any?) which should be the null DebugLoc. I suspect the more
general solution than this is to just ensure that instructions with no
DebugLoc continue to have no DebugLoc when they're inlined (they certainly
shouldn't end up with the call sites DebugLoc - without inlining info, etc).

This is likely the right solution.

Agreed, especially since we want the alloca in this example to be counted towards the prologue.

  • adrian

Thank you for the input, gentlemen. I'm concerned by the following comment in fixupLineNumbers() in Transforms/Utils/InlineFunction.cpp:

// If the inlined instruction has no line number, make it look as if it
 // originates from the call location. This is important for
 // ((__always_inline__, __nodebug__)) functions which must use caller
 // location for all instructions in their function body.
 BI->setDebugLoc(TheCallDL);
In D5401#12, @wolfgangp wrote:
In D5401#11, @aprantl wrote:
In D5401#10, @echristo wrote:

if there aren't any?) which should be the null DebugLoc. I suspect the more
general solution than this is to just ensure that instructions with no
DebugLoc continue to have no DebugLoc when they're inlined (they certainly
shouldn't end up with the call sites DebugLoc - without inlining info, etc).

This is likely the right solution.

Agreed, especially since we want the alloca in this example to be counted towards the prologue.

  • adrian

Thank you for the input, gentlemen. I'm concerned by the following comment in fixupLineNumbers() in Transforms/Utils/InlineFunction.cpp:

// If the inlined instruction has no line number, make it look as if it
 // originates from the call location. This is important for
 // ((__always_inline__, __nodebug__)) functions which must use caller
 // location for all instructions in their function body.
 BI->setDebugLoc(TheCallDL);

worth being concerned about - I wonder if we could detect that case specifically and special-case it.

I can imagine a few ways - literally checking whether the function we're inlining has the nodebug attribute (does that even persist to IR? Or does the frontend handle it entirely). Checking if the function we're inlining is a known debug info function (might have to build the FunctionDIs to have that around to reference... )...

What would happen to your test case if we had nodebug on the callee? I guess your solution would still work for that (adjusting the debugloc of the allocas) - maybe we're just going to have to clear the allocas' DebugLocs anyway?

To sum up:

If we special case non-debug functions when inlining, attributing their instructions to the call site, but non-debug instructions in debug functions were just persisted with no debug location then we'd still need to special case allocas from non-debug functions to avoid this bug in that case.

So it sounds like we need to special case the static allocas, but the special case should probably be to not add a debugloc for them (unlike other instructions).

Then there remains an open question as to whether, for debugloc-less instructions in non-nodebug functions, should they be treated the same as instructions in nodebug functions (attributed to the call site) or unattributed? (for now I'm happy to leave them handled the same way as nodebug instructions, attributing them to the call site)

To sum up:

If we special case non-debug functions when inlining, attributing their instructions to the call site, but non-debug instructions in debug functions were just persisted with no debug location then we'd still need to special case allocas from non-debug functions to avoid this bug in that case.

Yep.

So it sounds like we need to special case the static allocas, but the special case should probably be to not add a debugloc for them (unlike other instructions).

Agreed.

Then there remains an open question as to whether, for debugloc-less instructions in non-nodebug functions, should they be treated the same as instructions in nodebug functions (attributed to the call site) or unattributed? (for now I'm happy to leave them handled the same way as nodebug instructions, attributing them to the call site)

Probably just leave them as is for now. It's not a great solution, but unless we want to construct a "nodebug" line node I think this is going to be the best.

In D5401#13, @dblaikie wrote:

What would happen to your test case if we had nodebug on the callee? I guess your solution would still work for that (adjusting the debugloc of the allocas) - maybe we're just going to have to clear the allocas' DebugLocs anyway?

FWIW, nodebug on the callee doesn't change anything since the callee's allocas have no DebugLoc in either case.

My original thought of adjusting the allocas' DebugLocs instead of just removing them was that an inlined alloca is equivalent to a caller's alloca and should be treated as such in every respect, but that's probably not exactly correct wrt debug information.

To sum up:

If we special case non-debug functions when inlining, attributing their instructions to the call site, but non-debug instructions in debug functions were just persisted with no debug location then we'd still need to special case allocas from non-debug functions to avoid this bug in that case.

So it sounds like we need to special case the static allocas, but the special case should probably be to not add a debugloc for them (unlike other instructions).

Yes. I'll prepare another patch.

Then there remains an open question as to whether, for debugloc-less instructions in non-nodebug functions, should they be treated the same as instructions in nodebug functions (attributed to the call site) or unattributed? (for now I'm happy to leave them handled the same way as nodebug instructions, attributing them to the call site)

wolfgangp updated this revision to Diff 14973.Oct 15 2014, 6:03 PM
wolfgangp edited edge metadata.

New patch avoiding updating the DebugLoc for static allocas when they are inlined.
No change to the test case.

dblaikie added inline comments.Oct 17 2014, 10:12 AM
lib/Transforms/Utils/InlineFunction.cpp
813

To avoid isa + cast, we'd probably write this as:

if (auto *AI = dyn_cast<AllocaInst>(BI))
  if (isa<Constant>(AI->getArraySize()))
    continue;

Also, given that static allocas are so special, is there not a more direct way to test for them than isa<Constant> on the array size?

test/DebugInfo/debug-info-always-inline.ll
8

Should we add nodebug to this function just to demonstrate why the other solutions we discussed were insufficient? (eg: we can't describe it as inlining, etc)

One day we might want to describe it as inlining in the non-nodebug case (and update the prologue handling code to cope with this), but we'd still need to do this for nodebug.

Or test both & describe that one /could/ change but mention the prologue complications.

Or just write a comment describing the gotchas here.

wolfgangp added inline comments.Oct 17 2014, 11:53 AM
lib/Transforms/Utils/InlineFunction.cpp
813

Regarding the static allocas, there is AllocaInst::isStaticAlloca() but it's not usable here because it tests for the instruction being part of the entry block, which the freshly inlined alloca is not (yet). Otherwise, isStaticAlloca also just tests for a constant array size.

Thanks for the pointer on the dyn_cast, I'll make that change.

Actually, I'm thinking now that my change is in the wrong place. It should be inside the if (DL.isUnkown()) branch. We only want to skip allocas that have no debug information to begin with, not the ones that do.

test/DebugInfo/debug-info-always-inline.ll
8

I think nodebug is handled by the front end, so the difference in the IR is simply that the individual instructions have no debug information. There does not seem to be any distinction on the function level.

I'll describe the problems with the allocas/prologue in more detail. There is the more general question of what to do with inlined instructions without debug info, but that seems beyond the scope of this patch. Do you want me to add the gist of the discussion to the test case?

wolfgangp updated this revision to Diff 15108.Oct 17 2014, 5:22 PM

Updated InlineFunction.cpp to incorporate David's comments and moved the check for static allocas to inside the check for unknown DebugLoc.

Added commentary to the test case describing the issues addressed by the patch.

Hope I caught all the points in the discussion...

dblaikie accepted this revision.Oct 20 2014, 1:17 PM
dblaikie edited edge metadata.

Looks good. Thanks for your patience/help.

This revision is now accepted and ready to land.Oct 20 2014, 1:17 PM
wolfgangp closed this revision.Aug 14 2015, 2:31 PM

Committed with r220255.