diff options
author | Alon Zakai <azakai@google.com> | 2021-01-27 17:46:31 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-01-27 09:46:31 -0800 |
commit | 2bdc4841b680ee44e132bbb07f5167eaa7226f99 (patch) | |
tree | 170585bbb9c74022201efeeee91962fdb5f0a1f1 | |
parent | f82e94363a231bf570fbe3d7dc49259c8668206f (diff) | |
download | binaryen-2bdc4841b680ee44e132bbb07f5167eaa7226f99.tar.gz binaryen-2bdc4841b680ee44e132bbb07f5167eaa7226f99.tar.bz2 binaryen-2bdc4841b680ee44e132bbb07f5167eaa7226f99.zip |
[GC] ref.as_* (#3520)
These are similar to is, but instead of returning an i32 answer, they trap on
an invalid value, and return it otherwise.
These could in theory be in a single RefDoThing, with opcodes for both As
and Is, but as the return values are different, that would be a little odd, and
the name would be less clear.
-rwxr-xr-x | scripts/gen-s-parser.py | 3 | ||||
-rw-r--r-- | src/gen-s-parser.inc | 14 | ||||
-rw-r--r-- | src/ir/ReFinalize.cpp | 1 | ||||
-rw-r--r-- | src/ir/cost.h | 1 | ||||
-rw-r--r-- | src/ir/effects.h | 6 | ||||
-rw-r--r-- | src/passes/Print.cpp | 22 | ||||
-rw-r--r-- | src/wasm-binary.h | 7 | ||||
-rw-r--r-- | src/wasm-builder.h | 7 | ||||
-rw-r--r-- | src/wasm-delegations-fields.h | 7 | ||||
-rw-r--r-- | src/wasm-delegations.h | 1 | ||||
-rw-r--r-- | src/wasm-interpreter.h | 32 | ||||
-rw-r--r-- | src/wasm-s-parser.h | 1 | ||||
-rw-r--r-- | src/wasm.h | 18 | ||||
-rw-r--r-- | src/wasm/wasm-binary.cpp | 25 | ||||
-rw-r--r-- | src/wasm/wasm-s-parser.cpp | 4 | ||||
-rw-r--r-- | src/wasm/wasm-stack.cpp | 16 | ||||
-rw-r--r-- | src/wasm/wasm.cpp | 31 | ||||
-rw-r--r-- | src/wasm2js.h | 4 | ||||
-rw-r--r-- | test/heap-types.wast | 5 | ||||
-rw-r--r-- | test/heap-types.wast.from-wast | 19 | ||||
-rw-r--r-- | test/heap-types.wast.fromBinary | 19 | ||||
-rw-r--r-- | test/heap-types.wast.fromBinary.noDebugInfo | 19 |
22 files changed, 257 insertions, 5 deletions
diff --git a/scripts/gen-s-parser.py b/scripts/gen-s-parser.py index ff6172138..5c1ed5dd7 100755 --- a/scripts/gen-s-parser.py +++ b/scripts/gen-s-parser.py @@ -568,6 +568,9 @@ instructions = [ ("ref.is_func", "makeRefIs(s, RefIsFunc)"), ("ref.is_data", "makeRefIs(s, RefIsData)"), ("ref.is_i31", "makeRefIs(s, RefIsI31)"), + ("ref.as_func", "makeRefAs(s, RefAsFunc)"), + ("ref.as_data", "makeRefAs(s, RefAsData)"), + ("ref.as_i31", "makeRefAs(s, RefAsI31)"), ] diff --git a/src/gen-s-parser.inc b/src/gen-s-parser.inc index 2bc0ba36d..255a4bfa8 100644 --- a/src/gen-s-parser.inc +++ b/src/gen-s-parser.inc @@ -2824,6 +2824,20 @@ switch (op[0]) { switch (op[2]) { case 'f': { switch (op[4]) { + case 'a': { + switch (op[7]) { + case 'd': + if (strcmp(op, "ref.as_data") == 0) { return makeRefAs(s, RefAsData); } + goto parse_error; + case 'f': + if (strcmp(op, "ref.as_func") == 0) { return makeRefAs(s, RefAsFunc); } + goto parse_error; + case 'i': + if (strcmp(op, "ref.as_i31") == 0) { return makeRefAs(s, RefAsI31); } + goto parse_error; + default: goto parse_error; + } + } case 'c': if (strcmp(op, "ref.cast") == 0) { return makeRefCast(s); } goto parse_error; diff --git a/src/ir/ReFinalize.cpp b/src/ir/ReFinalize.cpp index 785b36c80..b8d620560 100644 --- a/src/ir/ReFinalize.cpp +++ b/src/ir/ReFinalize.cpp @@ -163,6 +163,7 @@ void ReFinalize::visitArrayNew(ArrayNew* curr) { curr->finalize(); } void ReFinalize::visitArrayGet(ArrayGet* curr) { curr->finalize(); } void ReFinalize::visitArraySet(ArraySet* curr) { curr->finalize(); } void ReFinalize::visitArrayLen(ArrayLen* curr) { curr->finalize(); } +void ReFinalize::visitRefAs(RefAs* curr) { curr->finalize(); } void ReFinalize::visitFunction(Function* curr) { // we may have changed the body from unreachable to none, which might be bad diff --git a/src/ir/cost.h b/src/ir/cost.h index 95c483601..8c23c91f8 100644 --- a/src/ir/cost.h +++ b/src/ir/cost.h @@ -623,6 +623,7 @@ struct CostAnalyzer : public OverriddenVisitor<CostAnalyzer, Index> { Index visitArrayLen(ArrayLen* curr) { return 1 + nullCheckCost(curr->ref) + visit(curr->ref); } + Index visitRefAs(RefAs* curr) { return 1 + visit(curr->value); } private: Index nullCheckCost(Expression* ref) { diff --git a/src/ir/effects.h b/src/ir/effects.h index 603ed49a3..ca440ce51 100644 --- a/src/ir/effects.h +++ b/src/ir/effects.h @@ -598,6 +598,12 @@ private: parent.implicitTrap = true; } } + void visitRefAs(RefAs* curr) { + // traps when the arg is not valid + if (curr->value->type.isNullable()) { + parent.implicitTrap = true; + } + } }; public: diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index ba1371d9e..a6efd5f1f 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -1856,6 +1856,21 @@ struct PrintExpressionContents printMedium(o, "array.len "); printHeapTypeName(o, curr->ref->type.getHeapType()); } + void visitRefAs(RefAs* curr) { + switch (curr->op) { + case RefAsFunc: + printMedium(o, "ref.as_func"); + break; + case RefAsData: + printMedium(o, "ref.as_data"); + break; + case RefAsI31: + printMedium(o, "ref.as_i31"); + break; + default: + WASM_UNREACHABLE("invalid ref.is_*"); + } + } }; // Prints an expression in s-expr format, including both the @@ -2603,6 +2618,13 @@ struct PrintSExpression : public OverriddenVisitor<PrintSExpression> { printFullLine(curr->ref); decIndent(); } + void visitRefAs(RefAs* curr) { + o << '('; + PrintExpressionContents(currFunction, o).visit(curr); + incIndent(); + printFullLine(curr->value); + decIndent(); + } // Module-level visitors void handleSignature(Signature curr, Name name = Name()) { o << "(func"; diff --git a/src/wasm-binary.h b/src/wasm-binary.h index 4e1095056..f6b2925fe 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -1037,8 +1037,10 @@ enum ASTNodes { BrOnCast = 0x42, RefIsFunc = 0x50, RefIsData = 0x51, - RefIsI31 = 0x52 - + RefIsI31 = 0x52, + RefAsFunc = 0x58, + RefAsData = 0x59, + RefAsI31 = 0x5a, }; enum MemoryAccess { @@ -1547,6 +1549,7 @@ public: void visitThrow(Throw* curr); void visitRethrow(Rethrow* curr); void visitCallRef(CallRef* curr); + void visitRefAs(RefAs* curr, uint8_t code); // Let is lowered into a block. void visitLet(Block* curr); diff --git a/src/wasm-builder.h b/src/wasm-builder.h index 9f74f05d2..6716bca20 100644 --- a/src/wasm-builder.h +++ b/src/wasm-builder.h @@ -796,6 +796,13 @@ public: ret->finalize(); return ret; } + RefAs* makeRefAs(RefAsOp op, Expression* value) { + auto* ret = wasm.allocator.alloc<RefAs>(); + ret->op = op; + ret->value = value; + ret->finalize(); + return ret; + } // Additional helpers diff --git a/src/wasm-delegations-fields.h b/src/wasm-delegations-fields.h index ad137e58d..a39a8b7af 100644 --- a/src/wasm-delegations-fields.h +++ b/src/wasm-delegations-fields.h @@ -661,6 +661,13 @@ switch (DELEGATE_ID) { DELEGATE_END(ArrayLen); break; } + case Expression::Id::RefAsId: { + DELEGATE_START(RefAs); + DELEGATE_FIELD_INT(RefAs, op); + DELEGATE_FIELD_CHILD(RefAs, value); + DELEGATE_END(RefAs); + break; + } } #undef DELEGATE_ID diff --git a/src/wasm-delegations.h b/src/wasm-delegations.h index 04df283c3..fe2a73d5b 100644 --- a/src/wasm-delegations.h +++ b/src/wasm-delegations.h @@ -79,5 +79,6 @@ DELEGATE(ArrayNew); DELEGATE(ArrayGet); DELEGATE(ArraySet); DELEGATE(ArrayLen); +DELEGATE(RefAs); #undef DELEGATE diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index e8716efbc..ab5644bf4 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -1635,6 +1635,38 @@ public: } return Literal(int32_t(data->values.size())); } + Flow visitRefAs(RefAs* curr) { + NOTE_ENTER("RefAs"); + Flow flow = visit(curr->value); + if (flow.breaking()) { + return flow; + } + const auto& value = flow.getSingleValue(); + NOTE_EVAL1(value); + if (value.isNull()) { + trap("null ref"); + } + switch (curr->op) { + case RefAsFunc: + if (value.type.isFunction()) { + trap("not a func"); + } + break; + case RefAsData: + if (value.isGCData()) { + trap("not a data"); + } + break; + case RefAsI31: + if (value.type.getHeapType() != HeapType::i31) { + trap("not an i31"); + } + break; + default: + WASM_UNREACHABLE("unimplemented ref.is_*"); + } + return value; + } virtual void trap(const char* why) { WASM_UNREACHABLE("unimp"); } diff --git a/src/wasm-s-parser.h b/src/wasm-s-parser.h index db7b41178..98cd0560b 100644 --- a/src/wasm-s-parser.h +++ b/src/wasm-s-parser.h @@ -269,6 +269,7 @@ private: Expression* makeArrayGet(Element& s, bool signed_ = false); Expression* makeArraySet(Element& s); Expression* makeArrayLen(Element& s); + Expression* makeRefAs(Element& s, RefAsOp op); // Helper functions Type parseOptionalResultType(Element& s, Index& i); diff --git a/src/wasm.h b/src/wasm.h index 949ec3b72..094233b5c 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -549,6 +549,12 @@ enum RefIsOp { RefIsI31, }; +enum RefAsOp { + RefAsFunc, + RefAsData, + RefAsI31, +}; + // // Expressions // @@ -639,6 +645,7 @@ public: ArrayGetId, ArraySetId, ArrayLenId, + RefAsId, NumExpressionIds }; Id _id; @@ -1492,6 +1499,17 @@ public: void finalize(); }; +class RefAs : public SpecificExpression<Expression::RefAsId> { +public: + RefAs(MixedArena& allocator) {} + + RefAsOp op; + + Expression* value; + + void finalize(); +}; + // Globals struct Importable { diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index fc14510e9..020953c1b 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -3052,6 +3052,12 @@ BinaryConsts::ASTNodes WasmBinaryBuilder::readExpression(Expression*& curr) { visitRefIs((curr = allocator.alloc<RefIs>())->cast<RefIs>(), opcode); break; } + if (opcode == BinaryConsts::RefAsFunc || + opcode == BinaryConsts::RefAsData || + opcode == BinaryConsts::RefAsI31) { + visitRefAs((curr = allocator.alloc<RefAs>())->cast<RefAs>(), opcode); + break; + } throwError("invalid code after GC prefix: " + std::to_string(opcode)); break; } @@ -5971,6 +5977,25 @@ bool WasmBinaryBuilder::maybeVisitArrayLen(Expression*& out, uint32_t code) { return true; } +void WasmBinaryBuilder::visitRefAs(RefAs* curr, uint8_t code) { + BYN_TRACE("zz node: RefAs\n"); + switch (code) { + case BinaryConsts::RefAsFunc: + curr->op = RefAsFunc; + break; + case BinaryConsts::RefAsData: + curr->op = RefAsData; + break; + case BinaryConsts::RefAsI31: + curr->op = RefAsI31; + break; + default: + WASM_UNREACHABLE("invalid code for ref.as_*"); + } + curr->value = popNonVoidExpression(); + curr->finalize(); +} + void WasmBinaryBuilder::throwError(std::string text) { throw ParseException(text, 0, pos); } diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp index b9927cbc7..5dec22eb3 100644 --- a/src/wasm/wasm-s-parser.cpp +++ b/src/wasm/wasm-s-parser.cpp @@ -2236,6 +2236,10 @@ Expression* SExpressionWasmBuilder::makeArrayLen(Element& s) { return Builder(wasm).makeArrayLen(ref); } +Expression* SExpressionWasmBuilder::makeRefAs(Element& s, RefAsOp op) { + return Builder(wasm).makeRefAs(op, parseExpression(s[1])); +} + // converts an s-expression string representing binary data into an output // sequence of raw bytes this appends to data, which may already contain // content. diff --git a/src/wasm/wasm-stack.cpp b/src/wasm/wasm-stack.cpp index 5609dbb0e..197581e33 100644 --- a/src/wasm/wasm-stack.cpp +++ b/src/wasm/wasm-stack.cpp @@ -2072,6 +2072,22 @@ void BinaryInstWriter::visitArrayLen(ArrayLen* curr) { parent.writeHeapType(curr->ref->type.getHeapType()); } +void BinaryInstWriter::visitRefAs(RefAs* curr) { + switch (curr->op) { + case RefAsFunc: + o << int8_t(BinaryConsts::GCPrefix) << int8_t(BinaryConsts::RefAsFunc); + break; + case RefAsData: + o << int8_t(BinaryConsts::GCPrefix) << int8_t(BinaryConsts::RefAsData); + break; + case RefAsI31: + o << int8_t(BinaryConsts::GCPrefix) << int8_t(BinaryConsts::RefAsI31); + break; + default: + WASM_UNREACHABLE("invalid ref.as_*"); + } +} + void BinaryInstWriter::emitScopeEnd(Expression* curr) { assert(!breakStack.empty()); breakStack.pop_back(); diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index cd1fb716c..2921f4974 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -245,6 +245,17 @@ const char* getExpressionName(Expression* curr) { return "array.set"; case Expression::Id::ArrayLenId: return "array.len"; + case Expression::Id::RefAsId: + switch (curr->cast<RefAs>()->op) { + case RefAsFunc: + return "ref.as_func"; + case RefAsData: + return "ref.as_data"; + case RefAsI31: + return "ref.as_i31"; + default: + WASM_UNREACHABLE("unimplemented ref.is_*"); + } case Expression::Id::NumExpressionIds: WASM_UNREACHABLE("invalid expr id"); } @@ -1153,6 +1164,26 @@ void ArrayLen::finalize() { } } +void RefAs::finalize() { + if (value->type == Type::unreachable) { + type = Type::unreachable; + return; + } + switch (op) { + case RefAsFunc: + type = Type::funcref; + break; + case RefAsData: + type = Type::dataref; + break; + case RefAsI31: + type = Type::i31ref; + break; + default: + WASM_UNREACHABLE("unimplemented ref.is_*"); + } +} + size_t Function::getNumParams() { return sig.params.size(); } size_t Function::getNumVars() { return vars.size(); } diff --git a/src/wasm2js.h b/src/wasm2js.h index 4a58a20d8..382cde895 100644 --- a/src/wasm2js.h +++ b/src/wasm2js.h @@ -2246,6 +2246,10 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m, unimplemented(curr); WASM_UNREACHABLE("unimp"); } + Ref visitRefAs(RefAs* curr) { + unimplemented(curr); + WASM_UNREACHABLE("unimp"); + } private: Ref makePointer(Expression* ptr, Address offset) { diff --git a/test/heap-types.wast b/test/heap-types.wast index 614bbb1c5..a36f12daf 100644 --- a/test/heap-types.wast +++ b/test/heap-types.wast @@ -187,4 +187,9 @@ (if (ref.is_data (local.get $x)) (unreachable)) (if (ref.is_i31 (local.get $x)) (unreachable)) ) + (func $ref.as_X (param $x anyref) + (drop (ref.as_func (local.get $x))) + (drop (ref.as_data (local.get $x))) + (drop (ref.as_i31 (local.get $x))) + ) ) diff --git a/test/heap-types.wast.from-wast b/test/heap-types.wast.from-wast index b717de63f..4ff3bceec 100644 --- a/test/heap-types.wast.from-wast +++ b/test/heap-types.wast.from-wast @@ -3,8 +3,8 @@ (type $[mut:f64] (array (mut f64))) (type ${} (struct )) (type ${mut:f32} (struct (field (mut f32)))) - (type $none_=>_none (func)) (type $anyref_=>_none (func (param anyref))) + (type $none_=>_none (func)) (type ${i32} (struct (field i32))) (type ${i32_i64} (struct (field i32) (field i64))) (type $[mut:i32] (array (mut i32))) @@ -217,4 +217,21 @@ (unreachable) ) ) + (func $ref.as_X (param $x anyref) + (drop + (ref.as_func + (local.get $x) + ) + ) + (drop + (ref.as_data + (local.get $x) + ) + ) + (drop + (ref.as_i31 + (local.get $x) + ) + ) + ) ) diff --git a/test/heap-types.wast.fromBinary b/test/heap-types.wast.fromBinary index 5b6dca2f6..9f0220c53 100644 --- a/test/heap-types.wast.fromBinary +++ b/test/heap-types.wast.fromBinary @@ -3,8 +3,8 @@ (type $[mut:f64] (array (mut f64))) (type ${} (struct )) (type ${mut:f32} (struct (field (mut f32)))) - (type $none_=>_none (func)) (type $anyref_=>_none (func (param anyref))) + (type $none_=>_none (func)) (type ${i32} (struct (field i32))) (type ${i32_i64} (struct (field i32) (field i64))) (type $[mut:i32] (array (mut i32))) @@ -216,5 +216,22 @@ (unreachable) ) ) + (func $ref.as_X (param $x anyref) + (drop + (ref.as_func + (local.get $x) + ) + ) + (drop + (ref.as_data + (local.get $x) + ) + ) + (drop + (ref.as_i31 + (local.get $x) + ) + ) + ) ) diff --git a/test/heap-types.wast.fromBinary.noDebugInfo b/test/heap-types.wast.fromBinary.noDebugInfo index fef210e11..864d66abb 100644 --- a/test/heap-types.wast.fromBinary.noDebugInfo +++ b/test/heap-types.wast.fromBinary.noDebugInfo @@ -3,8 +3,8 @@ (type $[mut:f64] (array (mut f64))) (type ${} (struct )) (type ${mut:f32} (struct (field (mut f32)))) - (type $none_=>_none (func)) (type $anyref_=>_none (func (param anyref))) + (type $none_=>_none (func)) (type ${i32} (struct (field i32))) (type ${i32_i64} (struct (field i32) (field i64))) (type $[mut:i32] (array (mut i32))) @@ -216,5 +216,22 @@ (unreachable) ) ) + (func $6 (param $0 anyref) + (drop + (ref.as_func + (local.get $0) + ) + ) + (drop + (ref.as_data + (local.get $0) + ) + ) + (drop + (ref.as_i31 + (local.get $0) + ) + ) + ) ) |