Skip to content

Commit fe7d2b2

Browse files
committed
Add support for reference types proposal
This adds support for the reference type proposal. This adds support for all reference types (anyref, funcref(=anyfunc), and nullref) and four new instructions: `ref.null`, `ref.is_null`, `ref.func`, and new typed `select`. This also adds subtype relationship support between reference types. This does not include table instructions yet. This also does not include wasm2js support. This does not pass `fuzz_opt.py` yet because there are remaining bugs related to `nullref` handling, because `nullref` is not allowed to be written to text or binary format. We are discussing whether we should allow it to be written in WebAssembly/reference-types#60 now. This is not ready for review yet because it does not pass fuzzer now; I'm uploading this just to show how the current a bit ad-hoc `nullref` handling looks like. So don't spend too much time on reviewing each detail for now. I apprciate some high-level comments on how we should handle `nullref` or other stuff.
1 parent 24d2749 commit fe7d2b2

File tree

83 files changed

+5840
-1316
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

83 files changed

+5840
-1316
lines changed

scripts/gen-s-parser.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@
5151
("v128.pop", "makePop(v128)"),
5252
("anyref.pop", "makePop(anyref)"),
5353
("exnref.pop", "makePop(exnref)"),
54+
("funcref.pop", "makePop(funcref)"),
55+
("nullref.pop", "makePop(nullref)"),
5456
("i32.load", "makeLoad(s, i32, /*isAtomic=*/false)"),
5557
("i64.load", "makeLoad(s, i64, /*isAtomic=*/false)"),
5658
("f32.load", "makeLoad(s, f32, /*isAtomic=*/false)"),
@@ -467,6 +469,11 @@
467469
("i32x4.widen_low_i16x8_u", "makeUnary(s, UnaryOp::WidenLowUVecI16x8ToVecI32x4)"),
468470
("i32x4.widen_high_i16x8_u", "makeUnary(s, UnaryOp::WidenHighUVecI16x8ToVecI32x4)"),
469471
("v8x16.swizzle", "makeBinary(s, BinaryOp::SwizzleVec8x16)"),
472+
# reference types instructions
473+
# TODO Add table instructions
474+
("ref.null", "makeRefNull(s)"),
475+
("ref.is_null", "makeRefIsNull(s)"),
476+
("ref.func", "makeRefFunc(s)"),
470477
# exception handling instructions
471478
("try", "makeTry(s)"),
472479
("throw", "makeThrow(s)"),

