code wiki / (root) / nx_nir.nx

nx_nir.nx source

↩ module page · 164 lines · 5812 B

1// nx_nir.nx -- NishiLang IR builder, in pure NishiLang. 2// 3// THIRD CONCRETE STEP of the nxc2 self-hosting trajectory. Consumes 4// the AST produced by nx_nparse and emits IR instructions (mirroring 5// a subset of nxc2/ir.h's Opcode enum). 6// 7// Minimum viable opcodes: 8// NX_IR_CONST_I64 (value in operand 0) 9// NX_IR_RETURN (value-id in operand 0) 10// NX_IR_ADD/SUB/MUL (operand 0 + 1 -> result) 11// 12// SSA value-ids are dense integers (0, 1, 2, ...). Each NxInstr's 13// result_id is its own slot index. Operands reference earlier slots. 14 15import "syscalls.nx" 16import "nx_nlex.nx" 17import "nx_nparse.nx" 18 19// === opcodes (subset of nxc2/ir.h) ================================= 20const NX_IR_CONST_I64: i64 = 1 21const NX_IR_RETURN: i64 = 2 22const NX_IR_ADD: i64 = 3 23const NX_IR_SUB: i64 = 4 24const NX_IR_MUL: i64 = 5 25const NX_IR_STORE_LOCAL: i64 = 6 // op0 = slot index, op1 = value-id 26const NX_IR_LOAD_LOCAL: i64 = 7 // op0 = slot index, result = loaded value 27const NX_IR_BR_IF_ZERO: i64 = 8 // op0 = value-id, op1 = label-id (jump if value is 0) 28const NX_IR_LABEL: i64 = 9 // op0 = label-id (placeholder, emits .Lid:) 29 30struct NxInstr { 31 opcode: i64, 32 op0: i64, // value-id or const value (for CONST_I64) 33 op1: i64, 34 result_id: i64, // index = position in instrs array 35} 36 37struct NxIrFunc { 38 name_start: i64, 39 name_len: i64, 40 return_ty: i64, // 0 = i64 (only one supported for now) 41 instrs: *NxInstr, 42 n_instrs: i64, 43 cap: i64, 44 n_locals: i64, // stack frame size = 8 * n_locals 45 n_labels: i64, // monotonic label-id allocator (for if/while later) 46} 47 48func nx_nir_func_new(cap: i64) -> *NxIrFunc { 49 let raw: *u8 = sys_mmap(64) 50 let f: *NxIrFunc = raw as *NxIrFunc 51 f.name_start = 0 52 f.name_len = 0 53 f.return_ty = 0 54 f.instrs = sys_mmap(cap * 32) as *NxInstr 55 f.n_instrs = 0 56 f.cap = cap 57 f.n_locals = 0 58 f.n_labels = 0 59 return f 60} 61 62func nx_nir_new_label(f: *NxIrFunc) -> i64 { 63 let id: i64 = f.n_labels 64 f.n_labels = f.n_labels + 1 65 return id 66} 67 68func nx_nir_instr_at(f: *NxIrFunc, i: i64) -> *NxInstr { 69 return (f.instrs as i64 + i * 32) as *NxInstr 70} 71 72func nx_nir_emit(f: *NxIrFunc, opcode: i64, op0: i64, op1: i64) -> i64 { 73 if f.n_instrs >= f.cap { return -1 } 74 let id: i64 = f.n_instrs 75 let ins: *NxInstr = nx_nir_instr_at(f, id) 76 ins.opcode = opcode 77 ins.op0 = op0 78 ins.op1 = op1 79 ins.result_id = id 80 f.n_instrs = f.n_instrs + 1 81 return id 82} 83 84// === AST -> IR lowering ============================================ 85 86func nx_nir_lower_expr(f: *NxIrFunc, parser: *NxNParser, ast_idx: i64) -> i64 { 87 let node: *NxAst = nx_nparse_node_at(parser, ast_idx) 88 if node.kind == NX_AST_INT { 89 return nx_nir_emit(f, NX_IR_CONST_I64, node.a, 0) 90 } 91 if node.kind == NX_AST_BINOP { 92 let lhs_id: i64 = nx_nir_lower_expr(f, parser, node.a) 93 let rhs_id: i64 = nx_nir_lower_expr(f, parser, node.b) 94 var op: i64 = NX_IR_ADD 95 if node.c == NX_BIN_SUB { op = NX_IR_SUB } 96 if node.c == NX_BIN_MUL { op = NX_IR_MUL } 97 // NB: NX_BIN_DIV currently lowered as NX_IR_MUL (no IR_DIV yet). 98 return nx_nir_emit(f, op, lhs_id, rhs_id) 99 } 100 if node.kind == NX_AST_IDENT { 101 // node.c carries the local-slot index (from parser symbol table). 102 return nx_nir_emit(f, NX_IR_LOAD_LOCAL, node.c, 0) 103 } 104 return -1 105} 106 107func nx_nir_lower_stmt(f: *NxIrFunc, parser: *NxNParser, ast_idx: i64) -> i64 { 108 let node: *NxAst = nx_nparse_node_at(parser, ast_idx) 109 if node.kind == NX_AST_RETURN { 110 if node.a < 0 { 111 return nx_nir_emit(f, NX_IR_RETURN, -1, 0) 112 } 113 let val_id: i64 = nx_nir_lower_expr(f, parser, node.a) 114 return nx_nir_emit(f, NX_IR_RETURN, val_id, 0) 115 } 116 if node.kind == NX_AST_LET { 117 // Evaluate the value expression, then emit STORE_LOCAL. 118 let val_id: i64 = nx_nir_lower_expr(f, parser, node.a) 119 return nx_nir_emit(f, NX_IR_STORE_LOCAL, node.c, val_id) 120 } 121 if node.kind == NX_AST_IF { 122 // <cond> 123 // br_if_zero cond, Lend 124 // <then-block> (inlined since lower_block is defined later) 125 // Lend: 126 let cond_id: i64 = nx_nir_lower_expr(f, parser, node.a) 127 let lend: i64 = nx_nir_new_label(f) 128 nx_nir_emit(f, NX_IR_BR_IF_ZERO, cond_id, lend) 129 let block: *NxAst = nx_nparse_node_at(parser, node.b) 130 if block.kind == NX_AST_BLOCK { 131 let bn: i64 = block.e 132 if bn >= 1 { nx_nir_lower_stmt(f, parser, block.a) } 133 if bn >= 2 { nx_nir_lower_stmt(f, parser, block.b) } 134 if bn >= 3 { nx_nir_lower_stmt(f, parser, block.c) } 135 if bn >= 4 { nx_nir_lower_stmt(f, parser, block.d) } 136 } 137 return nx_nir_emit(f, NX_IR_LABEL, lend, 0) 138 } 139 return -1 140} 141 142func nx_nir_lower_block(f: *NxIrFunc, parser: *NxNParser, ast_idx: i64) -> i64 { 143 let block: *NxAst = nx_nparse_node_at(parser, ast_idx) 144 if block.kind != NX_AST_BLOCK { return -1 } 145 let n: i64 = block.e 146 if n >= 1 { nx_nir_lower_stmt(f, parser, block.a) } 147 if n >= 2 { nx_nir_lower_stmt(f, parser, block.b) } 148 if n >= 3 { nx_nir_lower_stmt(f, parser, block.c) } 149 if n >= 4 { nx_nir_lower_stmt(f, parser, block.d) } 150 return 0 151} 152 153func nx_nir_lower_func(parser: *NxNParser, func_ast_idx: i64, cap: i64) -> *NxIrFunc { 154 let ast: *NxAst = nx_nparse_node_at(parser, func_ast_idx) 155 if ast.kind != NX_AST_FUNC { return 0 as *NxIrFunc } 156 let f: *NxIrFunc = nx_nir_func_new(cap) 157 f.name_start = ast.a 158 f.name_len = ast.b 159 f.return_ty = 0 160 // Body is at ast.d 161 nx_nir_lower_block(f, parser, ast.d) 162 f.n_locals = parser.n_locals 163 return f 164}