From 15d7745e77900afab3961589526fb89e6d4667ee Mon Sep 17 00:00:00 2001 From: Surma Date: Sun, 7 Mar 2021 19:06:17 +0000 Subject: [PATCH 1/6] Invert reference type null checks --- src/compiler.ts | 7 +- tests/compiler/features/reference-types.js | 6 ++ tests/compiler/features/reference-types.ts | 40 ++++++++ .../features/reference-types.untouched.wat | 94 ++++++++++++------- 4 files changed, 110 insertions(+), 37 deletions(-) diff --git a/src/compiler.ts b/src/compiler.ts index 7a6a435a72..3bb4d860b1 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -9941,7 +9941,12 @@ export class Compiler extends DiagnosticEmitter { case TypeKind.EQREF: case TypeKind.DATAREF: case TypeKind.I31REF: { - return module.ref_is(RefIsOp.RefIsNull, expr); + // Needs to be true when the ref is _not_ null. + // Therefore: 1 - + return type.size == 64 + ? module.binary(BinaryOp.SubI64, module.i64(1), module.ref_is(RefIsOp.RefIsNull, expr)) + : module.binary(BinaryOp.SubI32, module.i32(1), module.ref_is(RefIsOp.RefIsNull, expr)); + } default: { assert(false); diff --git a/tests/compiler/features/reference-types.js b/tests/compiler/features/reference-types.js index 50aeab08ba..078c91423f 100644 --- a/tests/compiler/features/reference-types.js +++ b/tests/compiler/features/reference-types.js @@ -9,6 +9,12 @@ exports.preInstantiate = function(imports, exports) { external: function(a) { return a; }, + somethingReal() { + return {}; + }, + somethingNull() { + return null; + }, someObject: { theKey: "Hello world!" }, diff --git a/tests/compiler/features/reference-types.ts b/tests/compiler/features/reference-types.ts index d0b3e8d208..982c885c29 100644 --- a/tests/compiler/features/reference-types.ts +++ b/tests/compiler/features/reference-types.ts @@ -1,6 +1,8 @@ // can use externref as a parameter or return type export declare function external(a: externref): externref; +export declare function somethingReal(): externref; +export declare function somethingNull(): externref; export function internal(a: externref): externref { const b = external(a); @@ -26,6 +28,44 @@ console.log(someObject); console.log(someKey); console.log(Reflect.get(someObject, someKey)); +// Truthiness conversion +if(!somethingReal()) { + assert(false); +} +if(!somethingNull()) { + // nop +} else { + assert(false); +} +if(somethingReal()) { + // nop +} else { + assert(false); +} +if(somethingNull()) { + assert(false); +} + +// Explicit null checks (don’t work yet) +/* +if(somethingReal() !== null) { + // nop +} else { + assert(false); +} +if(somethingReal() === null) { + assert(false); +} +if(somethingNull() === null) { + // nop +} else { + assert(false); +} +if(somethingNull() !== null) { + assert(false); +} +*/ + // can represent and recognize 'null' var funcGlobal: funcref; diff --git a/tests/compiler/features/reference-types.untouched.wat b/tests/compiler/features/reference-types.untouched.wat index b4212781f2..04f4a0f65b 100644 --- a/tests/compiler/features/reference-types.untouched.wat +++ b/tests/compiler/features/reference-types.untouched.wat @@ -1,5 +1,6 @@ (module (type $none_=>_none (func)) + (type $none_=>_externref (func (result externref))) (type $externref_=>_externref (func (param externref) (result externref))) (type $externref_=>_none (func (param externref))) (type $i32_i32_i32_i32_=>_none (func (param i32 i32 i32 i32))) @@ -11,6 +12,8 @@ (import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32))) (import "console" "log" (func $~lib/bindings/console/log (param externref))) (import "Reflect" "get" (func $~lib/bindings/Reflect/get (param externref externref) (result externref))) + (import "reference-types" "somethingReal" (func $features/reference-types/somethingReal (result externref))) + (import "reference-types" "somethingNull" (func $features/reference-types/somethingNull (result externref))) (import "reference-types" "external" (func $features/reference-types/external (param externref) (result externref))) (memory $0 1) (data (i32.const 12) "L\00\00\00\00\00\00\00\00\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\00\00\00\00\00\00") @@ -26,6 +29,8 @@ (global $~lib/memory/__stack_pointer (mut i32) (i32.const 16476)) (global $~lib/memory/__heap_base i32 (i32.const 16476)) (export "external" (func $features/reference-types/external)) + (export "somethingReal" (func $features/reference-types/somethingReal)) + (export "somethingNull" (func $features/reference-types/somethingNull)) (export "internal" (func $features/reference-types/internal)) (export "memory" (memory $0)) (start $~start) @@ -165,7 +170,6 @@ nop ) (func $start:features/reference-types - (local $0 funcref) global.get $features/reference-types/someObject global.get $features/reference-types/someKey call $~lib/bindings/Reflect/has @@ -175,7 +179,7 @@ if i32.const 0 i32.const 32 - i32.const 19 + i32.const 21 i32.const 1 call $~lib/builtins/abort unreachable @@ -188,57 +192,75 @@ global.get $features/reference-types/someKey call $~lib/bindings/Reflect/get call $~lib/bindings/console/log - global.get $features/reference-types/funcGlobal + i32.const 1 + call $features/reference-types/somethingReal ref.is_null - i32.eqz + i32.sub i32.eqz if i32.const 0 - i32.const 32 - i32.const 32 - i32.const 1 - call $~lib/builtins/abort - unreachable + i32.eqz + if + i32.const 0 + i32.const 32 + i32.const 33 + i32.const 3 + call $~lib/builtins/abort + unreachable + end end - ref.null func - global.set $features/reference-types/funcGlobal - global.get $features/reference-types/funcGlobal + i32.const 1 + call $features/reference-types/somethingNull ref.is_null - i32.eqz + i32.sub i32.eqz if + nop + else i32.const 0 - i32.const 32 - i32.const 34 - i32.const 1 - call $~lib/builtins/abort - unreachable + i32.eqz + if + i32.const 0 + i32.const 32 + i32.const 38 + i32.const 3 + call $~lib/builtins/abort + unreachable + end end - ref.null func - global.set $features/reference-types/funcGlobalInit - global.get $features/reference-types/funcGlobalInit + i32.const 1 + call $features/reference-types/somethingReal ref.is_null - i32.eqz - i32.eqz + i32.sub if + nop + else i32.const 0 - i32.const 32 - i32.const 36 - i32.const 1 - call $~lib/builtins/abort - unreachable + i32.eqz + if + i32.const 0 + i32.const 32 + i32.const 43 + i32.const 3 + call $~lib/builtins/abort + unreachable + end end - global.get $features/reference-types/externGlobal + i32.const 1 + call $features/reference-types/somethingNull ref.is_null - i32.eqz - i32.eqz + i32.sub if i32.const 0 - i32.const 32 - i32.const 39 - i32.const 1 - call $~lib/builtins/abort - unreachable + i32.eqz + if + i32.const 0 + i32.const 32 + i32.const 46 + i32.const 3 + call $~lib/builtins/abort + unreachable + end end ref.null extern global.set $features/reference-types/externGlobal From 8ec6c9611c6a8d5448b29dd449a14100ae3b1278 Mon Sep 17 00:00:00 2001 From: Surma Date: Sun, 7 Mar 2021 19:17:03 +0000 Subject: [PATCH 2/6] Fix fixture --- .../features/reference-types.untouched.wat | 123 +++++++++++++++--- 1 file changed, 106 insertions(+), 17 deletions(-) diff --git a/tests/compiler/features/reference-types.untouched.wat b/tests/compiler/features/reference-types.untouched.wat index 04f4a0f65b..067ed2996e 100644 --- a/tests/compiler/features/reference-types.untouched.wat +++ b/tests/compiler/features/reference-types.untouched.wat @@ -37,42 +37,48 @@ (func $features/reference-types/testLocal (local $0 funcref) (local $1 funcref) + i32.const 1 local.get $0 ref.is_null + i32.sub i32.eqz i32.eqz if i32.const 0 i32.const 32 - i32.const 54 + i32.const 94 i32.const 3 call $~lib/builtins/abort unreachable end ref.null func local.set $0 + i32.const 1 local.get $0 ref.is_null + i32.sub i32.eqz i32.eqz if i32.const 0 i32.const 32 - i32.const 56 + i32.const 96 i32.const 3 call $~lib/builtins/abort unreachable end ref.null func local.set $1 + i32.const 1 local.get $1 ref.is_null + i32.sub i32.eqz i32.eqz if i32.const 0 i32.const 32 - i32.const 58 + i32.const 98 i32.const 3 call $~lib/builtins/abort unreachable @@ -81,42 +87,48 @@ (func $features/reference-types/testLocal (local $0 externref) (local $1 externref) + i32.const 1 local.get $0 ref.is_null + i32.sub i32.eqz i32.eqz if i32.const 0 i32.const 32 - i32.const 54 + i32.const 94 i32.const 3 call $~lib/builtins/abort unreachable end ref.null extern local.set $0 + i32.const 1 local.get $0 ref.is_null + i32.sub i32.eqz i32.eqz if i32.const 0 i32.const 32 - i32.const 56 + i32.const 96 i32.const 3 call $~lib/builtins/abort unreachable end ref.null extern local.set $1 + i32.const 1 local.get $1 ref.is_null + i32.sub i32.eqz i32.eqz if i32.const 0 i32.const 32 - i32.const 58 + i32.const 98 i32.const 3 call $~lib/builtins/abort unreachable @@ -125,42 +137,48 @@ (func $features/reference-types/testLocal (local $0 anyref) (local $1 anyref) + i32.const 1 local.get $0 ref.is_null + i32.sub i32.eqz i32.eqz if i32.const 0 i32.const 32 - i32.const 54 + i32.const 94 i32.const 3 call $~lib/builtins/abort unreachable end ref.null any local.set $0 + i32.const 1 local.get $0 ref.is_null + i32.sub i32.eqz i32.eqz if i32.const 0 i32.const 32 - i32.const 56 + i32.const 96 i32.const 3 call $~lib/builtins/abort unreachable end ref.null any local.set $1 + i32.const 1 local.get $1 ref.is_null + i32.sub i32.eqz i32.eqz if i32.const 0 i32.const 32 - i32.const 58 + i32.const 98 i32.const 3 call $~lib/builtins/abort unreachable @@ -170,6 +188,7 @@ nop ) (func $start:features/reference-types + (local $0 funcref) global.get $features/reference-types/someObject global.get $features/reference-types/someKey call $~lib/bindings/Reflect/has @@ -262,70 +281,140 @@ unreachable end end + i32.const 1 + global.get $features/reference-types/funcGlobal + ref.is_null + i32.sub + i32.eqz + i32.eqz + if + i32.const 0 + i32.const 32 + i32.const 72 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + ref.null func + global.set $features/reference-types/funcGlobal + i32.const 1 + global.get $features/reference-types/funcGlobal + ref.is_null + i32.sub + i32.eqz + i32.eqz + if + i32.const 0 + i32.const 32 + i32.const 74 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + ref.null func + global.set $features/reference-types/funcGlobalInit + i32.const 1 + global.get $features/reference-types/funcGlobalInit + ref.is_null + i32.sub + i32.eqz + i32.eqz + if + i32.const 0 + i32.const 32 + i32.const 76 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + i32.const 1 + global.get $features/reference-types/externGlobal + ref.is_null + i32.sub + i32.eqz + i32.eqz + if + i32.const 0 + i32.const 32 + i32.const 79 + i32.const 1 + call $~lib/builtins/abort + unreachable + end ref.null extern global.set $features/reference-types/externGlobal + i32.const 1 global.get $features/reference-types/externGlobal ref.is_null + i32.sub i32.eqz i32.eqz if i32.const 0 i32.const 32 - i32.const 41 + i32.const 81 i32.const 1 call $~lib/builtins/abort unreachable end ref.null extern global.set $features/reference-types/externGlobalInit + i32.const 1 global.get $features/reference-types/externGlobalInit ref.is_null + i32.sub i32.eqz i32.eqz if i32.const 0 i32.const 32 - i32.const 43 + i32.const 83 i32.const 1 call $~lib/builtins/abort unreachable end + i32.const 1 global.get $features/reference-types/anyGlobal ref.is_null + i32.sub i32.eqz i32.eqz if i32.const 0 i32.const 32 - i32.const 46 + i32.const 86 i32.const 1 call $~lib/builtins/abort unreachable end ref.null any global.set $features/reference-types/anyGlobal + i32.const 1 global.get $features/reference-types/anyGlobal ref.is_null + i32.sub i32.eqz i32.eqz if i32.const 0 i32.const 32 - i32.const 48 + i32.const 88 i32.const 1 call $~lib/builtins/abort unreachable end ref.null any global.set $features/reference-types/anyGlobalInit + i32.const 1 global.get $features/reference-types/anyGlobalInit ref.is_null + i32.sub i32.eqz i32.eqz if i32.const 0 i32.const 32 - i32.const 50 + i32.const 90 i32.const 1 call $~lib/builtins/abort unreachable @@ -340,7 +429,7 @@ if i32.const 0 i32.const 32 - i32.const 68 + i32.const 108 i32.const 1 call $~lib/builtins/abort unreachable @@ -352,7 +441,7 @@ if i32.const 0 i32.const 32 - i32.const 70 + i32.const 110 i32.const 1 call $~lib/builtins/abort unreachable @@ -364,7 +453,7 @@ if i32.const 0 i32.const 32 - i32.const 73 + i32.const 113 i32.const 3 call $~lib/builtins/abort unreachable From 528fc7dbda4c13b842c39ce9f6e0fe052672609b Mon Sep 17 00:00:00 2001 From: Surma Date: Sun, 7 Mar 2021 19:17:53 +0000 Subject: [PATCH 3/6] Use Eqz instead Co-authored-by: Max Graey --- src/compiler.ts | 4 +- .../features/reference-types.untouched.wat | 66 +++++++------------ 2 files changed, 24 insertions(+), 46 deletions(-) diff --git a/src/compiler.ts b/src/compiler.ts index 3bb4d860b1..80621132ac 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -9944,8 +9944,8 @@ export class Compiler extends DiagnosticEmitter { // Needs to be true when the ref is _not_ null. // Therefore: 1 - return type.size == 64 - ? module.binary(BinaryOp.SubI64, module.i64(1), module.ref_is(RefIsOp.RefIsNull, expr)) - : module.binary(BinaryOp.SubI32, module.i32(1), module.ref_is(RefIsOp.RefIsNull, expr)); + ? module.unary(UnaryOp.EqzI64, module.ref_is(RefIsOp.RefIsNull, expr)) + : module.unary(UnaryOp.EqzI32, module.ref_is(RefIsOp.RefIsNull, expr)); } default: { diff --git a/tests/compiler/features/reference-types.untouched.wat b/tests/compiler/features/reference-types.untouched.wat index 067ed2996e..d11e0d6bcb 100644 --- a/tests/compiler/features/reference-types.untouched.wat +++ b/tests/compiler/features/reference-types.untouched.wat @@ -37,10 +37,9 @@ (func $features/reference-types/testLocal (local $0 funcref) (local $1 funcref) - i32.const 1 local.get $0 ref.is_null - i32.sub + i32.eqz i32.eqz i32.eqz if @@ -53,10 +52,9 @@ end ref.null func local.set $0 - i32.const 1 local.get $0 ref.is_null - i32.sub + i32.eqz i32.eqz i32.eqz if @@ -69,10 +67,9 @@ end ref.null func local.set $1 - i32.const 1 local.get $1 ref.is_null - i32.sub + i32.eqz i32.eqz i32.eqz if @@ -87,10 +84,9 @@ (func $features/reference-types/testLocal (local $0 externref) (local $1 externref) - i32.const 1 local.get $0 ref.is_null - i32.sub + i32.eqz i32.eqz i32.eqz if @@ -103,10 +99,9 @@ end ref.null extern local.set $0 - i32.const 1 local.get $0 ref.is_null - i32.sub + i32.eqz i32.eqz i32.eqz if @@ -119,10 +114,9 @@ end ref.null extern local.set $1 - i32.const 1 local.get $1 ref.is_null - i32.sub + i32.eqz i32.eqz i32.eqz if @@ -137,10 +131,9 @@ (func $features/reference-types/testLocal (local $0 anyref) (local $1 anyref) - i32.const 1 local.get $0 ref.is_null - i32.sub + i32.eqz i32.eqz i32.eqz if @@ -153,10 +146,9 @@ end ref.null any local.set $0 - i32.const 1 local.get $0 ref.is_null - i32.sub + i32.eqz i32.eqz i32.eqz if @@ -169,10 +161,9 @@ end ref.null any local.set $1 - i32.const 1 local.get $1 ref.is_null - i32.sub + i32.eqz i32.eqz i32.eqz if @@ -211,10 +202,9 @@ global.get $features/reference-types/someKey call $~lib/bindings/Reflect/get call $~lib/bindings/console/log - i32.const 1 call $features/reference-types/somethingReal ref.is_null - i32.sub + i32.eqz i32.eqz if i32.const 0 @@ -228,10 +218,9 @@ unreachable end end - i32.const 1 call $features/reference-types/somethingNull ref.is_null - i32.sub + i32.eqz i32.eqz if nop @@ -247,10 +236,9 @@ unreachable end end - i32.const 1 call $features/reference-types/somethingReal ref.is_null - i32.sub + i32.eqz if nop else @@ -265,10 +253,9 @@ unreachable end end - i32.const 1 call $features/reference-types/somethingNull ref.is_null - i32.sub + i32.eqz if i32.const 0 i32.eqz @@ -281,10 +268,9 @@ unreachable end end - i32.const 1 global.get $features/reference-types/funcGlobal ref.is_null - i32.sub + i32.eqz i32.eqz i32.eqz if @@ -297,10 +283,9 @@ end ref.null func global.set $features/reference-types/funcGlobal - i32.const 1 global.get $features/reference-types/funcGlobal ref.is_null - i32.sub + i32.eqz i32.eqz i32.eqz if @@ -313,10 +298,9 @@ end ref.null func global.set $features/reference-types/funcGlobalInit - i32.const 1 global.get $features/reference-types/funcGlobalInit ref.is_null - i32.sub + i32.eqz i32.eqz i32.eqz if @@ -327,10 +311,9 @@ call $~lib/builtins/abort unreachable end - i32.const 1 global.get $features/reference-types/externGlobal ref.is_null - i32.sub + i32.eqz i32.eqz i32.eqz if @@ -343,10 +326,9 @@ end ref.null extern global.set $features/reference-types/externGlobal - i32.const 1 global.get $features/reference-types/externGlobal ref.is_null - i32.sub + i32.eqz i32.eqz i32.eqz if @@ -359,10 +341,9 @@ end ref.null extern global.set $features/reference-types/externGlobalInit - i32.const 1 global.get $features/reference-types/externGlobalInit ref.is_null - i32.sub + i32.eqz i32.eqz i32.eqz if @@ -373,10 +354,9 @@ call $~lib/builtins/abort unreachable end - i32.const 1 global.get $features/reference-types/anyGlobal ref.is_null - i32.sub + i32.eqz i32.eqz i32.eqz if @@ -389,10 +369,9 @@ end ref.null any global.set $features/reference-types/anyGlobal - i32.const 1 global.get $features/reference-types/anyGlobal ref.is_null - i32.sub + i32.eqz i32.eqz i32.eqz if @@ -405,10 +384,9 @@ end ref.null any global.set $features/reference-types/anyGlobalInit - i32.const 1 global.get $features/reference-types/anyGlobalInit ref.is_null - i32.sub + i32.eqz i32.eqz i32.eqz if From be87ecd3699eb4949ace57a08e15ca0a80572043 Mon Sep 17 00:00:00 2001 From: Surma Date: Sun, 7 Mar 2021 19:20:28 +0000 Subject: [PATCH 4/6] Update src/compiler.ts Co-authored-by: Max Graey --- src/compiler.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/compiler.ts b/src/compiler.ts index 80621132ac..d74f43b79c 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -9943,6 +9943,7 @@ export class Compiler extends DiagnosticEmitter { case TypeKind.I31REF: { // Needs to be true when the ref is _not_ null. // Therefore: 1 - + // Therefore: !() return type.size == 64 ? module.unary(UnaryOp.EqzI64, module.ref_is(RefIsOp.RefIsNull, expr)) : module.unary(UnaryOp.EqzI32, module.ref_is(RefIsOp.RefIsNull, expr)); From 186a78f51ccafedc342a9a2b3cd3ea83ead0a2f2 Mon Sep 17 00:00:00 2001 From: Surma Date: Sun, 7 Mar 2021 22:04:32 +0000 Subject: [PATCH 5/6] Update src/compiler.ts Co-authored-by: Daniel Wirtz --- src/compiler.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/compiler.ts b/src/compiler.ts index d74f43b79c..c653c237e5 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -9944,9 +9944,7 @@ export class Compiler extends DiagnosticEmitter { // Needs to be true when the ref is _not_ null. // Therefore: 1 - // Therefore: !() - return type.size == 64 - ? module.unary(UnaryOp.EqzI64, module.ref_is(RefIsOp.RefIsNull, expr)) - : module.unary(UnaryOp.EqzI32, module.ref_is(RefIsOp.RefIsNull, expr)); + return module.unary(UnaryOp.EqzI32, module.ref_is(RefIsOp.RefIsNull, expr)); } default: { From 8384aa3e184bc67a1bf4e50e9f82e19da946b576 Mon Sep 17 00:00:00 2001 From: Surma Date: Sun, 7 Mar 2021 22:16:54 +0000 Subject: [PATCH 6/6] Update comment --- src/compiler.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/compiler.ts b/src/compiler.ts index c653c237e5..9a60c12214 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -9941,9 +9941,8 @@ export class Compiler extends DiagnosticEmitter { case TypeKind.EQREF: case TypeKind.DATAREF: case TypeKind.I31REF: { - // Needs to be true when the ref is _not_ null. - // Therefore: 1 - - // Therefore: !() + // Needs to be true (i.e. not zero) when the ref is _not_ null, + // which means `ref.is_null` returns false (i.e. zero). return module.unary(UnaryOp.EqzI32, module.ref_is(RefIsOp.RefIsNull, expr)); }