src/asmjs/asm_v_wasm.cpp

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,11 @@ AsmType wasmToAsmType(Type type) {
5353
return ASM_INT64;
5454
case v128:
5555
assert(false && "v128 not implemented yet");
56+
case funcref:
5657
case anyref:
57-
assert(false && "anyref is not supported by asm2wasm");
58+
case nullref:
5859
case exnref:
59-
assert(false && "exnref is not supported by asm2wasm");
60+
assert(false && "reference types are not supported by asm2wasm");
6061
case none:
6162
return ASM_NONE;
6263
case unreachable:
@@ -77,10 +78,14 @@ char getSig(Type type) {
7778
return 'd';
7879
case v128:
7980
return 'V';
81+
case funcref:
82+
return 'F';
8083
case anyref:
81-
return 'a';
84+
return 'A';
85+
case nullref:
86+
return 'N';
8287
case exnref:
83-
return 'e';
88+
return 'E';
8489
case none:
8590
return 'v';
8691
case unreachable:
@@ -109,9 +114,13 @@ Type sigToType(char sig) {
109114
return f64;
110115
case 'V':
111116
return v128;
112-
case 'a':
117+
case 'F':
118+
return funcref;
119+
case 'A':
113120
return anyref;
114-
case 'e':
121+
case 'N':
122+
return nullref;
123+
case 'E':
115124
return exnref;
116125
case 'v':
117126
return none;

src/binaryen-c.cpp

Lines changed: 71 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,6 @@ using namespace wasm;
4646

4747
// Literal utilities
4848

49-
static_assert(sizeof(BinaryenLiteral) == sizeof(Literal),
50-
"Binaryen C API literal must match wasm.h");
51-
5249
BinaryenLiteral toBinaryenLiteral(Literal x) {
5350
BinaryenLiteral ret;
5451
ret.type = x.type;
@@ -69,11 +66,7 @@ BinaryenLiteral toBinaryenLiteral(Literal x) {
6966
memcpy(&ret.v128, x.getv128Ptr(), 16);
7067
break;
7168
}
72-
73-
case Type::anyref: // there's no anyref literals
74-
case Type::exnref: // there's no exnref literals
75-
case Type::none:
76-
case Type::unreachable:
69+
default:
7770
WASM_UNREACHABLE();
7871
}
7972
return ret;
@@ -91,10 +84,7 @@ Literal fromBinaryenLiteral(BinaryenLiteral x) {
9184
return Literal(x.i64).castToF64();
9285
case Type::v128:
9386
return Literal(x.v128);
94-
case Type::anyref: // there's no anyref literals
95-
case Type::exnref: // there's no exnref literals
96-
case Type::none:
97-
case Type::unreachable:
87+
default:
9888
WASM_UNREACHABLE();
9989
}
10090
WASM_UNREACHABLE();
@@ -212,10 +202,7 @@ void printArg(std::ostream& setup, std::ostream& out, BinaryenLiteral arg) {
212202
out << "BinaryenLiteralVec128(" << array << ")";
213203
break;
214204
}
215-
case Type::anyref: // there's no anyref literals
216-
case Type::exnref: // there's no exnref literals
217-
case Type::none:
218-
case Type::unreachable:
205+
default:
219206
WASM_UNREACHABLE();
220207
}
221208
}
@@ -268,7 +255,9 @@ BinaryenType BinaryenTypeInt64(void) { return i64; }
268255
BinaryenType BinaryenTypeFloat32(void) { return f32; }
269256
BinaryenType BinaryenTypeFloat64(void) { return f64; }
270257
BinaryenType BinaryenTypeVec128(void) { return v128; }
258+
BinaryenType BinaryenTypeFuncref(void) { return funcref; }
271259
BinaryenType BinaryenTypeAnyref(void) { return anyref; }
260+
BinaryenType BinaryenTypeNullref(void) { return nullref; }
272261
BinaryenType BinaryenTypeExnref(void) { return exnref; }
273262
BinaryenType BinaryenTypeUnreachable(void) { return unreachable; }
274263
BinaryenType BinaryenTypeAuto(void) { return uint32_t(-1); }
@@ -400,6 +389,15 @@ BinaryenExpressionId BinaryenMemoryCopyId(void) {
400389
BinaryenExpressionId BinaryenMemoryFillId(void) {
401390
return Expression::Id::MemoryFillId;
402391
}
392+
BinaryenExpressionId BinaryenRefNullId(void) {
393+
return Expression::Id::RefNullId;
394+
}
395+
BinaryenExpressionId BinaryenRefIsNullId(void) {
396+
return Expression::Id::RefIsNullId;
397+
}
398+
BinaryenExpressionId BinaryenRefFuncId(void) {
399+
return Expression::Id::RefFuncId;
400+
}
403401
BinaryenExpressionId BinaryenTryId(void) { return Expression::Id::TryId; }
404402
BinaryenExpressionId BinaryenThrowId(void) { return Expression::Id::ThrowId; }
405403
BinaryenExpressionId BinaryenRethrowId(void) {
@@ -1391,17 +1389,22 @@ BinaryenExpressionRef BinaryenBinary(BinaryenModuleRef module,
13911389
BinaryenExpressionRef BinaryenSelect(BinaryenModuleRef module,
13921390
BinaryenExpressionRef condition,
13931391
BinaryenExpressionRef ifTrue,
1394-
BinaryenExpressionRef ifFalse) {
1392+
BinaryenExpressionRef ifFalse,
1393+
BinaryenType type) {
13951394
auto* ret = ((Module*)module)->allocator.alloc<Select>();
13961395

13971396
if (tracing) {
1398-
traceExpression(ret, "BinaryenSelect", condition, ifTrue, ifFalse);
1397+
traceExpression(ret, "BinaryenSelect", condition, ifTrue, ifFalse, type);
13991398
}
14001399

14011400
ret->condition = (Expression*)condition;
14021401
ret->ifTrue = (Expression*)ifTrue;
14031402
ret->ifFalse = (Expression*)ifFalse;
1404-
ret->finalize();
1403+
if (type != BinaryenTypeAuto()) {
1404+
ret->finalize(Type(type));
1405+
} else {
1406+
ret->finalize();
1407+
}
14051408
return static_cast<Expression*>(ret);
14061409
}
14071410
BinaryenExpressionRef BinaryenDrop(BinaryenModuleRef module,
@@ -1756,6 +1759,33 @@ BinaryenExpressionRef BinaryenPop(BinaryenModuleRef module, BinaryenType type) {
17561759
return static_cast<Expression*>(ret);
17571760
}
17581761

1762+
BinaryenExpressionRef BinaryenRefNull(BinaryenModuleRef module) {
1763+
auto* ret = Builder(*(Module*)module).makeRefNull();
1764+
if (tracing) {
1765+
traceExpression(ret, "BinaryenRefNull");
1766+
}
1767+
return static_cast<Expression*>(ret);
1768+
}
1769+
1770+
BinaryenExpressionRef BinaryenRefIsNull(BinaryenModuleRef module,
1771+
BinaryenExpressionRef anyref) {
1772+
auto* ret = Builder(*(Module*)module).makeRefIsNull((Expression*)anyref);
1773+
if (tracing) {
1774+
traceExpression(ret, "BinaryenRefIsNull", anyref);
1775+
}
1776+
return static_cast<Expression*>(ret);
1777+
}
1778+
1779+
BinaryenExpressionRef BinaryenRefFunc(BinaryenModuleRef module,
1780+
const char* func) {
1781+
auto* ret = Builder(*(Module*)module).makeRefFunc(func);
1782+
if (tracing) {
1783+
traceExpression(ret, "BinaryenRefFunc", StringLit(func));
1784+
}
1785+
ret->finalize();
1786+
return static_cast<Expression*>(ret);
1787+
}
1788+
17591789
BinaryenExpressionRef BinaryenTry(BinaryenModuleRef module,
17601790
BinaryenExpressionRef body,
17611791
BinaryenExpressionRef catchBody) {
@@ -3025,6 +3055,28 @@ BinaryenExpressionRef BinaryenPushGetValue(BinaryenExpressionRef expr) {
30253055
assert(expression->is<Push>());
30263056
return static_cast<Push*>(expression)->value;
30273057
}
3058+
// RefIsNull
3059+
BinaryenExpressionRef BinaryenRefIsNullGetAnyref(BinaryenExpressionRef expr) {
3060+
if (tracing) {
3061+
std::cout << " BinaryenRefIsNullGetAnyref(expressions["
3062+
<< expressions[expr] << "]);\n";
3063+
}
3064+
3065+
auto* expression = (Expression*)expr;
3066+
assert(expression->is<RefIsNull>());
3067+
return static_cast<RefIsNull*>(expression)->anyref;
3068+
}
3069+
// RefFunc
3070+
const char* BinaryenRefFuncGetFunc(BinaryenExpressionRef expr) {
3071+
if (tracing) {
3072+
std::cout << " BinaryenRefFuncGetFunc(expressions[" << expressions[expr]
3073+
<< "]);\n";
3074+
}
3075+
3076+
auto* expression = (Expression*)expr;
3077+
assert(expression->is<RefFunc>());
3078+
return static_cast<RefFunc*>(expression)->func.c_str();
3079+
}
30283080
// Try
30293081
BinaryenExpressionRef BinaryenTryGetBody(BinaryenExpressionRef expr) {
30303082
if (tracing) {

src/binaryen-c.h

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,9 @@ BINARYEN_API BinaryenType BinaryenTypeInt64(void);
9999
BINARYEN_API BinaryenType BinaryenTypeFloat32(void);
100100
BINARYEN_API BinaryenType BinaryenTypeFloat64(void);
101101
BINARYEN_API BinaryenType BinaryenTypeVec128(void);
102+
BINARYEN_API BinaryenType BinaryenTypeFuncref(void);
102103
BINARYEN_API BinaryenType BinaryenTypeAnyref(void);
104+
BINARYEN_API BinaryenType BinaryenTypeNullref(void);
103105
BINARYEN_API BinaryenType BinaryenTypeExnref(void);
104106
BINARYEN_API BinaryenType BinaryenTypeUnreachable(void);
105107
// Not a real type. Used as the last parameter to BinaryenBlock to let
@@ -159,6 +161,9 @@ BINARYEN_API BinaryenExpressionId BinaryenMemoryInitId(void);
159161
BINARYEN_API BinaryenExpressionId BinaryenDataDropId(void);
160162
BINARYEN_API BinaryenExpressionId BinaryenMemoryCopyId(void);
161163
BINARYEN_API BinaryenExpressionId BinaryenMemoryFillId(void);
164+
BINARYEN_API BinaryenExpressionId BinaryenRefNullId(void);
165+
BINARYEN_API BinaryenExpressionId BinaryenRefIsNullId(void);
166+
BINARYEN_API BinaryenExpressionId BinaryenRefFuncId(void);
162167
BINARYEN_API BinaryenExpressionId BinaryenTryId(void);
163168
BINARYEN_API BinaryenExpressionId BinaryenThrowId(void);
164169
BINARYEN_API BinaryenExpressionId BinaryenRethrowId(void);
@@ -703,7 +708,8 @@ BINARYEN_API BinaryenExpressionRef
703708
BinaryenSelect(BinaryenModuleRef module,
704709
BinaryenExpressionRef condition,
705710
BinaryenExpressionRef ifTrue,
706-
BinaryenExpressionRef ifFalse);
711+
BinaryenExpressionRef ifFalse,
712+
BinaryenType type);
707713
BINARYEN_API BinaryenExpressionRef BinaryenDrop(BinaryenModuleRef module,
708714
BinaryenExpressionRef value);
709715
// Return: value can be NULL
@@ -808,6 +814,11 @@ BinaryenMemoryFill(BinaryenModuleRef module,
808814
BinaryenExpressionRef dest,
809815
BinaryenExpressionRef value,
810816
BinaryenExpressionRef size);
817+
BINARYEN_API BinaryenExpressionRef BinaryenRefNull(BinaryenModuleRef module);
818+
BINARYEN_API BinaryenExpressionRef
819+
BinaryenRefIsNull(BinaryenModuleRef module, BinaryenExpressionRef anyref);
820+
BINARYEN_API BinaryenExpressionRef BinaryenRefFunc(BinaryenModuleRef module,
821+
const char* func);
811822
BINARYEN_API BinaryenExpressionRef BinaryenTry(BinaryenModuleRef module,
812823
BinaryenExpressionRef body,
813824
BinaryenExpressionRef catchBody);
@@ -1046,6 +1057,11 @@ BinaryenMemoryFillGetValue(BinaryenExpressionRef expr);
10461057
BINARYEN_API BinaryenExpressionRef
10471058
BinaryenMemoryFillGetSize(BinaryenExpressionRef expr);
10481059

1060+
BINARYEN_API BinaryenExpressionRef
1061+
BinaryenRefIsNullGetAnyref(BinaryenExpressionRef expr);
1062+
1063+
BINARYEN_API const char* BinaryenRefFuncGetFunc(BinaryenExpressionRef expr);
1064+
10491065
BINARYEN_API BinaryenExpressionRef
10501066
BinaryenTryGetBody(BinaryenExpressionRef expr);
10511067
BINARYEN_API BinaryenExpressionRef

src/gen-s-parser.inc

Lines changed: 47 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -653,6 +653,9 @@ switch (op[0]) {
653653
default: goto parse_error;
654654
}
655655
}
656+
case 'u':
657+
if (strcmp(op, "funcref.pop") == 0) { return makePop(funcref); }
658+
goto parse_error;
656659
default: goto parse_error;
657660
}
658661
}
@@ -2474,30 +2477,57 @@ switch (op[0]) {
24742477
default: goto parse_error;
24752478
}
24762479
}
2477-
case 'n':
2478-
if (strcmp(op, "nop") == 0) { return makeNop(); }
2479-
goto parse_error;
2480+
case 'n': {
2481+
switch (op[1]) {
2482+
case 'o':
2483+
if (strcmp(op, "nop") == 0) { return makeNop(); }
2484+
goto parse_error;
2485+
case 'u':
2486+
if (strcmp(op, "nullref.pop") == 0) { return makePop(nullref); }
2487+
goto parse_error;
2488+
default: goto parse_error;
2489+
}
2490+
}
24802491
case 'p':
24812492
if (strcmp(op, "push") == 0) { return makePush(s); }
24822493
goto parse_error;
24832494
case 'r': {
2484-
switch (op[3]) {
2485-
case 'h':
2486-
if (strcmp(op, "rethrow") == 0) { return makeRethrow(s); }
2487-
goto parse_error;
2488-
case 'u': {
2489-
switch (op[6]) {
2490-
case '\0':
2491-
if (strcmp(op, "return") == 0) { return makeReturn(s); }
2495+
switch (op[2]) {
2496+
case 'f': {
2497+
switch (op[4]) {
2498+
case 'f':
2499+
if (strcmp(op, "ref.func") == 0) { return makeRefFunc(s); }
24922500
goto parse_error;
2493-
case '_': {
2494-
switch (op[11]) {
2501+
case 'i':
2502+
if (strcmp(op, "ref.is_null") == 0) { return makeRefIsNull(s); }
2503+
goto parse_error;
2504+
case 'n':
2505+
if (strcmp(op, "ref.null") == 0) { return makeRefNull(s); }
2506+
goto parse_error;
2507+
default: goto parse_error;
2508+
}
2509+
}
2510+
case 't': {
2511+
switch (op[3]) {
2512+
case 'h':
2513+
if (strcmp(op, "rethrow") == 0) { return makeRethrow(s); }
2514+
goto parse_error;
2515+
case 'u': {
2516+
switch (op[6]) {
24952517
case '\0':
2496-
if (strcmp(op, "return_call") == 0) { return makeCall(s, /*isReturn=*/true); }
2497-
goto parse_error;
2498-
case '_':
2499-
if (strcmp(op, "return_call_indirect") == 0) { return makeCallIndirect(s, /*isReturn=*/true); }
2518+
if (strcmp(op, "return") == 0) { return makeReturn(s); }
25002519
goto parse_error;
2520+
case '_': {
2521+
switch (op[11]) {
2522+
case '\0':
2523+
if (strcmp(op, "return_call") == 0) { return makeCall(s, /*isReturn=*/true); }
2524+
goto parse_error;
2525+
case '_':
2526+
if (strcmp(op, "return_call_indirect") == 0) { return makeCallIndirect(s, /*isReturn=*/true); }
2527+
goto parse_error;
2528+
default: goto parse_error;
2529+
}
2530+
}
25012531
default: goto parse_error;
25022532
}
25032533
}

src/ir/ExpressionAnalyzer.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,9 @@ template<typename T> void visitImmediates(Expression* curr, T& visitor) {
217217
visitor.visitInt(curr->op);
218218
visitor.visitNonScopeName(curr->nameOperand);
219219
}
220+
void visitRefNull(RefNull* curr) {}
221+
void visitRefIsNull(RefIsNull* curr) {}
222+
void visitRefFunc(RefFunc* curr) { visitor.visitNonScopeName(curr->func); }
220223
void visitTry(Try* curr) {}
221224
void visitThrow(Throw* curr) { visitor.visitNonScopeName(curr->event); }
222225
void visitRethrow(Rethrow* curr) {}

0 commit comments

Comments
 (0)