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

Add fillers for 'ref.null', 'ref.is_null' and 'ref.eq' #960

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
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
4 changes: 4 additions & 0 deletions lib/loader/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ function preInstantiate(imports) {
const memory = baseModule.memory || env.memory;
console.log("trace: " + getString(memory, mesg) + (n ? " " : "") + Array.prototype.slice.call(arguments, 2, 2 + n).join(", "));
}
const ref = (imports.ref = imports.ref || {});
ref.null = null;
ref.is_null = function(v) { return v == null; }
ref.eq = function(a, b) { return a === b; }
imports.Math = imports.Math || Math;
imports.Date = imports.Date || Date;

Expand Down
5 changes: 5 additions & 0 deletions src/builtins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -572,6 +572,11 @@ export namespace BuiltinSymbols {
export const Float32Array = "~lib/typedarray/Float32Array";
export const Float64Array = "~lib/typedarray/Float64Array";

// std/reference.ts
export const ref_null = "~lib/reference/ref.null";
export const ref_is_null = "~lib/reference/ref.is_null";
export const ref_eq = "~lib/reference/ref.eq";

// compiler generated
export const started = "~lib/started";
export const argc = "~lib/argc";
Expand Down
41 changes: 24 additions & 17 deletions src/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2681,7 +2681,7 @@ export class Compiler extends DiagnosticEmitter {
}
}
} else {
if (isManaged) {
if (type.is(TypeFlags.REFERENCE)) {
// This is necessary because the first use (and assign) of the local could be taking place
// in a loop, subsequently marking it retained, but the second iteration of the loop
// still wouldn't release whatever is assigned in the first. Likewise, if the variable wasn't
Expand All @@ -2692,7 +2692,9 @@ export class Compiler extends DiagnosticEmitter {
this.makeZero(type)
)
);
flow.setLocalFlag(local.index, LocalFlags.CONDITIONALLY_RETAINED);
if (isManaged) {
flow.setLocalFlag(local.index, LocalFlags.CONDITIONALLY_RETAINED);
}
} else if (local.type.is(TypeFlags.SHORT | TypeFlags.INTEGER)) {
flow.setLocalFlag(local.index, LocalFlags.WRAPPED);
}
Expand Down Expand Up @@ -3739,12 +3741,9 @@ export class Compiler extends DiagnosticEmitter {
break;
}
case TypeKind.ANYREF: {
// TODO: ref.eq
this.error(
DiagnosticCode.Not_implemented,
expression.range
);
expr = module.unreachable();
let ref_eq = assert(this.program.refEqInstance);
assert(this.compileFunction(ref_eq));
expr = module.call(ref_eq.internalName, [ leftExpr, rightExpr], NativeType.I32);
break;
}
default: {
Expand Down Expand Up @@ -3836,12 +3835,11 @@ export class Compiler extends DiagnosticEmitter {
break;
}
case TypeKind.ANYREF: {
// TODO: !ref.eq
this.error(
DiagnosticCode.Not_implemented,
expression.range
let ref_eq = assert(this.program.refEqInstance);
assert(this.compileFunction(ref_eq));
expr = module.unary(UnaryOp.EqzI32,
module.call(ref_eq.internalName, [ leftExpr, rightExpr], NativeType.I32)
);
expr = module.unreachable();
break;
}
default: {
Expand Down Expand Up @@ -7168,7 +7166,7 @@ export class Compiler extends DiagnosticEmitter {
this.currentType = signatureReference.type.asNullable();
return module.i32(0);
}
// TODO: anyref context yields <usize>0
return this.makeZero(contextualType); // anyref
}
this.currentType = options.usizeType;
return options.isWasm64
Expand Down Expand Up @@ -8971,6 +8969,11 @@ export class Compiler extends DiagnosticEmitter {
case TypeKind.F32: return module.f32(0);
case TypeKind.F64: return module.f64(0);
case TypeKind.V128: return module.v128(v128_zero);
case TypeKind.ANYREF: {
let ref_null = assert(this.program.refNull);
assert(this.compileGlobal(ref_null));
return module.global_get(ref_null.internalName, NativeType.Anyref);
}
}
}

Expand Down Expand Up @@ -9069,9 +9072,13 @@ export class Compiler extends DiagnosticEmitter {
flow.freeTempLocal(temp);
return ret;
}
// case TypeKind.ANYREF: {
// TODO: !ref.is_null
// }
case TypeKind.ANYREF: {
let ref_is_null = assert(this.program.refIsNullInstance);
assert(this.compileFunction(ref_is_null));
return module.unary(UnaryOp.EqzI32,
module.call(ref_is_null.internalName, [ expr ], NativeType.I32)
);
}
default: {
assert(false);
return module.i32(0);
Expand Down
16 changes: 16 additions & 0 deletions src/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,10 @@ import {
Flow
} from "./flow";

import {
BuiltinSymbols
} from "./builtins";

/** Represents a yet unresolved `import`. */
class QueuedImport {
constructor(
Expand Down Expand Up @@ -465,6 +469,13 @@ export class Program extends DiagnosticEmitter {
/** RT `__allocArray(length: i32, alignLog2: usize, id: u32, data: usize = 0): usize` */
allocArrayInstance: Function;

/** Temporary filler for the `ref.null` instruction. */
refNull: Global | null = null;
/** Temporary filler for the `ref.is_null` instruction. */
refIsNullInstance: Function | null = null;
/** Temporary filler for the `ref.eq` instruction. */
refEqInstance: Function | null = null;

/** Next class id. */
nextClassId: u32 = 0;
/** Next signature id. */
Expand Down Expand Up @@ -966,6 +977,11 @@ export class Program extends DiagnosticEmitter {
this.instanceofInstance = this.requireFunction(CommonSymbols.instanceof_);
this.visitInstance = this.requireFunction(CommonSymbols.visit);
this.allocArrayInstance = this.requireFunction(CommonSymbols.allocArray);
if (options.hasFeature(Feature.REFERENCE_TYPES)) {
this.refNull = <Global>this.require(BuiltinSymbols.ref_null, ElementKind.GLOBAL);
this.refIsNullInstance = this.requireFunction(BuiltinSymbols.ref_is_null);
this.refEqInstance = this.requireFunction(BuiltinSymbols.ref_eq);
}

// mark module exports, i.e. to apply proper wrapping behavior on the boundaries
for (let file of this.filesByName.values()) {
Expand Down
2 changes: 1 addition & 1 deletion std/assembly/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ declare type f64 = number;
/** A 128-bit vector. */
declare type v128 = object;
/** A host reference. */
declare type anyref = object;
declare type anyref = object | null;

// Compiler hints

Expand Down
11 changes: 11 additions & 0 deletions std/assembly/reference.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
/** Filler implementations. */
@sealed @unmanaged
export declare abstract class ref {
@lazy @external("ref", "null")
static readonly null: anyref;
@external("ref", "is_null")
static is_null(ref: anyref): bool;
@external("ref", "eq")
static eq(a: anyref, b: anyref): bool;
}

/** Host reference abstraction. */
@sealed @unmanaged
export abstract class Anyref {
Expand Down
59 changes: 59 additions & 0 deletions tests/binaryen/anyref-locals.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
var binaryen = require("binaryen");
// var mod = binaryen.parseText(`
// (module
// (import "ref" "null" (global $ref_null anyref))
// (import "ref" "is_null" (func $ref_is_null (param anyref) (result i32)))
// (import "ref" "eq" (func $ref_eq (param anyref anyref) (result i32)))
// (func $test (result i32)
// (local $0 anyref)
// (local $1 anyref)
// (local.set $0
// (global.get $ref_null)
// )
// (local.set $1
// (global.get $ref_null)
// )
// (drop
// (call $ref_is_null
// (local.get $0)
// )
// )
// (return
// (call $ref_eq
// (local.get $0)
// (local.get $1)
// )
// )
// )
// (export $test $test)
// )`);
var mod = binaryen.parseText(`
(module
(import "ref" "null" (global $ref_null anyref))
(import "ref" "is_null" (func $ref_is_null (param anyref) (result i32)))
(func $test
(local $0 anyref)
(local.set $0
(global.get $ref_null)
)
(drop
(call $ref_is_null
(local.get $0)
)
)
;; remove this to make it work:
(drop
(call $ref_is_null
(local.get $0)
)
)
)
(export $test $test)
)`);
mod.setFeatures(binaryen.Features.ReferenceTypes);
if (!mod.validate()) console.log(":-(");
else console.log(mod.emitText());

mod.optimize();
if (!mod.validate()) console.log(":-(");
else console.log("-- optimized --\n", mod.emitText());
7 changes: 6 additions & 1 deletion tests/compiler.js
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,12 @@ function testInstantiate(basename, binaryBuffer, name, glue) {
},
Math,
Date,
Reflect
Reflect,
ref: {
null: null,
is_null: (ref) => ref == null,
eq: (a, b) => a === b
}
};
if (glue.preInstantiate) {
console.log(colorsUtil.white(" [preInstantiate]"));
Expand Down
102 changes: 98 additions & 4 deletions tests/compiler/features/reference-types.optimized.wat
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,100 @@
(type $FUNCSIG$va (func (param anyref)))
(type $FUNCSIG$aaa (func (param anyref anyref) (result anyref)))
(type $FUNCSIG$v (func))
(type $FUNCSIG$ia (func (param anyref) (result i32)))
(type $FUNCSIG$vaa (func (param anyref anyref)))
(type $FUNCSIG$aa (func (param anyref) (result anyref)))
(import "reference-types" "someObject" (global $features/reference-types/someObject anyref))
(import "reference-types" "someKey" (global $features/reference-types/someKey anyref))
(import "ref" "null" (global $~lib/reference/ref.null anyref))
(import "Reflect" "has" (func $~lib/bindings/Reflect/has (param anyref anyref) (result i32)))
(import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32)))
(import "console" "log" (func $~lib/bindings/console/log (param anyref)))
(import "Reflect" "get" (func $~lib/bindings/Reflect/get (param anyref anyref) (result anyref)))
(import "ref" "is_null" (func $~lib/reference/ref.is_null (param anyref) (result i32)))
(import "ref" "eq" (func $~lib/reference/ref.eq (param anyref anyref) (result i32)))
(import "reference-types" "external" (func $features/reference-types/external (param anyref) (result anyref)))
(memory $0 1)
(data (i32.const 8) "6\00\00\00\01\00\00\00\01\00\00\006\00\00\00f\00e\00a\00t\00u\00r\00e\00s\00/\00r\00e\00f\00e\00r\00e\00n\00c\00e\00-\00t\00y\00p\00e\00s\00.\00t\00s")
(export "memory" (memory $0))
(export "external" (func $features/reference-types/external))
(export "internal" (func $features/reference-types/internal))
(start $start)
(func $start:features/reference-types (; 5 ;) (type $FUNCSIG$v)
(func $features/reference-types/testGlobalFillers (; 7 ;) (type $FUNCSIG$v)
global.get $~lib/reference/ref.null
call $~lib/reference/ref.is_null
i32.eqz
if
i32.const 0
i32.const 24
i32.const 36
i32.const 2
call $~lib/builtins/abort
unreachable
end
global.get $~lib/reference/ref.null
global.get $~lib/reference/ref.null
call $~lib/reference/ref.eq
i32.eqz
if
i32.const 0
i32.const 24
i32.const 37
i32.const 2
call $~lib/builtins/abort
unreachable
end
global.get $~lib/reference/ref.null
global.get $~lib/reference/ref.null
call $~lib/reference/ref.eq
i32.eqz
if
i32.const 0
i32.const 24
i32.const 38
i32.const 2
call $~lib/builtins/abort
unreachable
end
)
(func $features/reference-types/testLocalFillers2 (; 8 ;) (type $FUNCSIG$vaa) (param $0 anyref) (param $1 anyref)
local.get $0
call $~lib/reference/ref.is_null
i32.eqz
if
i32.const 0
i32.const 24
i32.const 54
i32.const 2
call $~lib/builtins/abort
unreachable
end
local.get $0
local.get $1
call $~lib/reference/ref.eq
i32.eqz
if
i32.const 0
i32.const 24
i32.const 56
i32.const 2
call $~lib/builtins/abort
unreachable
end
local.get $0
local.get $1
call $~lib/reference/ref.eq
i32.eqz
if
i32.const 0
i32.const 24
i32.const 57
i32.const 2
call $~lib/builtins/abort
unreachable
end
)
(func $start:features/reference-types (; 9 ;) (type $FUNCSIG$v)
global.get $features/reference-types/someObject
global.get $features/reference-types/someKey
call $~lib/bindings/Reflect/has
Expand All @@ -39,17 +118,32 @@
global.get $features/reference-types/someKey
call $~lib/bindings/Reflect/get
call $~lib/bindings/console/log
call $features/reference-types/testGlobalFillers
global.get $~lib/reference/ref.null
call $~lib/reference/ref.is_null
i32.eqz
if
i32.const 0
i32.const 24
i32.const 45
i32.const 2
call $~lib/builtins/abort
unreachable
end
global.get $~lib/reference/ref.null
global.get $~lib/reference/ref.null
call $features/reference-types/testLocalFillers2
)
(func $features/reference-types/internal (; 6 ;) (type $FUNCSIG$aa) (param $0 anyref) (result anyref)
(func $features/reference-types/internal (; 10 ;) (type $FUNCSIG$aa) (param $0 anyref) (result anyref)
local.get $0
call $features/reference-types/external
call $features/reference-types/external
call $features/reference-types/external
)
(func $start (; 7 ;) (type $FUNCSIG$v)
(func $start (; 11 ;) (type $FUNCSIG$v)
call $start:features/reference-types
)
(func $null (; 8 ;) (type $FUNCSIG$v)
(func $null (; 12 ;) (type $FUNCSIG$v)
nop
)
)
Loading