Stream: git-wasmtime

Topic: wasmtime / PR #14215 Fuel const expr operator cost


view this post on Zulip Wasmtime GitHub notifications bot (Aug 26 2026 at 19:49):

arcusbuilds opened PR #14215 from arcusbuilds:fuel-const-expr-operator-cost to bytecodealliance:main:

Closes #14204.

Operators inside constant expressions were each charged a hardcoded 1 fuel unit, so Config::operator_cost had no effect on any instantiation-time work: global initializers, element and data segment offsets, and element segment expressions. The same hardcoded 1 was used for the synthesized call to a module's start function in FuncEnvironment::module_start, which manually replicates the accounting fuel_before_op performs for Operator::Call.

Constant expressions are stored as ConstOp rather than wasmparser::Operator, so OperatorCost::cost cannot be called on them. This adds a const_op_cost lookup alongside it, reading the same table, and uses it in translate_const_expr. The match over ConstOp has no catch-all arm, so a future proposal that adds a variant will fail to compile rather than silently charge the wrong cost. module_start synthesizes a real call, so it uses the existing cost lookup.

Default behavior is unchanged. Every ConstOp still costs 1 under the default table, which the existing fuel.wast assertions confirm.

Two notes for reviewers:

  1. The issue's "Extra Info" also reports that the OperatorCost::variable per-element costs are not applied inside const-exprs. That part appears to be incorrect, so this PR does not change it. ConstOp::ArrayNew and ConstOp::ArrayNewDefault dispatch into translate_array_new and translate_array_new_default, which already call pre_translate_bulk_op with array_new_per_element and array_new_default_per_element. tests/all/fuel.wast already asserts 211 for a module whose global initializer is (array.new_default $a (i32.const 100)), and that total only decomposes if the 100 per-element charges are already being applied. Adding the charge in translate_const_expr would double it. array.new_fixed has no per-element variable cost on any path, since its elements are separate operators that are each charged individually.

  2. #14203 is a separate bug in the same area: when a module has no start function, the buffered const-expr charges are never flushed into fuel_var and are dropped. It is left for a follow-up. Both new tests here declare a start function, which is what forces the flush and makes the charges observable.

Config::operator_cost shipped in 48.0.0, so this changes the observable behavior of released public API. Added a note under 49.0.0's Changed section per docs/stability-release.md.

Tests: const_expr_honors_operator_cost sets I32Const to 7 and I32Add to 100 and asserts instantiation consumes 117, with the same module under the default table asserting 6. Two costs rather than one so a mis-wired arm in the new match cannot produce a passing total. module_start_call_honors_operator_cost sets Call to 50 on a module containing only a start function and asserts 52.

Both are plain #[test] rather than #[wasmtime_test] like the neighbouring cost tests. Module startup is compiled by Cranelift under every strategy, so parameterizing over compilers would add variants that exercise the same code.

view this post on Zulip Wasmtime GitHub notifications bot (Aug 26 2026 at 19:49):

arcusbuilds requested cfallin for a review on PR #14215.

view this post on Zulip Wasmtime GitHub notifications bot (Aug 26 2026 at 19:49):

arcusbuilds requested wasmtime-compiler-reviewers for a review on PR #14215.

view this post on Zulip Wasmtime GitHub notifications bot (Aug 26 2026 at 19:49):

arcusbuilds requested wasmtime-core-reviewers for a review on PR #14215.

view this post on Zulip Wasmtime GitHub notifications bot (Aug 26 2026 at 19:49):

arcusbuilds requested alexcrichton for a review on PR #14215.

view this post on Zulip Wasmtime GitHub notifications bot (Aug 26 2026 at 19:49):

arcusbuilds requested wasmtime-default-reviewers for a review on PR #14215.

view this post on Zulip Wasmtime GitHub notifications bot (Aug 26 2026 at 19:54):

arcusbuilds edited PR #14215:

Closes #14204.

