Introducing struct.get_indirect
and struct.set_indirect
opcode #397
Description
Purpose of this issue
Hi, in GC-05-16 meeting we have introduced our idea of adding struct.get_indirect
and struct.set_indirect
opcode to support language semantics like TypeScript's interface
in WasmGC, we had some discussion during the meeting and PR for the meeting notes, and this issue is to continue the discussion.
Background
WasmGC opened the door for supporting high level managed language more efficiently than compiling the whole language runtime to WebAssembly, we are interested in exploring a solution to compile TypeScript to WasmGC. Our strategy is:
- For those with explicit type information (like primitives, classes), we apply static compilation, and use wasm value types (i32, i64 ...) and WasmGC types to represent them
- For those with
any
type, they are represented as an externref, and we introduced alibdyntype
to support dynamic accessing and type checking on them, operation on these objects will be converted to API call tolibdyntype
This provide the flexibility to the developer:
- If performance matters, write more static type information to get a statically compiled module
- If dynamic type is needed,
any
is also supported but they will pay for the performance influence
Problem
Most types work well based on the strategy above, but we encountered problem with interface
. interface
does not describe the layout of a concrete type, its just an un-ordered collection of field names and types. This means the actual object type holding by the interface is not known at compile time, so we have two solutions:
- treat interface as
any
(interface
is heavily used in TypeScript, if we treat is as any, the performance impact may be huge) - record some meta data of every static object type in wasm module, and calculate the
field index
according to the meta data and field name (preferred)
The option 2
is preferred because we can still represent objects using WasmGC types, and we can do further optimization to avoid searching the meta info in some scenarios (e.g. check a pre-assigned type-id). Option 2 needs to calculate the field index
during runtime, but currently the struct.get/set
opcode require the field index
to be immediate.
Proposed Opcode
To accept field index
calculated during runtime, we proposed the indirect
version of struct access opcode:
name | immediates | stack signature |
---|---|---|
struct.get_indirect<_sx>? | <ti> | [anyref, i32] -> [ti] |
struct.set_indirect<_sx>? | <ti> | [anyref, i32, ti] -> [] |
This require some runtime type checking:
- Trap if ref is nul
- Trap if ref is not a struct
- Trap if index is invalid in the struct
- Trap if type of the accessing field is not ti
Then we can use this opcode to access the field of interface:
Influence to runtime implementation
-
performance:
The proposed opcode will be slow due to several runtime checking, but this will not influence the performance of any other opcodes -
memory usage:
To apply runtime checking, it is required that every GC object have a reference to its defined type. Since we already haveRTT
now, the only thing we need to add is atype index
in RTT object, so the impact on memory should be low
Is there any workaround without the proposed opcode?
-
Treat
interface
asany
as mentioned option 1 above- The performance will be slow
-
Compile whole language runtime into WebAssembly, and execute the source code in that "vm inside vm"
- Can't use WasmGC's capabilities, need to compile interpreter, memory management logic all into wasm opcode
- Large wasm module size