Skip to content

Conversation

@jakobbotsch
Copy link
Member

In debug codegen we expect basic blocks to have valid start IL offsets, potentially to an invalid IL end offset.

When we split a block at a node, we find the end offset by looking backwards from that node for an IL_OFFSET node. If we find such a node, we use it as the split point IL offset. Otherwise we use BAD_IL_OFFSET and start the next block at the end offset (making it have a 0 byte range).

This latter behavior is problematic if the bottom portion of the split has its own IL_OFFSET nodes. In that case we can end up with IL_OFFSET nodes pointing outside the region of the basic block. If we later split again, then we can end up creating illegal IL offset ranges for unoptimized code. In an example case we first ended up with the bottom block having an IL offset range of [0aa..0aa) but still containing an IL_OFFSET with an offset of 092. When we later split the bottom block again we ended up with [0aa..0aa), [092..0aa) and hit an assert.

To address the problem change the behavior in the following way:

  • If we find no IL_OFFSET node in the upper block, then look for one in the lower block, and use its value as the split point
  • If we find no IL_OFFSET in both blocks then consider the lower block empty and the upper block to have everything (same behavior as before).
  • One exception to above: if the end was already BAD_IL_OFFSET then we have to consider the upper block to be empty as we cannot represent the other case

This fixes #119616. However for that specific case, which is inside async code, we will have the exact IL offset of the async call we are splitting at after #120303, so we will be able to do better. I'll make that change as part of that PR.

In debug codegen we expect basic blocks to have valid start IL offsets,
potentially to an invalid IL end offset.

When we split a block at a node, we find the end offset by looking
backwards from that node for an `IL_OFFSET` node. If we find such a
node, we use it as the split point IL offset. Otherwise we use
`BAD_IL_OFFSET` and start the next block at the end offset (making it
have a 0 byte range).

This latter behavior is problematic if the bottom portion of the split
has its own `IL_OFFSET` nodes. In that case we can end up with
`IL_OFFSET` nodes pointing outside the region of the basic block. If we
later split again, then we can end up creating illegal IL offset ranges
for unoptimized code. In an example case we first ended up with the
bottom block having an IL offset range of `[0aa..0aa)` but still
containing an `IL_OFFSET` with an offset of `092`. When we later split
the bottom block again we ended up with `[0aa..0aa), [092..0aa)` and hit
an assert.

To address the problem change the behavior in the following way:
* If we find no `IL_OFFSET` node in the upper block, then look for one
  in the lower block, and use its value as the split point
* If we find no `IL_OFFSET` in both blocks then consider the lower block
  empty and the upper block to have everything (same behavior as
  before).
* One exception to above: if the end was already `BAD_IL_OFFSET` then we
  have to consider the upper block to be empty as we cannot represent
  the other case
@github-actions github-actions bot added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Nov 3, 2025
@jakobbotsch
Copy link
Member Author

PTAL @dotnet/jit-contrib. Failures are Android timeouts and known issues.

@jakobbotsch jakobbotsch requested a review from a team November 4, 2025 11:45
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This pull request improves the IL offset assignment logic when splitting basic blocks in LIR (Low-level Intermediate Representation) mode. The changes enhance how the compiler determines split point offsets by attempting to find valid IL_OFFSET nodes in both directions (backward and forward) before falling back to guessing.

Key changes:

  • Adds forward search in the new block when backward search in the current block fails to find a valid IL_OFFSET
  • Improves fallback logic to handle cases where no IL_OFFSET is found in either direction
  • Changes split point offset assignment from using max/min calculations to direct assignment

@jakobbotsch jakobbotsch added area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI and removed needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners labels Nov 4, 2025
@dotnet-policy-service
Copy link
Contributor

Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch
See info in area-owners.md if you want to be subscribed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[RuntimeAsync] Assertion failed 'lastBlockILEndOffset < beginOffs'

1 participant