Skip to content

Update Binaryen #1700

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 24 commits into from
Mar 5, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cli/asc.json
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@
" simd SIMD types and operations.",
" threads Threading and atomic operations.",
" reference-types Reference types and operations.",
" gc Garbage collection (anyref, WIP).",
" gc Garbage collection (WIP).",
""
],
"TODO_doesNothingYet": [
Expand Down
61 changes: 43 additions & 18 deletions src/builtins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -605,6 +605,9 @@ export namespace BuiltinNames {
export const f64x2_gt = "~lib/builtins/f64x2.gt";
export const f64x2_ge = "~lib/builtins/f64x2.ge";

export const i31_new = "~lib/builtins/i31.new";
export const i31_get = "~lib/builtins/i31.get";

// internals
export const data_end = "~lib/memory/__data_end";
export const stack_pointer = "~lib/memory/__stack_pointer";
Expand Down Expand Up @@ -2920,6 +2923,41 @@ function builtin_memory_data(ctx: BuiltinContext): ExpressionRef {
}
builtins.set(BuiltinNames.memory_data, builtin_memory_data);

// === GC =====================================================================================

function builtin_i31_new(ctx: BuiltinContext): ExpressionRef {
var compiler = ctx.compiler;
var module = compiler.module;
if (
checkTypeAbsent(ctx) |
checkArgsRequired(ctx, 1)
) return module.unreachable();
var operands = ctx.operands;
var arg0 = compiler.compileExpression(operands[0], Type.i32, Constraints.CONV_IMPLICIT);
compiler.currentType = Type.i31ref;
return module.i31_new(arg0);
}
builtins.set(BuiltinNames.i31_new, builtin_i31_new);

function builtin_i31_get(ctx: BuiltinContext): ExpressionRef {
var compiler = ctx.compiler;
var module = compiler.module;
if (
checkTypeAbsent(ctx) |
checkArgsRequired(ctx, 1)
) return module.unreachable();
var operands = ctx.operands;
var arg0 = compiler.compileExpression(operands[0], Type.i31ref, Constraints.CONV_IMPLICIT);
if (ctx.contextualType.is(TypeFlags.UNSIGNED)) {
compiler.currentType = Type.u32;
return module.i31_get(arg0, false);
} else {
compiler.currentType = Type.i32;
return module.i31_get(arg0, true);
}
}
builtins.set(BuiltinNames.i31_get, builtin_i31_get);

// === Helpers ================================================================================

// changetype<T!>(value: *) -> T
Expand Down Expand Up @@ -3040,8 +3078,8 @@ function builtin_assert(ctx: BuiltinContext): ExpressionRef {
case TypeKind.EXTERNREF:
case TypeKind.ANYREF:
case TypeKind.EQREF:
case TypeKind.DATAREF: return module.if(module.ref_is(RefIsOp.RefIsNull, arg0), abort);
case TypeKind.I31REF: return module.if(module.unary(UnaryOp.EqzI32, module.i31_get(arg0)), abort);
case TypeKind.DATAREF:
case TypeKind.I31REF: return module.if(module.ref_is(RefIsOp.RefIsNull, arg0), abort);

}
} else {
Expand Down Expand Up @@ -3124,28 +3162,15 @@ function builtin_assert(ctx: BuiltinContext): ExpressionRef {
case TypeKind.EXTERNREF:
case TypeKind.ANYREF:
case TypeKind.EQREF:
case TypeKind.DATAREF: {
case TypeKind.DATAREF:
case TypeKind.I31REF: {
let temp = flow.getTempLocal(type);
let ret = module.if(
module.ref_is(RefIsOp.RefIsNull,
module.local_tee(temp.index, arg0, false) // ref
),
abort,
module.local_get(temp.index, NativeType.F64)
);
flow.freeTempLocal(temp);
return ret;
}
case TypeKind.I31REF: {
let temp = flow.getTempLocal(type);
let ret = module.if(
module.unary(UnaryOp.EqzI32,
module.i31_get(
module.local_tee(temp.index, arg0, false) // ref
)
),
abort,
module.local_get(temp.index, NativeType.F64)
module.local_get(temp.index, type.toNativeType())
);
flow.freeTempLocal(temp);
return ret;
Expand Down
9 changes: 2 additions & 7 deletions src/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9940,14 +9940,9 @@ export class Compiler extends DiagnosticEmitter {
case TypeKind.EXTERNREF:
case TypeKind.ANYREF:
case TypeKind.EQREF:
case TypeKind.DATAREF: {
return module.ref_is(RefIsOp.RefIsNull, expr);
}
case TypeKind.DATAREF:
case TypeKind.I31REF: {
return module.binary(BinaryOp.NeI32,
module.i31_get(expr),
module.i32(0)
);
return module.ref_is(RefIsOp.RefIsNull, expr);
}
default: {
assert(false);
Expand Down
10 changes: 10 additions & 0 deletions src/glue/binaryen.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -864,6 +864,16 @@ export declare function _BinaryenTupleExtractSetIndex(expr: BinaryenExpressionRe

export declare function _BinaryenPop(module: BinaryenModuleRef, type: BinaryenType): BinaryenExpressionRef;

export declare function _BinaryenI31New(module: BinaryenModuleRef, value: BinaryenExpressionRef): BinaryenExpressionRef;
export declare function _BinaryenI31NewGetValue(expr: BinaryenExpressionRef): BinaryenExpressionRef;
export declare function _BinaryenI31NewSetValue(expr: BinaryenExpressionRef, valueExpr: BinaryenExpressionRef): void;

export declare function _BinaryenI31Get(module: BinaryenModuleRef, i31Expr: BinaryenExpressionRef, signed: bool): BinaryenExpressionRef;
export declare function _BinaryenI31GetGetI31(expr: BinaryenExpressionRef): BinaryenExpressionRef;
export declare function _BinaryenI31GetSetI31(expr: BinaryenExpressionRef, i31Expr: BinaryenExpressionRef): void;
export declare function _BinaryenI31GetIsSigned(expr: BinaryenExpressionRef): bool;
export declare function _BinaryenI31GetSetSigned(expr: BinaryenExpressionRef, signed: bool): void;

type BinaryenFunctionRef = usize;

export declare function _BinaryenAddFunction(module: BinaryenModuleRef, name: BinaryenString, params: BinaryenType, results: BinaryenType, varTypes: BinaryenArray<BinaryenType>, numVarTypes: BinaryenIndex, body: BinaryenExpressionRef): BinaryenFunctionRef;
Expand Down
13 changes: 5 additions & 8 deletions src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1179,19 +1179,16 @@ export class Module {
// gc

i31_new(
expr: ExpressionRef
value: ExpressionRef
): ExpressionRef {
assert(false);
return this.unreachable();
// return binaryen._BinaryenI31New(this.ref, expr);
return binaryen._BinaryenI31New(this.ref, value);
}

i31_get(
expr: ExpressionRef
expr: ExpressionRef,
signed: bool
): ExpressionRef {
assert(false);
return this.unreachable();
// return binaryen._BinaryenI31Get(this.ref, expr);
return binaryen._BinaryenI31Get(this.ref, expr, signed);
}

// globals
Expand Down
22 changes: 17 additions & 5 deletions src/passes/pass.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,11 @@ import {
_BinaryenRefEqGetRight,
_BinaryenRefEqSetLeft,
_BinaryenRefEqSetRight,
_BinaryenFunctionSetBody
_BinaryenFunctionSetBody,
_BinaryenI31NewGetValue,
_BinaryenI31GetGetI31,
_BinaryenI31NewSetValue,
_BinaryenI31GetSetI31
} from "../glue/binaryen";

/** Base class of custom Binaryen visitors. */
Expand Down Expand Up @@ -865,14 +869,14 @@ export abstract class Visitor {
}
case ExpressionId.I31New: {
this.stack.push(expr);
assert(false); // TODO
this.visit(_BinaryenI31NewGetValue(expr));
assert(this.stack.pop() == expr);
this.visitI31New(expr);
break;
}
case ExpressionId.I31Get: {
this.stack.push(expr);
assert(false); // TODO
this.visit(_BinaryenI31GetGetI31(expr));
assert(this.stack.pop() == expr);
this.visitI31Get(expr);
break;
Expand Down Expand Up @@ -1559,11 +1563,19 @@ export function replaceChild(
break;
}
case ExpressionId.I31New: {
assert(false); // TODO
let value = _BinaryenI31NewGetValue(parent);
if (value == search) {
_BinaryenI31NewSetValue(parent, replacement);
return value;
}
break;
}
case ExpressionId.I31Get: {
assert(false); // TODO
let i31Expr = _BinaryenI31GetGetI31(parent);
if (i31Expr == search) {
_BinaryenI31GetSetI31(parent, replacement);
return i31Expr;
}
break;
}
case ExpressionId.CallRef: {
Expand Down
12 changes: 12 additions & 0 deletions std/assembly/builtins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2121,6 +2121,18 @@ export namespace f64x2 {
export declare function ge(a: v128, b: v128): v128;
}

@final
export abstract class i31 { // FIXME: usage of 'new' requires a class :(

// @ts-ignore: decorator
@builtin
static new(value: i32): i31ref { return unreachable(); };

// @ts-ignore: decorator
@builtin
static get(i31expr: i31ref): i32 { return unreachable(); };
}

/* eslint-disable @typescript-eslint/no-unused-vars */

// @ts-ignore: decorator
Expand Down
8 changes: 8 additions & 0 deletions std/assembly/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1194,6 +1194,14 @@ declare namespace f64x2 {
/** Computes which 64-bit float lanes of the first vector are greater than or equal those of the second. */
export function ge(a: v128, b: v128): v128;
}

declare abstract class i31 {
/** Creates a new i31ref from the specified integer value. */
static new(value: i32): i31ref;
/** Gets the integer value of an i31ref. */
static get(i31expr: i31ref): i32;
}

/** Macro type evaluating to the underlying native WebAssembly type. */
declare type native<T> = T;
/** Special type evaluating the indexed access index type. */
Expand Down
6 changes: 6 additions & 0 deletions std/assembly/reference.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ export abstract class Eqref extends Ref {

@final @unmanaged
export abstract class I31ref extends Ref {
get(this: i31ref): i32 {
return i31.get(this);
}
getUnsigned(this: i31ref): u32 {
return <u32>i31.get(this);
}
}

@final @unmanaged
Expand Down
10 changes: 10 additions & 0 deletions tests/compiler/features/gc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"features": [
"gc"
],
"asc_flags": [
"--explicitStart",
"--noValidate"
],
"skipInstantiate": true
}
17 changes: 17 additions & 0 deletions tests/compiler/features/gc.optimized.wat
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
(module
(type $none_=>_none (func))
(memory $0 1)
(data (i32.const 1036) ",")
(data (i32.const 1048) "\01\00\00\00\1c\00\00\00f\00e\00a\00t\00u\00r\00e\00s\00/\00g\00c\00.\00t\00s")
(global $~started (mut i32) (i32.const 0))
(export "memory" (memory $0))
(export "_start" (func $~start))
(func $~start
global.get $~started
if
return
end
i32.const 1
global.set $~started
)
)
8 changes: 8 additions & 0 deletions tests/compiler/features/gc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
function test_i31(): void {
var ref = i31.new(123);
assert(ref);
var val = i31.get(ref);
var uval = <u32>i31.get(ref);
}

test_i31();
50 changes: 50 additions & 0 deletions tests/compiler/features/gc.untouched.wat
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
(module
(type $none_=>_none (func))
(type $i32_i32_i32_i32_=>_none (func (param i32 i32 i32 i32)))
(import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32)))
(memory $0 1)
(data (i32.const 12) ",\00\00\00\00\00\00\00\00\00\00\00\01\00\00\00\1c\00\00\00f\00e\00a\00t\00u\00r\00e\00s\00/\00g\00c\00.\00t\00s\00")
(table $0 1 funcref)
(global $~lib/memory/__data_end i32 (i32.const 60))
(global $~lib/memory/__stack_pointer (mut i32) (i32.const 16444))
(global $~lib/memory/__heap_base i32 (i32.const 16444))
(global $~started (mut i32) (i32.const 0))
(export "memory" (memory $0))
(export "_start" (func $~start))
(func $features/gc/test_i31
(local $0 i31ref)
(local $1 i32)
(local $2 i32)
i32.const 123
i31.new
local.set $0
local.get $0
ref.is_null
if
i32.const 0
i32.const 32
i32.const 3
i32.const 3
call $~lib/builtins/abort
unreachable
end
local.get $0
i31.get_s
local.set $1
local.get $0
i31.get_u
local.set $2
)
(func $start:features/gc
call $features/gc/test_i31
)
(func $~start
global.get $~started
if
return
end
i32.const 1
global.set $~started
call $start:features/gc
)
)
1 change: 1 addition & 0 deletions tests/features.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
},
"gc": {
"asc_flags": [
"--enable reference-types",
"--enable gc"
],
"v8_flags": [
Expand Down