Skip to content

Commit bcc7614

Browse files
authored
Add support for reference types proposal (#2451)
This adds support for the reference type proposal. This includes 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. Fixes #2444 and fixes #2447.
1 parent a30f1df commit bcc7614

File tree

118 files changed

+7083
-2284
lines changed

Some content is hidden

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

118 files changed

+7083
-2284
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ full changeset diff at the end of each section.
1515
Current Trunk
1616
-------------
1717

18+
- Reference type support is added. Supported instructions are `ref.null`,
19+
`ref.is_null`, `ref.func`, and typed `select`. Table instructions are not
20+
supported yet. For typed `select`, C/JS API can take an additional 'type'
21+
parameter.
22+
1823
v90
1924
---
2025

check.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -153,8 +153,10 @@ def check():
153153

154154
shared.fail_if_not_identical_to_file(actual, f)
155155

156-
shared.binary_format_check(t, wasm_as_args=['-g']) # test with debuginfo
157-
shared.binary_format_check(t, wasm_as_args=[], binary_suffix='.fromBinary.noDebugInfo') # test without debuginfo
156+
# FIXME Remove this condition after nullref is implemented in V8
157+
if 'reference-types.wast' not in t:
158+
shared.binary_format_check(t, wasm_as_args=['-g']) # test with debuginfo
159+
shared.binary_format_check(t, wasm_as_args=[], binary_suffix='.fromBinary.noDebugInfo') # test without debuginfo
158160

159161
shared.minify_check(t)
160162

@@ -271,9 +273,9 @@ def run_wasm_reduce_tests():
271273
before = os.stat('a.wasm').st_size
272274
support.run_command(shared.WASM_REDUCE + ['a.wasm', '--command=%s b.wasm --fuzz-exec -all' % shared.WASM_OPT[0], '-t', 'b.wasm', '-w', 'c.wasm'])
273275
after = os.stat('c.wasm').st_size
274-
# 0.65 is a custom threshold to check if we have shrunk the output
275-
# sufficiently
276-
assert after < 0.7 * before, [before, after]
276+
# This number is a custom threshold to check if we have shrunk the
277+
# output sufficiently
278+
assert after < 0.75 * before, [before, after]
277279

278280

279281
def run_spec_tests():
@@ -323,7 +325,10 @@ def check_expected(actual, expected):
323325
# some wast files cannot be split:
324326
# * comments.wast: contains characters that are not valid utf-8,
325327
# so our string splitting code fails there
326-
if os.path.basename(wast) not in ['comments.wast']:
328+
329+
# FIXME Remove reference type tests from this list after nullref is
330+
# implemented in V8
331+
if os.path.basename(wast) not in ['comments.wast', 'ref_null.wast', 'ref_is_null.wast', 'ref_func.wast', 'old_select.wast']:
327332
split_num = 0
328333
actual = ''
329334
for module, asserts in support.split_wast(wast):

scripts/fuzz_opt.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ def compare_vs(self, before, after):
228228
break
229229

230230
def can_run_on_feature_opts(self, feature_opts):
231-
return all([x in feature_opts for x in ['--disable-simd']])
231+
return all([x in feature_opts for x in ['--disable-simd', '--disable-reference-types', '--disable-exception-handling']])
232232

233233

234234
# Fuzz the interpreter with --fuzz-exec. This tests everything in a single command (no
@@ -294,7 +294,7 @@ def run(self, wasm):
294294
return out
295295

296296
def can_run_on_feature_opts(self, feature_opts):
297-
return all([x in feature_opts for x in ['--disable-exception-handling', '--disable-simd', '--disable-threads', '--disable-bulk-memory', '--disable-nontrapping-float-to-int', '--disable-tail-call', '--disable-sign-ext']])
297+
return all([x in feature_opts for x in ['--disable-exception-handling', '--disable-simd', '--disable-threads', '--disable-bulk-memory', '--disable-nontrapping-float-to-int', '--disable-tail-call', '--disable-sign-ext', '--disable-reference-types']])
298298

299299

300300
class Asyncify(TestCaseHandler):
@@ -339,7 +339,7 @@ def do_asyncify(wasm):
339339
compare(before, after_asyncify, 'Asyncify (before/after_asyncify)')
340340

341341
def can_run_on_feature_opts(self, feature_opts):
342-
return all([x in feature_opts for x in ['--disable-exception-handling', '--disable-simd', '--disable-tail-call']])
342+
return all([x in feature_opts for x in ['--disable-exception-handling', '--disable-simd', '--disable-tail-call', '--disable-reference-types']])
343343

344344

345345
# The global list of all test case handlers

scripts/gen-s-parser.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,9 @@
4949
("f32.pop", "makePop(f32)"),
5050
("f64.pop", "makePop(f64)"),
5151
("v128.pop", "makePop(v128)"),
52+
("funcref.pop", "makePop(funcref)"),
5253
("anyref.pop", "makePop(anyref)"),
54+
("nullref.pop", "makePop(nullref)"),
5355
("exnref.pop", "makePop(exnref)"),
5456
("i32.load", "makeLoad(s, i32, /*isAtomic=*/false)"),
5557
("i64.load", "makeLoad(s, i64, /*isAtomic=*/false)"),
@@ -469,6 +471,11 @@
469471
("i32x4.widen_low_i16x8_u", "makeUnary(s, UnaryOp::WidenLowUVecI16x8ToVecI32x4)"),
470472
("i32x4.widen_high_i16x8_u", "makeUnary(s, UnaryOp::WidenHighUVecI16x8ToVecI32x4)"),
471473
("v8x16.swizzle", "makeBinary(s, BinaryOp::SwizzleVec8x16)"),
474+
# reference types instructions
475+
# TODO Add table instructions
476+
("ref.null", "makeRefNull(s)"),
477+
("ref.is_null", "makeRefIsNull(s)"),
478+
("ref.func", "makeRefFunc(s)"),
472479
# exception handling instructions
473480
("try", "makeTry(s)"),
474481
("throw", "makeThrow(s)"),

src/asmjs/asm_v_wasm.cpp

Lines changed: 9 additions & 4 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:

src/binaryen-c.cpp

Lines changed: 89 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -64,13 +64,16 @@ BinaryenLiteral toBinaryenLiteral(Literal x) {
6464
case Type::f64:
6565
ret.i64 = x.reinterpreti64();
6666
break;
67-
case Type::v128: {
67+
case Type::v128:
6868
memcpy(&ret.v128, x.getv128Ptr(), 16);
6969
break;
70-
}
71-
72-
case Type::anyref: // there's no anyref literals
73-
case Type::exnref: // there's no exnref literals
70+
case Type::funcref:
71+
ret.func = x.getFunc().c_str();
72+
break;
73+
case Type::nullref:
74+
break;
75+
case Type::anyref:
76+
case Type::exnref:
7477
case Type::none:
7578
case Type::unreachable:
7679
WASM_UNREACHABLE("unexpected type");
@@ -90,8 +93,12 @@ Literal fromBinaryenLiteral(BinaryenLiteral x) {
9093
return Literal(x.i64).castToF64();
9194
case Type::v128:
9295
return Literal(x.v128);
93-
case Type::anyref: // there's no anyref literals
94-
case Type::exnref: // there's no exnref literals
96+
case Type::funcref:
97+
return Literal::makeFuncref(x.func);
98+
case Type::nullref:
99+
return Literal::makeNullref();
100+
case Type::anyref:
101+
case Type::exnref:
95102
case Type::none:
96103
case Type::unreachable:
97104
WASM_UNREACHABLE("unexpected type");
@@ -209,8 +216,14 @@ void printArg(std::ostream& setup, std::ostream& out, BinaryenLiteral arg) {
209216
out << "BinaryenLiteralVec128(" << array << ")";
210217
break;
211218
}
212-
case Type::anyref: // there's no anyref literals
213-
case Type::exnref: // there's no exnref literals
219+
case Type::funcref:
220+
out << "BinaryenLiteralFuncref(" << arg.func << ")";
221+
break;
222+
case Type::nullref:
223+
out << "BinaryenLiteralNullref()";
224+
break;
225+
case Type::anyref:
226+
case Type::exnref:
214227
case Type::none:
215228
case Type::unreachable:
216229
WASM_UNREACHABLE("unexpected type");
@@ -265,7 +278,9 @@ BinaryenType BinaryenTypeInt64(void) { return i64; }
265278
BinaryenType BinaryenTypeFloat32(void) { return f32; }
266279
BinaryenType BinaryenTypeFloat64(void) { return f64; }
267280
BinaryenType BinaryenTypeVec128(void) { return v128; }
281+
BinaryenType BinaryenTypeFuncref(void) { return funcref; }
268282
BinaryenType BinaryenTypeAnyref(void) { return anyref; }
283+
BinaryenType BinaryenTypeNullref(void) { return nullref; }
269284
BinaryenType BinaryenTypeExnref(void) { return exnref; }
270285
BinaryenType BinaryenTypeUnreachable(void) { return unreachable; }
271286
BinaryenType BinaryenTypeAuto(void) { return uint32_t(-1); }
@@ -397,6 +412,15 @@ BinaryenExpressionId BinaryenMemoryCopyId(void) {
397412
BinaryenExpressionId BinaryenMemoryFillId(void) {
398413
return Expression::Id::MemoryFillId;
399414
}
415+
BinaryenExpressionId BinaryenRefNullId(void) {
416+
return Expression::Id::RefNullId;
417+
}
418+
BinaryenExpressionId BinaryenRefIsNullId(void) {
419+
return Expression::Id::RefIsNullId;
420+
}
421+
BinaryenExpressionId BinaryenRefFuncId(void) {
422+
return Expression::Id::RefFuncId;
423+
}
400424
BinaryenExpressionId BinaryenTryId(void) { return Expression::Id::TryId; }
401425
BinaryenExpressionId BinaryenThrowId(void) { return Expression::Id::ThrowId; }
402426
BinaryenExpressionId BinaryenRethrowId(void) {
@@ -1330,17 +1354,22 @@ BinaryenExpressionRef BinaryenBinary(BinaryenModuleRef module,
13301354
BinaryenExpressionRef BinaryenSelect(BinaryenModuleRef module,
13311355
BinaryenExpressionRef condition,
13321356
BinaryenExpressionRef ifTrue,
1333-
BinaryenExpressionRef ifFalse) {
1357+
BinaryenExpressionRef ifFalse,
1358+
BinaryenType type) {
13341359
auto* ret = ((Module*)module)->allocator.alloc<Select>();
13351360

13361361
if (tracing) {
1337-
traceExpression(ret, "BinaryenSelect", condition, ifTrue, ifFalse);
1362+
traceExpression(ret, "BinaryenSelect", condition, ifTrue, ifFalse, type);
13381363
}
13391364

13401365
ret->condition = (Expression*)condition;
13411366
ret->ifTrue = (Expression*)ifTrue;
13421367
ret->ifFalse = (Expression*)ifFalse;
1343-
ret->finalize();
1368+
if (type != BinaryenTypeAuto()) {
1369+
ret->finalize(Type(type));
1370+
} else {
1371+
ret->finalize();
1372+
}
13441373
return static_cast<Expression*>(ret);
13451374
}
13461375
BinaryenExpressionRef BinaryenDrop(BinaryenModuleRef module,
@@ -1695,6 +1724,32 @@ BinaryenExpressionRef BinaryenPop(BinaryenModuleRef module, BinaryenType type) {
16951724
return static_cast<Expression*>(ret);
16961725
}
16971726

1727+
BinaryenExpressionRef BinaryenRefNull(BinaryenModuleRef module) {
1728+
auto* ret = Builder(*(Module*)module).makeRefNull();
1729+
if (tracing) {
1730+
traceExpression(ret, "BinaryenRefNull");
1731+
}
1732+
return static_cast<Expression*>(ret);
1733+
}
1734+
1735+
BinaryenExpressionRef BinaryenRefIsNull(BinaryenModuleRef module,
1736+
BinaryenExpressionRef value) {
1737+
auto* ret = Builder(*(Module*)module).makeRefIsNull((Expression*)value);
1738+
if (tracing) {
1739+
traceExpression(ret, "BinaryenRefIsNull", value);
1740+
}
1741+
return static_cast<Expression*>(ret);
1742+
}
1743+
1744+
BinaryenExpressionRef BinaryenRefFunc(BinaryenModuleRef module,
1745+
const char* func) {
1746+
auto* ret = Builder(*(Module*)module).makeRefFunc(func);
1747+
if (tracing) {
1748+
traceExpression(ret, "BinaryenRefFunc", StringLit(func));
1749+
}
1750+
return static_cast<Expression*>(ret);
1751+
}
1752+
16981753
BinaryenExpressionRef BinaryenTry(BinaryenModuleRef module,
16991754
BinaryenExpressionRef body,
17001755
BinaryenExpressionRef catchBody) {
@@ -2964,6 +3019,28 @@ BinaryenExpressionRef BinaryenPushGetValue(BinaryenExpressionRef expr) {
29643019
assert(expression->is<Push>());
29653020
return static_cast<Push*>(expression)->value;
29663021
}
3022+
// RefIsNull
3023+
BinaryenExpressionRef BinaryenRefIsNullGetValue(BinaryenExpressionRef expr) {
3024+
if (tracing) {
3025+
std::cout << " BinaryenRefIsNullGetValue(expressions[" << expressions[expr]
3026+
<< "]);\n";
3027+
}
3028+
3029+
auto* expression = (Expression*)expr;
3030+
assert(expression->is<RefIsNull>());
3031+
return static_cast<RefIsNull*>(expression)->value;
3032+
}
3033+
// RefFunc
3034+
const char* BinaryenRefFuncGetFunc(BinaryenExpressionRef expr) {
3035+
if (tracing) {
3036+
std::cout << " BinaryenRefFuncGetFunc(expressions[" << expressions[expr]
3037+
<< "]);\n";
3038+
}
3039+
3040+
auto* expression = (Expression*)expr;
3041+
assert(expression->is<RefFunc>());
3042+
return static_cast<RefFunc*>(expression)->func.c_str();
3043+
}
29673044
// Try
29683045
BinaryenExpressionRef BinaryenTryGetBody(BinaryenExpressionRef expr) {
29693046
if (tracing) {

src/binaryen-c.h

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,9 @@ BINARYEN_API BinaryenType BinaryenTypeInt64(void);
9898
BINARYEN_API BinaryenType BinaryenTypeFloat32(void);
9999
BINARYEN_API BinaryenType BinaryenTypeFloat64(void);
100100
BINARYEN_API BinaryenType BinaryenTypeVec128(void);
101+
BINARYEN_API BinaryenType BinaryenTypeFuncref(void);
101102
BINARYEN_API BinaryenType BinaryenTypeAnyref(void);
103+
BINARYEN_API BinaryenType BinaryenTypeNullref(void);
102104
BINARYEN_API BinaryenType BinaryenTypeExnref(void);
103105
BINARYEN_API BinaryenType BinaryenTypeUnreachable(void);
104106
// Not a real type. Used as the last parameter to BinaryenBlock to let
@@ -158,6 +160,9 @@ BINARYEN_API BinaryenExpressionId BinaryenMemoryInitId(void);
158160
BINARYEN_API BinaryenExpressionId BinaryenDataDropId(void);
159161
BINARYEN_API BinaryenExpressionId BinaryenMemoryCopyId(void);
160162
BINARYEN_API BinaryenExpressionId BinaryenMemoryFillId(void);
163+
BINARYEN_API BinaryenExpressionId BinaryenRefNullId(void);
164+
BINARYEN_API BinaryenExpressionId BinaryenRefIsNullId(void);
165+
BINARYEN_API BinaryenExpressionId BinaryenRefFuncId(void);
161166
BINARYEN_API BinaryenExpressionId BinaryenTryId(void);
162167
BINARYEN_API BinaryenExpressionId BinaryenThrowId(void);
163168
BINARYEN_API BinaryenExpressionId BinaryenRethrowId(void);
@@ -222,6 +227,7 @@ struct BinaryenLiteral {
222227
float f32;
223228
double f64;
224229
uint8_t v128[16];
230+
const char* func;
225231
};
226232
};
227233

@@ -692,7 +698,8 @@ BINARYEN_API BinaryenExpressionRef
692698
BinaryenSelect(BinaryenModuleRef module,
693699
BinaryenExpressionRef condition,
694700
BinaryenExpressionRef ifTrue,
695-
BinaryenExpressionRef ifFalse);
701+
BinaryenExpressionRef ifFalse,
702+
BinaryenType type);
696703
BINARYEN_API BinaryenExpressionRef BinaryenDrop(BinaryenModuleRef module,
697704
BinaryenExpressionRef value);
698705
// Return: value can be NULL
@@ -797,6 +804,11 @@ BinaryenMemoryFill(BinaryenModuleRef module,
797804
BinaryenExpressionRef dest,
798805
BinaryenExpressionRef value,
799806
BinaryenExpressionRef size);
807+
BINARYEN_API BinaryenExpressionRef BinaryenRefNull(BinaryenModuleRef module);
808+
BINARYEN_API BinaryenExpressionRef
809+
BinaryenRefIsNull(BinaryenModuleRef module, BinaryenExpressionRef value);
810+
BINARYEN_API BinaryenExpressionRef BinaryenRefFunc(BinaryenModuleRef module,
811+
const char* func);
800812
BINARYEN_API BinaryenExpressionRef BinaryenTry(BinaryenModuleRef module,
801813
BinaryenExpressionRef body,
802814
BinaryenExpressionRef catchBody);
@@ -1035,6 +1047,11 @@ BinaryenMemoryFillGetValue(BinaryenExpressionRef expr);
10351047
BINARYEN_API BinaryenExpressionRef
10361048
BinaryenMemoryFillGetSize(BinaryenExpressionRef expr);
10371049

1050+
BINARYEN_API BinaryenExpressionRef
1051+
BinaryenRefIsNullGetValue(BinaryenExpressionRef expr);
1052+
1053+
BINARYEN_API const char* BinaryenRefFuncGetFunc(BinaryenExpressionRef expr);
1054+
10381055
BINARYEN_API BinaryenExpressionRef
10391056
BinaryenTryGetBody(BinaryenExpressionRef expr);
10401057
BINARYEN_API BinaryenExpressionRef

0 commit comments

Comments
 (0)