mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-11-02 07:43:42 +08:00
A counted loop whose limit is an invariant value other than the array's length, such as `j < n` after `const n = a.length`, gets the same fast copy as a loop to `a.length`: one test before the loop bounds the limit by 2^31 - 1 and by each indexed array's hole-free prefix, for every array the loop provably never resizes, and the fast copy reads those slots directly. The length proof accepts number and boolean writes to class fields, which run no user code, and borrowed element reads in a fast copy load the slot without the prefix test or the runtime call. The fast copy's loop body may hold up to 160 IR nodes.
102 lines
2.9 KiB
TypeScript
102 lines
2.9 KiB
TypeScript
// Index loops bounded by an invariant limit other than the array's own length
|
|
// (a local copy of a length, a literal, an inclusive bound), over arrays the
|
|
// loop never resizes: class instances whose number and boolean fields the loop
|
|
// writes, and number arrays that are dense, short, holey or partially filled.
|
|
|
|
class Body {
|
|
x: number;
|
|
v = 0;
|
|
hit = false;
|
|
next: Body | null = null;
|
|
|
|
constructor(x: number) {
|
|
this.x = x;
|
|
}
|
|
}
|
|
|
|
function pairs(bodies: Body[]): string {
|
|
const n = bodies.length;
|
|
for (let i = 0; i < n; i++) {
|
|
const bi = bodies[i]!;
|
|
let vi = bi.v;
|
|
for (let j = i + 1; j < n; j++) {
|
|
const bj = bodies[j]!;
|
|
const d = bi.x - bj.x;
|
|
const f = 1 / (d * d + 1);
|
|
vi -= d * f;
|
|
bj.v += d * f;
|
|
bj.hit = f > 0.1;
|
|
}
|
|
bi.v = vi;
|
|
}
|
|
return bodies.map((b) => `${b.v.toFixed(6)}${b.hit ? "*" : ""}`).join(" ");
|
|
}
|
|
|
|
function firstThree(bodies: Body[]): number {
|
|
let s = 0;
|
|
for (let j = 0; j < 3; j++) s += bodies[j]!.x;
|
|
return s;
|
|
}
|
|
|
|
function inclusive(bodies: Body[]): number {
|
|
const last = bodies.length - 1;
|
|
let s = 0;
|
|
for (let j = 1; j <= last; j++) s += bodies[j]!.x * j;
|
|
return s;
|
|
}
|
|
|
|
function linked(bodies: Body[]): string {
|
|
const n = bodies.length;
|
|
// A reference field write: the loop keeps the ordinary copy.
|
|
for (let j = 0; j < n - 1; j++) bodies[j]!.next = bodies[j + 1]!;
|
|
let count = 0;
|
|
for (let b: Body | null = bodies[0]!; b !== null; b = b.next) count++;
|
|
return `${count}`;
|
|
}
|
|
|
|
function sumTo(a: number[], limit: number): string {
|
|
const n = limit;
|
|
let s = 0;
|
|
let missing = 0;
|
|
for (let i = 0; i < n; i++) {
|
|
const v = a[i];
|
|
if (v === undefined) missing++;
|
|
else s += v;
|
|
}
|
|
return `${s} ${missing}`;
|
|
}
|
|
|
|
function scaleTo(a: number[], out: number[]): string {
|
|
const n = a.length;
|
|
for (let i = 0; i < n; i++) out[i] = a[i] * 2;
|
|
return `${out.length}:${out.join(",")}`;
|
|
}
|
|
|
|
function window(a: number[]): number {
|
|
let s = 0;
|
|
for (let i = 2; i < 6; i++) s += a[i] * i;
|
|
return s;
|
|
}
|
|
|
|
const bodies = [new Body(0), new Body(1.5), new Body(-2), new Body(4), new Body(0.25)];
|
|
console.log(pairs(bodies));
|
|
console.log(pairs(bodies));
|
|
console.log(firstThree(bodies), inclusive(bodies), inclusive([new Body(7)]));
|
|
console.log(linked([new Body(1), new Body(2), new Body(3)]));
|
|
|
|
const dense = [1, 2, 3, 4, 5, 6, 7, 8];
|
|
console.log(sumTo(dense, 8), sumTo(dense, 5), sumTo(dense, 0), sumTo(dense, -3));
|
|
// Limits past the length or the filled prefix take the checked loop.
|
|
console.log(sumTo(dense, 10), sumTo([], 2));
|
|
const holey = [1];
|
|
holey[3] = 4;
|
|
console.log(sumTo(holey, 4), sumTo(holey, 1));
|
|
const shortened = [1, 2, 3, 4, 5, 6];
|
|
shortened.length = 3;
|
|
shortened.length = 6;
|
|
console.log(sumTo(shortened, 6), sumTo(shortened, 3));
|
|
console.log(scaleTo(dense, new Array<number>(8).fill(0)));
|
|
console.log(scaleTo(dense, [9, 9]));
|
|
console.log(scaleTo([1, 2, 3], []));
|
|
console.log(window(dense), window([1, 2, 3, 4, 5, 6]));
|