Open rossberg opened 5 years ago
Perhaps we can leave enough space for a v128
type, and rely on a pointer to allocated memory for anything larger that comes along. That would waste a lot of space, so we could also leave it pointer-sized, and use allocation for SIMD types.
@binji, good point about using an indirection, I'll edit the issue to mention it. Maybe that is indeed good enough, though it still would be nicer to have a "naturally compact" representation.
There are two problems with using a union to represent different Wasm values (especially in arrays):
The latter could be circumvented by requiring an indirection for passing types wider than i64 or pointers, though that raises additional memory management questions.
If that's not an option that the latter might be a showstopper. For example, it might prevent Apple from officially supporting the API, because they have strict backwards-compatibility policies.
To address these issues, function arguments and results should not be passed as a homogeneous array-of-union, but as a heterogeneous list/tuple/struct.
Possible alternatives considered:
Use varargs. For example:
auto Func::call(...) const -> own<Trap*>;
The
...
would be instantiated with the sequence of values with types corresponding to the function's parameters, followed by a sequence of out pointers corresponding to its results. Problems: (1) would not allow types with destructors, which we need to safely handle reference values in the C++ API; (2) varargs are out of reach for most FFI layers, so not a suitable option for the C API.Use std::tuple. For example:
template<class... T, class... R> auto Func::call(std::tuple<T...> args, std::tuple<R&...> results) const -> own<Trap*>;
Problems: (1) does not address C API; (2) cannot be wrapped into C without copying.
A custom variant of tuple that supports standard layout. For example:
template<class... T, class... R> auto Func::call(wasm::seq<T...> args, wasm::seq<R&...> results) const -> own<Trap*>;
This would allow casting from and to a struct defined in C. Problems: (1) on the C side, may need to declare auxiliary structs per call site/type; (2) may be difficult for FFIs.
Pack into byte array. For example:
auto Func::call(const byte args, byte results) const -> own<Trap*>;
Problems: (1) does not safely handle types with destructors, e.g. reference values; (2) requires manual handling of alignment, not clear how in a platform-agnostic manner; (3) tedious to use.
Other solutions?