A year ago I opened a draft pull request adding __externref_t* support:
#163610
The idea would be that llvm gains a blessed __externref_table of externrefs, the analogue of __indirect_function_table. Dereferencing an externref pointer would turn into a table_get operation on that table. The table could have a stack region and a heap region. If a pointer to an externref is taken inside of a function, the externref could spill to the stack, so we would bump the stack pointer down 1, use a table_set to assign the externref into the table, and then the address of the externref would be the table index. In the epilogue, we'd use a 𝗍𝖺𝖻𝗅𝖾.𝖿𝗂𝗅l instruction to null out the stack references and then restore the stack pointer. For heap references, we could have __externref_t* table_alloc(void) and void table_free(__externref_t*).
This would allow graceful handling of:
- externref in a struct
- externref arrays
- externref in a varargs
- functions with multiple externref return values
with the same ergonomics as normal pointers. Currently all of this stuff works, but the ergonomics are poor and each library has its own ad-hoc handling of these problems so there is no globally agreed-upon notion of an externref pointer.
The biggest problem is that currently static __externref_t blah[]; declares a table, and a table is represented in the clang IR as an array type. Because arrays freely decay to pointers, this means that clang currently interprets __externref_t* as coming from a decayed table and representing also a table. We will need some way to migrate the current behavior to the new behavior. I think tables ideally should be declared with an attribute e.g., __attribute__((wasmtable)) __externref_t table[]; so that pointers can be reserved for things that admit arithmetic, dereferencing, etc.
A year ago I opened a draft pull request adding
__externref_t*support:#163610
The idea would be that llvm gains a blessed
__externref_tableof externrefs, the analogue of__indirect_function_table. Dereferencing an externref pointer would turn into atable_getoperation on that table. The table could have a stack region and a heap region. If a pointer to an externref is taken inside of a function, the externref could spill to the stack, so we would bump the stack pointer down 1, use atable_setto assign the externref into the table, and then the address of the externref would be the table index. In the epilogue, we'd use a𝗍𝖺𝖻𝗅𝖾.𝖿𝗂𝗅linstruction to null out the stack references and then restore the stack pointer. For heap references, we could have__externref_t* table_alloc(void)andvoid table_free(__externref_t*).This would allow graceful handling of:
with the same ergonomics as normal pointers. Currently all of this stuff works, but the ergonomics are poor and each library has its own ad-hoc handling of these problems so there is no globally agreed-upon notion of an externref pointer.
The biggest problem is that currently
static __externref_t blah[];declares a table, and a table is represented in the clang IR as an array type. Because arrays freely decay to pointers, this means that clang currently interprets__externref_t*as coming from a decayed table and representing also a table. We will need some way to migrate the current behavior to the new behavior. I think tables ideally should be declared with an attribute e.g.,__attribute__((wasmtable)) __externref_t table[];so that pointers can be reserved for things that admit arithmetic, dereferencing, etc.