From d4c645f60f3e4b024b98253db8dd592a2c6e1afc Mon Sep 17 00:00:00 2001 From: dcode Date: Sun, 17 Nov 2019 01:27:12 +0100 Subject: [PATCH 1/6] Add fillers for anyref 'null' and comparisons --- lib/loader/index.js | 4 ++ src/builtins.ts | 7 +++- src/compiler.ts | 81 +++++++++++++++++++++++---------------- src/glue/binaryen.d.ts | 5 +++ src/module.ts | 11 ++++++ src/program.ts | 36 +++++++++++++---- src/types.ts | 28 +++++++++----- std/assembly/reference.ts | 11 ++++++ tests/compiler.js | 7 +++- 9 files changed, 139 insertions(+), 51 deletions(-) diff --git a/lib/loader/index.js b/lib/loader/index.js index 8ed61bd0bb..076273abde 100644 --- a/lib/loader/index.js +++ b/lib/loader/index.js @@ -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; diff --git a/src/builtins.ts b/src/builtins.ts index 0578325ec5..58c99f9ad6 100644 --- a/src/builtins.ts +++ b/src/builtins.ts @@ -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"; @@ -4788,7 +4793,7 @@ export function compileAbort( // essentially ignoring the message GC-wise. Doesn't matter anyway on a crash. messageArg = compiler.compileExpression(message, stringInstance.type, Constraints.CONV_IMPLICIT | Constraints.WILL_RETAIN); } else { - messageArg = stringInstance.type.toNativeZero(module); + messageArg = stringInstance.type.toNativeZero(compiler); } var filenameArg = compiler.ensureStaticString(reportNode.range.source.normalizedPath); diff --git a/src/compiler.ts b/src/compiler.ts index 161bd21b8f..b427fe5302 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -43,7 +43,9 @@ import { getLocalSetIndex, FeatureFlags, needsExplicitUnreachable, - getLocalSetValue + getLocalSetValue, + getGlobalGetName, + globalIsMutable } from "./module"; import { @@ -885,8 +887,8 @@ export class Compiler extends DiagnosticEmitter { return false; } - // The MVP does not yet support initializer expressions other than constant values (and constant - // get_globals), hence such initializations must be performed in the start function for now. + // The MVP does not yet support initializer expressions other than constants and gets of + // imported immutable globals, hence such initializations must be performed in the start. var initializeInStart = false; // Evaluate initializer if present @@ -903,6 +905,7 @@ export class Compiler extends DiagnosticEmitter { this.currentFlow = previousFlow; } + // If not a constant, attempt to precompute if (getExpressionId(initExpr) != ExpressionId.Const) { if (isDeclaredConstant) { initExpr = module.precomputeExpression(initExpr); @@ -912,6 +915,18 @@ export class Compiler extends DiagnosticEmitter { } } + // Handle special case of initializing from imported immutable global + if (initializeInStart && getExpressionId(initExpr) == ExpressionId.GlobalGet) { + let fromName = assert(getGlobalGetName(initExpr)); + if (!globalIsMutable(module.getGlobal(fromName))) { + let elementsByName = this.program.elementsByName; + if (elementsByName.has(fromName)) { + let global = elementsByName.get(fromName)!; + if (global.is(CommonFlags.AMBIENT)) initializeInStart = false; + } + } + } + // Explicitly inline if annotated if (isDeclaredInline) { if (initializeInStart) { @@ -957,7 +972,7 @@ export class Compiler extends DiagnosticEmitter { // Initialize to zero if there's no initializer } else { - initExpr = type.toNativeZero(module); + initExpr = type.toNativeZero(this); } var internalName = global.internalName; @@ -969,7 +984,7 @@ export class Compiler extends DiagnosticEmitter { assert(findDecorator(DecoratorKind.INLINE, global.decoratorNodes)).range, "inline" ); } - module.addGlobal(internalName, nativeType, true, type.toNativeZero(module)); + module.addGlobal(internalName, nativeType, true, type.toNativeZero(this)); if (type.isManaged && !initAutoreleaseSkipped) initExpr = this.makeRetain(initExpr); this.currentBody.push( module.global_set(internalName, initExpr) @@ -2657,7 +2672,7 @@ export class Compiler extends DiagnosticEmitter { // TODO: Detect this condition inside of a loop instead? initializers.push( module.local_set(local.index, - type.toNativeZero(module) + type.toNativeZero(this) ) ); flow.setLocalFlag(local.index, LocalFlags.CONDITIONALLY_RETAINED); @@ -3707,12 +3722,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: { @@ -3804,12 +3816,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: { @@ -6773,7 +6784,7 @@ export class Compiler extends DiagnosticEmitter { } } } - operands.push(parameterTypes[i].toNativeZero(module)); + operands.push(parameterTypes[i].toNativeZero(this)); allOptionalsAreConstant = false; } if (!allOptionalsAreConstant) { @@ -6887,7 +6898,7 @@ export class Compiler extends DiagnosticEmitter { } let parameterTypes = signature.parameterTypes; for (let i = numArguments; i < maxArguments; ++i) { - operands.push(parameterTypes[i].toNativeZero(module)); + operands.push(parameterTypes[i].toNativeZero(this)); } } @@ -7136,7 +7147,7 @@ export class Compiler extends DiagnosticEmitter { this.currentType = signatureReference.type.asNullable(); return module.i32(0); } - // TODO: anyref context yields 0 + return contextualType.toNativeZero(this); // anyref } this.currentType = options.usizeType; return options.isWasm64 @@ -7364,7 +7375,7 @@ export class Compiler extends DiagnosticEmitter { ? BinaryOp.NeI64 : BinaryOp.NeI32, expr, - actualType.toNativeZero(module) + actualType.toNativeZero(this) ); } @@ -7562,7 +7573,7 @@ export class Compiler extends DiagnosticEmitter { Constraints.CONV_IMPLICIT ) ) - : elementType.toNativeZero(module); + : elementType.toNativeZero(this); if (getExpressionId(expr) == ExpressionId.Const) { assert(getExpressionType(expr) == nativeElementType); } else { @@ -7937,7 +7948,7 @@ export class Compiler extends DiagnosticEmitter { ctor, argumentExpressions, reportNode, - this.options.usizeType.toNativeZero(this.module), + this.options.usizeType.toNativeZero(this), constraints ); if (getExpressionType(expr) != NativeType.None) { // possibly IMM_DROPPED @@ -8225,7 +8236,7 @@ export class Compiler extends DiagnosticEmitter { ? BinaryOp.AddI64 : BinaryOp.AddI32, getValue, - this.currentType.toNativeOne(module) + this.currentType.toNativeOne(this) ); break; } @@ -8314,7 +8325,7 @@ export class Compiler extends DiagnosticEmitter { ? BinaryOp.SubI64 : BinaryOp.SubI32, getValue, - this.currentType.toNativeOne(module) + this.currentType.toNativeOne(this) ); break; } @@ -8481,7 +8492,7 @@ export class Compiler extends DiagnosticEmitter { this.options.isWasm64 ? BinaryOp.SubI64 : BinaryOp.SubI32, - this.currentType.toNativeZero(module), + this.currentType.toNativeZero(this), expr ); break; @@ -8553,7 +8564,7 @@ export class Compiler extends DiagnosticEmitter { ? BinaryOp.AddI64 : BinaryOp.AddI32, expr, - this.currentType.toNativeOne(module) + this.currentType.toNativeOne(this) ); break; } @@ -8624,7 +8635,7 @@ export class Compiler extends DiagnosticEmitter { ? BinaryOp.SubI64 : BinaryOp.SubI32, expr, - this.currentType.toNativeOne(module) + this.currentType.toNativeOne(this) ); break; } @@ -8721,7 +8732,7 @@ export class Compiler extends DiagnosticEmitter { ? BinaryOp.XorI64 : BinaryOp.XorI32, expr, - this.currentType.toNativeNegOne(module) + this.currentType.toNativeNegOne(this) ); break; } @@ -8974,9 +8985,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); @@ -9053,7 +9068,7 @@ export class Compiler extends DiagnosticEmitter { ); if (fieldType.isManaged) initExpr = this.makeRetain(initExpr); } else { // initialize with zero - initExpr = fieldType.toNativeZero(module); + initExpr = fieldType.toNativeZero(this); } stmts.push( module.store(fieldType.byteSize, diff --git a/src/glue/binaryen.d.ts b/src/glue/binaryen.d.ts index 5f5b0befcb..c537d3dd9a 100644 --- a/src/glue/binaryen.d.ts +++ b/src/glue/binaryen.d.ts @@ -700,7 +700,12 @@ declare function _BinaryenGetExportByIndex(module: BinaryenModuleRef, index: Bin declare type BinaryenGlobalRef = usize; declare function _BinaryenAddGlobal(module: BinaryenModuleRef, name: usize, type: BinaryenType, mutable: bool, init: BinaryenExpressionRef): BinaryenGlobalRef; +declare function _BinaryenGetGlobal(module: BinaryenModuleRef, name: usize): BinaryenGlobalRef; declare function _BinaryenRemoveGlobal(module: BinaryenModuleRef, name: usize): void; +declare function _BinaryenGlobalGetName(global: BinaryenGlobalRef): usize; +declare function _BinaryenGlobalGetType(global: BinaryenGlobalRef): NativeType; +declare function _BinaryenGlobalIsMutable(global: BinaryenGlobalRef): bool; +declare function _BinaryenGlobalGetInitExpr(global: BinaryenGlobalRef): BinaryenExpressionRef; declare type BinaryenEventRef = usize; diff --git a/src/module.ts b/src/module.ts index 22e98b4cbb..f899277e52 100644 --- a/src/module.ts +++ b/src/module.ts @@ -976,6 +976,13 @@ export class Module { // meta + getGlobal( + name: string + ): GlobalRef { + var cStr = this.allocStringCached(name); + return _BinaryenGetGlobal(this.ref, cStr); + } + addGlobal( name: string, type: NativeType, @@ -1556,6 +1563,10 @@ export function getGlobalGetName(expr: ExpressionRef): string | null { return readString(_BinaryenGlobalGetGetName(expr)); } +export function globalIsMutable(global: GlobalRef): bool { + return _BinaryenGlobalIsMutable(global); +} + export function getBinaryOp(expr: ExpressionRef): BinaryOp { return _BinaryenBinaryGetOp(expr); } diff --git a/src/program.ts b/src/program.ts index a7b9f4dce5..095b5e4d5d 100644 --- a/src/program.ts +++ b/src/program.ts @@ -101,6 +101,10 @@ import { Flow } from "./flow"; +import { + BuiltinSymbols +} from "./builtins"; + /** Represents a yet unresolved `import`. */ class QueuedImport { constructor( @@ -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. */ @@ -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 = 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()) { @@ -1317,17 +1333,20 @@ export class Program extends DiagnosticEmitter { var name = declaration.name.text; var decorators = declaration.decorators; var element: DeclaredElement; + var acceptedFlags: DecoratorFlags = DecoratorFlags.UNSAFE; + if (parent.is(CommonFlags.AMBIENT)) { + acceptedFlags |= DecoratorFlags.EXTERNAL; + } if (declaration.is(CommonFlags.STATIC)) { // global variable assert(parent.kind != ElementKind.INTERFACE_PROTOTYPE); + acceptedFlags |= DecoratorFlags.LAZY; + if (declaration.is(CommonFlags.READONLY)) { + acceptedFlags |= DecoratorFlags.INLINE; + } element = new Global( name, parent, - this.checkDecorators(decorators, - (declaration.is(CommonFlags.READONLY) - ? DecoratorFlags.INLINE - : DecoratorFlags.NONE - ) | DecoratorFlags.LAZY | DecoratorFlags.UNSAFE - ), + this.checkDecorators(decorators, acceptedFlags), declaration ); if (!parent.add(name, element)) return; @@ -1337,7 +1356,7 @@ export class Program extends DiagnosticEmitter { name, parent, declaration, - this.checkDecorators(decorators, DecoratorFlags.UNSAFE) + this.checkDecorators(decorators, acceptedFlags) ); if (!parent.addInstance(name, element)) return; } @@ -1358,6 +1377,9 @@ export class Program extends DiagnosticEmitter { | DecoratorFlags.OPERATOR_PREFIX | DecoratorFlags.OPERATOR_POSTFIX; } + if (parent.is(CommonFlags.AMBIENT)) { + acceptedFlags |= DecoratorFlags.EXTERNAL; + } var element = new FunctionPrototype( name, parent, diff --git a/src/types.ts b/src/types.ts index abf42c8458..3c26e7f4f2 100644 --- a/src/types.ts +++ b/src/types.ts @@ -12,10 +12,13 @@ import { import { NativeType, - ExpressionRef, - Module + ExpressionRef } from "./module"; +import { + Compiler +} from "./compiler"; + /** Indicates the kind of a type. */ export const enum TypeKind { @@ -350,16 +353,16 @@ export class Type { case TypeKind.F64: return NativeType.F64; case TypeKind.V128: return NativeType.V128; case TypeKind.ANYREF: return NativeType.Anyref; - case TypeKind.VOID: return NativeType.None; + case TypeKind.VOID: return NativeType.None; } } /** Converts this type to its native `0` value. */ - toNativeZero(module: Module): ExpressionRef { + toNativeZero(compiler: Compiler): ExpressionRef { + var module = compiler.module; switch (this.kind) { - case TypeKind.ANYREF: case TypeKind.VOID: assert(false); - default: return module.i32(0); + default: return compiler.module.i32(0); case TypeKind.ISIZE: case TypeKind.USIZE: if (this.size != 64) return module.i32(0); case TypeKind.I64: @@ -367,11 +370,17 @@ export class Type { 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(compiler.program.refNull); + assert(compiler.compileGlobal(ref_null)); + return module.global_get(ref_null.internalName, NativeType.Anyref); + } } } /** Converts this type to its native `1` value. */ - toNativeOne(module: Module): ExpressionRef { + toNativeOne(compiler: Compiler): ExpressionRef { + var module = compiler.module; switch (this.kind) { case TypeKind.V128: case TypeKind.ANYREF: @@ -387,10 +396,11 @@ export class Type { } /** Converts this type to its native `-1` value. */ - toNativeNegOne(module: Module): ExpressionRef { + toNativeNegOne(compiler: Compiler): ExpressionRef { + var module = compiler.module; switch (this.kind) { case TypeKind.V128: - case TypeKind.ANYREF: + case TypeKind.ANYREF: case TypeKind.VOID: assert(false); default: return module.i32(-1); case TypeKind.ISIZE: diff --git a/std/assembly/reference.ts b/std/assembly/reference.ts index e3ca7a6d72..159f0b901e 100644 --- a/std/assembly/reference.ts +++ b/std/assembly/reference.ts @@ -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 { diff --git a/tests/compiler.js b/tests/compiler.js index 3f286b89df..b2d23e120d 100644 --- a/tests/compiler.js +++ b/tests/compiler.js @@ -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]")); From 917766faa1d0880c06fb97f657fd2bd8f2e2c09f Mon Sep 17 00:00:00 2001 From: dcode Date: Sun, 17 Nov 2019 01:38:41 +0100 Subject: [PATCH 2/6] tests --- src/compiler.ts | 6 +- .../features/reference-types.optimized.wat | 66 +++++++++++++-- tests/compiler/features/reference-types.ts | 23 ++++++ .../features/reference-types.untouched.wat | 80 ++++++++++++++++++- 4 files changed, 164 insertions(+), 11 deletions(-) diff --git a/src/compiler.ts b/src/compiler.ts index b427fe5302..b70516f231 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -2664,7 +2664,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 @@ -2675,7 +2675,9 @@ export class Compiler extends DiagnosticEmitter { type.toNativeZero(this) ) ); - 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); } diff --git a/tests/compiler/features/reference-types.optimized.wat b/tests/compiler/features/reference-types.optimized.wat index cf8faebb89..54d8ceb8e4 100644 --- a/tests/compiler/features/reference-types.optimized.wat +++ b/tests/compiler/features/reference-types.optimized.wat @@ -4,13 +4,17 @@ (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$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") @@ -18,7 +22,44 @@ (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 $start:features/reference-types (; 8 ;) (type $FUNCSIG$v) global.get $features/reference-types/someObject global.get $features/reference-types/someKey call $~lib/bindings/Reflect/has @@ -26,7 +67,7 @@ if i32.const 0 i32.const 24 - i32.const 16 + i32.const 19 i32.const 0 call $~lib/builtins/abort unreachable @@ -39,14 +80,29 @@ 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 ) - (func $features/reference-types/internal (; 6 ;) (type $FUNCSIG$aa) (param $0 anyref) (result anyref) + (func $features/reference-types/internal (; 9 ;) (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 (; 10 ;) (type $FUNCSIG$v) call $start:features/reference-types ) - (func $null (; 8 ;) (type $FUNCSIG$v) + (func $null (; 11 ;) (type $FUNCSIG$v) nop ) ) diff --git a/tests/compiler/features/reference-types.ts b/tests/compiler/features/reference-types.ts index 61a75ead93..9146a3ee54 100644 --- a/tests/compiler/features/reference-types.ts +++ b/tests/compiler/features/reference-types.ts @@ -25,3 +25,26 @@ import * as console from "bindings/console"; console.log(someObject); console.log(someKey); console.log(Reflect.get(someObject, someKey)); + +// can initialize and compare anyref globals and locals using fillers +// for ref.null, ref.is_null and ref.eq + +var a: anyref; +var b: anyref = null; + +function testGlobalFillers(): void { + assert(!a); + assert(a == b); + assert(!(a != b)); +} +testGlobalFillers(); + +function testLocalFillers(): void { + var a: anyref; + var b: anyref = null; + assert(!a); + // TODO: 'Assertion failed: false, at: ./src/literal.h,87,makeFromInt32' when optimizing? + // assert(a == b); + // assert(!(a != b)); +} +testLocalFillers(); diff --git a/tests/compiler/features/reference-types.untouched.wat b/tests/compiler/features/reference-types.untouched.wat index b73dd87ff1..c182626666 100644 --- a/tests/compiler/features/reference-types.untouched.wat +++ b/tests/compiler/features/reference-types.untouched.wat @@ -4,23 +4,93 @@ (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$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\00") (table $0 1 funcref) (elem (i32.const 0) $null) + (global $features/reference-types/a (mut anyref) (global.get $~lib/reference/ref.null)) + (global $features/reference-types/b (mut anyref) (global.get $~lib/reference/ref.null)) (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 $features/reference-types/a + call $~lib/reference/ref.is_null + i32.eqz + i32.eqz + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 36 + i32.const 2 + call $~lib/builtins/abort + unreachable + end + global.get $features/reference-types/a + global.get $features/reference-types/b + call $~lib/reference/ref.eq + i32.const 0 + i32.ne + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 37 + i32.const 2 + call $~lib/builtins/abort + unreachable + end + global.get $features/reference-types/a + global.get $features/reference-types/b + call $~lib/reference/ref.eq + i32.eqz + i32.eqz + 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/testLocalFillers (; 8 ;) (type $FUNCSIG$v) + (local $0 anyref) + (local $1 anyref) + global.get $~lib/reference/ref.null + local.set $0 + global.get $~lib/reference/ref.null + local.set $1 + local.get $0 + call $~lib/reference/ref.is_null + i32.eqz + i32.eqz + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 45 + 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 @@ -43,8 +113,10 @@ global.get $features/reference-types/someKey call $~lib/bindings/Reflect/get call $~lib/bindings/console/log + call $features/reference-types/testGlobalFillers + call $features/reference-types/testLocalFillers ) - (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 $1 anyref) (local $2 anyref) (local $3 anyref) @@ -59,9 +131,9 @@ local.set $3 local.get $3 ) - (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) ) ) From 2cd02c2c5f741d25d867691b930145cf705e5c46 Mon Sep 17 00:00:00 2001 From: dcode Date: Sun, 17 Nov 2019 01:45:18 +0100 Subject: [PATCH 3/6] works for function parameters --- std/assembly/index.d.ts | 2 +- .../features/reference-types.optimized.wat | 49 +++++++++++++++-- tests/compiler/features/reference-types.ts | 8 +++ .../features/reference-types.untouched.wat | 55 +++++++++++++++++-- 4 files changed, 105 insertions(+), 9 deletions(-) diff --git a/std/assembly/index.d.ts b/std/assembly/index.d.ts index 12a5c09127..3c97941a31 100644 --- a/std/assembly/index.d.ts +++ b/std/assembly/index.d.ts @@ -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 diff --git a/tests/compiler/features/reference-types.optimized.wat b/tests/compiler/features/reference-types.optimized.wat index 54d8ceb8e4..d47f6d51fd 100644 --- a/tests/compiler/features/reference-types.optimized.wat +++ b/tests/compiler/features/reference-types.optimized.wat @@ -5,6 +5,7 @@ (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)) @@ -59,7 +60,44 @@ unreachable end ) - (func $start:features/reference-types (; 8 ;) (type $FUNCSIG$v) + (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 53 + 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 55 + 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 + ) + (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 @@ -92,17 +130,20 @@ 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 (; 9 ;) (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 (; 10 ;) (type $FUNCSIG$v) + (func $start (; 11 ;) (type $FUNCSIG$v) call $start:features/reference-types ) - (func $null (; 11 ;) (type $FUNCSIG$v) + (func $null (; 12 ;) (type $FUNCSIG$v) nop ) ) diff --git a/tests/compiler/features/reference-types.ts b/tests/compiler/features/reference-types.ts index 9146a3ee54..cd89fcfff8 100644 --- a/tests/compiler/features/reference-types.ts +++ b/tests/compiler/features/reference-types.ts @@ -48,3 +48,11 @@ function testLocalFillers(): void { // assert(!(a != b)); } testLocalFillers(); + +function testLocalFillers2(a: anyref, b: anyref): void { + assert(!a); + // TODO: Works when these are arguments + assert(a == b); + assert(!(a != b)); +} +testLocalFillers2(null, null); diff --git a/tests/compiler/features/reference-types.untouched.wat b/tests/compiler/features/reference-types.untouched.wat index c182626666..dc1df13951 100644 --- a/tests/compiler/features/reference-types.untouched.wat +++ b/tests/compiler/features/reference-types.untouched.wat @@ -5,6 +5,7 @@ (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)) @@ -90,7 +91,50 @@ unreachable end ) - (func $start:features/reference-types (; 9 ;) (type $FUNCSIG$v) + (func $features/reference-types/testLocalFillers2 (; 9 ;) (type $FUNCSIG$vaa) (param $0 anyref) (param $1 anyref) + local.get $0 + call $~lib/reference/ref.is_null + i32.eqz + i32.eqz + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 53 + i32.const 2 + call $~lib/builtins/abort + unreachable + end + local.get $0 + local.get $1 + call $~lib/reference/ref.eq + i32.const 0 + i32.ne + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 55 + i32.const 2 + call $~lib/builtins/abort + unreachable + end + local.get $0 + local.get $1 + call $~lib/reference/ref.eq + i32.eqz + i32.eqz + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 56 + i32.const 2 + call $~lib/builtins/abort + unreachable + end + ) + (func $start:features/reference-types (; 10 ;) (type $FUNCSIG$v) global.get $features/reference-types/someObject global.get $features/reference-types/someKey call $~lib/bindings/Reflect/has @@ -115,8 +159,11 @@ call $~lib/bindings/console/log call $features/reference-types/testGlobalFillers call $features/reference-types/testLocalFillers + global.get $~lib/reference/ref.null + global.get $~lib/reference/ref.null + call $features/reference-types/testLocalFillers2 ) - (func $features/reference-types/internal (; 10 ;) (type $FUNCSIG$aa) (param $0 anyref) (result anyref) + (func $features/reference-types/internal (; 11 ;) (type $FUNCSIG$aa) (param $0 anyref) (result anyref) (local $1 anyref) (local $2 anyref) (local $3 anyref) @@ -131,9 +178,9 @@ local.set $3 local.get $3 ) - (func $start (; 11 ;) (type $FUNCSIG$v) + (func $start (; 12 ;) (type $FUNCSIG$v) call $start:features/reference-types ) - (func $null (; 12 ;) (type $FUNCSIG$v) + (func $null (; 13 ;) (type $FUNCSIG$v) ) ) From 8b744a0e0ffbfddb2a9f0b99371ae23a50140113 Mon Sep 17 00:00:00 2001 From: dcode Date: Sun, 17 Nov 2019 07:42:59 +0100 Subject: [PATCH 4/6] add binaryen test case --- tests/binaryen/anyref-locals.js | 59 +++++++++++++++++++ .../features/reference-types.optimized.wat | 6 +- tests/compiler/features/reference-types.ts | 3 +- .../features/reference-types.untouched.wat | 6 +- 4 files changed, 67 insertions(+), 7 deletions(-) create mode 100644 tests/binaryen/anyref-locals.js diff --git a/tests/binaryen/anyref-locals.js b/tests/binaryen/anyref-locals.js new file mode 100644 index 0000000000..a81df1fc17 --- /dev/null +++ b/tests/binaryen/anyref-locals.js @@ -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()); diff --git a/tests/compiler/features/reference-types.optimized.wat b/tests/compiler/features/reference-types.optimized.wat index d47f6d51fd..f3a49f11b0 100644 --- a/tests/compiler/features/reference-types.optimized.wat +++ b/tests/compiler/features/reference-types.optimized.wat @@ -67,7 +67,7 @@ if i32.const 0 i32.const 24 - i32.const 53 + i32.const 54 i32.const 2 call $~lib/builtins/abort unreachable @@ -79,7 +79,7 @@ if i32.const 0 i32.const 24 - i32.const 55 + i32.const 56 i32.const 2 call $~lib/builtins/abort unreachable @@ -91,7 +91,7 @@ if i32.const 0 i32.const 24 - i32.const 56 + i32.const 57 i32.const 2 call $~lib/builtins/abort unreachable diff --git a/tests/compiler/features/reference-types.ts b/tests/compiler/features/reference-types.ts index cd89fcfff8..35129f9a89 100644 --- a/tests/compiler/features/reference-types.ts +++ b/tests/compiler/features/reference-types.ts @@ -43,7 +43,8 @@ function testLocalFillers(): void { var a: anyref; var b: anyref = null; assert(!a); - // TODO: 'Assertion failed: false, at: ./src/literal.h,87,makeFromInt32' when optimizing? + // FIXME: 'Assertion failed: false, at: ./src/literal.h,87,makeFromInt32' as soon + // as 'a' is used twice // assert(a == b); // assert(!(a != b)); } diff --git a/tests/compiler/features/reference-types.untouched.wat b/tests/compiler/features/reference-types.untouched.wat index dc1df13951..14cc13d431 100644 --- a/tests/compiler/features/reference-types.untouched.wat +++ b/tests/compiler/features/reference-types.untouched.wat @@ -100,7 +100,7 @@ if i32.const 0 i32.const 24 - i32.const 53 + i32.const 54 i32.const 2 call $~lib/builtins/abort unreachable @@ -114,7 +114,7 @@ if i32.const 0 i32.const 24 - i32.const 55 + i32.const 56 i32.const 2 call $~lib/builtins/abort unreachable @@ -128,7 +128,7 @@ if i32.const 0 i32.const 24 - i32.const 56 + i32.const 57 i32.const 2 call $~lib/builtins/abort unreachable From fd238d99afc8ce6a644a71e4de91c8558a514795 Mon Sep 17 00:00:00 2001 From: dcode Date: Mon, 18 Nov 2019 11:29:06 +0100 Subject: [PATCH 5/6] fix --- src/glue/binaryen.d.ts | 4 ---- src/module.ts | 4 ---- 2 files changed, 8 deletions(-) diff --git a/src/glue/binaryen.d.ts b/src/glue/binaryen.d.ts index e1b8a3f2f2..9c4d32bdfc 100644 --- a/src/glue/binaryen.d.ts +++ b/src/glue/binaryen.d.ts @@ -702,10 +702,6 @@ declare type BinaryenGlobalRef = usize; declare function _BinaryenAddGlobal(module: BinaryenModuleRef, name: usize, type: BinaryenType, mutable: bool, init: BinaryenExpressionRef): BinaryenGlobalRef; declare function _BinaryenGetGlobal(module: BinaryenModuleRef, name: usize): BinaryenGlobalRef; declare function _BinaryenRemoveGlobal(module: BinaryenModuleRef, name: usize): void; -declare function _BinaryenGlobalGetName(global: BinaryenGlobalRef): usize; -declare function _BinaryenGlobalGetType(global: BinaryenGlobalRef): BinaryenType; -declare function _BinaryenGlobalIsMutable(global: BinaryenGlobalRef): bool; -declare function _BinaryenGlobalGetInitExpr(global: BinaryenGlobalRef): BinaryenExpressionRef; declare function _BinaryenGlobalGetName(global: BinaryenGlobalRef): usize; declare function _BinaryenGlobalGetType(global: BinaryenGlobalRef): BinaryenType; diff --git a/src/module.ts b/src/module.ts index 389f441d3a..0fe47d92f0 100644 --- a/src/module.ts +++ b/src/module.ts @@ -1597,10 +1597,6 @@ export function getGlobalGetName(expr: ExpressionRef): string | null { return readString(_BinaryenGlobalGetGetName(expr)); } -export function globalIsMutable(global: GlobalRef): bool { - return _BinaryenGlobalIsMutable(global); -} - export function getBinaryOp(expr: ExpressionRef): BinaryOp { return _BinaryenBinaryGetOp(expr); } From 78c3574985ae64f652aea12165a53b88427136c4 Mon Sep 17 00:00:00 2001 From: dcode Date: Mon, 18 Nov 2019 11:30:03 +0100 Subject: [PATCH 6/6] fix --- src/types.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/types.ts b/src/types.ts index 0d79501e18..f0d4f55456 100644 --- a/src/types.ts +++ b/src/types.ts @@ -14,10 +14,6 @@ import { NativeType } from "./module"; -import { - Compiler -} from "./compiler"; - /** Indicates the kind of a type. */ export const enum TypeKind {