nx_main.nx source
↩ module page · 90 lines · 3403 B
1// main.nx -- NishiLang-side compiler driver.
2//
3// Pipeline: source text -> lex -> parse -> opt -> regalloc -> riscv
4// -> write-to-stdout. Stands in for main.c. Real source-file reading
5// (openat + read + grow buffer) is a follow-up; this turn drives the
6// pipeline from an in-memory source string so the whole stack of
7// nx-side modules links cleanly through a single entry point.
8//
9// Once file I/O is in syscalls.nx and string concatenation lands, this
10// driver becomes the authoritative NishiLang compiler -- the C nxc2
11// retires behind it.
12
13// nx_safety_envelope:
14// intended_use: AUTO_APPLIED -- primitive-specific tuning queued
15// sil_target: SIL1
16// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail]
17// verdict: NOT_YET_EVALUATED
18
19import "nx_syscalls.nx"
20import "nx_types.nx"
21import "nx_lex_kinds.nx"
22import "nx_ir.nx"
23import "nx_tokenizer.nx"
24import "nx_parse.nx"
25import "nx_opt.nx"
26import "nx_regalloc.nx"
27import "nx_riscv.nx"
28
29// Compile one source string end-to-end, emit asm to stdout. Each
30// function gets opt_run + regalloc + emit_function. Output format
31// matches the C-side backend's .s output closely enough that a nxasm
32// pass can assemble it to ELF.
33func compile(src: *u8) -> i64 {
34 let toks: *Tok = lex_source(src, 4096)
35 if toks == (0 as *Tok) { return 1 }
36
37 let m: *Module = parse_module(toks, 0 as *Module)
38 if m == (0 as *Module) { return 2 }
39
40 let o: *OutBuf = out_new(65536)
41 out_str(o, "# Auto-generated by nxc2.nx (NishiLang-side driver).\n")
42
43 var fi: i64 = 0
44 while fi < m.n_functions {
45 let fn_base: i64 = m.functions as i64
46 let f: *Function = (fn_base + fi * 176) as *Function
47
48 opt_run(f)
49
50 let locs_raw: *u8 = sys_mmap(f.n_values * 24 + 64)
51 let locs: *ValueLoc = locs_raw as *ValueLoc
52
53 let cs_raw: *u8 = sys_mmap(16)
54 let cs: *i64 = cs_raw as *i64
55 *cs = 0
56 let cs_fpr_raw: *u8 = sys_mmap(16)
57 let cs_fpr: *i64 = cs_fpr_raw as *i64
58 *cs_fpr = 0
59
60 let sb_raw: *u8 = sys_mmap(16)
61 let sb: *i64 = sb_raw as *i64
62 *sb = 0
63
64 regalloc_function(f, locs, cs, cs_fpr, sb)
65
66 // Real function name: ir_function_new stashed the *u8 name
67 // pointer in f.name_start so the driver can emit `name:` as
68 // the label instead of the hardcoded "main". Frame size +
69 // ra slot are still stubbed (16/8); per-function layout is
70 // a follow-up that threads the regalloc result through.
71 let fn_name_addr: i64 = f.name_start
72 let fn_name: *u8 = fn_name_addr as *u8
73 emit_function(f, locs, o, fn_name, 16, 8, *cs, *cs_fpr)
74 fi = fi + 1
75 }
76
77 sys_write(1, o.buf, o.pos)
78 return 0
79}
80
81func main() -> i64 {
82 // Real NishiLang source covering the breadth of what the
83 // nx-side front-end handles: syscall intrinsic, const folding,
84 // for-loop, function call. The resulting IR exercises every
85 // critical path (lex → parse_module → parse_primary w/ __syscall
86 // → ir_emit_syscall → opt_run → regalloc_function → rv emit w/
87 // OP_SYSCALL → ecall).
88 let src: *u8 = "const N: i64 = 5\nfunc add(a: i64, b: i64) -> i64 { return a + b }\nfunc main() -> i64 { var acc: i64 = 0\nfor i in 0..N { acc = add(acc, i) }\nreturn __syscall(93, acc, 0, 0, 0, 0, 0) }"
89 return compile(src)
90}