mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-31 07:23:06 +08:00
Keep speculative copies in typed-array loops over shared storage (#2184)
This commit is contained in:
@@ -2061,7 +2061,8 @@ function emitCallbackVersionedLoop(
|
||||
/** The copy of a versioned loop that runs when its guard fails. Its loops
|
||||
* get no speculative or int32 kind copies, which would emit each loop again
|
||||
* for a path that rarely runs. Proven integer loops, which emit one copy,
|
||||
* are kept. */
|
||||
* are kept. A typed-array version whose guard requires thread-owned
|
||||
* storage does not use it (emitVersionedLoop). */
|
||||
function emitSlowCopy(emitter: LlEmitter, s: IrStmt): void {
|
||||
const copies = emitter.loopCopiesEnabled;
|
||||
emitter.loopCopiesEnabled = false;
|
||||
@@ -2145,7 +2146,11 @@ function emitVersionedLoop(
|
||||
LOOPS.of(emitter).bytesLoopVersioning.add(s);
|
||||
try {
|
||||
B.startBlock(slow);
|
||||
emitSlowCopy(emitter, s);
|
||||
// A guard that requires thread-owned storage fails for every view of
|
||||
// shared memory, which worker programs pass routinely: that checked
|
||||
// copy keeps its loops' speculative and int32 kind copies.
|
||||
if (version.ownedReceivers.length > 0) emitter.emitStmt(s);
|
||||
else emitSlowCopy(emitter, s);
|
||||
B.br(done);
|
||||
if (version.owned.size > 0) emitter.bytesOwned = new Set([...originalOwned, ...version.owned]);
|
||||
if (version.ranges.size > 0)
|
||||
|
||||
@@ -129,3 +129,33 @@ if (isMainThread) {
|
||||
expect(order.slice(slow, fast).some((l) => l.startsWith("integer.leave"))).toBe(true);
|
||||
expect(order.slice(fast).some((l) => l.startsWith("integer.leave"))).toBe(true);
|
||||
});
|
||||
|
||||
test("the shared-storage copy of a typed-array version keeps its speculative copy", async () => {
|
||||
// In a program that can start a worker, the guard requires thread-owned
|
||||
// storage, so every view of a SharedArrayBuffer runs the checked copy.
|
||||
const llvm = await emitLlvm(`
|
||||
import { Worker, isMainThread } from "node:worker_threads";
|
||||
function trim(value: number, bits: number): number {
|
||||
return value - Math.floor(value / Math.pow(2, bits)) * Math.pow(2, bits);
|
||||
}
|
||||
function key(bytes: Uint8Array, bits: number): number {
|
||||
let k = 0;
|
||||
for (let i = 0; i < bytes.length; i++) k = trim(k, bits) * 4 + bytes[i]!;
|
||||
return k;
|
||||
}
|
||||
if (isMainThread) {
|
||||
if (process.argv[2] === "spawn") new Worker(new URL(import.meta.url));
|
||||
const shared = new Uint8Array(new SharedArrayBuffer(4));
|
||||
shared.set([1, 2, 3, 0]);
|
||||
console.log(key(shared, 6), key(new Uint8Array([3, 2, 1, 0]), 6));
|
||||
}
|
||||
`);
|
||||
const fn = /^define internal double @sc_bf_\w*key[\w.]*\([^]*?^}/m.exec(llvm)?.[0] ?? "";
|
||||
const order = labels(fn);
|
||||
const slow = order.findIndex((l) => l.startsWith("bytes.version.slow"));
|
||||
const fast = order.findIndex((l) => l.startsWith("bytes.version.fast"));
|
||||
expect(slow).toBeGreaterThanOrEqual(0);
|
||||
expect(fast).toBeGreaterThan(slow);
|
||||
expect(order.slice(slow, fast).some((l) => l.startsWith("spec.fast"))).toBe(true);
|
||||
expect(order.slice(fast).some((l) => l.startsWith("spec.fast"))).toBe(true);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user