summaryrefslogtreecommitdiff
path: root/src/wasm-builder.h
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-builder.h
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-builder.h')
-rw-r--r--src/wasm-builder.h38
1 files changed, 24 insertions, 14 deletions
diff --git a/src/wasm-builder.h b/src/wasm-builder.h
index cc3b99138..b07af4627 100644
--- a/src/wasm-builder.h
+++ b/src/wasm-builder.h
@@ -699,10 +699,11 @@ public:
}
RefNull* makeRefNull(HeapType type) {
auto* ret = wasm.allocator.alloc<RefNull>();
- ret->finalize(Type(type, Nullable));
+ ret->finalize(Type(type.getBottom(), Nullable));
return ret;
}
RefNull* makeRefNull(Type type) {
+ assert(type.isNullable() && type.isNull());
auto* ret = wasm.allocator.alloc<RefNull>();
ret->finalize(type);
return ret;
@@ -942,11 +943,14 @@ public:
ret->finalize();
return ret;
}
- ArrayGet*
- makeArrayGet(Expression* ref, Expression* index, bool signed_ = false) {
+ ArrayGet* makeArrayGet(Expression* ref,
+ Expression* index,
+ Type type,
+ bool signed_ = false) {
auto* ret = wasm.allocator.alloc<ArrayGet>();
ret->ref = ref;
ret->index = index;
+ ret->type = type;
ret->signed_ = signed_;
ret->finalize();
return ret;
@@ -1262,7 +1266,7 @@ public:
if (curr->type.isTuple() && curr->type.isDefaultable()) {
return makeConstantExpression(Literal::makeZeros(curr->type));
}
- if (curr->type.isNullable()) {
+ if (curr->type.isNullable() && curr->type.isNull()) {
return ExpressionManipulator::refNull(curr, curr->type);
}
if (curr->type.isRef() && curr->type.getHeapType() == HeapType::i31) {
@@ -1329,18 +1333,24 @@ public:
Expression* validateAndMakeCallRef(Expression* target,
const T& args,
bool isReturn = false) {
- if (!target->type.isRef()) {
- if (target->type == Type::unreachable) {
- // An unreachable target is not supported. Similiar to br_on_cast, just
- // emit an unreachable sequence, since we don't have enough information
- // to create a full call_ref.
- auto* block = makeBlock(args);
- block->list.push_back(target);
- block->finalize(Type::unreachable);
- return block;
- }
+ if (target->type != Type::unreachable && !target->type.isRef()) {
throw ParseException("Non-reference type for a call_ref", line, col);
}
+ // TODO: This won't be necessary once type annotations are mandatory on
+ // call_ref.
+ if (target->type == Type::unreachable ||
+ target->type.getHeapType() == HeapType::nofunc) {
+ // An unreachable target is not supported. Similiar to br_on_cast, just
+ // emit an unreachable sequence, since we don't have enough information
+ // to create a full call_ref.
+ std::vector<Expression*> children;
+ for (auto* arg : args) {
+ children.push_back(makeDrop(arg));
+ }
+ children.push_back(makeDrop(target));
+ children.push_back(makeUnreachable());
+ return makeBlock(children, Type::unreachable);
+ }
auto heapType = target->type.getHeapType();
if (!heapType.isSignature()) {
throw ParseException("Invalid reference type for a call_ref", line, col);