diff options
author | Alon Zakai <azakai@google.com> | 2020-11-23 11:14:19 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-11-23 11:14:19 -0800 |
commit | b2d797f1f9f1192b8f4d2664f76a8d0b9278a0ef (patch) | |
tree | 10c773c5a21deb179043929e3e21db51ff4ccd59 /src/wasm/wasm-binary.cpp | |
parent | 68294338a1cc7337e808671e75933b1134d18a90 (diff) | |
download | binaryen-b2d797f1f9f1192b8f4d2664f76a8d0b9278a0ef.tar.gz binaryen-b2d797f1f9f1192b8f4d2664f76a8d0b9278a0ef.tar.bz2 binaryen-b2d797f1f9f1192b8f4d2664f76a8d0b9278a0ef.zip |
[TypedFunctionReferences] Add Typed Function References feature and use the types (#3388)
This adds the new feature and starts to use the new types where relevant. We
use them even without the feature being enabled, as we don't know the features
during wasm loading - but the hope is that given the type is a subtype, it should
all work out. In practice, if you print out the internal type you may see a typed
function reference-specific type for a ref.func for example, instead of a generic
funcref, but it should not affect anything else.
This PR does not support non-nullable types, that is, everything is nullable
for now. As suggested by @tlively this is simpler for now and leaves nullability
for later work (which will apparently require let or something else, and many
passes may need to be changed).
To allow this PR to work, we need to provide a type on creating a RefFunc. The
wasm-builder.h internal API is updated for this, as are the C and JS APIs,
which are breaking changes. cc @dcodeIO
We must also write and read function types properly. This PR improves
collectSignatures to find all the types, and also to sort them by the
dependencies between them (as we can't emit X in the binary if it depends
on Y, and Y has not been emitted - we need to give Y's index). This sorting
ends up changing a few test outputs.
InstrumentLocals support for printing function types that are not funcref
is disabled for now, until we figure out how to make that work and/or
decide if it's important enough to work on.
The fuzzer has various fixes to emit valid types for things (mostly
whitespace there). Also two drive-by fixes to call makeTrivial where it
should be (when we fail to create a specific node, we can't just try to make
another node, in theory it could infinitely recurse).
Binary writing changes here to replace calls to a standalone function to
write out a type with one that is called on the binary writer object itself,
which maintains a mapping of type indexes (getFunctionSignatureByIndex).
Diffstat (limited to 'src/wasm/wasm-binary.cpp')
-rw-r--r-- | src/wasm/wasm-binary.cpp | 145 |
1 files changed, 129 insertions, 16 deletions
diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index b343c6caf..a96039bc2 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -221,7 +221,7 @@ void WasmBinaryWriter::writeTypes() { for (auto& sigType : {sig.params, sig.results}) { o << U32LEB(sigType.size()); for (const auto& type : sigType) { - o << binaryType(type); + writeType(type); } } } @@ -250,7 +250,7 @@ void WasmBinaryWriter::writeImports() { BYN_TRACE("write one global\n"); writeImportHeader(global); o << U32LEB(int32_t(ExternalKind::Global)); - o << binaryType(global->type); + writeType(global->type); o << U32LEB(global->mutable_); }); ModuleUtils::iterImportedEvents(*wasm, [&](Event* event) { @@ -389,7 +389,7 @@ void WasmBinaryWriter::writeGlobals() { BYN_TRACE("write one\n"); size_t i = 0; for (const auto& t : global->type) { - o << binaryType(t); + writeType(t); o << U32LEB(global->mutable_); if (global->type.size() == 1) { writeExpression(global->init); @@ -492,7 +492,12 @@ uint32_t WasmBinaryWriter::getEventIndex(Name name) const { uint32_t WasmBinaryWriter::getTypeIndex(Signature sig) const { auto it = typeIndices.find(sig); - assert(it != typeIndices.end()); +#ifndef NDEBUG + if (it == typeIndices.end()) { + std::cout << "Missing signature: " << sig << '\n'; + assert(0); + } +#endif return it->second; } @@ -799,6 +804,8 @@ void WasmBinaryWriter::writeFeaturesSection() { return BinaryConsts::UserSections::GCFeature; case FeatureSet::Memory64: return BinaryConsts::UserSections::Memory64Feature; + case FeatureSet::TypedFunctionReferences: + return BinaryConsts::UserSections::TypedFunctionReferencesFeature; default: WASM_UNREACHABLE("unexpected feature flag"); } @@ -950,6 +957,100 @@ void WasmBinaryWriter::finishUp() { } } +void WasmBinaryWriter::writeType(Type type) { + if (type.isRef()) { + auto heapType = type.getHeapType(); + // TODO: fully handle non-signature reference types (GC), and in reading + if (heapType.isSignature()) { + if (type.isNullable()) { + o << S32LEB(BinaryConsts::EncodedType::nullable); + } else { + o << S32LEB(BinaryConsts::EncodedType::nonnullable); + } + writeHeapType(heapType); + return; + } + } + int ret = 0; + TODO_SINGLE_COMPOUND(type); + switch (type.getBasic()) { + // None only used for block signatures. TODO: Separate out? + case Type::none: + ret = BinaryConsts::EncodedType::Empty; + break; + case Type::i32: + ret = BinaryConsts::EncodedType::i32; + break; + case Type::i64: + ret = BinaryConsts::EncodedType::i64; + break; + case Type::f32: + ret = BinaryConsts::EncodedType::f32; + break; + case Type::f64: + ret = BinaryConsts::EncodedType::f64; + break; + case Type::v128: + ret = BinaryConsts::EncodedType::v128; + break; + case Type::funcref: + ret = BinaryConsts::EncodedType::funcref; + break; + case Type::externref: + ret = BinaryConsts::EncodedType::externref; + break; + case Type::exnref: + ret = BinaryConsts::EncodedType::exnref; + break; + case Type::anyref: + ret = BinaryConsts::EncodedType::anyref; + break; + case Type::eqref: + ret = BinaryConsts::EncodedType::eqref; + break; + case Type::i31ref: + ret = BinaryConsts::EncodedType::i31ref; + break; + default: + WASM_UNREACHABLE("unexpected type"); + } + o << S32LEB(ret); +} + +void WasmBinaryWriter::writeHeapType(HeapType type) { + if (type.isSignature()) { + auto sig = type.getSignature(); + o << S32LEB(getTypeIndex(sig)); + return; + } + int ret = 0; + switch (type.kind) { + case HeapType::FuncKind: + ret = BinaryConsts::EncodedHeapType::func; + break; + case HeapType::ExternKind: + ret = BinaryConsts::EncodedHeapType::extern_; + break; + case HeapType::ExnKind: + ret = BinaryConsts::EncodedHeapType::exn; + break; + case HeapType::AnyKind: + ret = BinaryConsts::EncodedHeapType::any; + break; + case HeapType::EqKind: + ret = BinaryConsts::EncodedHeapType::eq; + break; + case HeapType::I31Kind: + ret = BinaryConsts::EncodedHeapType::i31; + break; + case HeapType::SignatureKind: + case HeapType::StructKind: + case HeapType::ArrayKind: + WASM_UNREACHABLE("TODO: compound GC types"); + } + o << S32LEB(ret); // TODO: Actually encoded as s33 +} + // reader bool WasmBinaryBuilder::hasDWARFSections() { @@ -1253,6 +1354,10 @@ Type WasmBinaryBuilder::getType() { return Type::anyref; case BinaryConsts::EncodedType::eqref: return Type::eqref; + case BinaryConsts::EncodedType::nullable: + return Type(getHeapType(), /* nullable = */ true); + case BinaryConsts::EncodedType::nonnullable: + return Type(getHeapType(), /* nullable = */ false); case BinaryConsts::EncodedType::i31ref: return Type::i31ref; default: @@ -1581,6 +1686,18 @@ void WasmBinaryBuilder::readFunctionSignatures() { } } +Signature WasmBinaryBuilder::getFunctionSignatureByIndex(Index index) { + Signature sig; + if (index < functionImports.size()) { + return functionImports[index]->sig; + } + Index adjustedIndex = index - functionImports.size(); + if (adjustedIndex >= functionSignatures.size()) { + throwError("invalid function index"); + } + return functionSignatures[adjustedIndex]; +} + void WasmBinaryBuilder::readFunctions() { BYN_TRACE("== readFunctions\n"); size_t total = getU32LEB(); @@ -2471,6 +2588,9 @@ void WasmBinaryBuilder::readFeatures(size_t payloadLen) { wasm.features.setGC(); } else if (name == BinaryConsts::UserSections::Memory64Feature) { wasm.features.setMemory64(); + } else if (name == + BinaryConsts::UserSections::TypedFunctionReferencesFeature) { + wasm.features.setTypedFunctionReferences(); } } } @@ -3042,17 +3162,7 @@ void WasmBinaryBuilder::visitSwitch(Switch* curr) { void WasmBinaryBuilder::visitCall(Call* curr) { BYN_TRACE("zz node: Call\n"); auto index = getU32LEB(); - Signature sig; - if (index < functionImports.size()) { - auto* import = functionImports[index]; - sig = import->sig; - } else { - Index adjustedIndex = index - functionImports.size(); - if (adjustedIndex >= functionSignatures.size()) { - throwError("invalid call index"); - } - sig = functionSignatures[adjustedIndex]; - } + auto sig = getFunctionSignatureByIndex(index); auto num = sig.params.size(); curr->operands.resize(num); for (size_t i = 0; i < num; i++) { @@ -5169,7 +5279,10 @@ void WasmBinaryBuilder::visitRefFunc(RefFunc* curr) { throwError("ref.func: invalid call index"); } functionRefs[index].push_back(curr); // we don't know function names yet - curr->finalize(); + // To support typed function refs, we give the reference not just a general + // funcref, but a specific subtype with the actual signature. + curr->finalize( + Type(HeapType(getFunctionSignatureByIndex(index)), /* nullable = */ true)); } void WasmBinaryBuilder::visitRefEq(RefEq* curr) { |