mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-28 06:53:42 +08:00
Stage S3 of the two-tier design: dynamic-tier functions compute with jsval, a new IR type holding a 64-bit NaN-boxed word whose numbers, booleans, undefined and null are immediates, so a number between two dynamic operators is no longer a heap node. - IR: the jsval type and js.* builtins (operators and the boundary conversions fromDyn/toDyn/fromF64/fromBool/fromStr/fromBigInt/toF64). The jsval pass rewrites dynamic-tier functions after tier assignment: operator chains, comparisons, number checks and truthiness of operator results run on jsvals; dyn locals become jsval locals when that saves boxing; dynamic functions called only from dynamic functions return a jsval. IR version 17 serializes each function's tier. - Runtime: scr_js.h and scr_js_value.c behind the dynTier feature (invariant I3); static runtime units never include scr_js.h. - Backend: number cases inline (immediate doubles and NUM cells), always inlined jsval retain/release; 32-bit targets keep boxed cells. - Interprocedural analyses see jsval conversions as the dyn forms they replace, so no fact about a static function changes. Static functions are untouched: typed programs and the self-hosted compiler's static functions compile to byte-identical LLVM. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
63 lines
1.9 KiB
TypeScript
63 lines
1.9 KiB
TypeScript
import { expect, test } from "vitest";
|
|
import { emitLlvmModule } from "../src/backend/llvm/emitter.js";
|
|
import {
|
|
DYN,
|
|
NETSOCKET_T,
|
|
STRING,
|
|
VOID,
|
|
type IrExpr,
|
|
type IrLibFn,
|
|
type IrModule,
|
|
type IrType,
|
|
} from "../src/ir/ir.js";
|
|
import { validateModule } from "../src/ir/validate.js";
|
|
|
|
const loc = { file: "llvm-lib-dispatch.ts", start: 0, end: 0 };
|
|
|
|
const str = (value: string): IrExpr => ({ kind: "strLit", value, type: STRING, loc });
|
|
const dyn = (): IrExpr => ({ kind: "dynFrom", value: str("value"), type: DYN, loc });
|
|
|
|
function libCallModule(fn: IrLibFn, args: IrExpr[], type: IrType): IrModule {
|
|
return {
|
|
irVersion: 17,
|
|
sourceFile: loc.file,
|
|
entry: "__main",
|
|
functions: [
|
|
{
|
|
name: "__main",
|
|
params: [],
|
|
returnType: VOID,
|
|
locals: [],
|
|
body: [
|
|
{
|
|
kind: "exprStmt",
|
|
expr: { kind: "libCall", fn, args, type, loc },
|
|
loc,
|
|
},
|
|
],
|
|
loc,
|
|
},
|
|
],
|
|
};
|
|
}
|
|
|
|
test.each([
|
|
["fs.mkdtempChk", "scr_fs_mkdtemp_chk", 2],
|
|
["fs.readFileChk", "scr_fs_read_file_chk", 3],
|
|
["fs.opendirChk", "scr_fs_opendir_chk", 2],
|
|
["fs.watchFileChk", "scr_fs_watch_file_chk", 2],
|
|
["fs.lchmodChk", "scr_fs_lchmod_chk", 3],
|
|
["fs.readChk", "scr_fs_read_chk", 5],
|
|
["fs.streamOptsChk", "scr_fs_stream_opts_chk", 2],
|
|
] as const)("LLVM routes %s through filesystem validation emission", (fn, sym, dynArgs) => {
|
|
const mod = libCallModule(fn, [...Array.from({ length: dynArgs }, dyn), str("fence")], VOID);
|
|
expect(validateModule(mod)).toEqual([]);
|
|
expect(emitLlvmModule(mod)).toContain(`call void @${sym}(`);
|
|
});
|
|
|
|
test("LLVM routes net.connectOptsChk through network validation emission", () => {
|
|
const mod = libCallModule("net.connectOptsChk", [dyn(), str("fence")], NETSOCKET_T);
|
|
expect(validateModule(mod)).toEqual([]);
|
|
expect(emitLlvmModule(mod)).toContain("call void @scr_net_connect_opts_chk(");
|
|
});
|