diff options
author | Thomas Lively <tlively@google.com> | 2022-10-07 08:02:09 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-10-07 06:02:09 -0700 |
commit | 7fc26f3e78f72ecaa5b79ebe042b95a0be422327 (patch) | |
tree | f87c84fc691aaf311fbd71c176ee37723c76ae20 /src/wasm/wasm-binary.cpp | |
parent | e8884de3c880a7de4bb1f8eae3df5f00f4164b4d (diff) | |
download | binaryen-7fc26f3e78f72ecaa5b79ebe042b95a0be422327.tar.gz binaryen-7fc26f3e78f72ecaa5b79ebe042b95a0be422327.tar.bz2 binaryen-7fc26f3e78f72ecaa5b79ebe042b95a0be422327.zip |
Implement bottom heap types (#5115)
These types, `none`, `nofunc`, and `noextern` are uninhabited, so references to
them can only possibly be null. To simplify the IR and increase type precision,
introduce new invariants that all `ref.null` instructions must be typed with one
of these new bottom types and that `Literals` have a bottom type iff they
represent null values. These new invariants requires several additional changes.
First, it is now possible that the `ref` or `target` child of a `StructGet`,
`StructSet`, `ArrayGet`, `ArraySet`, or `CallRef` instruction has a bottom
reference type, so it is not possible to determine what heap type annotation to
emit in the binary or text formats. (The bottom types are not valid type
annotations since they do not have indices in the type section.)
To fix that problem, update the printer and binary emitter to emit unreachables
instead of the instruction with undetermined type annotation. This is a valid
transformation because the only possible value that could flow into those
instructions in that case is null, and all of those instructions trap on nulls.
That fix uncovered a latent bug in the binary parser in which new unreachables
within unreachable code were handled incorrectly. This bug was not previously
found by the fuzzer because we generally stop emitting code once we encounter an
instruction with type `unreachable`. Now, however, it is possible to emit an
`unreachable` for instructions that do not have type `unreachable` (but are
known to trap at runtime), so we will continue emitting code. See the new
test/lit/parse-double-unreachable.wast for details.
Update other miscellaneous code that creates `RefNull` expressions and null
`Literals` to maintain the new invariants as well.
Diffstat (limited to 'src/wasm/wasm-binary.cpp')
-rw-r--r-- | src/wasm/wasm-binary.cpp | 168 |
1 files changed, 117 insertions, 51 deletions
diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index 55dafadd4..f2698bd79 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -1429,6 +1429,25 @@ void WasmBinaryWriter::writeType(Type type) { case HeapType::stringview_iter: o << S32LEB(BinaryConsts::EncodedType::stringview_iter); return; + case HeapType::none: + o << S32LEB(BinaryConsts::EncodedType::nullref); + return; + case HeapType::noext: + // See comment on writeHeapType. + if (!wasm->features.hasGC()) { + o << S32LEB(BinaryConsts::EncodedType::externref); + } else { + o << S32LEB(BinaryConsts::EncodedType::nullexternref); + } + return; + case HeapType::nofunc: + // See comment on writeHeapType. + if (!wasm->features.hasGC()) { + o << S32LEB(BinaryConsts::EncodedType::funcref); + } else { + o << S32LEB(BinaryConsts::EncodedType::nullfuncref); + } + return; } } if (type.isNullable()) { @@ -1468,46 +1487,63 @@ void WasmBinaryWriter::writeType(Type type) { } void WasmBinaryWriter::writeHeapType(HeapType type) { + // ref.null always has a bottom heap type in Binaryen IR, but those types are + // only actually valid with GC enabled. When GC is not enabled, emit the + // corresponding valid top types instead. + if (!wasm->features.hasGC()) { + if (type == HeapType::nofunc || type.isSignature()) { + type = HeapType::func; + } else if (type == HeapType::noext) { + type = HeapType::ext; + } + } + if (type.isSignature() || type.isStruct() || type.isArray()) { o << S64LEB(getTypeIndex(type)); // TODO: Actually s33 return; } int ret = 0; - if (type.isBasic()) { - switch (type.getBasic()) { - case HeapType::ext: - ret = BinaryConsts::EncodedHeapType::ext; - break; - case HeapType::func: - ret = BinaryConsts::EncodedHeapType::func; - break; - case HeapType::any: - ret = BinaryConsts::EncodedHeapType::any; - break; - case HeapType::eq: - ret = BinaryConsts::EncodedHeapType::eq; - break; - case HeapType::i31: - ret = BinaryConsts::EncodedHeapType::i31; - break; - case HeapType::data: - ret = BinaryConsts::EncodedHeapType::data; - break; - case HeapType::string: - ret = BinaryConsts::EncodedHeapType::string; - break; - case HeapType::stringview_wtf8: - ret = BinaryConsts::EncodedHeapType::stringview_wtf8_heap; - break; - case HeapType::stringview_wtf16: - ret = BinaryConsts::EncodedHeapType::stringview_wtf16_heap; - break; - case HeapType::stringview_iter: - ret = BinaryConsts::EncodedHeapType::stringview_iter_heap; - break; - } - } else { - WASM_UNREACHABLE("TODO: compound GC types"); + assert(type.isBasic()); + switch (type.getBasic()) { + case HeapType::ext: + ret = BinaryConsts::EncodedHeapType::ext; + break; + case HeapType::func: + ret = BinaryConsts::EncodedHeapType::func; + break; + case HeapType::any: + ret = BinaryConsts::EncodedHeapType::any; + break; + case HeapType::eq: + ret = BinaryConsts::EncodedHeapType::eq; + break; + case HeapType::i31: + ret = BinaryConsts::EncodedHeapType::i31; + break; + case HeapType::data: + ret = BinaryConsts::EncodedHeapType::data; + break; + case HeapType::string: + ret = BinaryConsts::EncodedHeapType::string; + break; + case HeapType::stringview_wtf8: + ret = BinaryConsts::EncodedHeapType::stringview_wtf8_heap; + break; + case HeapType::stringview_wtf16: + ret = BinaryConsts::EncodedHeapType::stringview_wtf16_heap; + break; + case HeapType::stringview_iter: + ret = BinaryConsts::EncodedHeapType::stringview_iter_heap; + break; + case HeapType::none: + ret = BinaryConsts::EncodedHeapType::none; + break; + case HeapType::noext: + ret = BinaryConsts::EncodedHeapType::noext; + break; + case HeapType::nofunc: + ret = BinaryConsts::EncodedHeapType::nofunc; + break; } o << S64LEB(ret); // TODO: Actually s33 } @@ -1867,6 +1903,15 @@ bool WasmBinaryBuilder::getBasicType(int32_t code, Type& out) { case BinaryConsts::EncodedType::stringview_iter: out = Type(HeapType::stringview_iter, Nullable); return true; + case BinaryConsts::EncodedType::nullref: + out = Type(HeapType::none, Nullable); + return true; + case BinaryConsts::EncodedType::nullexternref: + out = Type(HeapType::noext, Nullable); + return true; + case BinaryConsts::EncodedType::nullfuncref: + out = Type(HeapType::nofunc, Nullable); + return true; default: return false; } @@ -1904,6 +1949,15 @@ bool WasmBinaryBuilder::getBasicHeapType(int64_t code, HeapType& out) { case BinaryConsts::EncodedHeapType::stringview_iter_heap: out = HeapType::stringview_iter; return true; + case BinaryConsts::EncodedHeapType::none: + out = HeapType::none; + return true; + case BinaryConsts::EncodedHeapType::noext: + out = HeapType::noext; + return true; + case BinaryConsts::EncodedHeapType::nofunc: + out = HeapType::nofunc; + return true; default: return false; } @@ -2849,7 +2903,14 @@ void WasmBinaryBuilder::skipUnreachableCode() { expressionStack = savedStack; return; } - pushExpression(curr); + if (curr->type == Type::unreachable) { + // Nothing before this unreachable should be available to future + // expressions. They will get `(unreachable)`s if they try to pop past + // this point. + expressionStack.clear(); + } else { + pushExpression(curr); + } } } @@ -6530,7 +6591,7 @@ void WasmBinaryBuilder::visitDrop(Drop* curr) { void WasmBinaryBuilder::visitRefNull(RefNull* curr) { BYN_TRACE("zz node: RefNull\n"); - curr->finalize(getHeapType()); + curr->finalize(getHeapType().getBottom()); } void WasmBinaryBuilder::visitRefIs(RefIs* curr, uint8_t code) { @@ -6941,28 +7002,29 @@ bool WasmBinaryBuilder::maybeVisitStructNew(Expression*& out, uint32_t code) { } bool WasmBinaryBuilder::maybeVisitStructGet(Expression*& out, uint32_t code) { - StructGet* curr; + bool signed_ = false; switch (code) { case BinaryConsts::StructGet: - curr = allocator.alloc<StructGet>(); + case BinaryConsts::StructGetU: break; case BinaryConsts::StructGetS: - curr = allocator.alloc<StructGet>(); - curr->signed_ = true; - break; - case BinaryConsts::StructGetU: - curr = allocator.alloc<StructGet>(); - curr->signed_ = false; + signed_ = true; break; default: return false; } auto heapType = getIndexedHeapType(); - curr->index = getU32LEB(); - curr->ref = popNonVoidExpression(); - validateHeapTypeUsingChild(curr->ref, heapType); - curr->finalize(); - out = curr; + if (!heapType.isStruct()) { + throwError("Expected struct heaptype"); + } + auto index = getU32LEB(); + if (index >= heapType.getStruct().fields.size()) { + throwError("Struct field index out of bounds"); + } + auto type = heapType.getStruct().fields[index].type; + auto ref = popNonVoidExpression(); + validateHeapTypeUsingChild(ref, heapType); + out = Builder(wasm).makeStructGet(index, ref, type, signed_); return true; } @@ -7022,10 +7084,14 @@ bool WasmBinaryBuilder::maybeVisitArrayGet(Expression*& out, uint32_t code) { return false; } auto heapType = getIndexedHeapType(); + if (!heapType.isArray()) { + throwError("Expected array heaptype"); + } + auto type = heapType.getArray().element.type; auto* index = popNonVoidExpression(); auto* ref = popNonVoidExpression(); validateHeapTypeUsingChild(ref, heapType); - out = Builder(wasm).makeArrayGet(ref, index, signed_); + out = Builder(wasm).makeArrayGet(ref, index, type, signed_); return true; } |