diff options
-rw-r--r-- | src/wasm/wasm-binary.cpp | 26 | ||||
-rw-r--r-- | test/lit/passes/roundtrip.wast | 46 |
2 files changed, 67 insertions, 5 deletions
diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index 5bd368e17..5425c1551 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -2516,14 +2516,30 @@ void WasmBinaryBuilder::skipUnreachableCode() { } void WasmBinaryBuilder::pushExpression(Expression* curr) { - if (curr->type.isTuple()) { + auto type = curr->type; + if (type.isTuple()) { // Store tuple to local and push individual extracted values Builder builder(wasm); - Index tuple = builder.addVar(currFunction, curr->type); + // Non-nullable types require special handling as they cannot be stored to + // a local. + std::vector<Type> nullableTypes; + for (auto t : type) { + if (t.isRef() && !t.isNullable()) { + t = Type(t.getHeapType(), Nullable); + } + nullableTypes.push_back(t); + } + auto nullableType = Type(Tuple(nullableTypes)); + Index tuple = builder.addVar(currFunction, nullableType); expressionStack.push_back(builder.makeLocalSet(tuple, curr)); - for (Index i = 0; i < curr->type.size(); ++i) { - expressionStack.push_back( - builder.makeTupleExtract(builder.makeLocalGet(tuple, curr->type), i)); + for (Index i = 0; i < nullableType.size(); ++i) { + Expression* value = + builder.makeTupleExtract(builder.makeLocalGet(tuple, nullableType), i); + if (nullableType[i] != type[i]) { + // We modified this to be nullable; undo that. + value = builder.makeRefAs(RefAsNonNull, value); + } + expressionStack.push_back(value); } } else { expressionStack.push_back(curr); diff --git a/test/lit/passes/roundtrip.wast b/test/lit/passes/roundtrip.wast new file mode 100644 index 000000000..a0288c600 --- /dev/null +++ b/test/lit/passes/roundtrip.wast @@ -0,0 +1,46 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited. +;; RUN: wasm-opt %s -all --roundtrip -S -o - | filecheck %s + +(module + (type $none (func)) + ;; CHECK: (func $foo + ;; CHECK-NEXT: (local $0 (funcref (ref null $none))) + ;; CHECK-NEXT: (local $1 funcref) + ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (block $label$1 (result funcref (ref $none)) + ;; CHECK-NEXT: (tuple.make + ;; CHECK-NEXT: (ref.null func) + ;; CHECK-NEXT: (ref.func $foo) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result funcref) + ;; CHECK-NEXT: (local.set $1 + ;; CHECK-NEXT: (tuple.extract 0 + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (tuple.extract 1 + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $foo + (drop + ;; a tuple type with a non-nullable element, that must be carefully handled + (block (result funcref (ref $none)) + (tuple.make + (ref.null func) + (ref.func $foo) + ) + ) + ) + ) +) |