From b2d797f1f9f1192b8f4d2664f76a8d0b9278a0ef Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 23 Nov 2020 11:14:19 -0800 Subject: [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). --- src/wasm/wasm-binary.cpp | 145 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 129 insertions(+), 16 deletions(-) (limited to 'src/wasm/wasm-binary.cpp') 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) { -- cgit v1.2.3