mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-31 07:23:06 +08:00
% now checks at run time whether both operands are integers (dividend |x| <= 2^53, nonzero integer divisor) when the integer-range analysis cannot prove it, and then uses i64 srem with JS sign-of-zero semantics; fractions, NaN, Infinity, zero divisors and huge dividends keep frem. Proven integers whose divisor may be zero now only check for zero. push() appends inline while len < cap (the runtime append is exactly a dense store at len plus len+1 there) and keeps a cold runtime call that grows storage otherwise. Element writes were already inlined by H04. Built on the H04 commits cherry-picked from perf/h04-array-reads (d88b58a1, a6057a52, 9109cb5a, 657aa3fb). New corpus programs 3150-integer-remainder and 3151-array-push-writes. Marginal vs perf/h04-array-reads, 15 runs: | workload | macOS | Linux (focused) | | numeric-kernels | -18.3% | -10.3% | | functional-pipeline | -8.8% | -4.3% | | template-render | -2.9% | (layout, sweep -0.6%) | | alloc-trees | -2.0% | (layout, sweep +0.3%) | | records-sort | -1.5% | -0.3% | | csv-numbers | -0.3% | +0.1% | geomean marginal: macOS -2.8%, Linux full -0.6%. vs perf/harness (incl. H04): macOS -14.4% (1.222x Node), Linux -16.0% (1.160x Node). Linux .text growth <= 3.6%. Tests: LLVM differential corpus 2008/2008 (Linux, 6 shards, release+dev), sanitized 3140/3150/3151 pass, backend+ir unit tests pass except one load-flaky zig COFF cache test that passes in isolation.
74 lines
2.5 KiB
TypeScript
74 lines
2.5 KiB
TypeScript
// `%` takes an inline integer path when both operands turn out to be
|
|
// integers at run time and keeps the floating remainder for everything
|
|
// else. Results must match Node exactly, including the sign of zero, NaN for
|
|
// zero divisors and non-finite operands, and integers beyond 2^53.
|
|
|
|
function show(label: string, v: number): void {
|
|
console.log(label, Object.is(v, -0) ? "-0" : String(v));
|
|
}
|
|
|
|
// Unproven operands: values that flow through arrays, records, and globals.
|
|
const dividends: number[] = [
|
|
0, -0, 1, -1, 7, -7, 8, -8, 12, -12, 2 ** 31 - 1, -(2 ** 31), 2 ** 31, 2 ** 32 + 5,
|
|
2 ** 53 - 1, -(2 ** 53 - 1), 2 ** 53, -(2 ** 53), 2 ** 53 + 2, -(2 ** 53 + 2), 2 ** 62,
|
|
2 ** 63, -(2 ** 63), 2 ** 64, -(2 ** 64), 1e21, -1e21, 1.5, -1.5, 5.25, -5.25, 1e-300,
|
|
Number.MAX_VALUE, -Number.MAX_VALUE, NaN, Infinity, -Infinity,
|
|
];
|
|
const divisors: number[] = [
|
|
1, -1, 2, -2, 3, -3, 4, 7, -7, 10, 1000000007, 2 ** 31, -(2 ** 31), 2 ** 32, 2 ** 53,
|
|
-(2 ** 53), 2 ** 53 + 2, 2 ** 63, -(2 ** 63), 2 ** 64, 0, -0, 0.5, -0.5, 2.5, NaN,
|
|
Infinity, -Infinity, Number.MIN_VALUE,
|
|
];
|
|
let checksum = 0;
|
|
for (let i = 0; i < dividends.length; i++) {
|
|
for (let j = 0; j < divisors.length; j++) {
|
|
const r = dividends[i]! % divisors[j]!;
|
|
show(`${dividends[i]} % ${divisors[j]}`, r);
|
|
if (r === r) checksum = (checksum + Math.abs(r)) % 1000003;
|
|
}
|
|
}
|
|
show("checksum", checksum);
|
|
|
|
// Constant divisors with an unproven dividend (the common loop shape).
|
|
for (const d of dividends) {
|
|
show(`${d} % 5`, d % 5);
|
|
show(`${d} % -5`, d % -5);
|
|
show(`${d} % 0`, d % 0);
|
|
show(`${d} % 1`, d % 1);
|
|
}
|
|
|
|
// Unproven divisors with a proven integer dividend.
|
|
for (let i = -6; i <= 6; i += 3) {
|
|
for (const d of divisors) show(`${i} % ${d}`, i % d);
|
|
}
|
|
|
|
// Proven loop counters with a possibly zero divisor.
|
|
for (let i = -3; i <= 3; i++) {
|
|
for (let k = -2; k <= 2; k++) show(`${i} % ${k}`, i % k);
|
|
}
|
|
|
|
// Record fields, compound assignment, and a global accumulator.
|
|
interface Ev {
|
|
time: number;
|
|
}
|
|
const evs: Ev[] = [];
|
|
for (let i = -10; i < 30; i++) evs.push({ time: i * 1.5 });
|
|
let kept = 0;
|
|
for (const e of evs) if (e.time % 5 !== 0) kept++;
|
|
console.log("kept", kept);
|
|
let acc = 1;
|
|
for (let i = 0; i < 50; i++) {
|
|
acc = (acc * 48271 + 11) % 2147483647;
|
|
acc %= 1000003;
|
|
}
|
|
console.log("acc", acc);
|
|
let neg = -100;
|
|
neg %= 10;
|
|
show("neg %= 10", neg);
|
|
let big = 2 ** 60;
|
|
big %= 1000;
|
|
show("2^60 %= 1000", big);
|
|
const parsed = Number("-12") % Number("4");
|
|
show("parsed", parsed);
|
|
show("1/(-12 % 4)", 1 / parsed);
|