mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-11-01 07:33:12 +08:00
Give every native function an unwind table entry (#2377)
This commit is contained in:
@@ -54,7 +54,7 @@ test.runIf(process.platform === "linux" && process.arch === "x64")(
|
||||
expect(baseline).not.toContain("sanitize_address");
|
||||
expect(await build(["--target-cpu=baseline"])).toBe(baseline);
|
||||
const v3 = await build(["--target-cpu=x86-64-v3"]);
|
||||
expect(v3).toMatch(/^attributes #0 = \{ "target-cpu"="x86-64-v3" \}$/m);
|
||||
expect(v3).toMatch(/^attributes #0 = \{ uwtable "target-cpu"="x86-64-v3" \}$/m);
|
||||
if (runsV3) expect((await execFileAsync(outPath)).stdout).toBe(expected);
|
||||
// The bootstrap's cached route builds without a level.
|
||||
expect(await build([], true)).toBe(baseline);
|
||||
|
||||
@@ -18,10 +18,10 @@ const COPY_GROUPS: ReadonlyMap<string, string> = new Map([
|
||||
["2", "4"],
|
||||
]);
|
||||
|
||||
export function cpuDispatchAttributes(asan: string): string[] {
|
||||
export function cpuDispatchAttributes(common: string): string[] {
|
||||
return [
|
||||
`attributes #3 = {${asan} "target-cpu"="x86-64-v3" }`,
|
||||
`attributes #4 = { noinline${asan} "target-cpu"="x86-64-v3" }`,
|
||||
`attributes #3 = {${common} "target-cpu"="x86-64-v3" }`,
|
||||
`attributes #4 = { noinline${common} "target-cpu"="x86-64-v3" }`,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -199,19 +199,22 @@ function readRange(dir: string, range: number): { result: ChunkResult; texts: st
|
||||
|
||||
/** The attribute groups of the emitted functions: #0 for every function,
|
||||
* #1 for coroutine bodies and #2 for out-of-line record clones. Each carries
|
||||
* the build's `--target-cpu` level, and `sanitize_address` in sanitized
|
||||
* builds. A group needs one attribute: without either, #0 holds a string
|
||||
* attribute that LLVM ignores. Dispatching builds add the x86-64-v3 copies'
|
||||
* #3 and #4 (cpu-dispatch.ts). */
|
||||
* the build's `--target-cpu` level, `sanitize_address` in sanitized builds,
|
||||
* and `uwtable` on native targets, so every function has an unwind table
|
||||
* entry, including those LLVM infers `nounwind` (profilers and debuggers
|
||||
* unwind through them). A group needs one attribute: without any, #0 holds a
|
||||
* string attribute that LLVM ignores. Dispatching builds add the x86-64-v3
|
||||
* copies' #3 and #4 (cpu-dispatch.ts). */
|
||||
function functionAttributes(emitter: LlEmitter, hasNoInlineRecordClone: boolean): string[] {
|
||||
const cpu = targetCpuAttribute(emitter.targetCpu);
|
||||
const asan = emitter.sanitizeAttributes ? " sanitize_address" : "";
|
||||
const base = `${asan}${cpu}` || ` "scriptc-function"`;
|
||||
const common = `${asan}${emitter.wasi ? "" : " uwtable"}`;
|
||||
const base = `${common}${cpu}` || ` "scriptc-function"`;
|
||||
return [
|
||||
`attributes #0 = {${base} }`,
|
||||
...(emitter.usesCoroutines ? [`attributes #1 = {${asan} presplitcoroutine${cpu} }`] : []),
|
||||
...(hasNoInlineRecordClone ? [`attributes #2 = { noinline${asan}${cpu} }`] : []),
|
||||
...(emitter.cpuDispatch ? cpuDispatchAttributes(asan) : []),
|
||||
...(emitter.usesCoroutines ? [`attributes #1 = {${common} presplitcoroutine${cpu} }`] : []),
|
||||
...(hasNoInlineRecordClone ? [`attributes #2 = { noinline${common}${cpu} }`] : []),
|
||||
...(emitter.cpuDispatch ? cpuDispatchAttributes(common) : []),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -206,7 +206,7 @@ test("a target CPU level reaches every function attribute group and nothing else
|
||||
const plain = emitLlvmModule(module);
|
||||
expect(plain).not.toContain("target-cpu");
|
||||
const v3 = emitLlvmModule(module, { targetCpu: "x86-64-v3" });
|
||||
expect(v3).toMatch(/^attributes #0 = \{ sanitize_address "target-cpu"="x86-64-v3" \}$/m);
|
||||
expect(v3).toMatch(/^attributes #0 = \{ sanitize_address uwtable "target-cpu"="x86-64-v3" \}$/m);
|
||||
expect(v3.replaceAll(' "target-cpu"="x86-64-v3"', "")).toBe(plain);
|
||||
expect(emitLlvmModule(module, { targetCpu: "baseline" })).toBe(plain);
|
||||
});
|
||||
@@ -215,13 +215,21 @@ test("only sanitized builds mark the emitted functions sanitize_address", () =>
|
||||
const module = moduleFor([call()]);
|
||||
const sanitized = emitLlvmModule(module);
|
||||
expect(emitLlvmModule(module, { sanitizeAttributes: true })).toBe(sanitized);
|
||||
expect(sanitized).toMatch(/^attributes #0 = \{ sanitize_address \}$/m);
|
||||
expect(sanitized).toMatch(/^attributes #0 = \{ sanitize_address uwtable \}$/m);
|
||||
const plain = emitLlvmModule(module, { sanitizeAttributes: false });
|
||||
expect(plain).not.toContain("sanitize_address");
|
||||
// A group needs an attribute: an ignored string attribute stands in.
|
||||
expect(plain).toMatch(/^attributes #0 = \{ "scriptc-function" \}$/m);
|
||||
expect(plain).toMatch(/^attributes #0 = \{ uwtable \}$/m);
|
||||
const v3 = emitLlvmModule(module, { sanitizeAttributes: false, targetCpu: "x86-64-v3" });
|
||||
expect(v3).toMatch(/^attributes #0 = \{ "target-cpu"="x86-64-v3" \}$/m);
|
||||
expect(v3).toMatch(/^attributes #0 = \{ uwtable "target-cpu"="x86-64-v3" \}$/m);
|
||||
});
|
||||
|
||||
test("native functions get unwind tables and wasm32-wasi functions do not", () => {
|
||||
const module = moduleFor([call()]);
|
||||
expect(emitLlvmModule(module)).toMatch(/^attributes #0 = \{ sanitize_address uwtable \}$/m);
|
||||
const wasi = emitLlvmModule(module, { wasi: true, pointerBits: 32, sanitizeAttributes: false });
|
||||
expect(wasi).not.toContain("uwtable");
|
||||
// A group needs an attribute: an ignored string attribute stands in.
|
||||
expect(wasi).toMatch(/^attributes #0 = \{ "scriptc-function" \}$/m);
|
||||
});
|
||||
|
||||
test("inline RC inlines every fast path at speed and keeps box releases as calls in release", () => {
|
||||
|
||||
@@ -404,7 +404,7 @@ test("large record clones stay outlined while small clones remain inlineable", (
|
||||
expect(small).toContain("define internal ptr @sc_rclone_small(ptr %src) #0");
|
||||
expect(small).not.toContain("attributes #2");
|
||||
expect(large).toContain("define internal ptr @sc_rclone_large(ptr %src) #2");
|
||||
expect(large).toContain("attributes #2 = { noinline sanitize_address }");
|
||||
expect(large).toContain("attributes #2 = { noinline sanitize_address uwtable }");
|
||||
});
|
||||
|
||||
test("Uint8ClampedArray stores clamp and round without a runtime call", () => {
|
||||
|
||||
@@ -119,10 +119,10 @@ test("a dispatching x86-64 build gives each program function a baseline and an x
|
||||
for (const [name] of coroutines) expect(defs.has(`${name}.v3`), name).toBe(false);
|
||||
// Functions used as values are referenced by their dispatching name.
|
||||
expect(ll).not.toMatch(/ptr @[\w.$-]+\.(v3|base)(?!\()/);
|
||||
expect(ll).toMatch(/^attributes #3 = \{ sanitize_address "target-cpu"="x86-64-v3" \}$/m);
|
||||
expect(ll).toMatch(/^attributes #3 = \{ sanitize_address uwtable "target-cpu"="x86-64-v3" \}$/m);
|
||||
// Unsanitized builds leave sanitize_address out of the copies' groups too.
|
||||
expect(emit({ ...x86, cpuDispatch: true, sanitizeAttributes: false })).toMatch(
|
||||
/^attributes #3 = \{ "target-cpu"="x86-64-v3" \}$/m,
|
||||
/^attributes #3 = \{ uwtable "target-cpu"="x86-64-v3" \}$/m,
|
||||
);
|
||||
// main sets the level before anything else runs.
|
||||
expect(defs.get("main")).toMatch(
|
||||
|
||||
@@ -56,7 +56,7 @@ console.log(sum);
|
||||
expect(definition(llvm, "sc_f_range")).not.toContain("@scr_coro_yield_f64(");
|
||||
expect(llvm).not.toContain("@scr_gen_take_out_f64(");
|
||||
expect(llvm).toContain("%ScrGenHead = type { i64, ptr, ptr, i32, %ScrGenSlot }");
|
||||
expect(llvm).toContain("attributes #1 = { presplitcoroutine }");
|
||||
expect(llvm).toContain("attributes #1 = { uwtable presplitcoroutine }");
|
||||
expect(definition(llvm, "sc_gs_range")).toContain("call ptr @scr_gen_new_stackless(");
|
||||
expect(llvm).not.toContain("@scr_gen_yield_f64(");
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user