Skip to content

bugc: give a called function's entry its source range - #400

Merged
gnidan merged 2 commits into
mainfrom
bugc-entry-range
Oct 16, 2026
Merged

gnidan merged 2 commits into
mainfrom
bugc-entry-range

Conversation

@gnidan

@gnidan gnidan commented Oct 16, 2026 •

Copy link
Copy Markdown
Member

A real (not inlined) internal call's entry JUMPDEST carried the invoke but no code, so a debugger stopped there highlighted nothing. It now has the function's declaration, the same range the invoke's declaration names.

A survey of the examples (and the arcade game) found other instructions bugc adds with no range. This PR gives them one too:

  • The end of a body with no return. The frame teardown and exit JUMP of a function, or the STOP of code, has the function's range, as the prologue and the guard STOP already do. So a return the source does not write highlights the whole function: the IR has no range for the body's closing brace. Recording that range in the IR and using it here could be a follow-up. The instructions that return a value from code now have the return statement's range; before, they had none.
  • A block's JUMPDEST (a branch target, a loop header, a join) has the range of the instruction after it, which is what runs next. This applies when that instruction is in the same inlined bodies and does not start or end a call. A JUMPDEST with nothing after it (a block that holds only the STOP that code leaves out) has the function's range.

Two kinds still have no range:

  • The end of the create program, which copies the runtime code and returns it (7 instructions, with a remark). The create block's range is the likely choice, or code's when there is no create.
  • A JUMPDEST just before an inlined call, at O2 and up. The JUMPDEST is at the call site. The first instruction after it is in the inlined body and does not always give the call site's range.

A runtime with no code block has no source to point at, so its compiler-made instructions keep their remark.

The new code-ranges.test.ts checks every instruction of every example at every level, with those exceptions named. It also checks the rules: an entry's range is the invoke's declaration, a block's JUMPDEST has the range of the instruction after it, and the exit of a body with no return has the function's range. The bytecode digests in activation-pairing.test.ts are unchanged, so code generation is unchanged. One check in local-variables.soundness.test.ts makes sure no instruction with a range before let x lists x. The function's range also starts there, so the check now exempts that range after the body only. It still checks the entry and the prologue.

A called function's entry JUMPDEST has the function's declaration, the
one its invoke names. The end of a body that has no return (the frame
teardown and exit JUMP, or the STOP of code) has the function's range,
and a block's JUMPDEST has the range of the instruction after it.
Code generation is unchanged.
@github-actions

github-actions Bot commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor
PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-10-16 00:37 UTC

Check that a block's JUMPDEST has the range of the instruction after
it, and that the exit of a body with no return has the function's.
Exempt only a JUMPDEST before an inlined call. In the locals check,
exempt only the function's range after the body, not the entry and
the prologue.
@gnidan
gnidan merged commit bb2d677 into main Oct 16, 2026
8 checks passed
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.

1 participant