Operators in constant expressions were charged a hardcoded 1 fuel unit each, ignoring Config::operator_cost. That covers everything that runs at instantiation: global initializers, element and data segment offsets, and element segment expressions. FuncEnvironment::module_start had the same literal for the synthesized call to a module's start function, where it hand-rolls the accounting fuel_before_op does for Operator::Call.

Const-exprs are stored as ConstOp, not wasmparser::Operator, so OperatorCost::cost can't be called on them. I added a const_op_cost lookup that reads the same table, and used it in translate_const_expr. It has no catch-all arm, so a future proposal that adds a ConstOp variant will fail to compile rather than silently mis-charge it. module_start synthesizes a real call, so it just uses the existing lookup.

Nothing changes under the default table, where every ConstOp still costs 1. Config::operator_cost shipped in 48.0.0, so I added a note under 49.0.0's Changed section.

One thing I deliberately didn't change: the issue also reports that the variable per-element costs are missing inside const-exprs. I don't think that's right. ConstOp::ArrayNew and ArrayNewDefault dispatch into translate_array_new and translate_array_new_default, which already call pre_translate_bulk_op. tests/all/fuel.wast asserts 211 for a module whose global initializer is (array.new_default $a (i32.const 100)), and that total only works if those 100 per-element charges are already being applied, so charging again in translate_const_expr would double them. array.new_fixed has no per-element cost on any path, since its elements are separate operators that each get charged.

Tests: const_expr_honors_operator_cost sets I32Const to 7 and I32Add to 100 and expects instantiation to consume 117, with the same module under the default table expecting 6. Two costs rather than one, so a mis-wired match arm can't still add up. module_start_call_honors_operator_cost sets Call to 50 on a module containing only a start function and expects 52. Both modules declare a start function because that's what flushes the buffered charges into the counter; without one they're dropped entirely (#14203, which I've left alone).

Both are plain #[test] rather than #[wasmtime_test] like the neighbouring cost tests. Module startup is compiled by Cranelift under every strategy, so the extra variants would exercise the same code.

view this post on Zulip Wasmtime GitHub notifications bot (Aug 26 2026 at 20:24):

cfallin commented on PR #14215:

@arcusbuilds can you please confirm that you have followed our AI tools policy, including the point about not ever using LLM output directly in a PR description or any other communications on GitHub?

(I ask because most folks would not write a paragraph about how they wrote a test that adds 7 and 100. If this is a false positive, my apologies.)

Once you have confirmed this and/or made adjustments to conform to our policy, I will then review the PR -- thanks.

view this post on Zulip Wasmtime GitHub notifications bot (Aug 26 2026 at 22:35):

github-actions[bot] added the label wasmtime:api on PR #14215.

view this post on Zulip Wasmtime GitHub notifications bot (Aug 26 2026 at 22:35):

github-actions[bot] added the label wasmtime:config on PR #14215.

view this post on Zulip Wasmtime GitHub notifications bot (Aug 26 2026 at 22:35):

github-actions[bot] added the label wasmtime:docs on PR #14215.

view this post on Zulip Wasmtime GitHub notifications bot (Aug 26 2026 at 23:51):

github-actions[bot] commented on PR #14215:

Label Messager: wasmtime:config

It looks like you are changing Wasmtime's configuration options. Make sure to
complete this check list:

[fuzzing-config]: https://github.com/bytecodealliance/wasmtime/blob/ca0e8d0a1d8cefc0496dba2f77a670571d8fdcab/crates/fuzzing/src/generators.rs#L182-L194
[fuzzing-docs]: https://docs.wasmtime.dev/contributing-fuzzing.html


<details>

To modify this label's message, edit the <code>.github/label-messager/wasmtime-config.md</code> file.

To add new label messages or remove existing label messages, edit the
<code>.github/label-messager.json</code> configuration file.

Learn more.

</details>

