Files
scriptc/packages/compiler/src/cli/output-options.ts
T
Malte Ubl 6a9475e931 Free the address space after process.exit off the critical path (--detached-exit)
`scriptc build|run --detached-exit` opts an executable into handing the address-space teardown after process.exit to a helper process on Linux. The helper shares the address space (CLONE_VM), blocks signals, closes every descriptor but the read end of a pipe and waits for EOF on it. The kernel releases an exiting process's memory (exit_mm) before its files (exit_files), so EOF means the helper holds the last reference; it then exits and the kernel frees the pages there instead of before the parent sees the exit (30-40 ms per GB of 4 KiB pages; tsc-ts 20-110 ms). The same latency mold hides with --fork.

It is a build-time choice, not a run-time knob, because the helper keeps the executable mapped (overwriting the binary right after it exits can fail with ETXTBSY) and the memory charged to the cgroup for up to ~100 ms, and the teardown CPU time leaves the parent's rusage. The program object defines `scr_detached_exit = 1`, overriding the runtime's weak default of 0, so no runtime flavor or linker flag is added. The option is part of the early executable cache and route keys; executables only, no effect on other platforms.
2026-10-11 07:05:29 -07:00

106 lines
3.4 KiB
TypeScript

import type { CompileOutputKind } from "../compile-types.js";
import type { CliOutputKind } from "./paths.js";
export interface OutputOptionValues {
emit?: string;
emitIr: boolean;
backend?: string;
keepLlvm: boolean;
sanitize: boolean;
optimization?: string;
strip?: boolean;
windowsSubsystem?: string;
detachedExit?: boolean;
ffi?: string;
}
export type OutputOptionResolution =
| {
ok: true;
outputKind: CompileOutputKind;
cliOutputKind: CliOutputKind;
backend?: "llvm";
emitIr: boolean;
deprecateEmitIr: boolean;
}
| { ok: false; message: string };
const SOURCE_KINDS = new Set<CliOutputKind>(["ir", "llvm"]);
const NATIVE_ARTIFACT_KINDS = new Set<CliOutputKind>(["asm", "obj"]);
/** Pure compatibility/validation matrix for build/run output selection. */
export function resolveOutputOptions(
command: "build" | "run",
values: OutputOptionValues,
): OutputOptionResolution {
if (values.backend !== undefined && values.backend !== "llvm") {
return { ok: false, message: `unknown backend "${values.backend}" (supported: llvm)` };
}
const backend = values.backend as "llvm" | undefined;
const rawEmit = values.emit;
if (
rawEmit !== undefined &&
rawEmit !== "ir" &&
rawEmit !== "llvm" &&
rawEmit !== "asm" &&
rawEmit !== "obj" &&
rawEmit !== "exe"
) {
return {
ok: false,
message: `unknown emit kind "${rawEmit}" (supported: ir, llvm, asm, obj, exe)`,
};
}
const emit = (rawEmit ?? "exe") as CliOutputKind;
if (command === "run" && emit !== "exe") {
return { ok: false, message: `scriptc run requires --emit=exe` };
}
if (values.emitIr && rawEmit !== undefined && emit !== "ir" && emit !== "exe") {
return {
ok: false,
message: `--emit-ir cannot be combined with --emit=${emit}; use --emit=ir`,
};
}
if (values.emitIr && emit === "ir") {
return { ok: false, message: `--emit-ir and --emit=ir select the same output; use --emit=ir` };
}
if (values.windowsSubsystem !== undefined && emit !== "exe") {
return { ok: false, message: `--windows-subsystem is only supported with --emit=exe` };
}
if (values.strip && emit !== "exe") {
return { ok: false, message: `--strip is only supported with --emit=exe` };
}
if (values.detachedExit && emit !== "exe") {
return { ok: false, message: `--detached-exit is only supported with --emit=exe` };
}
if (emit === "ir" && backend !== undefined) {
return {
ok: false,
message: `--emit=ir cannot be combined with --backend; IR is emitted before backend selection`,
};
}
if (SOURCE_KINDS.has(emit)) {
if (!values.keepLlvm) {
return { ok: false, message: `--no-keep-llvm is only meaningful with --emit=exe` };
}
if (values.sanitize) {
return { ok: false, message: `--sanitize is only meaningful with --emit=exe` };
}
if (values.optimization !== undefined) {
return { ok: false, message: `--optimization is only meaningful with --emit=exe` };
}
}
if (NATIVE_ARTIFACT_KINDS.has(emit) && !values.keepLlvm) {
return { ok: false, message: `--no-keep-llvm is only meaningful with --emit=exe` };
}
const outputKind = emit as CompileOutputKind;
return {
ok: true,
outputKind,
cliOutputKind: emit,
...(emit === "ir" && backend === undefined ? {} : { backend: "llvm" as const }),
emitIr: values.emitIr && emit === "exe",
deprecateEmitIr: values.emitIr,
};
}