summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlon Zakai <azakai@google.com>2021-01-27 17:46:31 +0000
committerGitHub <noreply@github.com>2021-01-27 09:46:31 -0800
commit2bdc4841b680ee44e132bbb07f5167eaa7226f99 (patch)
tree170585bbb9c74022201efeeee91962fdb5f0a1f1
parentf82e94363a231bf570fbe3d7dc49259c8668206f (diff)
downloadbinaryen-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-xscripts/gen-s-parser.py3
-rw-r--r--src/gen-s-parser.inc14
-rw-r--r--src/ir/ReFinalize.cpp1
-rw-r--r--src/ir/cost.h1
-rw-r--r--src/ir/effects.h6
-rw-r--r--src/passes/Print.cpp22
-rw-r--r--src/wasm-binary.h7
-rw-r--r--src/wasm-builder.h7
-rw-r--r--src/wasm-delegations-fields.h7
-rw-r--r--src/wasm-delegations.h1
-rw-r--r--src/wasm-interpreter.h32
-rw-r--r--src/wasm-s-parser.h1
-rw-r--r--src/wasm.h18
-rw-r--r--src/wasm/wasm-binary.cpp25
-rw-r--r--src/wasm/wasm-s-parser.cpp4
-rw-r--r--src/wasm/wasm-stack.cpp16
-rw-r--r--src/wasm/wasm.cpp31
-rw-r--r--src/wasm2js.h4
-rw-r--r--test/heap-types.wast5
-rw-r--r--test/heap-types.wast.from-wast19
-rw-r--r--test/heap-types.wast.fromBinary19
-rw-r--r--test/heap-types.wast.fromBinary.noDebugInfo19
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)
+ )
+ )
+ )
)