Mix weak-collection hashes in 64 bits on 32-bit targets

uintptr_t is 32 bits on wasm32, where the weak-collection hashes shifted it by 33 and 56 bits. The hashes now widen keys, owners and kinds to uint64_t before mixing; 64-bit targets compute the same values as before.
This commit is contained in:
Chris Tate
2026-10-24 16:01:33 -05:00
parent 7ba3827bd4
commit 190fb8d58e
3 changed files with 40 additions and 5 deletions
+7 -4
View File
@@ -16157,16 +16157,19 @@ typedef struct { void *key; ScrWeakEntry *head; } ScrWeakKeySlot;
static SCR_TL ScrWeakKeySlot *scr_weak_keys;
static SCR_TL size_t scr_weak_keys_mask, scr_weak_keys_used;
static inline size_t scr_weak_mix(uintptr_t bits) {
/* Mixed in 64 bits on every target: uintptr_t is 32 bits on wasm32, where
* these shifts would exceed its width. */
static inline size_t scr_weak_mix(uint64_t bits) {
bits ^= bits >> 33;
bits *= 0xff51afd7ed558ccdull;
bits *= UINT64_C(0xff51afd7ed558ccd);
bits ^= bits >> 29;
return (size_t)bits;
}
static inline size_t scr_weak_entry_hash(const ScrWeakMap *owner, const void *key, unsigned kind) {
return scr_weak_mix((uintptr_t)key ^ ((uintptr_t)owner * 0x9E3779B97F4A7C15ull) ^ ((uintptr_t)kind << 56));
return scr_weak_mix((uint64_t)(uintptr_t)key ^ ((uint64_t)(uintptr_t)owner * UINT64_C(0x9E3779B97F4A7C15)) ^
((uint64_t)kind << 56));
}
static inline size_t scr_weak_key_hash(const void *key) { return scr_weak_mix((uintptr_t)key); }
static inline size_t scr_weak_key_hash(const void *key) { return scr_weak_mix((uint64_t)(uintptr_t)key); }
static void scr_weak_index_put(ScrWeakEntry *entry) {
size_t j = scr_weak_entry_hash(entry->owner, entry->key, entry->kind) & scr_weak_index_mask;
+30 -1
View File
@@ -1,4 +1,4 @@
import { execFile } from "node:child_process";
import { execFile, execFileSync } from "node:child_process";
import { mkdir } from "node:fs/promises";
import { join } from "node:path";
import { promisify } from "node:util";
@@ -85,3 +85,32 @@ test("json runtime: parser, dyn, dynCheck failure path, stringify buffer", async
].join("\n"),
);
});
function zigOnPath(): boolean {
try {
execFileSync("zig", ["version"], { stdio: "ignore" });
return true;
} catch {
return false;
}
}
// 32-bit uintptr_t: the weak-collection hashes mix in 64 bits, so no shift
// exceeds the width of its operand on wasm32.
test.skipIf(!zigOnPath())("json runtime builds for wasm32 without oversized shifts", async () => {
await execFileAsync("zig", [
"cc",
"-target",
"wasm32-wasi",
"-std=c11",
"-O2",
"-D_GNU_SOURCE",
"-D_WASI_EMULATED_SIGNAL",
"-D_WASI_EMULATED_PROCESS_CLOCKS",
"-Werror=shift-count-overflow",
"-c",
"-o",
join(testDir, "build", "scr_json.wasm32.o"),
join(testDir, "../src/scr_json.c"),
]);
});
+3
View File
@@ -313,6 +313,9 @@ describe.skipIf(!zigOnPath())("wasm32-wasi differential", () => {
// ThreadSlots blocks and pages hold 32-bit pointers.
"5727-thread-slots-basics.ts",
"5729-thread-slots-values.ts",
// Weak-collection hashes mix 32-bit pointers in 64 bits.
"4591-weak-keys-union-records.ts",
"7742-weak-collections-many-keys.ts",
])("the LLVM target runs %s byte-identically to Node", async (fixture) => {
const entry = join(repoRoot, "tests/corpus", fixture);
const outDir = await mkdtemp("/tmp/scriptc-wasi-");