diff --git a/src/compiler.ts b/src/compiler.ts index 48f85a18b0..20039b5c9f 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -8874,10 +8874,32 @@ export class Compiler extends DiagnosticEmitter { return module.unary(type.size == 64 ? UnaryOp.EqzI64 : UnaryOp.EqzI32, expr); } case TypeKind.F32: { - return module.binary(BinaryOp.EqF32, expr, module.f32(0)); + // (x == 0.0) | (x != x) + let temp = this.currentFlow.getTempLocal(type); + let isEqzExpr = module.binary(BinaryOp.EqF32, + module.local_tee(temp.index, expr), + module.f32(0) + ); + let isNaNExpr = module.binary(BinaryOp.NeF32, + module.local_get(temp.index, type.toNativeType()), + module.local_get(temp.index, type.toNativeType()) + ); + this.currentFlow.freeTempLocal(temp); + return module.binary(BinaryOp.OrI32, isEqzExpr, isNaNExpr); } case TypeKind.F64: { - return module.binary(BinaryOp.EqF64, expr, module.f64(0)); + // (x == 0.0) | (x != x) + let temp = this.currentFlow.getTempLocal(type); + let isEqzExpr = module.binary(BinaryOp.EqF64, + module.local_tee(temp.index, expr), + module.f64(0) + ); + let isNaNExpr = module.binary(BinaryOp.NeF64, + module.local_get(temp.index, type.toNativeType()), + module.local_get(temp.index, type.toNativeType()) + ); + this.currentFlow.freeTempLocal(temp); + return module.binary(BinaryOp.OrI32, isEqzExpr, isNaNExpr); } // case TypeKind.ANYREF: { // TODO: ref.is_null @@ -8916,10 +8938,32 @@ export class Compiler extends DiagnosticEmitter { : expr; } case TypeKind.F32: { - return module.binary(BinaryOp.NeF32, expr, module.f32(0)); + // (x != 0.0) & (x == x) + let temp = this.currentFlow.getTempLocal(type); + let isEqzExpr = module.binary(BinaryOp.NeF32, + module.local_tee(temp.index, expr), + module.f32(0) + ); + let isNotNaNExpr = module.binary(BinaryOp.EqF32, + module.local_get(temp.index, type.toNativeType()), + module.local_get(temp.index, type.toNativeType()) + ); + this.currentFlow.freeTempLocal(temp); + return module.binary(BinaryOp.AndI32, isEqzExpr, isNotNaNExpr); } case TypeKind.F64: { - return module.binary(BinaryOp.NeF64, expr, module.f64(0)); + // (x != 0.0) & (x == x) + let temp = this.currentFlow.getTempLocal(type); + let isEqzExpr = module.binary(BinaryOp.NeF64, + module.local_tee(temp.index, expr), + module.f64(0) + ); + let isNotNaNExpr = module.binary(BinaryOp.EqF64, + module.local_get(temp.index, type.toNativeType()), + module.local_get(temp.index, type.toNativeType()) + ); + this.currentFlow.freeTempLocal(temp); + return module.binary(BinaryOp.AndI32, isEqzExpr, isNotNaNExpr); } // case TypeKind.ANYREF: { // TODO: !ref.is_null diff --git a/tests/compiler/logical.optimized.wat b/tests/compiler/logical.optimized.wat index 9047619606..7a14788b70 100644 --- a/tests/compiler/logical.optimized.wat +++ b/tests/compiler/logical.optimized.wat @@ -1,5 +1,9 @@ (module + (type $FUNCSIG$viiii (func (param i32 i32 i32 i32))) + (type $FUNCSIG$if (func (param f32) (result i32))) + (type $FUNCSIG$id (func (param f64) (result i32))) (type $FUNCSIG$v (func)) + (import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32))) (memory $0 1) (data (i32.const 8) "\14\00\00\00\01\00\00\00\01\00\00\00\14\00\00\00l\00o\00g\00i\00c\00a\00l\00.\00t\00s") (global $logical/i (mut i32) (i32.const 0)) @@ -8,7 +12,17 @@ (global $logical/F (mut f64) (f64.const 0)) (export "memory" (memory $0)) (start $start) - (func $start:logical (; 0 ;) (type $FUNCSIG$v) + (func $~lib/number/isNaN (; 1 ;) (type $FUNCSIG$if) (param $0 f32) (result i32) + local.get $0 + local.get $0 + f32.ne + ) + (func $~lib/number/isNaN (; 2 ;) (type $FUNCSIG$id) (param $0 f64) (result i32) + local.get $0 + local.get $0 + f64.ne + ) + (func $start:logical (; 3 ;) (type $FUNCSIG$v) i32.const 2 global.set $logical/i i32.const 1 @@ -25,11 +39,71 @@ global.set $logical/F f64.const 1 global.set $logical/F + f32.const 1 + global.set $logical/f + f32.const 1 + global.set $logical/f + f64.const 1 + global.set $logical/F + f64.const 1 + global.set $logical/F + f32.const nan:0x400000 + global.set $logical/f + f32.const nan:0x400000 + call $~lib/number/isNaN + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 54 + i32.const 0 + call $~lib/builtins/abort + unreachable + end + f32.const nan:0x400000 + global.set $logical/f + f32.const nan:0x400000 + call $~lib/number/isNaN + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 57 + i32.const 0 + call $~lib/builtins/abort + unreachable + end + f64.const nan:0x8000000000000 + global.set $logical/F + f64.const nan:0x8000000000000 + call $~lib/number/isNaN + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 60 + i32.const 0 + call $~lib/builtins/abort + unreachable + end + f64.const nan:0x8000000000000 + global.set $logical/F + f64.const nan:0x8000000000000 + call $~lib/number/isNaN + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 63 + i32.const 0 + call $~lib/builtins/abort + unreachable + end ) - (func $start (; 1 ;) (type $FUNCSIG$v) + (func $start (; 4 ;) (type $FUNCSIG$v) call $start:logical ) - (func $null (; 2 ;) (type $FUNCSIG$v) + (func $null (; 5 ;) (type $FUNCSIG$v) nop ) ) diff --git a/tests/compiler/logical.ts b/tests/compiler/logical.ts index 14218e9ac5..70e159bfac 100644 --- a/tests/compiler/logical.ts +++ b/tests/compiler/logical.ts @@ -37,3 +37,27 @@ assert(F == 2.0); F = 0.0 || 1.0; assert(F == 1.0); + +f = NaN as f32 || 1.0 as f32; +assert(f == 1.0); + +f = 1.0 as f32 || NaN as f32; +assert(f == 1.0); + +F = NaN || 1.0; +assert(F == 1.0); + +F = 1.0 || NaN; +assert(F == 1.0); + +f = 1.0 as f32 && NaN as f32; +assert(isNaN(f)); + +f = NaN as f32 && 1.0 as f32; +assert(isNaN(f)); + +F = 1.0 && NaN; +assert(isNaN(F)); + +F = NaN && 1.0; +assert(isNaN(F)); diff --git a/tests/compiler/logical.untouched.wat b/tests/compiler/logical.untouched.wat index bfbefd3c16..e8f2e02e21 100644 --- a/tests/compiler/logical.untouched.wat +++ b/tests/compiler/logical.untouched.wat @@ -1,5 +1,7 @@ (module (type $FUNCSIG$viiii (func (param i32 i32 i32 i32))) + (type $FUNCSIG$if (func (param f32) (result i32))) + (type $FUNCSIG$id (func (param f64) (result i32))) (type $FUNCSIG$v (func)) (import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32))) (memory $0 1) @@ -12,7 +14,19 @@ (global $logical/F (mut f64) (f64.const 0)) (export "memory" (memory $0)) (start $start) - (func $start:logical (; 1 ;) (type $FUNCSIG$v) + (func $~lib/number/isNaN (; 1 ;) (type $FUNCSIG$if) (param $0 f32) (result i32) + local.get $0 + local.get $0 + f32.ne + ) + (func $~lib/number/isNaN (; 2 ;) (type $FUNCSIG$id) (param $0 f64) (result i32) + local.get $0 + local.get $0 + f64.ne + ) + (func $start:logical (; 3 ;) (type $FUNCSIG$v) + (local $0 f64) + (local $1 f32) i32.const 0 if (result i32) unreachable @@ -21,8 +35,13 @@ end drop f64.const 0 + local.tee $0 f64.const 0 f64.ne + local.get $0 + local.get $0 + f64.eq + i32.and if (result i32) unreachable else @@ -37,8 +56,13 @@ end drop f64.const 1 + local.tee $0 f64.const 0 f64.ne + local.get $0 + local.get $0 + f64.eq + i32.and if (result i32) i32.const 1 else @@ -58,15 +82,25 @@ end drop f64.const 1 + local.tee $0 f64.const 0 f64.ne + local.get $0 + local.get $0 + f64.eq + i32.and if (result f64) f64.const 2 else f64.const 1 end + local.tee $0 f64.const 0 f64.ne + local.get $0 + local.get $0 + f64.eq + i32.and if (result i32) i32.const 1 else @@ -154,8 +188,13 @@ unreachable end f32.const 1 + local.tee $1 f32.const 0 f32.ne + local.get $1 + local.get $1 + f32.eq + i32.and if (result f32) f32.const 2 else @@ -175,8 +214,13 @@ unreachable end f32.const 0 + local.tee $1 f32.const 0 f32.ne + local.get $1 + local.get $1 + f32.eq + i32.and if (result f32) f32.const 0 else @@ -196,8 +240,13 @@ unreachable end f64.const 1 + local.tee $0 f64.const 0 f64.ne + local.get $0 + local.get $0 + f64.eq + i32.and if (result f64) f64.const 2 else @@ -217,8 +266,13 @@ unreachable end f64.const 0 + local.tee $0 f64.const 0 f64.ne + local.get $0 + local.get $0 + f64.eq + i32.and if (result f64) f64.const 0 else @@ -237,10 +291,214 @@ call $~lib/builtins/abort unreachable end + f32.const nan:0x400000 + local.tee $1 + f32.const 0 + f32.ne + local.get $1 + local.get $1 + f32.eq + i32.and + if (result f32) + f32.const nan:0x400000 + else + f32.const 1 + end + global.set $logical/f + global.get $logical/f + f32.const 1 + f32.eq + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 42 + i32.const 0 + call $~lib/builtins/abort + unreachable + end + f32.const 1 + local.tee $1 + f32.const 0 + f32.ne + local.get $1 + local.get $1 + f32.eq + i32.and + if (result f32) + f32.const 1 + else + f32.const nan:0x400000 + end + global.set $logical/f + global.get $logical/f + f32.const 1 + f32.eq + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 45 + i32.const 0 + call $~lib/builtins/abort + unreachable + end + f64.const nan:0x8000000000000 + local.tee $0 + f64.const 0 + f64.ne + local.get $0 + local.get $0 + f64.eq + i32.and + if (result f64) + f64.const nan:0x8000000000000 + else + f64.const 1 + end + global.set $logical/F + global.get $logical/F + f64.const 1 + f64.eq + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 48 + i32.const 0 + call $~lib/builtins/abort + unreachable + end + f64.const 1 + local.tee $0 + f64.const 0 + f64.ne + local.get $0 + local.get $0 + f64.eq + i32.and + if (result f64) + f64.const 1 + else + f64.const nan:0x8000000000000 + end + global.set $logical/F + global.get $logical/F + f64.const 1 + f64.eq + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 51 + i32.const 0 + call $~lib/builtins/abort + unreachable + end + f32.const 1 + local.tee $1 + f32.const 0 + f32.ne + local.get $1 + local.get $1 + f32.eq + i32.and + if (result f32) + f32.const nan:0x400000 + else + f32.const 1 + end + global.set $logical/f + global.get $logical/f + call $~lib/number/isNaN + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 54 + i32.const 0 + call $~lib/builtins/abort + unreachable + end + f32.const nan:0x400000 + local.tee $1 + f32.const 0 + f32.ne + local.get $1 + local.get $1 + f32.eq + i32.and + if (result f32) + f32.const 1 + else + f32.const nan:0x400000 + end + global.set $logical/f + global.get $logical/f + call $~lib/number/isNaN + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 57 + i32.const 0 + call $~lib/builtins/abort + unreachable + end + f64.const 1 + local.tee $0 + f64.const 0 + f64.ne + local.get $0 + local.get $0 + f64.eq + i32.and + if (result f64) + f64.const nan:0x8000000000000 + else + f64.const 1 + end + global.set $logical/F + global.get $logical/F + call $~lib/number/isNaN + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 60 + i32.const 0 + call $~lib/builtins/abort + unreachable + end + f64.const nan:0x8000000000000 + local.tee $0 + f64.const 0 + f64.ne + local.get $0 + local.get $0 + f64.eq + i32.and + if (result f64) + f64.const 1 + else + f64.const nan:0x8000000000000 + end + global.set $logical/F + global.get $logical/F + call $~lib/number/isNaN + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 63 + i32.const 0 + call $~lib/builtins/abort + unreachable + end ) - (func $start (; 2 ;) (type $FUNCSIG$v) + (func $start (; 4 ;) (type $FUNCSIG$v) call $start:logical ) - (func $null (; 3 ;) (type $FUNCSIG$v) + (func $null (; 5 ;) (type $FUNCSIG$v) ) ) diff --git a/tests/compiler/unary.optimized.wat b/tests/compiler/unary.optimized.wat index cfa2553843..38afb337fa 100644 --- a/tests/compiler/unary.optimized.wat +++ b/tests/compiler/unary.optimized.wat @@ -8,10 +8,10 @@ (export "memory" (memory $0)) (start $start) (func $start:unary (; 0 ;) (type $FUNCSIG$v) - (local $0 i32) - (local $1 i64) - (local $2 f32) - (local $3 f64) + (local $0 f32) + (local $1 f64) + (local $2 i32) + (local $3 i64) global.get $unary/i i32.const 1 i32.add @@ -54,18 +54,18 @@ i32.sub global.set $unary/i global.get $unary/i - local.tee $0 + local.tee $2 i32.const 1 i32.add global.set $unary/i - local.get $0 + local.get $2 global.set $unary/i global.get $unary/i - local.tee $0 + local.tee $2 i32.const 1 i32.sub global.set $unary/i - local.get $0 + local.get $2 global.set $unary/i global.get $unary/I i64.const 1 @@ -110,18 +110,18 @@ i64.sub global.set $unary/I global.get $unary/I - local.tee $1 + local.tee $3 i64.const 1 i64.add global.set $unary/I - local.get $1 + local.get $3 global.set $unary/I global.get $unary/I - local.tee $1 + local.tee $3 i64.const 1 i64.sub global.set $unary/I - local.get $1 + local.get $3 global.set $unary/I global.get $unary/f f32.const 1 @@ -148,8 +148,15 @@ f32.const 1.25 global.set $unary/f global.get $unary/f + local.tee $0 f32.const 0 f32.eq + local.get $0 + local.get $0 + f32.ne + i32.or + i32.const 0 + i32.ne global.set $unary/i global.get $unary/f f32.const 1 @@ -160,18 +167,18 @@ f32.sub global.set $unary/f global.get $unary/f - local.tee $2 + local.tee $0 f32.const 1 f32.add global.set $unary/f - local.get $2 + local.get $0 global.set $unary/f global.get $unary/f - local.tee $2 + local.tee $0 f32.const 1 f32.sub global.set $unary/f - local.get $2 + local.get $0 global.set $unary/f global.get $unary/F f64.const 1 @@ -198,8 +205,15 @@ f64.const 1.25 global.set $unary/F global.get $unary/F + local.tee $1 f64.const 0 f64.eq + local.get $1 + local.get $1 + f64.ne + i32.or + i32.const 0 + i32.ne i64.extend_i32_u global.set $unary/I global.get $unary/F @@ -211,18 +225,18 @@ f64.sub global.set $unary/F global.get $unary/F - local.tee $3 + local.tee $1 f64.const 1 f64.add global.set $unary/F - local.get $3 + local.get $1 global.set $unary/F global.get $unary/F - local.tee $3 + local.tee $1 f64.const 1 f64.sub global.set $unary/F - local.get $3 + local.get $1 global.set $unary/F ) (func $start (; 1 ;) (type $FUNCSIG$v) diff --git a/tests/compiler/unary.untouched.wat b/tests/compiler/unary.untouched.wat index e4196758f7..98bc212981 100644 --- a/tests/compiler/unary.untouched.wat +++ b/tests/compiler/unary.untouched.wat @@ -10,10 +10,10 @@ (export "memory" (memory $0)) (start $start) (func $start:unary (; 0 ;) (type $FUNCSIG$v) - (local $0 i32) - (local $1 i64) - (local $2 f32) - (local $3 f64) + (local $0 f64) + (local $1 i32) + (local $2 i64) + (local $3 f32) i32.const 1 drop i32.const -1 @@ -30,8 +30,13 @@ f64.const -1.25 drop f64.const 1.25 + local.tee $0 f64.const 0 f64.eq + local.get $0 + local.get $0 + f64.ne + i32.or drop global.get $unary/i drop @@ -99,18 +104,18 @@ global.get $unary/i global.set $unary/i global.get $unary/i - local.tee $0 + local.tee $1 i32.const 1 i32.add global.set $unary/i - local.get $0 + local.get $1 global.set $unary/i global.get $unary/i - local.tee $0 + local.tee $1 i32.const 1 i32.sub global.set $unary/i - local.get $0 + local.get $1 global.set $unary/i global.get $unary/I drop @@ -180,18 +185,18 @@ global.get $unary/I global.set $unary/I global.get $unary/I - local.tee $1 + local.tee $2 i64.const 1 i64.add global.set $unary/I - local.get $1 + local.get $2 global.set $unary/I global.get $unary/I - local.tee $1 + local.tee $2 i64.const 1 i64.sub global.set $unary/I - local.get $1 + local.get $2 global.set $unary/I global.get $unary/f drop @@ -199,8 +204,13 @@ f32.neg drop global.get $unary/f + local.tee $3 f32.const 0 f32.eq + local.get $3 + local.get $3 + f32.ne + i32.or drop global.get $unary/f f32.const 1 @@ -223,8 +233,15 @@ f32.const -1.25 global.set $unary/f f64.const 1.25 + local.tee $0 f64.const 0 f64.eq + local.get $0 + local.get $0 + f64.ne + i32.or + i32.const 0 + i32.ne global.set $unary/i global.get $unary/f global.set $unary/f @@ -232,8 +249,15 @@ f32.neg global.set $unary/f global.get $unary/f + local.tee $3 f32.const 0 f32.eq + local.get $3 + local.get $3 + f32.ne + i32.or + i32.const 0 + i32.ne global.set $unary/i global.get $unary/f f32.const 1 @@ -248,18 +272,18 @@ global.get $unary/f global.set $unary/f global.get $unary/f - local.tee $2 + local.tee $3 f32.const 1 f32.add global.set $unary/f - local.get $2 + local.get $3 global.set $unary/f global.get $unary/f - local.tee $2 + local.tee $3 f32.const 1 f32.sub global.set $unary/f - local.get $2 + local.get $3 global.set $unary/f global.get $unary/F drop @@ -267,8 +291,13 @@ f64.neg drop global.get $unary/F + local.tee $0 f64.const 0 f64.eq + local.get $0 + local.get $0 + f64.ne + i32.or drop global.get $unary/F f64.const 1 @@ -291,8 +320,15 @@ f64.const -1.25 global.set $unary/F f64.const 1.25 + local.tee $0 f64.const 0 f64.eq + local.get $0 + local.get $0 + f64.ne + i32.or + i32.const 0 + i32.ne i64.extend_i32_u global.set $unary/I global.get $unary/F @@ -301,8 +337,15 @@ f64.neg global.set $unary/F global.get $unary/F + local.tee $0 f64.const 0 f64.eq + local.get $0 + local.get $0 + f64.ne + i32.or + i32.const 0 + i32.ne i64.extend_i32_u global.set $unary/I global.get $unary/F @@ -318,18 +361,18 @@ global.get $unary/F global.set $unary/F global.get $unary/F - local.tee $3 + local.tee $0 f64.const 1 f64.add global.set $unary/F - local.get $3 + local.get $0 global.set $unary/F global.get $unary/F - local.tee $3 + local.tee $0 f64.const 1 f64.sub global.set $unary/F - local.get $3 + local.get $0 global.set $unary/F ) (func $start (; 1 ;) (type $FUNCSIG$v)