Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

spec: Runtime spec for gas and stack accounting #4

Merged
merged 4 commits into from
Jul 28, 2022
Merged

Conversation

nagisa
Copy link
Collaborator

@nagisa nagisa commented Jul 14, 2022

Split out from #2

@nagisa
Copy link
Collaborator Author

nagisa commented Jul 14, 2022

I’m… no longer finding a way to set PR as a draft...

@nagisa nagisa force-pushed the nagisa/gas-stack branch from 02953e7 to 3a04ca6 Compare July 14, 2022 10:30
Copy link

@Ekleog Ekleog left a comment

Choose a reason for hiding this comment

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

Just carrying one leftover comment from the previous PR, and I also noticed that the wording was not exactly right yet for stack height metering. It was written as though the check was supposed to validate the whole call tree.

spec/runtime.mkd Outdated

**TODO**:

## `gas(will_use: u64)`
Copy link

Choose a reason for hiding this comment

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

Note that today it is gas(u32) where u32 measures opcodes rather than gas cost directly. Ie, it's conceptually the host who multiplies ops by gas-cost.

Spec-wise it seems easier to count instructions, but that might close the door to differently-weighting instructions. Or at least make that doorframe narrower -- even with ops, we can say that sqrt is 2x usual op.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I don’t believe the spec should specify a specific scheme in that regard. I adjusted the wording to use a fee(insn) mechanism where fee is defined by the embedder – this way they can specify whatever they want – counting instructions (fee = 1), or something else entirely.

However, in practice it is impossible to continue counting each instruction as 1. Some instructions, even today, scale linearly depending on the operands they pop (memory.grow) and they will only continue increasing (e.g. memory.fill).

And either way, we’ve had problems with fitting some things into u32, so making the gas limit an u64 seems like an obvious improvement in flexibility.

@nagisa nagisa force-pushed the nagisa/gas-stack branch from 3a04ca6 to 9f31926 Compare July 27, 2022 13:34
@nagisa nagisa force-pushed the nagisa/gas-stack branch from 9f31926 to 253a2ee Compare July 27, 2022 13:35
@nagisa
Copy link
Collaborator Author

nagisa commented Jul 27, 2022

I went ahead and reworded the specification to achieve the following:

  • Both gas and stack limits are, for the purposes of the specification ensured at each charged operation (for each instruction executed for gas, for each stack push/pop for stack size). These can then be optimized by the implementation by collapsing within basic blocks etc. cf. used_gas: how can we provide exact measure of gas used/remaining gas back to the contract? #3
  • We don’t talk about implementation details anywhere except side notes. Implementation details is for the implementation to decide on ^^
  • Fixed the wording to allow for stack entries to be counted however the impementation prefers;
  • Fixed the wording charging gas for local initialization – this is now part of semantics for invoking a function address, exactly where it belongs.

This new wording however has some interesting implications. For example, as written, we ought to charge some gas when evaluating the i32.const 0 in

(data (offset (i32.const 0)) ...)

which clearly requires involvement of the VM. Implementation wise, we can also charge this after the fact too in e.g. the start function, much like we might need to do for local initialization.

and is out of scope for this specification.

**Note:** Instructions may decrease gas based on the operands the instruction would consume. This
is particularly useful for bulk instructions such as `memory.grow`.
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This I believe effectively requires special handling for these kinds of instructions in the instrumentation or the VM. That is, you can’t just

(call $gas (i32.const 1))
(memory.grow (i32.const 123))

You actually need either a variadic $gas that is able to take all the operands as an argument (before returning them) or maybe some other nasty hack…

Copy link

Choose a reason for hiding this comment

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

Yeah, I think our current impl rewrites memory.grow into a funciton call.

Copy link

@matklad matklad left a comment

Choose a reason for hiding this comment

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

Overal, LGTM! Fees surprisingly simple even

@@ -0,0 +1,106 @@
# [Execution](https://www.w3.org/TR/wasm-core-1/#execution%E2%91%A1)

The specific mechanism to instrument a WebAssembly module is implementation-specific. This section
Copy link

Choose a reason for hiding this comment

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

Might be worth to explicitly mention that

the specification aims to support at least two general implementation strategies: desugaring in the terms of core WASM, or built-in accounting in the VM itself.

and is out of scope for this specification.

**Note:** Instructions may decrease gas based on the operands the instruction would consume. This
is particularly useful for bulk instructions such as `memory.grow`.
Copy link

Choose a reason for hiding this comment

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

Yeah, I think our current impl rewrites memory.grow into a funciton call.

spec/runtime.mkd Outdated
instantiation process.

Before the `module.start` start function is invoked, charge gas for each local of the invoked
`funcaddr` and .
Copy link

Choose a reason for hiding this comment

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

incomplete sentence

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Aha, because this is no longer necessary due to this logic having moved to the “Invocation of function address a” portion of the spec. Nice catch, removed.

@nagisa nagisa force-pushed the nagisa/gas-stack branch from aa201f8 to 04da196 Compare July 28, 2022 10:48
@nagisa nagisa merged commit 77af2cd into main Jul 28, 2022
@nagisa nagisa deleted the nagisa/gas-stack branch July 28, 2022 10:50
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