main.nx source
↩ module page · 84 lines · 3203 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
13import "syscalls.nx"
14import "types.nx"
15import "lex_kinds.nx"
16import "ir.nx"
17import "lex.nx"
18import "parse.nx"
19import "opt.nx"
20import "regalloc.nx"
21import "riscv.nx"
22
23// Compile one source string end-to-end, emit asm to stdout. Each
24// function gets opt_run + regalloc + emit_function. Output format
25// matches the C-side backend's .s output closely enough that a nxasm
26// pass can assemble it to ELF.
27func compile(src: *u8) -> i64 {
28 let toks: *Tok = lex_source(src, 4096)
29 if toks == (0 as *Tok) { return 1 }
30
31 let m: *Module = parse_module(toks, 0 as *Module)
32 if m == (0 as *Module) { return 2 }
33
34 let o: *OutBuf = out_new(65536)
35 out_str(o, "# Auto-generated by nxc2.nx (NishiLang-side driver).\n")
36
37 var fi: i64 = 0
38 while fi < m.n_functions {
39 let fn_base: i64 = m.functions as i64
40 let f: *Function = (fn_base + fi * 176) as *Function
41
42 opt_run(f)
43
44 let locs_raw: *u8 = sys_mmap(f.n_values * 24 + 64)
45 let locs: *ValueLoc = locs_raw as *ValueLoc
46
47 let cs_raw: *u8 = sys_mmap(16)
48 let cs: *i64 = cs_raw as *i64
49 *cs = 0
50 let cs_fpr_raw: *u8 = sys_mmap(16)
51 let cs_fpr: *i64 = cs_fpr_raw as *i64
52 *cs_fpr = 0
53
54 let sb_raw: *u8 = sys_mmap(16)
55 let sb: *i64 = sb_raw as *i64
56 *sb = 0
57
58 regalloc_function(f, locs, cs, cs_fpr, sb)
59
60 // Real function name: ir_function_new stashed the *u8 name
61 // pointer in f.name_start so the driver can emit `name:` as
62 // the label instead of the hardcoded "main". Frame size +
63 // ra slot are still stubbed (16/8); per-function layout is
64 // a follow-up that threads the regalloc result through.
65 let fn_name_addr: i64 = f.name_start
66 let fn_name: *u8 = fn_name_addr as *u8
67 emit_function(f, locs, o, fn_name, 16, 8, *cs, *cs_fpr)
68 fi = fi + 1
69 }
70
71 sys_write(1, o.buf, o.pos)
72 return 0
73}
74
75func main() -> i64 {
76 // Real NishiLang source covering the breadth of what the
77 // nx-side front-end handles: syscall intrinsic, const folding,
78 // for-loop, function call. The resulting IR exercises every
79 // critical path (lex → parse_module → parse_primary w/ __syscall
80 // → ir_emit_syscall → opt_run → regalloc_function → rv emit w/
81 // OP_SYSCALL → ecall).
82 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) }"
83 return compile(src)
84}