view this post on Zulip Wasmtime GitHub notifications bot (Aug 27 2026 at 21:26):

arcusbuilds edited PR #14215:

Closes #14204.

Const-expr operators and the synthesized start call now use the configured operator costs instead of a hardcoded fuel charge of 1. Adds regression tests and a release note.

The issue also reports that variable per-element costs are missing from const-exprs, but those costs are already applied by the shared array.new translation path used by both const-exprs and function bodies. Existing fuel tests confirm this behavior: tests/all/fuel.wast:289 asserts 211 fuel for a module whose global initializer is (array.new_default $a (i32.const 100)), and that total only works if the 100 per-element charges are already applied.

view this post on Zulip Wasmtime GitHub notifications bot (Aug 27 2026 at 21:27):

arcusbuilds edited PR #14215:

Closes #14204.

Const-expr operators and the synthesized start call now use the configured operator costs instead of a hardcoded fuel charge of 1. Adds regression tests and a release note.

The issue also reports that variable per-element costs are missing from const-exprs, but those costs are already applied by the shared array.new translation path used by both const-exprs and function bodies. Existing fuel tests confirm this behavior: tests/all/fuel.wast:289 .

view this post on Zulip Wasmtime GitHub notifications bot (Aug 27 2026 at 21:27):

arcusbuilds commented on PR #14215:

@cfallin, I have done the adjustment as per the policy.

view this post on Zulip Wasmtime GitHub notifications bot (Aug 27 2026 at 21:33):

:memo: cfallin submitted PR review.

view this post on Zulip Wasmtime GitHub notifications bot (Aug 27 2026 at 21:33):

:speech_balloon: cfallin created PR review comment:

Rather than doing this, could we translate to Operator and then use the existing cost machinery?

view this post on Zulip Wasmtime GitHub notifications bot (Aug 27 2026 at 21:33):

:speech_balloon: cfallin created PR review comment:

No need for the excessively verbose (and LLM-produced) comment here -- anyone wanting to know what the test does can look at it.

view this post on Zulip Wasmtime GitHub notifications bot (Aug 27 2026 at 21:33):

:speech_balloon: cfallin created PR review comment:

Likewise here.

view this post on Zulip Wasmtime GitHub notifications bot (Aug 28 2026 at 19:47):

arcusbuilds updated PR #14215.

view this post on Zulip Wasmtime GitHub notifications bot (Aug 28 2026 at 19:52):

:memo: cfallin submitted PR review:

Almost there! Happy to merge with the below comment addressed.

view this post on Zulip Wasmtime GitHub notifications bot (Aug 28 2026 at 19:52):

:speech_balloon: cfallin created PR review comment:

Rather than putting this in tunables, could we put it near the definition of ConstOp, and make it a method on the type (e.g. const_op.to_operator())?

view this post on Zulip Wasmtime GitHub notifications bot (Aug 28 2026 at 20:51):

arcusbuilds updated PR #14215.

view this post on Zulip Wasmtime GitHub notifications bot (Aug 28 2026 at 21:01):

:memo: arcusbuilds submitted PR review.

view this post on Zulip Wasmtime GitHub notifications bot (Aug 28 2026 at 21:01):

:speech_balloon: arcusbuilds created PR review comment:

done!

view this post on Zulip Wasmtime GitHub notifications bot (Aug 28 2026 at 21:03):

:thumbs_up: cfallin submitted PR review.

view this post on Zulip Wasmtime GitHub notifications bot (Aug 28 2026 at 21:04):

cfallin added PR #14215 Fuel const expr operator cost to the merge queue.

view this post on Zulip Wasmtime GitHub notifications bot (Aug 28 2026 at 21:29):

:check: cfallin merged PR #14215.

view this post on Zulip Wasmtime GitHub notifications bot (Aug 28 2026 at 21:29):

cfallin removed PR #14215 Fuel const expr operator cost from the merge queue.


Last updated: Aug 30 2026 at 09:07 UTC