From 871127f7845f3257076006a09eea03d008d97ba2 Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Wed, 29 Jun 2022 14:47:36 +0300 Subject: [PATCH 1/6] add workaround --- src/compiler.ts | 16 +++++++ tests/compiler/std/math.debug.wat | 70 +++++++++++++++++++++++++++++++ tests/compiler/std/math.ts | 7 ++++ 3 files changed, 93 insertions(+) diff --git a/src/compiler.ts b/src/compiler.ts index 74518ef05d..bd553aa6bb 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -5364,6 +5364,22 @@ export class Compiler extends DiagnosticEmitter { let leftValue = getConstValueF64(leftExpr); let rightValue = getConstValueF64(rightExpr); this.currentType = type; + if (!ASC_TARGET) { // ASC_TARGET == JS + // Engines like V8, WebKit and SpiderMonkey uses powi fast path if exponent is integers + // This speculative optimization leads to loose precisions like: + // 10 ** 208 != 1e208 or 10 ** -5 != 1e-5 + // to avoid this we forcing exponent to fractional form and compensate + // this afterwards. + // if (isFinite(rightValue) && Math.trunc(rightValue) == rightValue) { + if ( + isFinite(rightValue) && + Math.abs(rightValue) >= 2 && + Math.trunc(rightValue) == rightValue + ) { + const res = Math.pow(leftValue, rightValue - 0.5) * Math.pow(leftValue, 0.5); + return module.f64(res); + } + } return module.f64(Math.pow(leftValue, rightValue)); } } diff --git a/tests/compiler/std/math.debug.wat b/tests/compiler/std/math.debug.wat index 10c8d5867d..99d9306693 100644 --- a/tests/compiler/std/math.debug.wat +++ b/tests/compiler/std/math.debug.wat @@ -59299,6 +59299,76 @@ call $~lib/builtins/abort unreachable end + f64.const 10 + f64.const 308 + call $~lib/math/NativeMath.pow + f64.const 1.e+308 + f64.eq + i32.eqz + if + i32.const 0 + i32.const 32 + i32.const 4136 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + f64.const 10 + f64.const 208 + call $~lib/math/NativeMath.pow + f64.const 1.e+208 + f64.eq + i32.eqz + if + i32.const 0 + i32.const 32 + i32.const 4137 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + f64.const 10 + f64.const -5 + call $~lib/math/NativeMath.pow + f64.const 1e-05 + f64.eq + i32.eqz + if + i32.const 0 + i32.const 32 + i32.const 4138 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + f32.const 10 + f32.const 38 + call $~lib/math/NativeMathf.pow + f32.const 9999999680285692465065626e13 + f32.eq + i32.eqz + if + i32.const 0 + i32.const 32 + i32.const 4139 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + f32.const 10 + f32.const -5 + call $~lib/math/NativeMathf.pow + f32.const 9.999999747378752e-06 + f32.eq + i32.eqz + if + i32.const 0 + i32.const 32 + i32.const 4140 + i32.const 1 + call $~lib/builtins/abort + unreachable + end ) (func $~start call $start:std/math diff --git a/tests/compiler/std/math.ts b/tests/compiler/std/math.ts index 80880dd99b..95eefbbe23 100644 --- a/tests/compiler/std/math.ts +++ b/tests/compiler/std/math.ts @@ -4131,3 +4131,10 @@ assert(0 ** 0.5 == 0.0); assert(0 ** -1.0 == Infinity); assert(0.0 ** 0 == 1.0); assert(1.0 ** 1 == 1.0); + +// Special cases for test constant fold correctness +assert(10.0 ** 308 == 1e308); +assert(10.0 ** 208 == 1e208); +assert(10.0 ** -5 == 1e-5); +assert(f32(10) ** 38 == f32(1e38)); +assert(f32(10) ** -5 == f32(1e-5)); From d84bd19745218813651eee1b5eeeff109b29859c Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Wed, 29 Jun 2022 14:55:40 +0300 Subject: [PATCH 2/6] upd comment --- src/compiler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler.ts b/src/compiler.ts index bd553aa6bb..b16ce08f20 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -5365,7 +5365,7 @@ export class Compiler extends DiagnosticEmitter { let rightValue = getConstValueF64(rightExpr); this.currentType = type; if (!ASC_TARGET) { // ASC_TARGET == JS - // Engines like V8, WebKit and SpiderMonkey uses powi fast path if exponent is integers + // Engines like V8, WebKit and SpiderMonkey uses powi fast path if exponent is integer // This speculative optimization leads to loose precisions like: // 10 ** 208 != 1e208 or 10 ** -5 != 1e-5 // to avoid this we forcing exponent to fractional form and compensate From 70f29c3986967727d4de14af0bd97e89d7698d94 Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Wed, 29 Jun 2022 14:57:19 +0300 Subject: [PATCH 3/6] more --- src/compiler.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/compiler.ts b/src/compiler.ts index b16ce08f20..78f613998d 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -5366,11 +5366,9 @@ export class Compiler extends DiagnosticEmitter { this.currentType = type; if (!ASC_TARGET) { // ASC_TARGET == JS // Engines like V8, WebKit and SpiderMonkey uses powi fast path if exponent is integer - // This speculative optimization leads to loose precisions like: - // 10 ** 208 != 1e208 or 10 ** -5 != 1e-5 - // to avoid this we forcing exponent to fractional form and compensate - // this afterwards. - // if (isFinite(rightValue) && Math.trunc(rightValue) == rightValue) { + // This speculative optimization leads to loose precisions like 10 ** 208 != 1e208 + // or/and 10 ** -5 != 1e-5 anymore. For avoid this behaviour we are forcing exponent + // to fractional form and compensate this afterwards. if ( isFinite(rightValue) && Math.abs(rightValue) >= 2 && From 28f48383eb9025a9e4a80b8cdf55d42d7d57f423 Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Wed, 29 Jun 2022 15:20:51 +0300 Subject: [PATCH 4/6] refactoring --- src/compiler.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/compiler.ts b/src/compiler.ts index 78f613998d..7be2d0a8d5 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -5374,8 +5374,7 @@ export class Compiler extends DiagnosticEmitter { Math.abs(rightValue) >= 2 && Math.trunc(rightValue) == rightValue ) { - const res = Math.pow(leftValue, rightValue - 0.5) * Math.pow(leftValue, 0.5); - return module.f64(res); + return module.f64(Math.pow(leftValue, rightValue - 0.5) * Math.pow(leftValue, 0.5)); } } return module.f64(Math.pow(leftValue, rightValue)); From 527e89d85af911ccc7835ff259625c283f3186a1 Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Wed, 29 Jun 2022 16:00:55 +0300 Subject: [PATCH 5/6] move accurate pow to util/math.ts --- src/compiler.ts | 18 +++--------------- src/util/math.ts | 13 +++++++++++++ 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/compiler.ts b/src/compiler.ts index 7be2d0a8d5..4fa5a803f4 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -199,7 +199,8 @@ import { isPowerOf2, v128_zero, readI32, - isIdentifier + isIdentifier, + accuratePow64 } from "./util"; import { @@ -5364,20 +5365,7 @@ export class Compiler extends DiagnosticEmitter { let leftValue = getConstValueF64(leftExpr); let rightValue = getConstValueF64(rightExpr); this.currentType = type; - if (!ASC_TARGET) { // ASC_TARGET == JS - // Engines like V8, WebKit and SpiderMonkey uses powi fast path if exponent is integer - // This speculative optimization leads to loose precisions like 10 ** 208 != 1e208 - // or/and 10 ** -5 != 1e-5 anymore. For avoid this behaviour we are forcing exponent - // to fractional form and compensate this afterwards. - if ( - isFinite(rightValue) && - Math.abs(rightValue) >= 2 && - Math.trunc(rightValue) == rightValue - ) { - return module.f64(Math.pow(leftValue, rightValue - 0.5) * Math.pow(leftValue, 0.5)); - } - } - return module.f64(Math.pow(leftValue, rightValue)); + return module.f64(accuratePow64(leftValue, rightValue)); } } let instance = this.f64PowInstance; diff --git a/src/util/math.ts b/src/util/math.ts index 592bfe8b6b..a52d07a4c4 100644 --- a/src/util/math.ts +++ b/src/util/math.ts @@ -7,3 +7,16 @@ export function isPowerOf2(x: i32): bool { return x != 0 && (x & (x - 1)) == 0; } + +export function accuratePow64(x: f64, y: f64): f64 { + if (!ASC_TARGET) { // ASC_TARGET == JS + // Engines like V8, WebKit and SpiderMonkey uses powi fast path if exponent is integer + // This speculative optimization leads to loose precisions like 10 ** 208 != 1e208 + // or/and 10 ** -5 != 1e-5 anymore. For avoid this behaviour we are forcing exponent + // to fractional form and compensate this afterwards. + if (isFinite(y) && Math.abs(y) >= 2 && Math.trunc(y) == y) { + return Math.pow(x, y - 0.5) * Math.pow(x, 0.5); + } + } + return Math.pow(x, y); +} From 98bbbe419a804af3b4b1ece588bf499640963e48 Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Wed, 29 Jun 2022 16:05:51 +0300 Subject: [PATCH 6/6] do the same for f32 pow --- src/compiler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler.ts b/src/compiler.ts index 4fa5a803f4..98d9a42084 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -5323,7 +5323,7 @@ export class Compiler extends DiagnosticEmitter { let leftValue = getConstValueF32(leftExpr); let rightValue = getConstValueF32(rightExpr); this.currentType = type; - return module.f32(f32(Math.pow(leftValue, rightValue))); + return module.f32(f32(accuratePow64(leftValue, rightValue))); } } let instance = this.f32PowInstance;