summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/wasm/wasm-binary.cpp26
-rw-r--r--test/lit/passes/roundtrip.wast46
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)
+ )
+ )
+ )
+ )
+)