Skip to content

Commit 9f38cc4

Browse files
authored
feat: Update reference types support to latest Binaryen (#1465)
1 parent c76eb81 commit 9f38cc4

34 files changed

+1095
-551
lines changed

.github/workflows/test.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,14 +121,14 @@ jobs:
121121
- uses: dcodeIO/setup-node-nvm@master
122122
with:
123123
node-mirror: https://nodejs.org/download/v8-canary/
124-
node-version: "15.0.0-v8-canary202007077c53168ead"
124+
node-version: "node"
125125
- name: Install dependencies
126126
run: npm ci --no-audit
127127
- name: Clean distribution files
128128
run: npm run clean
129129
- name: Test experimental features
130130
env:
131-
ASC_FEATURES: mutable-globals,threads,reference-types,bigint-integration
131+
ASC_FEATURES: mutable-globals,threads,reference-types,bigint-integration,gc
132132
run: |
133133
npm run test:compiler rt/flags features/js-bigint-integration features/reference-types features/threads
134134
test-runtimes:

cli/asc.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,13 +192,15 @@
192192
" simd SIMD types and operations.",
193193
" threads Threading and atomic operations.",
194194
" reference-types Reference types and operations.",
195+
" gc Garbage collection (anyref, WIP).",
195196
""
196197
],
197198
"TODO_doesNothingYet": [
198199
" nontrapping-f2i Non-trapping float to integer ops.",
199200
" exception-handling Exception handling.",
200201
" tail-calls Tail call operations.",
201-
" multi-value Multi value types."
202+
" multi-value Multi value types.",
203+
" memory64 Memory64 operations."
202204
],
203205
"type": "S",
204206
"mutuallyExclusive": "disable"

