nx_nx86.nx source
↩ module page · 323 lines · 15740 B
1// nx_nx86.nx -- x86_64 assembly emitter from nx_nir IR, in NishiLang.
2//
3// FOURTH AND FINAL CONCRETE STEP of the nxc2 self-hosting pipeline
4// (subset). Walks the IR produced by nx_nir and emits valid AT&T-
5// syntax x86_64 assembly into a string buffer.
6//
7// Closes the loop: NishiLang source -> tokens -> AST -> IR -> asm.
8// All four steps written in NishiLang. Compiler-piece self-hosting
9// demonstrated.
10//
11// Minimum opcode support:
12// NX_IR_CONST_I64 -> movabsq $val, %rax
13// NX_IR_RETURN -> mov %rbp, %rsp; popq %rbp; ret (epilogue)
14//
15// Function emission:
16// .globl main
17// main:
18// pushq %rbp
19// movq %rsp, %rbp
20// <body>
21// <epilogue>
22//
23// Output: byte buffer with assembly text + length.
24
25import "syscalls.nx"
26import "nx_nir.nx"
27
28struct NxAsmBuf {
29 buf: *u8,
30 len: i64,
31 cap: i64,
32}
33
34func nx_asm_new(cap: i64) -> *NxAsmBuf {
35 let raw: *u8 = sys_mmap(24)
36 let a: *NxAsmBuf = raw as *NxAsmBuf
37 a.buf = sys_mmap(cap)
38 a.len = 0
39 a.cap = cap
40 return a
41}
42
43func nx_asm_putc(a: *NxAsmBuf, c: i64) -> i64 {
44 if a.len >= a.cap { return -1 }
45 a.buf[a.len] = c as u8
46 a.len = a.len + 1
47 return 0
48}
49
50func nx_asm_puts(a: *NxAsmBuf, s: *u8, n: i64) -> i64 {
51 var i: i64 = 0
52 while i < n {
53 nx_asm_putc(a, s[i] as i64)
54 i = i + 1
55 }
56 return 0
57}
58
59// Decimal-print an i64 into the buffer. Supports negative.
60func nx_asm_put_i64(a: *NxAsmBuf, v: i64) -> i64 {
61 if v == 0 {
62 nx_asm_putc(a, 48)
63 return 0
64 }
65 var x: i64 = v
66 if x < 0 {
67 nx_asm_putc(a, 45) // '-'
68 x = 0 - x
69 }
70 // Generate digits in reverse, then reverse them
71 let tmp: *u8 = sys_mmap(32)
72 var n: i64 = 0
73 while x > 0 {
74 let d: i64 = x - (x / 10) * 10
75 tmp[n] = (48 + d) as u8
76 x = x / 10
77 n = n + 1
78 }
79 var j: i64 = n - 1
80 while j >= 0 {
81 nx_asm_putc(a, tmp[j] as i64)
82 j = j - 1
83 }
84 return 0
85}
86
87// Helper: emit static string from char codes.
88func nx_asm_emit_static(a: *NxAsmBuf, b: *u8, n: i64) -> i64 {
89 return nx_asm_puts(a, b, n)
90}
91
92// === emit one IR instruction =======================================
93
94func nx_nx86_emit_instr(a: *NxAsmBuf, ins: *NxInstr) -> i64 {
95 // Labels emit at column 0; everything else gets 4-space indent.
96 if ins.opcode != NX_IR_LABEL {
97 nx_asm_putc(a, 32)
98 nx_asm_putc(a, 32)
99 nx_asm_putc(a, 32)
100 nx_asm_putc(a, 32)
101 }
102 if ins.opcode == NX_IR_CONST_I64 {
103 // movabsq $val, %rax then pushq %rax (single-stack convention)
104 nx_asm_putc(a, 109) // m
105 nx_asm_putc(a, 111) // o
106 nx_asm_putc(a, 118) // v
107 nx_asm_putc(a, 97) // a
108 nx_asm_putc(a, 98) // b
109 nx_asm_putc(a, 115) // s
110 nx_asm_putc(a, 113) // q
111 nx_asm_putc(a, 32) // space
112 nx_asm_putc(a, 36) // $
113 nx_asm_put_i64(a, ins.op0)
114 nx_asm_putc(a, 44) // ,
115 nx_asm_putc(a, 32) // space
116 nx_asm_putc(a, 37) // %
117 nx_asm_putc(a, 114) // r
118 nx_asm_putc(a, 97) // a
119 nx_asm_putc(a, 120) // x
120 nx_asm_putc(a, 10) // \n
121 // pushq %rax (stack-based eval convention)
122 nx_asm_putc(a, 32); nx_asm_putc(a, 32); nx_asm_putc(a, 32); nx_asm_putc(a, 32)
123 nx_asm_putc(a, 112); nx_asm_putc(a, 117); nx_asm_putc(a, 115); nx_asm_putc(a, 104); nx_asm_putc(a, 113) // pushq
124 nx_asm_putc(a, 32); nx_asm_putc(a, 37); nx_asm_putc(a, 114); nx_asm_putc(a, 97); nx_asm_putc(a, 120) // %rax
125 nx_asm_putc(a, 10)
126 return 0
127 }
128 if ins.opcode == NX_IR_ADD {
129 // popq %rcx; popq %rax; addq %rcx, %rax; pushq %rax
130 nx_asm_putc(a, 112); nx_asm_putc(a, 111); nx_asm_putc(a, 112); nx_asm_putc(a, 113) // popq
131 nx_asm_putc(a, 32); nx_asm_putc(a, 37); nx_asm_putc(a, 114); nx_asm_putc(a, 99); nx_asm_putc(a, 120) // %rcx
132 nx_asm_putc(a, 10)
133 nx_asm_putc(a, 32); nx_asm_putc(a, 32); nx_asm_putc(a, 32); nx_asm_putc(a, 32)
134 nx_asm_putc(a, 112); nx_asm_putc(a, 111); nx_asm_putc(a, 112); nx_asm_putc(a, 113)
135 nx_asm_putc(a, 32); nx_asm_putc(a, 37); nx_asm_putc(a, 114); nx_asm_putc(a, 97); nx_asm_putc(a, 120) // %rax
136 nx_asm_putc(a, 10)
137 nx_asm_putc(a, 32); nx_asm_putc(a, 32); nx_asm_putc(a, 32); nx_asm_putc(a, 32)
138 nx_asm_putc(a, 97); nx_asm_putc(a, 100); nx_asm_putc(a, 100); nx_asm_putc(a, 113) // addq
139 nx_asm_putc(a, 32); nx_asm_putc(a, 37); nx_asm_putc(a, 114); nx_asm_putc(a, 99); nx_asm_putc(a, 120)
140 nx_asm_putc(a, 44); nx_asm_putc(a, 32)
141 nx_asm_putc(a, 37); nx_asm_putc(a, 114); nx_asm_putc(a, 97); nx_asm_putc(a, 120)
142 nx_asm_putc(a, 10)
143 // pushq %rax
144 nx_asm_putc(a, 32); nx_asm_putc(a, 32); nx_asm_putc(a, 32); nx_asm_putc(a, 32)
145 nx_asm_putc(a, 112); nx_asm_putc(a, 117); nx_asm_putc(a, 115); nx_asm_putc(a, 104); nx_asm_putc(a, 113)
146 nx_asm_putc(a, 32); nx_asm_putc(a, 37); nx_asm_putc(a, 114); nx_asm_putc(a, 97); nx_asm_putc(a, 120)
147 nx_asm_putc(a, 10)
148 return 0
149 }
150 if ins.opcode == NX_IR_SUB {
151 // popq %rcx; popq %rax; subq %rcx, %rax; pushq %rax
152 nx_asm_putc(a, 112); nx_asm_putc(a, 111); nx_asm_putc(a, 112); nx_asm_putc(a, 113)
153 nx_asm_putc(a, 32); nx_asm_putc(a, 37); nx_asm_putc(a, 114); nx_asm_putc(a, 99); nx_asm_putc(a, 120)
154 nx_asm_putc(a, 10)
155 nx_asm_putc(a, 32); nx_asm_putc(a, 32); nx_asm_putc(a, 32); nx_asm_putc(a, 32)
156 nx_asm_putc(a, 112); nx_asm_putc(a, 111); nx_asm_putc(a, 112); nx_asm_putc(a, 113)
157 nx_asm_putc(a, 32); nx_asm_putc(a, 37); nx_asm_putc(a, 114); nx_asm_putc(a, 97); nx_asm_putc(a, 120)
158 nx_asm_putc(a, 10)
159 nx_asm_putc(a, 32); nx_asm_putc(a, 32); nx_asm_putc(a, 32); nx_asm_putc(a, 32)
160 nx_asm_putc(a, 115); nx_asm_putc(a, 117); nx_asm_putc(a, 98); nx_asm_putc(a, 113) // subq
161 nx_asm_putc(a, 32); nx_asm_putc(a, 37); nx_asm_putc(a, 114); nx_asm_putc(a, 99); nx_asm_putc(a, 120)
162 nx_asm_putc(a, 44); nx_asm_putc(a, 32)
163 nx_asm_putc(a, 37); nx_asm_putc(a, 114); nx_asm_putc(a, 97); nx_asm_putc(a, 120)
164 nx_asm_putc(a, 10)
165 nx_asm_putc(a, 32); nx_asm_putc(a, 32); nx_asm_putc(a, 32); nx_asm_putc(a, 32)
166 nx_asm_putc(a, 112); nx_asm_putc(a, 117); nx_asm_putc(a, 115); nx_asm_putc(a, 104); nx_asm_putc(a, 113)
167 nx_asm_putc(a, 32); nx_asm_putc(a, 37); nx_asm_putc(a, 114); nx_asm_putc(a, 97); nx_asm_putc(a, 120)
168 nx_asm_putc(a, 10)
169 return 0
170 }
171 if ins.opcode == NX_IR_MUL {
172 // popq %rcx; popq %rax; imulq %rcx, %rax; pushq %rax
173 nx_asm_putc(a, 112); nx_asm_putc(a, 111); nx_asm_putc(a, 112); nx_asm_putc(a, 113)
174 nx_asm_putc(a, 32); nx_asm_putc(a, 37); nx_asm_putc(a, 114); nx_asm_putc(a, 99); nx_asm_putc(a, 120)
175 nx_asm_putc(a, 10)
176 nx_asm_putc(a, 32); nx_asm_putc(a, 32); nx_asm_putc(a, 32); nx_asm_putc(a, 32)
177 nx_asm_putc(a, 112); nx_asm_putc(a, 111); nx_asm_putc(a, 112); nx_asm_putc(a, 113)
178 nx_asm_putc(a, 32); nx_asm_putc(a, 37); nx_asm_putc(a, 114); nx_asm_putc(a, 97); nx_asm_putc(a, 120)
179 nx_asm_putc(a, 10)
180 nx_asm_putc(a, 32); nx_asm_putc(a, 32); nx_asm_putc(a, 32); nx_asm_putc(a, 32)
181 nx_asm_putc(a, 105); nx_asm_putc(a, 109); nx_asm_putc(a, 117); nx_asm_putc(a, 108); nx_asm_putc(a, 113) // imulq
182 nx_asm_putc(a, 32); nx_asm_putc(a, 37); nx_asm_putc(a, 114); nx_asm_putc(a, 99); nx_asm_putc(a, 120)
183 nx_asm_putc(a, 44); nx_asm_putc(a, 32)
184 nx_asm_putc(a, 37); nx_asm_putc(a, 114); nx_asm_putc(a, 97); nx_asm_putc(a, 120)
185 nx_asm_putc(a, 10)
186 nx_asm_putc(a, 32); nx_asm_putc(a, 32); nx_asm_putc(a, 32); nx_asm_putc(a, 32)
187 nx_asm_putc(a, 112); nx_asm_putc(a, 117); nx_asm_putc(a, 115); nx_asm_putc(a, 104); nx_asm_putc(a, 113)
188 nx_asm_putc(a, 32); nx_asm_putc(a, 37); nx_asm_putc(a, 114); nx_asm_putc(a, 97); nx_asm_putc(a, 120)
189 nx_asm_putc(a, 10)
190 return 0
191 }
192 if ins.opcode == NX_IR_STORE_LOCAL {
193 // Stack top has value; pop it, write to -(8*(slot+1))(%rbp).
194 nx_asm_putc(a, 112); nx_asm_putc(a, 111); nx_asm_putc(a, 112); nx_asm_putc(a, 113) // popq
195 nx_asm_putc(a, 32); nx_asm_putc(a, 37); nx_asm_putc(a, 114); nx_asm_putc(a, 97); nx_asm_putc(a, 120)
196 nx_asm_putc(a, 10)
197 // movq %rax, -OFF(%rbp)
198 nx_asm_putc(a, 32); nx_asm_putc(a, 32); nx_asm_putc(a, 32); nx_asm_putc(a, 32)
199 nx_asm_putc(a, 109); nx_asm_putc(a, 111); nx_asm_putc(a, 118); nx_asm_putc(a, 113) // movq
200 nx_asm_putc(a, 32); nx_asm_putc(a, 37); nx_asm_putc(a, 114); nx_asm_putc(a, 97); nx_asm_putc(a, 120)
201 nx_asm_putc(a, 44); nx_asm_putc(a, 32); nx_asm_putc(a, 45) // ", -"
202 nx_asm_put_i64(a, 8 * (ins.op0 + 1))
203 nx_asm_putc(a, 40); nx_asm_putc(a, 37); nx_asm_putc(a, 114); nx_asm_putc(a, 98); nx_asm_putc(a, 112); nx_asm_putc(a, 41) // (%rbp)
204 nx_asm_putc(a, 10)
205 return 0
206 }
207 if ins.opcode == NX_IR_LOAD_LOCAL {
208 // movq -OFF(%rbp), %rax then pushq %rax
209 nx_asm_putc(a, 109); nx_asm_putc(a, 111); nx_asm_putc(a, 118); nx_asm_putc(a, 113) // movq
210 nx_asm_putc(a, 32); nx_asm_putc(a, 45) // " -"
211 nx_asm_put_i64(a, 8 * (ins.op0 + 1))
212 nx_asm_putc(a, 40); nx_asm_putc(a, 37); nx_asm_putc(a, 114); nx_asm_putc(a, 98); nx_asm_putc(a, 112); nx_asm_putc(a, 41)
213 nx_asm_putc(a, 44); nx_asm_putc(a, 32)
214 nx_asm_putc(a, 37); nx_asm_putc(a, 114); nx_asm_putc(a, 97); nx_asm_putc(a, 120)
215 nx_asm_putc(a, 10)
216 nx_asm_putc(a, 32); nx_asm_putc(a, 32); nx_asm_putc(a, 32); nx_asm_putc(a, 32)
217 nx_asm_putc(a, 112); nx_asm_putc(a, 117); nx_asm_putc(a, 115); nx_asm_putc(a, 104); nx_asm_putc(a, 113)
218 nx_asm_putc(a, 32); nx_asm_putc(a, 37); nx_asm_putc(a, 114); nx_asm_putc(a, 97); nx_asm_putc(a, 120)
219 nx_asm_putc(a, 10)
220 return 0
221 }
222 if ins.opcode == NX_IR_BR_IF_ZERO {
223 // Stack top holds the cond value. popq %rax; testq %rax,%rax; jz .Lid
224 nx_asm_putc(a, 112); nx_asm_putc(a, 111); nx_asm_putc(a, 112); nx_asm_putc(a, 113) // popq
225 nx_asm_putc(a, 32); nx_asm_putc(a, 37); nx_asm_putc(a, 114); nx_asm_putc(a, 97); nx_asm_putc(a, 120) // %rax
226 nx_asm_putc(a, 10)
227 nx_asm_putc(a, 32); nx_asm_putc(a, 32); nx_asm_putc(a, 32); nx_asm_putc(a, 32)
228 nx_asm_putc(a, 116); nx_asm_putc(a, 101); nx_asm_putc(a, 115); nx_asm_putc(a, 116); nx_asm_putc(a, 113) // testq
229 nx_asm_putc(a, 32); nx_asm_putc(a, 37); nx_asm_putc(a, 114); nx_asm_putc(a, 97); nx_asm_putc(a, 120) // %rax
230 nx_asm_putc(a, 44); nx_asm_putc(a, 32)
231 nx_asm_putc(a, 37); nx_asm_putc(a, 114); nx_asm_putc(a, 97); nx_asm_putc(a, 120)
232 nx_asm_putc(a, 10)
233 nx_asm_putc(a, 32); nx_asm_putc(a, 32); nx_asm_putc(a, 32); nx_asm_putc(a, 32)
234 nx_asm_putc(a, 106); nx_asm_putc(a, 122) // jz
235 nx_asm_putc(a, 32); nx_asm_putc(a, 46); nx_asm_putc(a, 76) // " .L"
236 nx_asm_put_i64(a, ins.op1)
237 nx_asm_putc(a, 10)
238 return 0
239 }
240 if ins.opcode == NX_IR_LABEL {
241 // Emit `.Lid:` at column 0 (no leading indent).
242 nx_asm_putc(a, 46); nx_asm_putc(a, 76) // ".L"
243 nx_asm_put_i64(a, ins.op0)
244 nx_asm_putc(a, 58) // ":"
245 nx_asm_putc(a, 10)
246 return 0
247 }
248 if ins.opcode == NX_IR_RETURN {
249 // Stack-based: value to return is on top of stack. popq %rax then epilogue.
250 nx_asm_putc(a, 112); nx_asm_putc(a, 111); nx_asm_putc(a, 112); nx_asm_putc(a, 113) // popq
251 nx_asm_putc(a, 32); nx_asm_putc(a, 37); nx_asm_putc(a, 114); nx_asm_putc(a, 97); nx_asm_putc(a, 120)
252 nx_asm_putc(a, 10)
253 // Epilogue: mov %rbp, %rsp; popq %rbp; ret
254 nx_asm_putc(a, 32); nx_asm_putc(a, 32); nx_asm_putc(a, 32); nx_asm_putc(a, 32)
255 nx_asm_putc(a, 109); nx_asm_putc(a, 111); nx_asm_putc(a, 118); nx_asm_putc(a, 113) // movq
256 nx_asm_putc(a, 32); nx_asm_putc(a, 37); nx_asm_putc(a, 114); nx_asm_putc(a, 98); nx_asm_putc(a, 112) // " %rbp"
257 nx_asm_putc(a, 44); nx_asm_putc(a, 32) // ", "
258 nx_asm_putc(a, 37); nx_asm_putc(a, 114); nx_asm_putc(a, 115); nx_asm_putc(a, 112) // %rsp
259 nx_asm_putc(a, 10)
260 // popq %rbp
261 nx_asm_putc(a, 32); nx_asm_putc(a, 32); nx_asm_putc(a, 32); nx_asm_putc(a, 32)
262 nx_asm_putc(a, 112); nx_asm_putc(a, 111); nx_asm_putc(a, 112); nx_asm_putc(a, 113) // popq
263 nx_asm_putc(a, 32); nx_asm_putc(a, 37); nx_asm_putc(a, 114); nx_asm_putc(a, 98); nx_asm_putc(a, 112) // " %rbp"
264 nx_asm_putc(a, 10)
265 // ret
266 nx_asm_putc(a, 32); nx_asm_putc(a, 32); nx_asm_putc(a, 32); nx_asm_putc(a, 32)
267 nx_asm_putc(a, 114); nx_asm_putc(a, 101); nx_asm_putc(a, 116) // ret
268 nx_asm_putc(a, 10)
269 return 0
270 }
271 return -1
272}
273
274// === emit full function ============================================
275
276func nx_nx86_emit_func(a: *NxAsmBuf, src: *u8, f: *NxIrFunc) -> i64 {
277 // .globl <name>
278 nx_asm_putc(a, 46); nx_asm_putc(a, 103); nx_asm_putc(a, 108); nx_asm_putc(a, 111); nx_asm_putc(a, 98); nx_asm_putc(a, 108) // .globl
279 nx_asm_putc(a, 32)
280 var i: i64 = 0
281 while i < f.name_len {
282 nx_asm_putc(a, src[f.name_start + i] as i64)
283 i = i + 1
284 }
285 nx_asm_putc(a, 10)
286 // <name>:
287 i = 0
288 while i < f.name_len {
289 nx_asm_putc(a, src[f.name_start + i] as i64)
290 i = i + 1
291 }
292 nx_asm_putc(a, 58) // :
293 nx_asm_putc(a, 10)
294 // Prologue: pushq %rbp; movq %rsp, %rbp
295 nx_asm_putc(a, 32); nx_asm_putc(a, 32); nx_asm_putc(a, 32); nx_asm_putc(a, 32)
296 nx_asm_putc(a, 112); nx_asm_putc(a, 117); nx_asm_putc(a, 115); nx_asm_putc(a, 104); nx_asm_putc(a, 113) // pushq
297 nx_asm_putc(a, 32); nx_asm_putc(a, 37); nx_asm_putc(a, 114); nx_asm_putc(a, 98); nx_asm_putc(a, 112) // " %rbp"
298 nx_asm_putc(a, 10)
299 nx_asm_putc(a, 32); nx_asm_putc(a, 32); nx_asm_putc(a, 32); nx_asm_putc(a, 32)
300 nx_asm_putc(a, 109); nx_asm_putc(a, 111); nx_asm_putc(a, 118); nx_asm_putc(a, 113) // movq
301 nx_asm_putc(a, 32); nx_asm_putc(a, 37); nx_asm_putc(a, 114); nx_asm_putc(a, 115); nx_asm_putc(a, 112) // " %rsp"
302 nx_asm_putc(a, 44); nx_asm_putc(a, 32) // ", "
303 nx_asm_putc(a, 37); nx_asm_putc(a, 114); nx_asm_putc(a, 98); nx_asm_putc(a, 112) // %rbp
304 nx_asm_putc(a, 10)
305 // Reserve stack frame for locals: subq $N, %rsp
306 if f.n_locals > 0 {
307 nx_asm_putc(a, 32); nx_asm_putc(a, 32); nx_asm_putc(a, 32); nx_asm_putc(a, 32)
308 nx_asm_putc(a, 115); nx_asm_putc(a, 117); nx_asm_putc(a, 98); nx_asm_putc(a, 113) // subq
309 nx_asm_putc(a, 32); nx_asm_putc(a, 36) // " $"
310 nx_asm_put_i64(a, 8 * f.n_locals)
311 nx_asm_putc(a, 44); nx_asm_putc(a, 32) // ", "
312 nx_asm_putc(a, 37); nx_asm_putc(a, 114); nx_asm_putc(a, 115); nx_asm_putc(a, 112) // %rsp
313 nx_asm_putc(a, 10)
314 }
315 // Body
316 var k: i64 = 0
317 while k < f.n_instrs {
318 let ins: *NxInstr = nx_nir_instr_at(f, k)
319 nx_nx86_emit_instr(a, ins)
320 k = k + 1
321 }
322 return 0
323}