Skip to content

cranelift: charge Else and End operator fuel in target blocks - #14646

Merged
alexcrichton merged 1 commit into
bytecodealliance:mainfrom
ctiller:fix/else-end-operator-fuel
Oct 10, 2026
Merged

alexcrichton merged 1 commit into
bytecodealliance:mainfrom
ctiller:fix/else-end-operator-fuel

Conversation

@ctiller

@ctiller ctiller commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

FuncEnvironment::fuel_before_op previously added the operator cost for every instruction other than Nop and Drop before translate_operator ran, guarded by state.reachable at instruction entry. For Operator::Else and Operator::End, this caused two bugs when custom OperatorCost values were configured:

  1. At entry to Operator::Else, builder is still positioned at the end of the then block before translate_operator emits the jump to exit_block and switches builder to the else block. Charging Else in fuel_before_op therefore charged Else when the then branch was taken and failed to charge Else when the else branch was taken.
  2. When the then block (or the body preceding End) ended with an unconditional control transfer (return, br, etc.), state.reachable was false entering Else or End, so before_translate_operator skipped fuel_before_op even when the else block or merged End block was reachable via another incoming edge.

Defer Operator::Else and Operator::End fuel charging from fuel_before_op to fuel_after_op, after translate_operator has switched builder to the target block and updated state.reachable.

Assisted-by: Gemini

@ctiller
ctiller requested a review from a team as a code owner October 9, 2026 23:46
@ctiller
ctiller requested review from pchickey and removed request for a team October 9, 2026 23:46
`FuncEnvironment::fuel_before_op` previously added the operator cost for every
instruction other than `Nop` and `Drop` before `translate_operator` ran,
guarded by `state.reachable` at instruction entry. For `Operator::Else` and
`Operator::End`, this caused two bugs when custom `OperatorCost` values were
configured:

1. At entry to `Operator::Else`, `builder` is still positioned at the end of
   the `then` block before `translate_operator` emits the jump to `exit_block`
   and switches `builder` to the `else` block. Charging `Else` in
   `fuel_before_op` therefore charged `Else` when the `then` branch was taken
   and failed to charge `Else` when the `else` branch was taken.
2. When the `then` block (or the body preceding `End`) ended with an
   unconditional control transfer (`return`, `br`, etc.), `state.reachable` was
   `false` entering `Else` or `End`, so `before_translate_operator` skipped
   `fuel_before_op` even when the `else` block or merged `End` block was
   reachable via another incoming edge.

Defer `Operator::Else` and `Operator::End` fuel charging from `fuel_before_op`
to `fuel_after_op`, after `translate_operator` has switched `builder` to the
target block and updated `state.reachable`.

Assisted-by: Gemini
@ctiller
ctiller force-pushed the fix/else-end-operator-fuel branch from 2ec1d8a to 2db2014 Compare October 10, 2026 00:05
@ctiller
ctiller requested a review from a team as a code owner October 10, 2026 00:05
@ctiller
ctiller requested review from alexcrichton and removed request for a team October 10, 2026 00:06
@cfallin
cfallin added this pull request to the merge queue Oct 10, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch Oct 10, 2026
@alexcrichton
alexcrichton added this pull request to the merge queue Oct 10, 2026
Merged via the queue into bytecodealliance:main with commit e13b882 Oct 10, 2026
53 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.

3 participants