WebAssembly / interface-types

Other
642 stars 57 forks source link

Discussing `string.size` and `string.lower_memory` #107

Closed Hywan closed 4 years ago

Hywan commented 4 years ago

Discussing about this version of the proposal (the latest at the time of writing): https://github.com/WebAssembly/interface-types/blob/5af0606baceab6c5ecc8bd247fae944998c2aa61/proposals/interface-types/working-notes/Instructions.md#stringlower_memory

A typical string lowering requires the following instructions:

Instructions  Stack Comment
arg.get 0 [string("foo")] Let's assume the first argument is a string
string.size [i32(3)]
call-core 42 [i32(1024)] Let's assume 42 is the function index of an allocator
arg.get 0 [i32(1024), string("foo")] The string is required, again
string.lower_memory  []

My observation is the following. Considering string.lower_memory is going to appear often, and considering an allocator must be called before, the string to be lowered must be loaded twice on the stack. For small strings, it could be OK, for larger strings, it could be a problem.

It's very likely that I'm missing something. With the Covid-19 situation, I have difficulties to follow every discussions of this WG.

If my observation is valid, then we could probably modify string.size such as it peeks one value from the stack rather than popping it; thus its type becomes:

- [string] -> [i32]
+ [string] -> [string, i32]

Note: I know peeking can feel somewhat unusual, and more importantly, it would require another pop n or drop n instruction.

The set of instructions would become:

Instructions Stack Comment
arg.get 0 [string("foo")]
string.size [string("foo"), i32(3)]
call-core 42 [string("foo"), i32(1024)]
swap [i32(1024), string("foo")] swap is a new instruction
string.lower_memory []

Notice the new swap instruction. It is required to fulfill the type of string.lower_memory. It is possible to avoid swap if we update string.lower_memory type to:

- [$base: i32, string] -> []
+ [string, $base: i32] -> []

And thus, the set of instructions become:

Instructions Stack
arg.get 0 [string("foo")]
string.size [string("foo"), i32(3)]
call-core 42 [string("foo"), i32(1024)]
string.lower_memory []

Which is the simplest version for lowering a string.

Thoughts?

fgmccabe commented 4 years ago

A general comment: when a machine like wasm is implemented by lowering into actual machine code, stack movement instructions (like pop, push, pick etc) do not normally lead to generated code. This is because the lowering compiler uses these instructions to maintain a virtual stack. E.g., a push instruction leads to the lowering compiler to remember where the value is; then, when the value is actually needed, the remembered value is loaded from where it would have been loaded in the original push. So, apart from reducing the code size of a wasm module, (which is a legitimate concern) there is no merit from optimizing pushes with other stack manipulation.