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 a libdyntype to support dynamic accessing and type checking on them, operation on these objects will be converted to API call to libdyntype
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 have RTT now, the only thing we need to add is a type index in RTT object, so the impact on memory should be low
Is there any workaround without the proposed opcode?
-
Treat interface as any 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
Purpose of this issue
Hi, in GC-05-16 meeting we have introduced our idea of adding
struct.get_indirectandstruct.set_indirectopcode to support language semantics likeTypeScript's interfacein 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:
anytype, they are represented as an externref, and we introduced alibdyntypeto support dynamic accessing and type checking on them, operation on these objects will be converted to API call tolibdyntypeThis provide the flexibility to the developer:
anyis also supported but they will pay for the performance influenceProblem
Most types work well based on the strategy above, but we encountered problem with
interface.interfacedoes 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:any(interfaceis heavily used in TypeScript, if we treat is as any, the performance impact may be huge)field indexaccording to the meta data and field name (preferred)The
option 2is 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 thefield indexduring runtime, but currently thestruct.get/setopcode require thefield indexto be immediate.Proposed Opcode
To accept
field indexcalculated during runtime, we proposed theindirectversion of struct access opcode:This require some runtime type checking:
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 have
RTTnow, the only thing we need to add is atype indexin RTT object, so the impact on memory should be lowIs there any workaround without the proposed opcode?
Treat
interfaceasanyas mentioned option 1 aboveCompile whole language runtime into WebAssembly, and execute the source code in that "vm inside vm"