code wiki / (root) / nx_nx86.nx

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}