package-lock.json

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
"url": "https://github.com/AssemblyScript/assemblyscript/issues"
2222
},
2323
"dependencies": {
24-
"binaryen": "96.0.0-nightly.20200911",
24+
"binaryen": "97.0.0-nightly.20200919",
2525
"long": "^4.0.0",
2626
"source-map-support": "^0.5.19",
2727
"ts-node": "^6.2.0"

src/builtins.ts

Lines changed: 26 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@ import {
5353
import {
5454
BinaryOp,
5555
UnaryOp,
56-
HostOp,
5756
AtomicRMWOp,
5857
SIMDExtractOp,
5958
SIMDReplaceOp,
@@ -968,39 +967,16 @@ function builtin_nameof(ctx: BuiltinContext): ExpressionRef {
968967
return module.unreachable();
969968
}
970969
var value: string;
971-
if (resultType.isReference) {
970+
if (resultType.isInternalReference) {
972971
let classReference = resultType.getClass();
973972
if (classReference) {
974973
value = classReference.name;
975974
} else {
976-
let signatureReference = resultType.getSignature();
977-
if (signatureReference) {
978-
value = "Function";
979-
} else {
980-
assert(resultType.isExternalReference);
981-
value = "Externref";
982-
}
975+
assert(resultType.getSignature());
976+
value = "Function";
983977
}
984978
} else {
985-
switch (resultType.kind) {
986-
case TypeKind.BOOL: { value = "bool"; break; }
987-
case TypeKind.I8: { value = "i8"; break; }
988-
case TypeKind.U8: { value = "u8"; break; }
989-
case TypeKind.I16: { value = "i16"; break; }
990-
case TypeKind.U16: { value = "u16"; break; }
991-
case TypeKind.I32: { value = "i32"; break; }
992-
case TypeKind.U32: { value = "u32"; break; }
993-
case TypeKind.F32: { value = "f32"; break; }
994-
case TypeKind.I64: { value = "i64"; break; }
995-
case TypeKind.U64: { value = "u64"; break; }
996-
case TypeKind.F64: { value = "f64"; break; }
997-
case TypeKind.ISIZE: { value = "isize"; break; }
998-
case TypeKind.USIZE: { value = "usize"; break; }
999-
case TypeKind.V128: { value = "v128"; break; }
1000-
case TypeKind.EXTERNREF: { value = "externref"; break; }
1001-
default: assert(false);
1002-
case TypeKind.VOID: { value = "void"; break; }
1003-
}
979+
value = resultType.toString();
1004980
}
1005981
return compiler.ensureStaticString(value);
1006982
}
@@ -2472,7 +2448,7 @@ function builtin_memory_size(ctx: BuiltinContext): ExpressionRef {
24722448
checkTypeAbsent(ctx) |
24732449
checkArgsRequired(ctx, 0)
24742450
) return module.unreachable();
2475-
return module.host(HostOp.MemorySize);
2451+
return module.memory_size();
24762452
}
24772453
builtins.set(BuiltinNames.memory_size, builtin_memory_size);
24782454

@@ -2485,10 +2461,7 @@ function builtin_memory_grow(ctx: BuiltinContext): ExpressionRef {
24852461
checkTypeAbsent(ctx) |
24862462
checkArgsRequired(ctx, 1)
24872463
) return module.unreachable();
2488-
var operands = ctx.operands;
2489-
return module.host(HostOp.MemoryGrow, null, [
2490-
compiler.compileExpression(operands[0], Type.i32, Constraints.CONV_IMPLICIT)
2491-
]);
2464+
return module.memory_grow(compiler.compileExpression(ctx.operands[0], Type.i32, Constraints.CONV_IMPLICIT));
24922465
}
24932466
builtins.set(BuiltinNames.memory_grow, builtin_memory_grow);
24942467

@@ -2775,6 +2748,11 @@ function builtin_assert(ctx: BuiltinContext): ExpressionRef {
27752748
// TODO: also check for NaN in float assertions, as in `Boolean(NaN) -> false`?
27762749
case TypeKind.F32: return module.if(module.binary(BinaryOp.EqF32, arg0, module.f32(0)), abort);
27772750
case TypeKind.F64: return module.if(module.binary(BinaryOp.EqF64, arg0, module.f64(0)), abort);
2751+
case TypeKind.FUNCREF:
2752+
case TypeKind.EXTERNREF:
2753+
case TypeKind.EXNREF:
2754+
case TypeKind.ANYREF: return module.if(module.ref_is_null(arg0), abort);
2755+
27782756
}
27792757
} else {
27802758
compiler.currentType = type.nonNullableType;
@@ -2852,6 +2830,21 @@ function builtin_assert(ctx: BuiltinContext): ExpressionRef {
28522830
flow.freeTempLocal(temp);
28532831
return ret;
28542832
}
2833+
case TypeKind.FUNCREF:
2834+
case TypeKind.EXTERNREF:
2835+
case TypeKind.EXNREF:
2836+
case TypeKind.ANYREF: {
2837+
let temp = flow.getTempLocal(type);
2838+
let ret = module.if(
2839+
module.ref_is_null(
2840+
module.local_tee(temp.index, arg0)
2841+
),
2842+
abort,
2843+
module.local_get(temp.index, NativeType.F64)
2844+
);
2845+
flow.freeTempLocal(temp);
2846+
return ret;
2847+
}
28552848
}
28562849
}
28572850
compiler.error(

src/common.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,10 @@ export namespace CommonNames {
123123
export const f32 = "f32";
124124
export const f64 = "f64";
125125
export const v128 = "v128";
126+
export const funcref = "funcref";
126127
export const externref = "externref";
128+
export const exnref = "exnref";
129+
export const anyref = "anyref";
127130
export const i8x16 = "i8x16";
128131
export const u8x16 = "u8x16";
129132
export const i16x8 = "i16x8";
@@ -170,6 +173,8 @@ export namespace CommonNames {
170173
export const ASC_FEATURE_TAIL_CALLS = "ASC_FEATURE_TAIL_CALLS";
171174
export const ASC_FEATURE_REFERENCE_TYPES = "ASC_FEATURE_REFERENCE_TYPES";
172175
export const ASC_FEATURE_MULTI_VALUE = "ASC_FEATURE_MULTI_VALUE";
176+
export const ASC_FEATURE_GC = "ASC_FEATURE_GC";
177+
export const ASC_FEATURE_MEMORY64 = "ASC_FEATURE_MEMORY64";
173178
// classes
174179
export const I8 = "I8";
175180
export const I16 = "I16";
@@ -185,7 +190,10 @@ export namespace CommonNames {
185190
export const F32 = "F32";
186191
export const F64 = "F64";
187192
export const V128 = "V128";
193+
export const Funcref = "Funcref";
188194
export const Externref = "Externref";
195+
export const Exnref = "Exnref";
196+
export const Anyref = "Anyref";
189197
export const String = "String";
190198
export const Array = "Array";
191199
export const StaticArray = "StaticArray";

src/compiler.ts

Lines changed: 48 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,8 @@ export class Compiler extends DiagnosticEmitter {
405405
if (options.hasFeature(Feature.TAIL_CALLS)) featureFlags |= FeatureFlags.TailCall;
406406
if (options.hasFeature(Feature.REFERENCE_TYPES)) featureFlags |= FeatureFlags.ReferenceTypes;
407407
if (options.hasFeature(Feature.MULTI_VALUE)) featureFlags |= FeatureFlags.MultiValue;
408+
if (options.hasFeature(Feature.GC)) featureFlags |= FeatureFlags.GC;
409+
if (options.hasFeature(Feature.MEMORY64)) featureFlags |= FeatureFlags.Memory64;
408410
module.setFeatures(featureFlags);
409411

410412
// set up the main start function
@@ -3624,12 +3626,21 @@ export class Compiler extends DiagnosticEmitter {
36243626
fromType = fromType.nonNullableType;
36253627
}
36263628
if (fromType.isAssignableTo(toType)) { // downcast or same
3627-
assert(fromType.kind == toType.kind);
3629+
assert(toType.isExternalReference || fromType.kind == toType.kind);
36283630
this.currentType = toType;
36293631
return expr;
36303632
}
36313633
if (explicit && toType.nonNullableType.isAssignableTo(fromType)) { // upcast
36323634
// <Cat | null>(<Animal>maybeCat)
3635+
if (toType.isExternalReference) {
3636+
this.error(
3637+
DiagnosticCode.Not_implemented_0,
3638+
reportNode.range,
3639+
"ref.cast"
3640+
);
3641+
this.currentType = toType;
3642+
return module.unreachable();
3643+
}
36333644
assert(fromType.kind == toType.kind);
36343645
if (!this.options.noAssert) {
36353646
expr = this.makeRuntimeUpcastCheck(expr, fromType, toType, reportNode);
@@ -4390,12 +4401,15 @@ export class Compiler extends DiagnosticEmitter {
43904401
);
43914402
break;
43924403
}
4393-
case TypeKind.EXTERNREF: {
4394-
// TODO: ref.eq
4404+
case TypeKind.FUNCREF:
4405+
case TypeKind.EXTERNREF:
4406+
case TypeKind.EXNREF:
4407+
case TypeKind.ANYREF: {
43954408
this.error(
4396-
DiagnosticCode.Not_implemented_0,
4409+
DiagnosticCode.Operation_0_cannot_be_applied_to_type_1,
43974410
expression.range,
4398-
"ref.eq instruction"
4411+
"ref.eq",
4412+
commonType.toString()
43994413
);
44004414
expr = module.unreachable();
44014415
break;
@@ -4491,12 +4505,15 @@ export class Compiler extends DiagnosticEmitter {
44914505
);
44924506
break;
44934507
}
4494-
case TypeKind.EXTERNREF: {
4495-
// TODO: !ref.eq
4508+
case TypeKind.FUNCREF:
4509+
case TypeKind.EXTERNREF:
4510+
case TypeKind.EXNREF:
4511+
case TypeKind.ANYREF: {
44964512
this.error(
4497-
DiagnosticCode.Not_implemented_0,
4513+
DiagnosticCode.Operation_0_cannot_be_applied_to_type_1,
44984514
expression.range,
4499-
"ref.eq instruction"
4515+
"ref.eq",
4516+
commonType.toString()
45004517
);
45014518
expr = module.unreachable();
45024519
break;
@@ -8311,13 +8328,7 @@ export class Compiler extends DiagnosticEmitter {
83118328
this.currentType = signatureReference.type.asNullable();
83128329
return options.isWasm64 ? module.i64(0) : module.i32(0);
83138330
}
8314-
// TODO: return null ref for externref or funcref
8315-
this.error(
8316-
DiagnosticCode.Not_implemented_0,
8317-
expression.range,
8318-
"ref.null"
8319-
);
8320-
return module.unreachable();
8331+
return this.makeZero(contextualType, expression);
83218332
}
83228333
this.currentType = options.usizeType;
83238334
this.warning(
@@ -8508,7 +8519,7 @@ export class Compiler extends DiagnosticEmitter {
85088519
);
85098520
if (!functionInstance || !this.compileFunction(functionInstance)) return module.unreachable();
85108521
if (contextualType.isExternalReference) {
8511-
this.currentType = Type.externref;
8522+
this.currentType = Type.funcref;
85128523
return module.ref_func(functionInstance.internalName);
85138524
}
85148525
let offset = this.ensureRuntimeFunction(functionInstance);
@@ -10619,7 +10630,17 @@ export class Compiler extends DiagnosticEmitter {
1061910630
checkTypeSupported(type: Type, reportNode: Node): bool {
1062010631
switch (type.kind) {
1062110632
case TypeKind.V128: return this.checkFeatureEnabled(Feature.SIMD, reportNode);
10622-
case TypeKind.EXTERNREF: return this.checkFeatureEnabled(Feature.REFERENCE_TYPES, reportNode);
10633+
case TypeKind.FUNCREF:
10634+
case TypeKind.EXTERNREF:
10635+
return this.checkFeatureEnabled(Feature.REFERENCE_TYPES, reportNode);
10636+
case TypeKind.EXNREF: {
10637+
return this.checkFeatureEnabled(Feature.REFERENCE_TYPES, reportNode)
10638+
&& this.checkFeatureEnabled(Feature.EXCEPTION_HANDLING, reportNode);
10639+
}
10640+
case TypeKind.ANYREF: {
10641+
return this.checkFeatureEnabled(Feature.REFERENCE_TYPES, reportNode)
10642+
&& this.checkFeatureEnabled(Feature.GC, reportNode);
10643+
}
1062310644
}
1062410645
let classReference = type.getClass();
1062510646
if (classReference) {
@@ -10712,14 +10733,11 @@ export class Compiler extends DiagnosticEmitter {
1071210733
case TypeKind.F32: return module.f32(0);
1071310734
case TypeKind.F64: return module.f64(0);
1071410735
case TypeKind.V128: return module.v128(v128_zero);
10736+
case TypeKind.FUNCREF:
1071510737
case TypeKind.EXTERNREF:
10716-
// TODO: return null ref for both externref as well as funcref
10717-
this.error(
10718-
DiagnosticCode.Not_implemented_0,
10719-
reportNode.range,
10720-
"ref.null"
10721-
);
10722-
return module.unreachable();
10738+
case TypeKind.EXNREF:
10739+
case TypeKind.ANYREF:
10740+
return module.ref_null(type.toNativeType());
1072310741
}
1072410742
}
1072510743

@@ -10824,16 +10842,11 @@ export class Compiler extends DiagnosticEmitter {
1082410842
module.i64(0xFFFFFFFE, 0xFFDFFFFF) // (0x7FF0000000000000 - 1) << 1
1082510843
);
1082610844
}
10827-
case TypeKind.EXTERNREF: {
10828-
// TODO: non-null object might still be considered falseish
10829-
// i.e. a ref to Boolean(false), Number(0), String("") etc.
10830-
// TODO: return module.unary(UnaryOp.EqzI32, module.ref_is_null(expr));
10831-
this.error(
10832-
DiagnosticCode.Not_implemented_0,
10833-
reportNode.range,
10834-
"ref.is_null"
10835-
);
10836-
return module.unreachable();
10845+
case TypeKind.FUNCREF:
10846+
case TypeKind.EXTERNREF:
10847+
case TypeKind.EXNREF:
10848+
case TypeKind.ANYREF:{
10849+
return module.ref_is_null(expr);
1083710850
}
1083810851
default: {
1083910852
assert(false);

src/flow.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,8 +311,10 @@ export class Flow {
311311
case <u32>NativeType.F32: { temps = parentFunction.tempF32s; break; }
312312
case <u32>NativeType.F64: { temps = parentFunction.tempF64s; break; }
313313
case <u32>NativeType.V128: { temps = parentFunction.tempV128s; break; }
314+
case <u32>NativeType.Funcref: { temps = parentFunction.tempFuncrefs; break; }
314315
case <u32>NativeType.Externref: { temps = parentFunction.tempExternrefs; break; }
315316
case <u32>NativeType.Exnref: { temps = parentFunction.tempExnrefs; break; }
317+
case <u32>NativeType.Anyref: { temps = parentFunction.tempAnyrefs; break; }
316318
default: throw new Error("concrete type expected");
317319
}
318320
var local: Local;
@@ -395,6 +397,12 @@ export class Flow {
395397
else parentFunction.tempV128s = temps = [];
396398
break;
397399
}
400+
case <u32>NativeType.Funcref: {
401+
let tempFuncrefs = parentFunction.tempFuncrefs;
402+
if (tempFuncrefs) temps = tempFuncrefs;
403+
else parentFunction.tempFuncrefs = temps = [];
404+
break;
405+
}
398406
case <u32>NativeType.Externref: {
399407
let tempExternrefs = parentFunction.tempExternrefs;
400408
if (tempExternrefs) temps = tempExternrefs;
@@ -407,6 +415,12 @@ export class Flow {
407415
else parentFunction.tempExnrefs = temps = [];
408416
break;
409417
}
418+
case <u32>NativeType.Anyref: {
419+
let tempAnyrefs = parentFunction.tempAnyrefs;
420+
if (tempAnyrefs) temps = tempAnyrefs;
421+
else parentFunction.tempAnyrefs = temps = [];
422+
break;
423+
}
410424
default: throw new Error("concrete type expected");
411425
}
412426
assert(local.index >= 0);

0 commit comments

Comments
 (0)