Skip to content

Add workaround for Math.pow constant folding (only for JS builds) #2346

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

Merged
Merged
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
7 changes: 4 additions & 3 deletions src/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,8 @@ import {
isPowerOf2,
v128_zero,
readI32,
isIdentifier
isIdentifier,
accuratePow64
} from "./util";

import {
Expand Down Expand Up @@ -5322,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;
Expand Down Expand Up @@ -5364,7 +5365,7 @@ export class Compiler extends DiagnosticEmitter {
let leftValue = getConstValueF64(leftExpr);
let rightValue = getConstValueF64(rightExpr);
this.currentType = type;
return module.f64(Math.pow(leftValue, rightValue));
return module.f64(accuratePow64(leftValue, rightValue));
}
}
let instance = this.f64PowInstance;
Expand Down
13 changes: 13 additions & 0 deletions src/util/math.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
70 changes: 70 additions & 0 deletions tests/compiler/std/math.debug.wat
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions tests/compiler/std/math.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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));