Although I am not yet deeply familiar with the Swift language, I am highly interested in the 'WebAssembly Reference Types Support in Swift Compiler' project. I find the prospect of introducing a new type like externref and enabling seamless cross-language interoperability to be both fascinating and truly exciting.
Basically, my current thinking is that to support externref, Swift needs to maintain an externref array. When storing it into linear memory, the array index would be used, and it would then be restored back to the externref type when loading from memory onto the stack. I am not yet certain about the specific type-checking requirements, so it might be time for me to dive into Clang's implementation.
I’m currently in the process of setting up my development environment and getting up to speed with the Swift codebase. I look forward to your feedback!
Hello, thank you for your interest in this GSoC idea!
There's no need for Swift to maintain such table, since it's already available in the Wasm runtime for this purpose. I recommend checking out Reference Types proposal overview document that I hope clarifies the setup.
Yes, that's pretty much it, but I'm leaning towards making it less magical than you've described and more explicit. Per idea's description new WasmExternref would not live in linear memory and would have to replicate Clang's semantics and restrictions. Stretch goals then as stated would include builtins that store these references in the Wasm table, get a table index out of it, and allow users to operate on WasmExternrefIndex storable in linear memory when needed.
Yes, this also possibly needs to be aligned with existing ~Copyable and/or ~Escapable semantics. WasmExternref could probably be ~Escapable and WasmExternrefIndex could probably be ~Copyable, but establishing that is a part of the exercise.
I'm looking forward to your application, and please feel free to ask any questions here or in DM.
I have a question: Converting an externref to an index likely requires table.grow and table.set. However, which specific table would Swift call these methods on? My imagined implementation is: once such conversion code exists in the Swift source, the generated LLVM IR would contain:
Code snippet
@extrefs = local_unnamed_addr addrspace(1) global [0 x %externref] undef
All related table read/write operations would then be performed on this specific table. Or does WebAssembly provide some kind of 'built-in table' that can be used directly?
I understand, we need to add a builtin cast operation in SIL and lowering it to proper llvm ir later.
I’ve noticed that Clang provides __builtin_table_grow and other table-related operations, and it performs type checking on these operations. Does Swift also need to support these specific operations? Or is it sufficient to just support the conversion between WasmExternRef and WasmExternRefIndex?
Let me think out loud here: if WasmExternRef is ~Escapable, it implies that we cannot write any wrapper functions for __builtin_index_to_ref(). Furthermore, since __builtin_ref_to_index would cause a WasmExternRef to escape into a global table, it would also have to be prohibited. I’m not entirely sure what the rationale behind this is. To me, it seems more reasonable to apply ~Escapable and ~Copyable semantics to WasmExternRefIndex instead; this would effectively prevent an index from pointing to an invalid WasmExternRef.
I've addressed that above: in the linked code sample you can see that __builtin_wasm_table_set and most if not all related builtins in Clang takes a table as an argument instead of assuming a single table.
The "conversion" is a higher level operation and has to be implemented in terms of these built-in primitives. That means you have to expose these primitives in Swift first before anything else is possible.
~Escapable would not allow such externref value to escape into Wasm linear memory where all other values reside. IIRC consuming on ~Escapable still allows such value to "disappear", thus __builtin_wasm_table_set would consume it, and __builtin_wasm_table_get would produce it, but WasmExternRef: ~Escapable would ensure that it can't be stored anywhere else, only passed around as an argument. In Clang semantics it can also be returned, so there's a little research project here to understand how well ~Escapable lifetimes would cover that. If not, then semantics of WasmExternref would have to be even more special that ~Escapable.
I noticed that Clang designates __externref_t as a sizeless type to prevent classes from having data members of this type (as well as various other operations, except for built-ins). This effectively blocks __externref_t from being stored in linear memory. Clang uses special-case handling for function return values and parameters to ensure __externref_t can still be used in those positions. I believe the Swift implementation could adopt a similar approach.