Skip to content

Conversation

@vouillon
Copy link
Contributor

@vouillon vouillon commented Apr 26, 2024

Some debug locations could be dropped at the beginning and end of functions:

  • if a source map segment starts before the beginning of the code, it was not taken into account except for the function prologue;
  • the last segment of the source map was ignored after one instruction;
  • for coherence with the text printer, the text parser should propagate the debug location of the function prologue to its first instruction.
  • the text printer should always print the debug location of the function epilogue if there is one (for coherence, since one repeats a debug location at the same indentation level for readability, and for round-tripping since the test parser does not propagate debug locations to the function epilogue).

Also, the binary writer was smearing the debug location of the last instruction to the function epilogue, and the debug location of a previous function could smear to the prologue of the next function.

} else if (self->getFunction()->prologLocation.size()) {
// The first instruction may inherit its location from the
// function prolog
locs[curr] = *self->getFunction()->prologLocation.begin();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does this ensure it only affects the first instruction and not later ones?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should affect later instructions as well.
This piece of code:

 ;;@ a:1:1
 (func
    ;;@ a:1:1
    (nop)
    ;;@ a:1:1
    (nop)
 )

is printed as:

 ;;@ a:1:1
 (func
  (nop)
  (nop)
 )

So they should result in the same debug information when read back.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, you mean by the normal rules of propagating from the parent to children? So the first instruction inherits from the prolog, and children of the first instruction inherit from the first instruction, so effectively they inherit from the prologue too? If so that makes sense but the comment was confusing me. Perhaps The first instruction may inherit its location from the function prolog could be Instructions may inherit their locations from the function prolog. - ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, on reading the code again, I think a simpler way to do this would be, instead of adding code here, to add code to getPrevious. That method could look at the function prologue if it does not find something else.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what you mean since getPrevious returns a pointer to an expression. Do you mean I should move some of the code here to getPrevious so that it returns an optional location instead?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I'm missing something, but right now getPrevious is

Expression* getPrevious() {
  if (expressionStack.empty()) {
    return nullptr;
  }
  assert(expressionStack.size() >= 1);
  return expressionStack[expressionStack.size() - 1];
}

I am suggesting it be something like

Expression* getPrevious() {
  if (expressionStack.empty()) {
    // Use the prologue, if there is one.
    if (self->getFunction()->prologLocation.size()) {
        return *self->getFunction()->prologLocation.begin();
    }
    return nullptr;
  }
  assert(expressionStack.size() >= 1);
  return expressionStack[expressionStack.size() - 1];
}

Then this function needs no changes, and can keep calling getPrevious() on line 63 and if it returns a non-null value, to use that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But prologLocation has type std::set<Function::DebugLocation>.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, right. Then more code would need to move into getPrevious, and then really it would need to be renamed. I agree it's not worth it, sorry for not thinking this totally through.

Copy link
Member

@kripken kripken left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aside from the comments this looks right to me. I also verified it does not break any debug/source-map tests in Emscripten, which gives additional confidence.

if (func->epilogLocation.size()) {
parent.writeDebugLocation(*func->epilogLocation.begin());
} else {
parent.writeNoDebugLocation();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this needed, since we prevent smearing across functions in another way anyhow? That is, this is the end of the function, so marking no-debug will not help any code but the next function, which is already handled by this PR elsewhere I think.

Copy link
Contributor Author

@vouillon vouillon Apr 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is to prevent the debug location on the last instruction to smear on the function epilogue (that is, the end opcode at the end of the function code). So that this:

(func
   ;;@ a:1:1
   (nop)
   ;;@
)

does not get turned into this:

(func
   ;;@ a:1:1
   (nop)
   ;;@ a:1:1
)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, thanks. Maybe add a comment saying that?

vouillon and others added 6 commits May 2, 2024 15:57
The `fn_prolog_epilog.debugInfo` test is failing otherwise, since there
was debug information associated to the nop instruction at the beginning
of the function.
…e map

The last segment should extend to the end of the function.
The text parser no longer propagates locations to the epilogue, so we
should always print the location if there is one.
The debug location of the last instruction should not smear into the
function epilogue, and a debug location from a previous function should
not smear into the prologue of the current function.
@vouillon vouillon force-pushed the source-map-fixes branch from f3a2566 to 4b3a1d9 Compare May 2, 2024 13:58
@vouillon vouillon force-pushed the source-map-fixes branch from 4b3a1d9 to b68e90d Compare May 2, 2024 14:13
@kripken
Copy link
Member

kripken commented May 2, 2024

Before we land this, can you please merge in latest main, just to make sure there is no test conflict with other recent updates?

@vouillon
Copy link
Contributor Author

vouillon commented May 2, 2024

Before we land this, can you please merge in latest main, just to make sure there is no test conflict with other recent updates?

This is done already. I only had to make a small change to the Stack IR source map support (see last commit).

@kripken
Copy link
Member

kripken commented May 2, 2024

Thanks, I see, I missed that this was forced pushed and I was looking for a merge commit...

@kripken kripken merged commit d58c546 into WebAssembly:main May 2, 2024
@vouillon vouillon deleted the source-map-fixes branch May 2, 2024 18:12
@gkdn gkdn mentioned this pull request Aug 31, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants