Skip to content

Commit ec0b0c8

Browse files
authored
Add workaround for Math.pow constant folding (only for JS builds) (#2346)
1 parent 2ccea6b commit ec0b0c8

File tree

4 files changed

+94
-3
lines changed

4 files changed

+94
-3
lines changed

Diff for: src/compiler.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,8 @@ import {
199199
isPowerOf2,
200200
v128_zero,
201201
readI32,
202-
isIdentifier
202+
isIdentifier,
203+
accuratePow64
203204
} from "./util";
204205

205206
import {
@@ -5322,7 +5323,7 @@ export class Compiler extends DiagnosticEmitter {
53225323
let leftValue = getConstValueF32(leftExpr);
53235324
let rightValue = getConstValueF32(rightExpr);
53245325
this.currentType = type;
5325-
return module.f32(f32(Math.pow(leftValue, rightValue)));
5326+
return module.f32(f32(accuratePow64(leftValue, rightValue)));
53265327
}
53275328
}
53285329
let instance = this.f32PowInstance;
@@ -5364,7 +5365,7 @@ export class Compiler extends DiagnosticEmitter {
53645365
let leftValue = getConstValueF64(leftExpr);
53655366
let rightValue = getConstValueF64(rightExpr);
53665367
this.currentType = type;
5367-
return module.f64(Math.pow(leftValue, rightValue));
5368+
return module.f64(accuratePow64(leftValue, rightValue));
53685369
}
53695370
}
53705371
let instance = this.f64PowInstance;

Diff for: src/util/math.ts

+13
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,16 @@
77
export function isPowerOf2(x: i32): bool {
88
return x != 0 && (x & (x - 1)) == 0;
99
}
10+
11+
export function accuratePow64(x: f64, y: f64): f64 {
12+
if (!ASC_TARGET) { // ASC_TARGET == JS
13+
// Engines like V8, WebKit and SpiderMonkey uses powi fast path if exponent is integer
14+
// This speculative optimization leads to loose precisions like 10 ** 208 != 1e208
15+
// or/and 10 ** -5 != 1e-5 anymore. For avoid this behaviour we are forcing exponent
16+
// to fractional form and compensate this afterwards.
17+
if (isFinite(y) && Math.abs(y) >= 2 && Math.trunc(y) == y) {
18+
return Math.pow(x, y - 0.5) * Math.pow(x, 0.5);
19+
}
20+
}
21+
return Math.pow(x, y);
22+
}

Diff for: tests/compiler/std/math.debug.wat

+70
Original file line numberDiff line numberDiff line change
@@ -59299,6 +59299,76 @@
5929959299
call $~lib/builtins/abort
5930059300
unreachable
5930159301
end
59302+
f64.const 10
59303+
f64.const 308
59304+
call $~lib/math/NativeMath.pow
59305+
f64.const 1.e+308
59306+
f64.eq
59307+
i32.eqz
59308+
if
59309+
i32.const 0
59310+
i32.const 32
59311+
i32.const 4136
59312+
i32.const 1
59313+
call $~lib/builtins/abort
59314+
unreachable
59315+
end
59316+
f64.const 10
59317+
f64.const 208
59318+
call $~lib/math/NativeMath.pow
59319+
f64.const 1.e+208
59320+
f64.eq
59321+
i32.eqz
59322+
if
59323+
i32.const 0
59324+
i32.const 32
59325+
i32.const 4137
59326+
i32.const 1
59327+
call $~lib/builtins/abort
59328+
unreachable
59329+
end
59330+
f64.const 10
59331+
f64.const -5
59332+
call $~lib/math/NativeMath.pow
59333+
f64.const 1e-05
59334+
f64.eq
59335+
i32.eqz
59336+
if
59337+
i32.const 0
59338+
i32.const 32
59339+
i32.const 4138
59340+
i32.const 1
59341+
call $~lib/builtins/abort
59342+
unreachable
59343+
end
59344+
f32.const 10
59345+
f32.const 38
59346+
call $~lib/math/NativeMathf.pow
59347+
f32.const 9999999680285692465065626e13
59348+
f32.eq
59349+
i32.eqz
59350+
if
59351+
i32.const 0
59352+
i32.const 32
59353+
i32.const 4139
59354+
i32.const 1
59355+
call $~lib/builtins/abort
59356+
unreachable
59357+
end
59358+
f32.const 10
59359+
f32.const -5
59360+
call $~lib/math/NativeMathf.pow
59361+
f32.const 9.999999747378752e-06
59362+
f32.eq
59363+
i32.eqz
59364+
if
59365+
i32.const 0
59366+
i32.const 32
59367+
i32.const 4140
59368+
i32.const 1
59369+
call $~lib/builtins/abort
59370+
unreachable
59371+
end
5930259372
)
5930359373
(func $~start
5930459374
call $start:std/math

Diff for: tests/compiler/std/math.ts

+7
Original file line numberDiff line numberDiff line change
@@ -4131,3 +4131,10 @@ assert(0 ** 0.5 == 0.0);
41314131
assert(0 ** -1.0 == Infinity);
41324132
assert(0.0 ** 0 == 1.0);
41334133
assert(1.0 ** 1 == 1.0);
4134+
4135+
// Special cases for test constant fold correctness
4136+
assert(10.0 ** 308 == 1e308);
4137+
assert(10.0 ** 208 == 1e208);
4138+
assert(10.0 ** -5 == 1e-5);
4139+
assert(f32(10) ** 38 == f32(1e38));
4140+
assert(f32(10) ** -5 == f32(1e-5));

0 commit comments

Comments
 (0)