summaryrefslogtreecommitdiff
path: root/src/wasm/wasm-binary.cpp
diff options
context:
space:
mode:
authorThomas Lively <tlively@google.com>2022-10-07 08:02:09 -0500
committerGitHub <noreply@github.com>2022-10-07 06:02:09 -0700
commit7fc26f3e78f72ecaa5b79ebe042b95a0be422327 (patch)
treef87c84fc691aaf311fbd71c176ee37723c76ae20 /src/wasm/wasm-binary.cpp
parente8884de3c880a7de4bb1f8eae3df5f00f4164b4d (diff)
downloadbinaryen-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.cpp168
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;
}