Skip to content
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