summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDaniel Wirtz <dcode@dcode.io>2020-09-09 03:40:09 +0200
committerGitHub <noreply@github.com>2020-09-09 03:40:09 +0200
commit916ce6f1a9f7c85102a8c69f593b301c8df5d19d (patch)
tree93b22be9f2c0718248528d140b05221cb6878600 /src
parent0fdcf5b51a0c8c379b2d3ad8262aa22bb234f0e9 (diff)
downloadbinaryen-916ce6f1a9f7c85102a8c69f593b301c8df5d19d.tar.gz
binaryen-916ce6f1a9f7c85102a8c69f593b301c8df5d19d.tar.bz2
binaryen-916ce6f1a9f7c85102a8c69f593b301c8df5d19d.zip
Update reference types (#3084)
Align with the current state of the reference types proposal: * Remove `nullref` * Remove `externref` and `funcref` subtyping * A `Literal` of a nullable reference type can now represent `null` (previously was type `nullref`) * Update the tests and temporarily comment out those tests relying on subtyping
Diffstat (limited to 'src')
-rw-r--r--src/asmjs/asm_v_wasm.cpp3
-rw-r--r--src/binaryen-c.cpp19
-rw-r--r--src/binaryen-c.h4
-rw-r--r--src/gen-s-parser.inc14
-rw-r--r--src/ir/ExpressionManipulator.cpp4
-rw-r--r--src/ir/abstract.h2
-rw-r--r--src/ir/manipulation.h5
-rw-r--r--src/ir/properties.h8
-rw-r--r--src/js/binaryen.js-post.js11
-rw-r--r--src/literal.h45
-rw-r--r--src/parsing.h1
-rw-r--r--src/passes/ConstHoisting.cpp1
-rw-r--r--src/passes/Flatten.cpp10
-rw-r--r--src/passes/FuncCastEmulation.cpp2
-rw-r--r--src/passes/InstrumentLocals.cpp16
-rw-r--r--src/passes/Precompute.cpp2
-rw-r--r--src/passes/Print.cpp5
-rw-r--r--src/shell-interface.h3
-rw-r--r--src/tools/execution-results.h2
-rw-r--r--src/tools/fuzzing.h45
-rw-r--r--src/tools/spec-wrapper.h7
-rw-r--r--src/tools/wasm-reduce.cpp9
-rw-r--r--src/wasm-binary.h35
-rw-r--r--src/wasm-builder.h17
-rw-r--r--src/wasm-interpreter.h22
-rw-r--r--src/wasm-s-parser.h4
-rw-r--r--src/wasm-type.h8
-rw-r--r--src/wasm.h2
-rw-r--r--src/wasm/literal.cpp125
-rw-r--r--src/wasm/wasm-binary.cpp26
-rw-r--r--src/wasm/wasm-s-parser.cpp44
-rw-r--r--src/wasm/wasm-stack.cpp6
-rw-r--r--src/wasm/wasm-type.cpp62
-rw-r--r--src/wasm/wasm-validator.cpp2
-rw-r--r--src/wasm/wasm.cpp17
35 files changed, 341 insertions, 247 deletions
diff --git a/src/asmjs/asm_v_wasm.cpp b/src/asmjs/asm_v_wasm.cpp
index 4a79d9caf..ddcb27d3d 100644
--- a/src/asmjs/asm_v_wasm.cpp
+++ b/src/asmjs/asm_v_wasm.cpp
@@ -56,7 +56,6 @@ AsmType wasmToAsmType(Type type) {
assert(false && "v128 not implemented yet");
case Type::funcref:
case Type::externref:
- case Type::nullref:
case Type::exnref:
assert(false && "reference types are not supported by asm2wasm");
case Type::none:
@@ -84,8 +83,6 @@ char getSig(Type type) {
return 'F';
case Type::externref:
return 'X';
- case Type::nullref:
- return 'N';
case Type::exnref:
return 'E';
case Type::none:
diff --git a/src/binaryen-c.cpp b/src/binaryen-c.cpp
index 4c5063114..a3aa0cd94 100644
--- a/src/binaryen-c.cpp
+++ b/src/binaryen-c.cpp
@@ -69,12 +69,12 @@ BinaryenLiteral toBinaryenLiteral(Literal x) {
memcpy(&ret.v128, x.getv128Ptr(), 16);
break;
case Type::funcref:
- ret.func = x.getFunc().c_str();
- break;
- case Type::nullref:
+ ret.func = x.isNull() ? nullptr : x.getFunc().c_str();
break;
case Type::externref:
case Type::exnref:
+ assert(x.isNull());
+ break;
case Type::none:
case Type::unreachable:
WASM_UNREACHABLE("unexpected type");
@@ -95,11 +95,10 @@ Literal fromBinaryenLiteral(BinaryenLiteral x) {
case Type::v128:
return Literal(x.v128);
case Type::funcref:
- return Literal::makeFuncref(x.func);
- case Type::nullref:
- return Literal::makeNullref();
+ return Literal::makeFunc(x.func);
case Type::externref:
case Type::exnref:
+ return Literal::makeNull(Type(x.type));
case Type::none:
case Type::unreachable:
WASM_UNREACHABLE("unexpected type");
@@ -133,7 +132,6 @@ BinaryenType BinaryenTypeFloat64(void) { return Type::f64; }
BinaryenType BinaryenTypeVec128(void) { return Type::v128; }
BinaryenType BinaryenTypeFuncref(void) { return Type::funcref; }
BinaryenType BinaryenTypeExternref(void) { return Type::externref; }
-BinaryenType BinaryenTypeNullref(void) { return Type::nullref; }
BinaryenType BinaryenTypeExnref(void) { return Type::exnref; }
BinaryenType BinaryenTypeUnreachable(void) { return Type::unreachable; }
BinaryenType BinaryenTypeAuto(void) { return uintptr_t(-1); }
@@ -1264,8 +1262,11 @@ BinaryenExpressionRef BinaryenPop(BinaryenModuleRef module, BinaryenType type) {
Builder(*(Module*)module).makePop(Type(type)));
}
-BinaryenExpressionRef BinaryenRefNull(BinaryenModuleRef module) {
- return static_cast<Expression*>(Builder(*(Module*)module).makeRefNull());
+BinaryenExpressionRef BinaryenRefNull(BinaryenModuleRef module,
+ BinaryenType type) {
+ Type type_(type);
+ assert(type_.isNullable());
+ return static_cast<Expression*>(Builder(*(Module*)module).makeRefNull(type_));
}
BinaryenExpressionRef BinaryenRefIsNull(BinaryenModuleRef module,
diff --git a/src/binaryen-c.h b/src/binaryen-c.h
index 893d44e3c..beaf72499 100644
--- a/src/binaryen-c.h
+++ b/src/binaryen-c.h
@@ -100,7 +100,6 @@ BINARYEN_API BinaryenType BinaryenTypeFloat64(void);
BINARYEN_API BinaryenType BinaryenTypeVec128(void);
BINARYEN_API BinaryenType BinaryenTypeFuncref(void);
BINARYEN_API BinaryenType BinaryenTypeExternref(void);
-BINARYEN_API BinaryenType BinaryenTypeNullref(void);
BINARYEN_API BinaryenType BinaryenTypeExnref(void);
BINARYEN_API BinaryenType BinaryenTypeUnreachable(void);
// Not a real type. Used as the last parameter to BinaryenBlock to let
@@ -826,7 +825,8 @@ BinaryenMemoryFill(BinaryenModuleRef module,
BinaryenExpressionRef dest,
BinaryenExpressionRef value,
BinaryenExpressionRef size);
-BINARYEN_API BinaryenExpressionRef BinaryenRefNull(BinaryenModuleRef module);
+BINARYEN_API BinaryenExpressionRef BinaryenRefNull(BinaryenModuleRef module,
+ BinaryenType type);
BINARYEN_API BinaryenExpressionRef
BinaryenRefIsNull(BinaryenModuleRef module, BinaryenExpressionRef value);
BINARYEN_API BinaryenExpressionRef BinaryenRefFunc(BinaryenModuleRef module,
diff --git a/src/gen-s-parser.inc b/src/gen-s-parser.inc
index 5e6beb4ac..97ba15c32 100644
--- a/src/gen-s-parser.inc
+++ b/src/gen-s-parser.inc
@@ -2560,17 +2560,9 @@ switch (op[0]) {
default: goto parse_error;
}
}
- case 'n': {
- switch (op[1]) {
- case 'o':
- if (strcmp(op, "nop") == 0) { return makeNop(); }
- goto parse_error;
- case 'u':
- if (strcmp(op, "nullref.pop") == 0) { return makePop(Type::nullref); }
- goto parse_error;
- default: goto parse_error;
- }
- }
+ case 'n':
+ if (strcmp(op, "nop") == 0) { return makeNop(); }
+ goto parse_error;
case 'r': {
switch (op[2]) {
case 'f': {
diff --git a/src/ir/ExpressionManipulator.cpp b/src/ir/ExpressionManipulator.cpp
index 57048b9bd..6f64ec77b 100644
--- a/src/ir/ExpressionManipulator.cpp
+++ b/src/ir/ExpressionManipulator.cpp
@@ -227,7 +227,9 @@ flexibleCopy(Expression* original, Module& wasm, CustomCopier custom) {
builder.makeHost(curr->op, curr->nameOperand, std::move(operands));
return ret;
}
- Expression* visitRefNull(RefNull* curr) { return builder.makeRefNull(); }
+ Expression* visitRefNull(RefNull* curr) {
+ return builder.makeRefNull(curr->type);
+ }
Expression* visitRefIsNull(RefIsNull* curr) {
return builder.makeRefIsNull(copy(curr->value));
}
diff --git a/src/ir/abstract.h b/src/ir/abstract.h
index f706e9972..b00537bf5 100644
--- a/src/ir/abstract.h
+++ b/src/ir/abstract.h
@@ -103,7 +103,6 @@ inline UnaryOp getUnary(Type type, Op op) {
}
case Type::funcref:
case Type::externref:
- case Type::nullref:
case Type::exnref:
case Type::none:
case Type::unreachable: {
@@ -268,7 +267,6 @@ inline BinaryOp getBinary(Type type, Op op) {
}
case Type::funcref:
case Type::externref:
- case Type::nullref:
case Type::exnref:
case Type::none:
case Type::unreachable: {
diff --git a/src/ir/manipulation.h b/src/ir/manipulation.h
index 49ed7e11e..fb1f0181e 100644
--- a/src/ir/manipulation.h
+++ b/src/ir/manipulation.h
@@ -40,9 +40,10 @@ template<typename InputType> inline Nop* nop(InputType* target) {
return ret;
}
-template<typename InputType> inline RefNull* refNull(InputType* target) {
+template<typename InputType>
+inline RefNull* refNull(InputType* target, Type type) {
auto* ret = convert<InputType, RefNull>(target);
- ret->finalize();
+ ret->finalize(type);
return ret;
}
diff --git a/src/ir/properties.h b/src/ir/properties.h
index 0c6824e4a..ac61f787e 100644
--- a/src/ir/properties.h
+++ b/src/ir/properties.h
@@ -93,10 +93,10 @@ inline bool isConstantExpression(const Expression* curr) {
inline Literal getSingleLiteral(const Expression* curr) {
if (auto* c = curr->dynCast<Const>()) {
return c->value;
- } else if (curr->is<RefNull>()) {
- return Literal(Type::nullref);
- } else if (auto* c = curr->dynCast<RefFunc>()) {
- return Literal(c->func);
+ } else if (auto* n = curr->dynCast<RefNull>()) {
+ return Literal(n->type);
+ } else if (auto* r = curr->dynCast<RefFunc>()) {
+ return Literal(r->func);
} else {
WASM_UNREACHABLE("non-constant expression");
}
diff --git a/src/js/binaryen.js-post.js b/src/js/binaryen.js-post.js
index 4e673ab4b..66de10848 100644
--- a/src/js/binaryen.js-post.js
+++ b/src/js/binaryen.js-post.js
@@ -35,7 +35,6 @@ function initializeConstants() {
['v128', 'Vec128'],
['funcref', 'Funcref'],
['externref', 'Externref'],
- ['nullref', 'Nullref'],
['exnref', 'Exnref'],
['unreachable', 'Unreachable'],
['auto', 'Auto']
@@ -2058,12 +2057,6 @@ function wrapModule(module, self = {}) {
}
};
- self['nullref'] = {
- 'pop'() {
- return Module['_BinaryenPop'](module, Module['nullref']);
- }
- };
-
self['exnref'] = {
'pop'() {
return Module['_BinaryenPop'](module, Module['exnref']);
@@ -2071,8 +2064,8 @@ function wrapModule(module, self = {}) {
};
self['ref'] = {
- 'null'() {
- return Module['_BinaryenRefNull'](module);
+ 'null'(type) {
+ return Module['_BinaryenRefNull'](module, type);
},
'is_null'(value) {
return Module['_BinaryenRefIsNull'](module, value);
diff --git a/src/literal.h b/src/literal.h
index 4cbc16786..1b3949d21 100644
--- a/src/literal.h
+++ b/src/literal.h
@@ -39,8 +39,14 @@ class Literal {
int32_t i32;
int64_t i64;
uint8_t v128[16];
- Name func; // function name for funcref
+ // funcref function name. `isNull()` indicates a `null` value.
+ Name func;
+ // exnref package. `nullptr` indicates a `null` value.
std::unique_ptr<ExceptionPackage> exn;
+ // TODO: Literals of type `externref` can only be `null` currently but we
+ // will need to represent extern values eventually, to
+ // 1) run the spec tests and fuzzer with reference types enabled and
+ // 2) avoid bailing out when seeing a reference typed value in precompute
};
public:
@@ -48,10 +54,7 @@ public:
const Type type;
Literal() : v128(), type(Type::none) {}
- explicit Literal(Type type) : v128(), type(type) {
- assert(type != Type::unreachable && type != Type::funcref &&
- type != Type::exnref);
- }
+ explicit Literal(Type type);
explicit Literal(Type::BasicID typeId) : Literal(Type(typeId)) {}
explicit Literal(int32_t init) : i32(init), type(Type::i32) {}
explicit Literal(uint32_t init) : i32(init), type(Type::i32) {}
@@ -74,13 +77,25 @@ public:
Literal(const Literal& other);
Literal& operator=(const Literal& other);
~Literal() {
- if (type == Type::exnref) {
+ if (type.isException()) {
exn.~unique_ptr();
}
}
bool isConcrete() const { return type != Type::none; }
bool isNone() const { return type == Type::none; }
+ bool isNull() const {
+ if (type.isNullable()) {
+ if (type.isFunction()) {
+ return func.isNull();
+ }
+ if (type.isException()) {
+ return !exn;
+ }
+ return true;
+ }
+ return false;
+ }
static Literal makeFromInt32(int32_t x, Type type) {
switch (type.getBasic()) {
@@ -105,9 +120,12 @@ public:
static Literals makeZero(Type type);
static Literal makeSingleZero(Type type);
- static Literal makeNullref() { return Literal(Type(Type::nullref)); }
- static Literal makeFuncref(Name func) { return Literal(func.c_str()); }
- static Literal makeExnref(std::unique_ptr<ExceptionPackage>&& exn) {
+ static Literal makeNull(Type type) {
+ assert(type.isNullable());
+ return Literal(type);
+ }
+ static Literal makeFunc(Name func) { return Literal(func.c_str()); }
+ static Literal makeExn(std::unique_ptr<ExceptionPackage>&& exn) {
return Literal(std::move(exn));
}
@@ -134,7 +152,7 @@ public:
}
std::array<uint8_t, 16> getv128() const;
Name getFunc() const {
- assert(type == Type::funcref);
+ assert(type.isFunction() && !func.isNull());
return func;
}
ExceptionPackage getExceptionPackage() const;
@@ -502,6 +520,12 @@ public:
struct ExceptionPackage {
Name event;
Literals values;
+ bool operator==(const ExceptionPackage& other) const {
+ return event == other.event && values == other.values;
+ }
+ bool operator!=(const ExceptionPackage& other) const {
+ return !(*this == other);
+ }
};
std::ostream& operator<<(std::ostream& o, wasm::Literal literal);
@@ -554,7 +578,6 @@ template<> struct less<wasm::Literal> {
return memcmp(a.getv128Ptr(), b.getv128Ptr(), 16) < 0;
case wasm::Type::funcref:
case wasm::Type::externref:
- case wasm::Type::nullref:
case wasm::Type::exnref:
case wasm::Type::none:
case wasm::Type::unreachable:
diff --git a/src/parsing.h b/src/parsing.h
index a3eab1af3..663b901fd 100644
--- a/src/parsing.h
+++ b/src/parsing.h
@@ -265,7 +265,6 @@ parseConst(cashew::IString s, Type type, MixedArena& allocator) {
case Type::v128:
case Type::funcref:
case Type::externref:
- case Type::nullref:
case Type::exnref:
WASM_UNREACHABLE("unexpected const type");
case Type::none:
diff --git a/src/passes/ConstHoisting.cpp b/src/passes/ConstHoisting.cpp
index 3cef0773c..e3583d3bb 100644
--- a/src/passes/ConstHoisting.cpp
+++ b/src/passes/ConstHoisting.cpp
@@ -96,7 +96,6 @@ private:
case Type::v128:
case Type::funcref:
case Type::externref:
- case Type::nullref:
case Type::exnref: {
return false;
}
diff --git a/src/passes/Flatten.cpp b/src/passes/Flatten.cpp
index 54da8a86e..c7b4acbd9 100644
--- a/src/passes/Flatten.cpp
+++ b/src/passes/Flatten.cpp
@@ -211,19 +211,19 @@ struct Flatten
// the return type of the block this branch is targetting, which may
// not be the same with the innermost block's return type. For
// example,
- // (block $any (result externref)
- // (block (result nullref)
+ // (block $any (result anyref)
+ // (block (result funcref)
// (local.tee $0
// (br_if $any
- // (ref.null)
+ // (ref.null func)
// (i32.const 0)
// )
// )
// )
// )
// In this case we need two locals to store (ref.null); one with
- // externref type that's for the target block ($label0) and one more
- // with nullref type in case for flowing out. Here we create the
+ // funcref type that's for the target block ($label0) and one more
+ // with anyref type in case for flowing out. Here we create the
// second 'flowing out' local in case two block's types are
// different.
if (type != blockType) {
diff --git a/src/passes/FuncCastEmulation.cpp b/src/passes/FuncCastEmulation.cpp
index 13fb89988..160895f23 100644
--- a/src/passes/FuncCastEmulation.cpp
+++ b/src/passes/FuncCastEmulation.cpp
@@ -67,7 +67,6 @@ static Expression* toABI(Expression* value, Module* module) {
}
case Type::funcref:
case Type::externref:
- case Type::nullref:
case Type::exnref: {
WASM_UNREACHABLE("reference types cannot be converted to i64");
}
@@ -111,7 +110,6 @@ static Expression* fromABI(Expression* value, Type type, Module* module) {
}
case Type::funcref:
case Type::externref:
- case Type::nullref:
case Type::exnref: {
WASM_UNREACHABLE("reference types cannot be converted from i64");
}
diff --git a/src/passes/InstrumentLocals.cpp b/src/passes/InstrumentLocals.cpp
index f95d169e9..ee288c103 100644
--- a/src/passes/InstrumentLocals.cpp
+++ b/src/passes/InstrumentLocals.cpp
@@ -58,7 +58,6 @@ Name get_f32("get_f32");
Name get_f64("get_f64");
Name get_funcref("get_funcref");
Name get_externref("get_externref");
-Name get_nullref("get_nullref");
Name get_exnref("get_exnref");
Name get_v128("get_v128");
@@ -68,7 +67,6 @@ Name set_f32("set_f32");
Name set_f64("set_f64");
Name set_funcref("set_funcref");
Name set_externref("set_externref");
-Name set_nullref("set_nullref");
Name set_exnref("set_exnref");
Name set_v128("set_v128");
@@ -98,9 +96,6 @@ struct InstrumentLocals : public WalkerPass<PostWalker<InstrumentLocals>> {
case Type::externref:
import = get_externref;
break;
- case Type::nullref:
- import = get_nullref;
- break;
case Type::exnref:
import = get_exnref;
break;
@@ -147,9 +142,6 @@ struct InstrumentLocals : public WalkerPass<PostWalker<InstrumentLocals>> {
case Type::externref:
import = set_externref;
break;
- case Type::nullref:
- import = set_nullref;
- break;
case Type::exnref:
import = set_exnref;
break;
@@ -192,14 +184,6 @@ struct InstrumentLocals : public WalkerPass<PostWalker<InstrumentLocals>> {
set_externref,
{Type::i32, Type::i32, Type::externref},
Type::externref);
- addImport(curr,
- get_nullref,
- {Type::i32, Type::i32, Type::nullref},
- Type::nullref);
- addImport(curr,
- set_nullref,
- {Type::i32, Type::i32, Type::nullref},
- Type::nullref);
}
if (curr->features.hasExceptionHandling()) {
addImport(
diff --git a/src/passes/Precompute.cpp b/src/passes/Precompute.cpp
index 695b62ab2..0b67a25aa 100644
--- a/src/passes/Precompute.cpp
+++ b/src/passes/Precompute.cpp
@@ -134,7 +134,7 @@ struct Precompute
curr->finalize();
return;
}
- } else if (singleValue.type == Type::nullref &&
+ } else if (singleValue.isNull() &&
curr->value->template is<RefNull>()) {
return;
} else if (singleValue.type == Type::funcref) {
diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp
index f35f0d626..04fcdc0b3 100644
--- a/src/passes/Print.cpp
+++ b/src/passes/Print.cpp
@@ -1440,7 +1440,10 @@ struct PrintExpressionContents
break;
}
}
- void visitRefNull(RefNull* curr) { printMedium(o, "ref.null"); }
+ void visitRefNull(RefNull* curr) {
+ printMedium(o, "ref.null ");
+ o << curr->type.getHeapType();
+ }
void visitRefIsNull(RefIsNull* curr) { printMedium(o, "ref.is_null"); }
void visitRefFunc(RefFunc* curr) {
printMedium(o, "ref.func ");
diff --git a/src/shell-interface.h b/src/shell-interface.h
index a32ae6344..92f562b48 100644
--- a/src/shell-interface.h
+++ b/src/shell-interface.h
@@ -117,9 +117,8 @@ struct ShellExternalInterface : ModuleInstance::ExternalInterface {
assert(false && "v128 not implemented yet");
case Type::funcref:
case Type::externref:
- case Type::nullref:
case Type::exnref:
- globals[import->name] = {Literal::makeNullref()};
+ globals[import->name] = {Literal::makeNull(import->type)};
break;
case Type::none:
case Type::unreachable:
diff --git a/src/tools/execution-results.h b/src/tools/execution-results.h
index 38989aedf..c41b0c208 100644
--- a/src/tools/execution-results.h
+++ b/src/tools/execution-results.h
@@ -74,7 +74,7 @@ struct ExecutionResults {
// while the function contents are still the same
for (Literal& val : ret) {
if (val.type == Type::funcref) {
- val = Literal::makeFuncref(Name("funcref"));
+ val = Literal::makeFunc(Name("funcref"));
}
}
results[exp->name] = ret;
diff --git a/src/tools/fuzzing.h b/src/tools/fuzzing.h
index b3c1212d4..ef7844c60 100644
--- a/src/tools/fuzzing.h
+++ b/src/tools/fuzzing.h
@@ -315,21 +315,7 @@ private:
}
SmallVector<Type, 2> options;
options.push_back(type); // includes itself
- TODO_SINGLE_COMPOUND(type);
- switch (type.getBasic()) {
- case Type::externref:
- if (wasm.features.hasExceptionHandling()) {
- options.push_back(Type::exnref);
- }
- options.push_back(Type::funcref);
- // falls through
- case Type::funcref:
- case Type::exnref:
- options.push_back(Type::nullref);
- break;
- default:
- break;
- }
+ // TODO (GC): subtyping
return pick(options);
}
@@ -1362,7 +1348,6 @@ private:
}
case Type::funcref:
case Type::externref:
- case Type::nullref:
case Type::exnref:
case Type::none:
case Type::unreachable:
@@ -1466,7 +1451,6 @@ private:
}
case Type::funcref:
case Type::externref:
- case Type::nullref:
case Type::exnref:
case Type::none:
case Type::unreachable:
@@ -1595,7 +1579,6 @@ private:
case Type::v128:
case Type::funcref:
case Type::externref:
- case Type::nullref:
case Type::exnref:
case Type::none:
case Type::unreachable:
@@ -1640,7 +1623,6 @@ private:
case Type::v128:
case Type::funcref:
case Type::externref:
- case Type::nullref:
case Type::exnref:
case Type::none:
case Type::unreachable:
@@ -1708,7 +1690,6 @@ private:
case Type::v128:
case Type::funcref:
case Type::externref:
- case Type::nullref:
case Type::exnref:
case Type::none:
case Type::unreachable:
@@ -1735,7 +1716,6 @@ private:
case Type::v128:
case Type::funcref:
case Type::externref:
- case Type::nullref:
case Type::exnref:
case Type::none:
case Type::unreachable:
@@ -1763,7 +1743,7 @@ private:
}
return builder.makeRefFunc(target->name);
}
- return builder.makeRefNull();
+ return builder.makeRefNull(type);
}
if (type.isTuple()) {
std::vector<Expression*> operands;
@@ -1845,7 +1825,6 @@ private:
}
case Type::funcref:
case Type::externref:
- case Type::nullref:
case Type::exnref:
return makeTrivial(type);
case Type::none:
@@ -1990,7 +1969,6 @@ private:
}
case Type::funcref:
case Type::externref:
- case Type::nullref:
case Type::exnref:
case Type::none:
case Type::unreachable:
@@ -2227,7 +2205,6 @@ private:
}
case Type::funcref:
case Type::externref:
- case Type::nullref:
case Type::exnref:
case Type::none:
case Type::unreachable:
@@ -2434,7 +2411,6 @@ private:
case Type::v128:
case Type::funcref:
case Type::externref:
- case Type::nullref:
case Type::exnref:
case Type::none:
case Type::unreachable:
@@ -2610,14 +2586,13 @@ private:
Expression* makeRefIsNull(Type type) {
assert(type == Type::i32);
assert(wasm.features.hasReferenceTypes());
- Type refType;
+ SmallVector<Type, 2> options;
+ options.push_back(Type::externref);
+ options.push_back(Type::funcref);
if (wasm.features.hasExceptionHandling()) {
- refType =
- pick(Type::funcref, Type::externref, Type::nullref, Type::exnref);
- } else {
- refType = pick(Type::funcref, Type::externref, Type::nullref);
+ options.push_back(Type::exnref);
}
- return builder.makeRefIsNull(make(refType));
+ return builder.makeRefIsNull(make(pick(options)));
}
Expression* makeMemoryInit() {
@@ -2680,10 +2655,7 @@ private:
FeatureOptions<Type>()
.add(FeatureSet::MVP, Type::i32, Type::i64, Type::f32, Type::f64)
.add(FeatureSet::SIMD, Type::v128)
- .add(FeatureSet::ReferenceTypes,
- Type::funcref,
- Type::externref,
- Type::nullref)
+ .add(FeatureSet::ReferenceTypes, Type::funcref, Type::externref)
.add(FeatureSet::ReferenceTypes | FeatureSet::ExceptionHandling,
Type::exnref));
}
@@ -2732,7 +2704,6 @@ private:
FeatureOptions<Type>()
.add(FeatureSet::MVP, Type::i32, Type::i64, Type::f32, Type::f64)
.add(FeatureSet::SIMD, Type::v128)
- .add(FeatureSet::ReferenceTypes, Type::nullref)
.add(FeatureSet::ReferenceTypes | FeatureSet::ExceptionHandling,
Type::exnref));
}
diff --git a/src/tools/spec-wrapper.h b/src/tools/spec-wrapper.h
index 44f92b56d..c073a994c 100644
--- a/src/tools/spec-wrapper.h
+++ b/src/tools/spec-wrapper.h
@@ -50,10 +50,13 @@ static std::string generateSpecWrapper(Module& wasm) {
ret += "(v128.const i32x4 0 0 0 0)";
break;
case Type::funcref:
+ ret += "(ref.null func)";
+ break;
case Type::externref:
- case Type::nullref:
+ ret += "(ref.null extern)";
+ break;
case Type::exnref:
- ret += "(ref.null)";
+ ret += "(ref.null exn)";
break;
case Type::none:
case Type::unreachable:
diff --git a/src/tools/wasm-reduce.cpp b/src/tools/wasm-reduce.cpp
index 9c9978b63..1a7c3544d 100644
--- a/src/tools/wasm-reduce.cpp
+++ b/src/tools/wasm-reduce.cpp
@@ -597,7 +597,6 @@ struct Reducer
case Type::v128:
case Type::funcref:
case Type::externref:
- case Type::nullref:
case Type::exnref:
continue; // not implemented yet
case Type::none:
@@ -623,7 +622,6 @@ struct Reducer
case Type::v128:
case Type::funcref:
case Type::externref:
- case Type::nullref:
case Type::exnref:
continue; // not implemented yet
case Type::none:
@@ -649,7 +647,6 @@ struct Reducer
case Type::v128:
case Type::funcref:
case Type::externref:
- case Type::nullref:
case Type::exnref:
continue; // not implemented yet
case Type::none:
@@ -675,7 +672,6 @@ struct Reducer
case Type::v128:
case Type::funcref:
case Type::externref:
- case Type::nullref:
case Type::exnref:
continue; // not implemented yet
case Type::none:
@@ -687,7 +683,6 @@ struct Reducer
case Type::v128:
case Type::funcref:
case Type::externref:
- case Type::nullref:
case Type::exnref:
continue; // not implemented yet
case Type::none:
@@ -1015,8 +1010,8 @@ struct Reducer
return false;
}
// try to replace with a trivial value
- if (curr->type.isRef()) {
- RefNull* n = builder->makeRefNull();
+ if (curr->type.isNullable()) {
+ RefNull* n = builder->makeRefNull(curr->type);
return tryToReplaceCurrent(n);
}
if (curr->type.isTuple()) {
diff --git a/src/wasm-binary.h b/src/wasm-binary.h
index ab9ee5152..865a882df 100644
--- a/src/wasm-binary.h
+++ b/src/wasm-binary.h
@@ -347,8 +347,6 @@ enum EncodedType {
funcref = -0x10, // 0x70
// opaque host reference type
externref = -0x11, // 0x6f
- // null reference type
- nullref = -0x12, // 0x6e
// exception reference type
exnref = -0x18, // 0x68
// func_type form
@@ -357,6 +355,12 @@ enum EncodedType {
Empty = -0x40 // 0x40
};
+enum EncodedHeapType {
+ func = -0x10, // 0x70
+ extern_ = -0x11, // 0x6f
+ exn = -0x18, // 0x68
+};
+
namespace UserSections {
extern const char* Name;
extern const char* SourceMapUrl;
@@ -968,9 +972,6 @@ inline S32LEB binaryType(Type type) {
case Type::externref:
ret = BinaryConsts::EncodedType::externref;
break;
- case Type::nullref:
- ret = BinaryConsts::EncodedType::nullref;
- break;
case Type::exnref:
ret = BinaryConsts::EncodedType::exnref;
break;
@@ -980,6 +981,29 @@ inline S32LEB binaryType(Type type) {
return S32LEB(ret);
}
+inline S32LEB binaryHeapType(HeapType type) {
+ int ret = 0;
+ switch (type.kind) {
+ case HeapType::FuncKind:
+ ret = BinaryConsts::EncodedHeapType::func;
+ break;
+ case HeapType::ExternKind:
+ ret = BinaryConsts::EncodedHeapType::extern_;
+ break;
+ case HeapType::ExnKind:
+ ret = BinaryConsts::EncodedHeapType::exn;
+ break;
+ case HeapType::AnyKind:
+ case HeapType::EqKind:
+ case HeapType::I31Kind:
+ case HeapType::SignatureKind:
+ case HeapType::StructKind:
+ case HeapType::ArrayKind:
+ WASM_UNREACHABLE("TODO: GC types");
+ }
+ return S32LEB(ret); // TODO: Actually encoded as s33
+}
+
// Writes out wasm to the binary format
class WasmBinaryWriter {
@@ -1209,6 +1233,7 @@ public:
int32_t getS32LEB();
int64_t getS64LEB();
Type getType();
+ HeapType getHeapType();
Type getConcreteType();
Name getInlineString();
void verifyInt8(int8_t x);
diff --git a/src/wasm-builder.h b/src/wasm-builder.h
index 4e94da090..1ce3a507c 100644
--- a/src/wasm-builder.h
+++ b/src/wasm-builder.h
@@ -528,9 +528,9 @@ public:
ret->finalize();
return ret;
}
- RefNull* makeRefNull() {
+ RefNull* makeRefNull(Type type) {
auto* ret = allocator.alloc<RefNull>();
- ret->finalize();
+ ret->finalize(type);
return ret;
}
RefIsNull* makeRefIsNull(Expression* value) {
@@ -624,13 +624,15 @@ public:
Expression* makeConstantExpression(Literal value) {
TODO_SINGLE_COMPOUND(value.type);
switch (value.type.getBasic()) {
- case Type::nullref:
- return makeRefNull();
case Type::funcref:
- if (value.getFunc()[0] != 0) {
+ if (!value.isNull()) {
return makeRefFunc(value.getFunc());
}
- return makeRefNull();
+ return makeRefNull(value.type);
+ case Type::externref:
+ case Type::exnref: // TODO: ExceptionPackage?
+ assert(value.isNull());
+ return makeRefNull(value.type);
default:
assert(value.type.isNumber());
return makeConst(value);
@@ -822,9 +824,8 @@ public:
}
case Type::funcref:
case Type::externref:
- case Type::nullref:
case Type::exnref:
- return ExpressionManipulator::refNull(curr);
+ return ExpressionManipulator::refNull(curr, curr->type);
case Type::none:
return ExpressionManipulator::nop(curr);
case Type::unreachable:
diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h
index 3eab81cc5..44cac9d49 100644
--- a/src/wasm-interpreter.h
+++ b/src/wasm-interpreter.h
@@ -1242,7 +1242,7 @@ public:
Flow visitPop(Pop* curr) { WASM_UNREACHABLE("unimp"); }
Flow visitRefNull(RefNull* curr) {
NOTE_ENTER("RefNull");
- return Literal::makeNullref();
+ return Literal::makeNull(curr->type);
}
Flow visitRefIsNull(RefIsNull* curr) {
NOTE_ENTER("RefIsNull");
@@ -1250,14 +1250,14 @@ public:
if (flow.breaking()) {
return flow;
}
- Literal value = flow.getSingleValue();
+ const auto& value = flow.getSingleValue();
NOTE_EVAL1(value);
- return Literal(value.type == Type::nullref);
+ return Literal(value.isNull());
}
Flow visitRefFunc(RefFunc* curr) {
NOTE_ENTER("RefFunc");
NOTE_NAME(curr->func);
- return Literal::makeFuncref(curr->func);
+ return Literal::makeFunc(curr->func);
}
Flow visitTry(Try* curr) { WASM_UNREACHABLE("unimp"); }
Flow visitThrow(Throw* curr) {
@@ -1273,7 +1273,7 @@ public:
for (auto item : arguments) {
exn->values.push_back(item);
}
- throwException(Literal::makeExnref(std::move(exn)));
+ throwException(Literal::makeExn(std::move(exn)));
WASM_UNREACHABLE("throw");
}
Flow visitRethrow(Rethrow* curr) {
@@ -1282,10 +1282,11 @@ public:
if (flow.breaking()) {
return flow;
}
- if (flow.getType() == Type::nullref) {
+ const auto& value = flow.getSingleValue();
+ if (value.isNull()) {
trap("rethrow: argument is null");
}
- throwException(flow.getSingleValue());
+ throwException(value);
WASM_UNREACHABLE("rethrow");
}
Flow visitBrOnExn(BrOnExn* curr) {
@@ -1294,10 +1295,11 @@ public:
if (flow.breaking()) {
return flow;
}
- if (flow.getType() == Type::nullref) {
+ const auto& value = flow.getSingleValue();
+ if (value.isNull()) {
trap("br_on_exn: argument is null");
}
- auto ex = flow.getSingleValue().getExceptionPackage();
+ auto ex = value.getExceptionPackage();
if (curr->event != ex.event) { // Not taken
return flow;
}
@@ -1644,7 +1646,6 @@ public:
return Literal(load128(addr).data());
case Type::funcref:
case Type::externref:
- case Type::nullref:
case Type::exnref:
case Type::none:
case Type::unreachable:
@@ -1701,7 +1702,6 @@ public:
break;
case Type::funcref:
case Type::externref:
- case Type::nullref:
case Type::exnref:
case Type::none:
case Type::unreachable:
diff --git a/src/wasm-s-parser.h b/src/wasm-s-parser.h
index 6cbef5599..76a60d7d9 100644
--- a/src/wasm-s-parser.h
+++ b/src/wasm-s-parser.h
@@ -160,6 +160,10 @@ private:
}
Type
stringToType(const char* str, bool allowError = false, bool prefix = false);
+ HeapType stringToHeapType(cashew::IString str, bool prefix = false) {
+ return stringToHeapType(str.str, prefix);
+ }
+ HeapType stringToHeapType(const char* str, bool prefix = false);
Type elementToType(Element& s);
Type stringToLaneType(const char* str);
bool isType(cashew::IString str) {
diff --git a/src/wasm-type.h b/src/wasm-type.h
index 135c2e751..384cc589f 100644
--- a/src/wasm-type.h
+++ b/src/wasm-type.h
@@ -49,7 +49,6 @@ public:
v128,
funcref,
externref,
- nullref,
exnref,
_last_basic_id = exnref
};
@@ -93,7 +92,6 @@ public:
// │ anyref ║ x │ │ x │ x │ f? n │ │ ┐
// │ eqref ║ x │ │ x │ x │ n │ │ │ TODO (GC)
// │ i31ref ║ x │ │ x │ x │ │ │ ┘
- // │ nullref ║ x │ │ x │ x │ f? n │ │ ◄ TODO (removed)
// │ exnref ║ x │ │ x │ x │ n │ │
// ├─ Compound ──╫───┼───┼───┼───┤───────┤ │
// │ Ref ║ │ x │ x │ x │ f? n? │◄┘
@@ -110,6 +108,8 @@ public:
bool isTuple() const;
bool isSingle() const { return isConcrete() && !isTuple(); }
bool isRef() const;
+ bool isFunction() const;
+ bool isException() const;
bool isNullable() const;
bool isRtt() const;
@@ -154,6 +154,9 @@ public:
// Returns the feature set required to use this type.
FeatureSet getFeatures() const;
+ // Gets the heap type corresponding to this type
+ HeapType getHeapType() const;
+
// Returns a number type based on its size in bytes and whether it is a float
// type.
static Type get(unsigned byteSize, bool float_);
@@ -368,6 +371,7 @@ struct HeapType {
assert(isArray() && "Not an array");
return array;
}
+ bool isException() const { return kind == ExnKind; }
bool operator==(const HeapType& other) const;
bool operator!=(const HeapType& other) const { return !(*this == other); }
diff --git a/src/wasm.h b/src/wasm.h
index 3d658d367..edcda4219 100644
--- a/src/wasm.h
+++ b/src/wasm.h
@@ -1095,6 +1095,8 @@ public:
RefNull(MixedArena& allocator) {}
void finalize();
+ void finalize(HeapType heapType);
+ void finalize(Type type);
};
class RefIsNull : public SpecificExpression<Expression::RefIsNullId> {
diff --git a/src/wasm/literal.cpp b/src/wasm/literal.cpp
index 3f635ca19..ec8642865 100644
--- a/src/wasm/literal.cpp
+++ b/src/wasm/literal.cpp
@@ -29,37 +29,52 @@ namespace wasm {
template<int N> using LaneArray = std::array<Literal, N>;
+Literal::Literal(Type type) : type(type) {
+ assert(type != Type::unreachable && (!type.isRef() || type.isNullable()));
+ if (type.isException()) {
+ new (&exn) std::unique_ptr<ExceptionPackage>();
+ } else {
+ memset(&v128, 0, 16);
+ }
+}
+
Literal::Literal(const uint8_t init[16]) : type(Type::v128) {
memcpy(&v128, init, 16);
}
Literal::Literal(const Literal& other) : type(other.type) {
- TODO_SINGLE_COMPOUND(type);
- switch (type.getBasic()) {
- case Type::i32:
- case Type::f32:
- i32 = other.i32;
- break;
- case Type::i64:
- case Type::f64:
- i64 = other.i64;
- break;
- case Type::v128:
- memcpy(&v128, other.v128, 16);
- break;
- case Type::funcref:
- func = other.func;
- break;
- case Type::exnref:
- // Avoid calling the destructor on an uninitialized value
+ if (type.isException()) {
+ // Avoid calling the destructor on an uninitialized value
+ if (other.exn != nullptr) {
new (&exn) auto(std::make_unique<ExceptionPackage>(*other.exn));
- break;
- case Type::none:
- case Type::nullref:
- break;
- case Type::externref:
- case Type::unreachable:
- WASM_UNREACHABLE("unexpected type");
+ } else {
+ new (&exn) std::unique_ptr<ExceptionPackage>();
+ }
+ } else if (type.isFunction()) {
+ func = other.func;
+ } else {
+ TODO_SINGLE_COMPOUND(type);
+ switch (type.getBasic()) {
+ case Type::i32:
+ case Type::f32:
+ i32 = other.i32;
+ break;
+ case Type::i64:
+ case Type::f64:
+ i64 = other.i64;
+ break;
+ case Type::v128:
+ memcpy(&v128, other.v128, 16);
+ break;
+ case Type::none:
+ break;
+ case Type::externref:
+ break; // null
+ case Type::funcref:
+ case Type::exnref:
+ case Type::unreachable:
+ WASM_UNREACHABLE("unexpected type");
+ }
}
}
@@ -116,7 +131,7 @@ Literals Literal::makeZero(Type type) {
Literal Literal::makeSingleZero(Type type) {
assert(type.isSingle());
if (type.isRef()) {
- return makeNullref();
+ return makeNull(type);
} else {
return makeFromInt32(0, type);
}
@@ -130,8 +145,8 @@ std::array<uint8_t, 16> Literal::getv128() const {
}
ExceptionPackage Literal::getExceptionPackage() const {
- assert(type == Type::exnref);
- return *exn.get();
+ assert(type.isException() && exn != nullptr);
+ return *exn;
}
Literal Literal::castToF32() {
@@ -199,11 +214,17 @@ void Literal::getBits(uint8_t (&buf)[16]) const {
case Type::v128:
memcpy(buf, &v128, sizeof(v128));
break;
+ // TODO: investigate changing bits returned for reference types. currently,
+ // `null` values and even non-`null` functions return all zeroes, but only
+ // to avoid introducing a functional change.
case Type::funcref:
- case Type::nullref:
break;
case Type::externref:
case Type::exnref:
+ if (isNull()) {
+ break;
+ }
+ // falls through
case Type::none:
case Type::unreachable:
WASM_UNREACHABLE("invalid type");
@@ -211,22 +232,22 @@ void Literal::getBits(uint8_t (&buf)[16]) const {
}
bool Literal::operator==(const Literal& other) const {
- if (type.isRef() && other.type.isRef()) {
- if (type == Type::nullref && other.type == Type::nullref) {
- return true;
- }
- if (type == Type::funcref && other.type == Type::funcref &&
- func == other.func) {
- return true;
- }
- return false;
- }
if (type != other.type) {
return false;
}
if (type == Type::none) {
return true;
}
+ if (isNull() || other.isNull()) {
+ return isNull() == other.isNull();
+ }
+ if (type.isFunction()) {
+ return func == other.func;
+ }
+ if (type.isException()) {
+ assert(exn != nullptr && other.exn != nullptr);
+ return *exn == *other.exn;
+ }
uint8_t bits[16], other_bits[16];
getBits(bits);
other.getBits(other_bits);
@@ -350,15 +371,23 @@ std::ostream& operator<<(std::ostream& o, Literal literal) {
literal.printVec128(o, literal.getv128());
break;
case Type::funcref:
- o << "funcref(" << literal.getFunc() << ")";
- break;
- case Type::nullref:
- o << "nullref";
+ if (literal.isNull()) {
+ o << "funcref(null)";
+ } else {
+ o << "funcref(" << literal.getFunc() << ")";
+ }
break;
case Type::exnref:
- o << "exnref(" << literal.getExceptionPackage() << ")";
+ if (literal.isNull()) {
+ o << "exnref(null)";
+ } else {
+ o << "exnref(" << literal.getExceptionPackage() << ")";
+ }
break;
case Type::externref:
+ assert(literal.isNull() && "TODO: non-null externref values");
+ o << "externref(null)";
+ break;
case Type::unreachable:
WASM_UNREACHABLE("invalid type");
}
@@ -582,7 +611,6 @@ Literal Literal::eqz() const {
case Type::v128:
case Type::funcref:
case Type::externref:
- case Type::nullref:
case Type::exnref:
case Type::none:
case Type::unreachable:
@@ -604,7 +632,6 @@ Literal Literal::neg() const {
case Type::v128:
case Type::funcref:
case Type::externref:
- case Type::nullref:
case Type::exnref:
case Type::none:
case Type::unreachable:
@@ -626,7 +653,6 @@ Literal Literal::abs() const {
case Type::v128:
case Type::funcref:
case Type::externref:
- case Type::nullref:
case Type::exnref:
case Type::none:
case Type::unreachable:
@@ -731,7 +757,6 @@ Literal Literal::add(const Literal& other) const {
case Type::v128:
case Type::funcref:
case Type::externref:
- case Type::nullref:
case Type::exnref:
case Type::none:
case Type::unreachable:
@@ -753,7 +778,6 @@ Literal Literal::sub(const Literal& other) const {
case Type::v128:
case Type::funcref:
case Type::externref:
- case Type::nullref:
case Type::exnref:
case Type::none:
case Type::unreachable:
@@ -846,7 +870,6 @@ Literal Literal::mul(const Literal& other) const {
case Type::v128:
case Type::funcref:
case Type::externref:
- case Type::nullref:
case Type::exnref:
case Type::none:
case Type::unreachable:
@@ -1100,7 +1123,6 @@ Literal Literal::eq(const Literal& other) const {
case Type::v128:
case Type::funcref:
case Type::externref:
- case Type::nullref:
case Type::exnref:
case Type::none:
case Type::unreachable:
@@ -1122,7 +1144,6 @@ Literal Literal::ne(const Literal& other) const {
case Type::v128:
case Type::funcref:
case Type::externref:
- case Type::nullref:
case Type::exnref:
case Type::none:
case Type::unreachable:
diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp
index ae10687fb..b78d6b86b 100644
--- a/src/wasm/wasm-binary.cpp
+++ b/src/wasm/wasm-binary.cpp
@@ -1136,8 +1136,6 @@ Type WasmBinaryBuilder::getType() {
return Type::funcref;
case BinaryConsts::EncodedType::externref:
return Type::externref;
- case BinaryConsts::EncodedType::nullref:
- return Type::nullref;
case BinaryConsts::EncodedType::exnref:
return Type::exnref;
default:
@@ -1146,6 +1144,28 @@ Type WasmBinaryBuilder::getType() {
WASM_UNREACHABLE("unexpeced type");
}
+HeapType WasmBinaryBuilder::getHeapType() {
+ int type = getS32LEB(); // TODO: Actually encoded as s33
+ // Single heap types are negative; heap type indices are non-negative
+ if (type >= 0) {
+ if (size_t(type) >= signatures.size()) {
+ throwError("invalid signature index: " + std::to_string(type));
+ }
+ return HeapType(signatures[type]);
+ }
+ switch (type) {
+ case BinaryConsts::EncodedHeapType::func:
+ return HeapType::FuncKind;
+ case BinaryConsts::EncodedHeapType::extern_:
+ return HeapType::ExternKind;
+ case BinaryConsts::EncodedHeapType::exn:
+ return HeapType::ExnKind;
+ default:
+ throwError("invalid wasm heap type: " + std::to_string(type));
+ }
+ WASM_UNREACHABLE("unexpeced type");
+}
+
Type WasmBinaryBuilder::getConcreteType() {
auto type = getType();
if (!type.isConcrete()) {
@@ -4689,7 +4709,7 @@ void WasmBinaryBuilder::visitDrop(Drop* curr) {
void WasmBinaryBuilder::visitRefNull(RefNull* curr) {
BYN_TRACE("zz node: RefNull\n");
- curr->finalize();
+ curr->finalize(getHeapType());
}
void WasmBinaryBuilder::visitRefIsNull(RefIsNull* curr) {
diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp
index 931cd1bf0..0e4202a24 100644
--- a/src/wasm/wasm-s-parser.cpp
+++ b/src/wasm/wasm-s-parser.cpp
@@ -869,9 +869,6 @@ Type SExpressionWasmBuilder::stringToType(const char* str,
if (strncmp(str, "externref", 9) == 0 && (prefix || str[9] == 0)) {
return Type::externref;
}
- if (strncmp(str, "nullref", 7) == 0 && (prefix || str[7] == 0)) {
- return Type::nullref;
- }
if (strncmp(str, "exnref", 6) == 0 && (prefix || str[6] == 0)) {
return Type::exnref;
}
@@ -881,6 +878,41 @@ Type SExpressionWasmBuilder::stringToType(const char* str,
throw ParseException(std::string("invalid wasm type: ") + str);
}
+HeapType SExpressionWasmBuilder::stringToHeapType(const char* str,
+ bool prefix) {
+ if (str[0] == 'a') {
+ if (str[1] == 'n' && str[2] == 'y' && (prefix || str[3] == 0)) {
+ return HeapType::AnyKind;
+ }
+ }
+ if (str[0] == 'e') {
+ if (str[1] == 'q' && (prefix || str[2] == 0)) {
+ return HeapType::EqKind;
+ }
+ if (str[1] == 'x') {
+ if (str[2] == 'n' && (prefix || str[3] == 0)) {
+ return HeapType::ExnKind;
+ }
+ if (str[2] == 't' && str[3] == 'e' && str[4] == 'r' && str[5] == 'n' &&
+ (prefix || str[6] == 0)) {
+ return HeapType::ExternKind;
+ }
+ }
+ }
+ if (str[0] == 'i') {
+ if (str[1] == '3' && str[2] == '1' && (prefix || str[3] == 0)) {
+ return HeapType::I31Kind;
+ }
+ }
+ if (str[0] == 'f') {
+ if (str[1] == 'u' && str[2] == 'n' && str[3] == 'c' &&
+ (prefix || str[4] == 0)) {
+ return HeapType::FuncKind;
+ }
+ }
+ throw ParseException(std::string("invalid wasm heap type: ") + str);
+}
+
Type SExpressionWasmBuilder::elementToType(Element& s) {
if (s.isStr()) {
return stringToType(s.str(), false, false);
@@ -1779,8 +1811,12 @@ Expression* SExpressionWasmBuilder::makeReturn(Element& s) {
}
Expression* SExpressionWasmBuilder::makeRefNull(Element& s) {
+ if (s.size() != 2) {
+ throw ParseException("invalid heap type reference", s.line, s.col);
+ }
+ auto heapType = stringToHeapType(s[1]->str());
auto ret = allocator.alloc<RefNull>();
- ret->finalize();
+ ret->finalize(heapType);
return ret;
}
diff --git a/src/wasm/wasm-stack.cpp b/src/wasm/wasm-stack.cpp
index 88f90378c..aee5079c2 100644
--- a/src/wasm/wasm-stack.cpp
+++ b/src/wasm/wasm-stack.cpp
@@ -190,7 +190,6 @@ void BinaryInstWriter::visitLoad(Load* curr) {
return;
case Type::funcref:
case Type::externref:
- case Type::nullref:
case Type::exnref:
case Type::none:
WASM_UNREACHABLE("unexpected type");
@@ -292,7 +291,6 @@ void BinaryInstWriter::visitStore(Store* curr) {
break;
case Type::funcref:
case Type::externref:
- case Type::nullref:
case Type::exnref:
case Type::none:
case Type::unreachable:
@@ -695,7 +693,6 @@ void BinaryInstWriter::visitConst(Const* curr) {
}
case Type::funcref:
case Type::externref:
- case Type::nullref:
case Type::exnref:
case Type::none:
case Type::unreachable:
@@ -1686,7 +1683,8 @@ void BinaryInstWriter::visitHost(Host* curr) {
}
void BinaryInstWriter::visitRefNull(RefNull* curr) {
- o << int8_t(BinaryConsts::RefNull);
+ o << int8_t(BinaryConsts::RefNull)
+ << binaryHeapType(curr->type.getHeapType());
}
void BinaryInstWriter::visitRefIsNull(RefIsNull* curr) {
diff --git a/src/wasm/wasm-type.cpp b/src/wasm/wasm-type.cpp
index e46cb5886..729cc88f5 100644
--- a/src/wasm/wasm-type.cpp
+++ b/src/wasm/wasm-type.cpp
@@ -260,7 +260,6 @@ std::unordered_map<TypeInfo, uintptr_t> indices = {
// * `(ref null any) == anyref`
// * `(ref null eq) == eqref`
// * `(ref i31) == i31ref`
- {TypeInfo({Type::nullref}), Type::nullref}, // TODO (removed)
{TypeInfo({Type::exnref}), Type::exnref},
{TypeInfo(HeapType(HeapType::ExnKind), true), Type::exnref},
};
@@ -347,6 +346,24 @@ bool Type::isRef() const {
}
}
+bool Type::isFunction() const {
+ if (isBasic()) {
+ return id == funcref;
+ } else {
+ auto* info = getTypeInfo(*this);
+ return info->isRef() && info->ref.heapType.isSignature();
+ }
+}
+
+bool Type::isException() const {
+ if (isBasic()) {
+ return id == exnref;
+ } else {
+ auto* info = getTypeInfo(*this);
+ return info->isRef() && info->ref.heapType.isException();
+ }
+}
+
bool Type::isNullable() const {
if (isBasic()) {
return id >= funcref && id <= exnref;
@@ -389,7 +406,6 @@ unsigned Type::getByteSize() const {
return 16;
case Type::funcref:
case Type::externref:
- case Type::nullref:
case Type::exnref:
case Type::none:
case Type::unreachable:
@@ -432,7 +448,6 @@ FeatureSet Type::getFeatures() const {
return FeatureSet::SIMD;
case Type::funcref:
case Type::externref:
- case Type::nullref:
return FeatureSet::ReferenceTypes;
case Type::exnref:
return FeatureSet::ReferenceTypes | FeatureSet::ExceptionHandling;
@@ -451,6 +466,25 @@ FeatureSet Type::getFeatures() const {
return getSingleFeatures(*this);
}
+HeapType Type::getHeapType() const {
+ if (isRef()) {
+ if (isCompound()) {
+ return getTypeInfo(*this)->ref.heapType;
+ }
+ switch (getBasic()) {
+ case funcref:
+ return HeapType::FuncKind;
+ case externref:
+ return HeapType::ExternKind;
+ case exnref:
+ return HeapType::ExnKind;
+ default:
+ break;
+ }
+ }
+ WASM_UNREACHABLE("unexpected type");
+}
+
Type Type::get(unsigned byteSize, bool float_) {
if (byteSize < 4) {
return Type::i32;
@@ -468,13 +502,10 @@ Type Type::get(unsigned byteSize, bool float_) {
}
bool Type::isSubType(Type left, Type right) {
+ // TODO (GC): subtyping, currently checks for equality only
if (left == right) {
return true;
}
- if (left.isRef() && right.isRef() &&
- (right == Type::externref || left == Type::nullref)) {
- return true;
- }
if (left.isTuple() && right.isTuple()) {
if (left.size() != right.size()) {
return false;
@@ -513,16 +544,7 @@ Type Type::getLeastUpperBound(Type a, Type b) {
}
return Type(types);
}
- if (!a.isRef() || !b.isRef()) {
- return Type::none;
- }
- if (a == Type::nullref) {
- return b;
- }
- if (b == Type::nullref) {
- return a;
- }
- return Type::externref;
+ return Type::none;
}
Type::Iterator Type::end() const {
@@ -554,8 +576,7 @@ const Type& Type::operator[](size_t index) const {
}
}
-HeapType::HeapType(const HeapType& other) {
- kind = other.kind;
+HeapType::HeapType(const HeapType& other) : kind(other.kind) {
switch (kind) {
case FuncKind:
case ExternKind:
@@ -708,9 +729,6 @@ std::ostream& operator<<(std::ostream& os, Type type) {
case Type::externref:
os << "externref";
break;
- case Type::nullref:
- os << "nullref";
- break;
case Type::exnref:
os << "exnref";
break;
diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp
index 3e1a057ce..b236f9120 100644
--- a/src/wasm/wasm-validator.cpp
+++ b/src/wasm/wasm-validator.cpp
@@ -1264,7 +1264,6 @@ void FunctionValidator::validateMemBytes(uint8_t bytes,
break;
case Type::funcref:
case Type::externref:
- case Type::nullref:
case Type::exnref:
case Type::none:
WASM_UNREACHABLE("unexpected type");
@@ -2074,7 +2073,6 @@ void FunctionValidator::validateAlignment(
break;
case Type::funcref:
case Type::externref:
- case Type::nullref:
case Type::exnref:
case Type::none:
WASM_UNREACHABLE("invalid type");
diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp
index 387ea577f..fe7c1b229 100644
--- a/src/wasm/wasm.cpp
+++ b/src/wasm/wasm.cpp
@@ -209,10 +209,10 @@ const char* getExpressionName(Expression* curr) {
Literal getSingleLiteralFromConstExpression(Expression* curr) {
if (auto* c = curr->dynCast<Const>()) {
return c->value;
- } else if (curr->is<RefNull>()) {
- return Literal::makeNullref();
+ } else if (auto* n = curr->dynCast<RefNull>()) {
+ return Literal::makeNull(n->type);
} else if (auto* r = curr->dynCast<RefFunc>()) {
- return Literal::makeFuncref(r->func);
+ return Literal::makeFunc(r->func);
} else {
WASM_UNREACHABLE("Not a constant expression");
}
@@ -896,7 +896,16 @@ void Host::finalize() {
}
}
-void RefNull::finalize() { type = Type::nullref; }
+void RefNull::finalize(HeapType heapType) { type = Type(heapType, true); }
+
+void RefNull::finalize(Type type_) {
+ assert(type_ == Type::unreachable || type_.isNullable());
+ type = type_;
+}
+
+void RefNull::finalize() {
+ assert(type == Type::unreachable || type.isNullable());
+}
void RefIsNull::finalize() {
if (value->type == Type::unreachable) {