commit 47d3a9dea9ae7fda2c7edfcbf2d49786734010bf
parent 6c0d662cbed9802c4cecf322b3572db3ed833e64
Author: Brian Swetland <swetland@frotz.net>
Date: Tue, 14 Dec 2021 09:57:19 -0800
new compiler2 ir backend
- currently only generates IR from AST and displays it
- done, but unverified (no means to execute ir yet) for all but || and && codegen
- ir is riscy, three-arg form, stored as an instruction graph
- local labels are positive integers
- global labels are negative integers
- virtual registers start at -2 and continue to MININT
- physical registers start at 0
- register -1 is REG_NONE, for void, error conditions, etc
- virtual registers print as %0 %1 ... (percent + positive integer)
- physical registers print as $r0 $r1 ... $sp $lr (dollar + arch name)
Diffstat:
M | Makefile | | | 2 | +- |
A | src/codegen-ir.c | | | 712 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | src/ir.h | | | 74 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
3 files changed, 787 insertions(+), 1 deletion(-)
diff --git a/Makefile b/Makefile
@@ -1,5 +1,5 @@
-all: bin/cold bin/cast bin/fs bin/r5d bin/r5e bin/mkinstab
+all: bin/cold bin/cast bin/cir bin/fs bin/r5d bin/r5e bin/mkinstab
runtests: out/test/summary.txt
diff --git a/src/codegen-ir.c b/src/codegen-ir.c
@@ -0,0 +1,712 @@
+// Copyright 2021, Brian Swetland <swetland@frotz.net>
+// Licensed under the Apache License, Version 2.0.
+
+#include "ir.h"
+
+// architecture-specific
+
+#define REG_PHYS_COUNT 16
+
+str reg_phys_name[REG_PHYS_COUNT] = {
+ "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
+ "r8", "r9", "r10", "r11", "fp", "sb", "sp", "lr",
+};
+
+#define REG_R0 (0)
+#define REG_FP (12)
+#define REG_SB (13)
+#define REG_SP (14)
+#define REG_LR (15)
+
+// for errors, etc
+#define REG_NONE -1
+
+#define REG_VIRT_0 -2
+
+// ------------------------------------------------------------------
+
+Ast err_last_func = nil;
+Ast err_ast = nil;
+
+void dump_error_ctxt() {
+ fprintf(stderr, "\n");
+ if (err_last_func) {
+ ast_dump(stderr, err_last_func, err_ast);
+ }
+ fprintf(stderr, "\n");
+}
+
+// global labels start at -1 ...
+i32 global_next = 1;
+str global_names[1024];
+
+i32 label_get_global(str name) {
+ i32 n = 1;
+ while (n < global_next) {
+ if (global_names[n] == name) {
+ return -n;
+ }
+ n++;
+ }
+ global_next = n + 1;
+ global_names[n] = name;
+ return -n;
+}
+
+// local labels start at 0..
+// virtual registers start at 0...
+Inst ins_last = nil;
+i32 reg_next = REG_VIRT_0;
+i32 label_next = 0;
+
+i32 reg_get() {
+ i32 r = reg_next;
+ reg_next = r - 1;
+ return r;
+}
+
+i32 label_get() {
+ i32 n = label_next;
+ label_next = n + 1;
+ return n;
+}
+
+i32 inst_make(u32 op, i32 a, i32 b, i32 c) {
+ Inst ins = malloc(sizeof(InstRec));
+ ins->next = nil;
+ ins->op = op;
+ ins->a = a;
+ ins->b = b;
+ ins->c = c;
+ ins_last->next = ins;
+ ins_last = ins;
+ return a;
+}
+
+i32 inst_alu(u32 op, i32 b, i32 c) {
+ if (op > INS_XOR) error("inst_alu invalid alu op");
+ return inst_make(op, reg_get(), b, c);
+}
+i32 inst_alui(u32 op, i32 b, i32 imm) {
+ if (op > INS_XOR) error("inst_alui invalid alu op");
+ return inst_make(op | INF_C_IMM, reg_get(), b, imm);
+}
+void inst_aluix(u32 op, i32 a, i32 b, i32 imm) {
+ if (op > INS_XOR) error("inst_alui invalid alu op");
+ inst_make(op | INF_C_IMM, a, b, imm);
+}
+i32 inst_phi(i32 b, i32 c) {
+ return inst_make(INS_PHI, reg_get(), b, c);
+}
+i32 inst_movi(i32 imm) {
+ return inst_make(INS_MOV | INF_C_IMM, reg_get(), 0, imm);
+}
+i32 inst_mov(i32 c) {
+ return inst_make(INS_MOV, reg_get(), 0, c);
+}
+i32 inst_movx(i32 a, i32 c) {
+ return inst_make(INS_MOV, a, 0, c);
+}
+i32 inst_ldb(i32 b, i32 c) {
+ return inst_make(INS_LD | INF_SZ_U8, reg_get(), b, c);
+}
+i32 inst_ldw(i32 b, i32 c) {
+ return inst_make(INS_LD | INF_SZ_U32, reg_get(), b, c);
+}
+i32 inst_ldbi(i32 b, i32 imm) {
+ return inst_make(INS_LD | INF_SZ_U8 | INF_C_IMM, reg_get(), b, imm);
+}
+i32 inst_ldwi(i32 b, i32 imm) {
+ return inst_make(INS_LD | INF_SZ_U32 | INF_C_IMM, reg_get(), b, imm);
+}
+void inst_ldwix(i32 a, i32 b, i32 imm) {
+ inst_make(INS_LD | INF_SZ_U32 | INF_C_IMM, a, b, imm);
+}
+void inst_stb(i32 a, i32 b, i32 c) {
+ inst_make(INS_ST | INF_SZ_U8, a, b, c);
+}
+void inst_stw(i32 a, i32 b, i32 c) {
+ inst_make(INS_ST | INF_SZ_U32, a, b, c);
+}
+void inst_stbi(i32 a, i32 b, i32 imm) {
+ inst_make(INS_ST | INF_SZ_U8 | INF_C_IMM, a, b, imm);
+}
+void inst_stwi(i32 a, i32 b, i32 imm) {
+ inst_make(INS_ST | INF_SZ_U32 | INF_C_IMM, a, b, imm);
+}
+i32 inst_label(i32 a) {
+ return inst_make(INS_LABEL, a, 0, 0);
+}
+i32 inst_label_global(str name) {
+ return inst_label(label_get_global(name));
+}
+i32 inst_br(i32 label) {
+ return inst_make(INS_B, label, 0, 0);
+}
+i32 inst_br_cmp(u32 op, i32 label, i32 b, i32 c) {
+ if ((op < INS_BEQ) || (op > INS_BGE)) {
+ error("inst_branch_cond inavlid branch op");
+ }
+ return inst_make(op, label, b, c);
+}
+i32 inst_br_cmpi(u32 op, i32 label, i32 b, i32 imm) {
+ if ((op < INS_BEQ) || (op > INS_BGE)) {
+ error("inst_branch_condi inavlid branch op");
+ }
+ return inst_make(op | INF_C_IMM, label, b, imm);
+}
+void inst_call(i32 label) {
+ inst_make(INS_CALL, label, 0, 0);
+}
+void inst_ret(i32 a) {
+ inst_make(INS_RET, a, 0, 0);
+}
+
+u32 rel_op_to_ins_tab[6] = { INS_BEQ, INS_BNE, INS_BLT, INS_BLE, INS_BGT, INS_BGE };
+u32 add_op_to_ins_tab[4] = { INS_ADD, INS_SUB, INS_OR, INS_XOR };
+u32 mul_op_to_ins_tab[6] = { INS_MUL, INS_SDIV, INS_SREM, INS_AND, INS_LSL, INS_ASR };
+
+// current loop continue and exit labels
+i32 loop_continue = 0;
+i32 loop_exit = 0;
+// current function exit label
+i32 func_exit = 0;
+
+void gen_block(Ast node);
+i32 gen_expr(Ast node);
+
+void gen_trace(str msg) {
+// fprintf(stderr, "%p %p %s\n", err_last_func, err_ast, msg);
+}
+
+void gen_src_xref(Ast node) {
+ ctx.xref[ctx.pc/4] = node->srcloc;
+}
+
+// obtain base register and offset
+// for the memory backing a Symbol
+void sym_get_loc(Symbol sym, i32* base, i32* offset) {
+ if (sym->kind == SYM_LOCAL) {
+ *base = REG_FP;
+ *offset = -(4 + sym->value);
+ } else if (sym->kind == SYM_PARAM) {
+ *base = REG_FP;
+ *offset = 8 + sym->value;
+ } else if (sym->kind == SYM_GLOBAL) {
+ *base = REG_SB;
+ *offset = sym->value;
+ } else {
+ error("non-register-relative symbol");
+ }
+}
+
+i32 gen_addr_expr(Ast node) {
+ if (node->kind == AST_DEREF) {
+ return inst_ldwi(gen_addr_expr(node->c0), 0);
+ } else if (node->kind == AST_INDEX) {
+ i32 esz = node->type->size;
+ i32 raddr = gen_addr_expr(node->c0);
+ i32 roff = gen_expr(node->c1);
+ if (esz > 1) {
+ roff = inst_alui(INS_MUL, roff, esz);
+ }
+ return inst_ldw(raddr, roff);
+ } else if (node->kind == AST_FIELD) {
+ i32 raddr = gen_addr_expr(node->c0);
+ i32 off = node->c1->sym->value;
+ // HANDLE non-word-sized
+ return inst_alui(INS_ADD, raddr, off);
+ } else if (node->kind == AST_SYMBOL) {
+ i32 base;
+ i32 offset;
+ sym_get_loc(node->sym, &base, &offset);
+ return inst_alui(INS_ADD, base, offset);
+ } else if (node->kind == AST_ADDROF) {
+ return gen_addr_expr(node->c0);
+ } else {
+ err_ast = node;
+ error("gen_addr_expr cannot handle %s", ast_kind[node->kind]);
+ return -1;
+ }
+}
+
+i32 gen_assign(Ast lhs, Ast expr) {
+ gen_trace("gen_assign()");
+
+ i32 raddr = gen_addr_expr(lhs);
+ i32 rval = gen_expr(expr);
+
+ if (lhs->type->size == 4) {
+ inst_stwi(rval, raddr, 0);
+ return rval;
+ } else if (lhs->type->size == 1) {
+ inst_stbi(rval, raddr, 0);
+ return rval;
+ } else {
+ err_ast = lhs;
+ error("unexpected size %u store", lhs->type->size);
+ return -1;
+ }
+}
+
+i32 gen_call(Ast node) {
+ gen_src_xref(node);
+ gen_trace("gen_call()");
+ Symbol sym = node->c0->sym;
+ Ast arg = node->c2;
+ Symbol param = sym->first;
+
+ if (sym->flags & SYM_IS_BUILTIN) {
+ // store to special io range
+ i32 rval = gen_expr(arg);
+ i32 raddr = inst_movi(0xFFFF0000);
+ inst_stwi(rval, raddr, 0x100 + sym->value * 4);
+ } else {
+ u32 sizeargs = 4 * sym->type->len;
+ if (sizeargs > 0) {
+ inst_aluix(INS_SUB, REG_SP, REG_SP, sizeargs);
+ u32 n = 0;
+ while (arg != nil) {
+ u32 r;
+ if (param->type->kind == TYPE_POINTER) {
+ // XXX or ptr type?
+ r = gen_addr_expr(arg);
+ } else {
+ r = gen_expr(arg);
+ }
+ inst_stwi(r, REG_SP, 4 * n);
+ arg = arg->c2;
+ param = param->next;
+ n = n + 1;
+ }
+ inst_call(label_get_global(sym->name->text));
+ inst_aluix(INS_ADD, REG_SP, REG_SP, sizeargs);
+ } else {
+ inst_call(label_get_global(sym->name->text));
+ }
+ }
+ if (sym->type->base != ctx.type_void) {
+ // return is in phys r0
+ return inst_mov(REG_R0);
+ } else {
+ return REG_NONE;
+ }
+}
+
+i32 gen_binop(Ast node, u32 op) {
+ gen_trace("gen_binop()");
+ i32 left = gen_expr(node->c0);
+ i32 right = gen_expr(node->c1);
+ return inst_alu(op, left, right);
+}
+
+i32 gen_relop(Ast node, u32 op) {
+ gen_trace("gen_relop()");
+ i32 left = gen_expr(node->c0);
+ i32 right = gen_expr(node->c1);
+
+ i32 label = label_get();
+ i32 rtrue = inst_movi(1);
+ inst_br_cmp(op, label, left, right);
+ i32 rfalse = inst_movi(0);
+ inst_label(label);
+ return inst_phi(rtrue, rfalse);
+}
+
+i32 gen_short_circuit_op(Ast node, u32 cc, u32 sc) {
+#if 0
+ u32 r = gen_expr(node->c0);
+ emit_mov(R11, r); // set z flag
+ put_reg(r);
+ u32 l0_br_sc = ctx.pc;
+ emit_bi(cc, 0);
+ r = gen_expr(node->c1);
+ emit_mov(R11, r); // set z flag
+ put_reg(r);
+ u32 l1_br_sc = ctx.pc;
+ emit_bi(cc, 0);
+ r = get_reg_tmp();
+ emit_movi(r, !sc);
+ u32 l2_br_exit = ctx.pc;
+ emit_bi(AL, 0);
+ fixup_branch_fwd(l0_br_sc);
+ fixup_branch_fwd(l1_br_sc);
+ emit_movi(r, sc);
+ fixup_branch_fwd(l2_br_exit);
+ return r;
+#endif
+ return -1;
+}
+
+i32 gen_array_read(Ast node) {
+ i32 raddr = gen_addr_expr(node->c0);
+ i32 roff = gen_expr(node->c1);
+
+ u32 sz = node->c0->type->base->size;
+ if (sz > 1) {
+ roff = inst_alui(INS_MUL, roff, sz);
+ }
+ if (sz == 1) {
+ return inst_ldb(raddr, roff);
+ } else {
+ return inst_ldw(raddr, roff);
+ }
+}
+
+i32 gen_struct_read(Ast node) {
+ u32 raddr = gen_addr_expr(node->c0);
+ u32 off = node->c1->sym->value;
+ u32 sz = node->c1->type->size;
+ if (sz == 1) {
+ return inst_ldbi(raddr, off);
+ } else if (sz == 4) {
+ return inst_ldwi(raddr, off);
+ } else {
+ err_ast = node;
+ error("unsupported field size");
+ return -1;
+ }
+}
+
+i32 gen_expr(Ast node) {
+ err_ast = node;
+ gen_src_xref(node);
+ gen_trace("gen_expr()");
+ u32 kind = node->kind;
+ if (kind == AST_CONST) {
+ return inst_movi(node->ival);
+ } else if (kind == AST_SYMBOL) {
+ // XXX type checking here or before
+ if (node->sym->kind == SYM_CONST) {
+ return inst_movi(node->sym->value);
+ } else {
+ i32 base;
+ i32 offset;
+ //XXX size
+ sym_get_loc(node->sym, &base, &offset);
+ return inst_ldwi(base, offset);
+ }
+ } else if (ast_kind_is_relop(kind)) {
+ return gen_relop(node, rel_op_to_ins_tab[kind - AST_EQ]);
+ } else if (ast_kind_is_addop(kind)) {
+ return gen_binop(node, add_op_to_ins_tab[kind - AST_ADD]);
+ } else if (ast_kind_is_mulop(kind)) {
+ return gen_binop(node, mul_op_to_ins_tab[kind - AST_MUL]);
+ } else if (kind == AST_BOOL_OR) {
+ return gen_short_circuit_op(node, INS_BNE, 1);
+ } else if (kind == AST_BOOL_AND) {
+ return gen_short_circuit_op(node, INS_BEQ, 0);
+ } else if (kind == AST_ASSIGN) {
+ return gen_assign(node->c0, node->c1);
+ } else if (kind == AST_NEG) {
+ i32 r = gen_expr(node->c0);
+ i32 z = inst_movi(0);
+ return inst_alu(INS_SUB, z, r);
+ } else if (kind == AST_NOT) {
+ i32 r = gen_expr(node->c0);
+ return inst_alui(INS_XOR, r, 0xffffffff);
+ } else if (kind == AST_BOOL_NOT) {
+ u32 r = gen_expr(node->c0);
+ return inst_alui(INS_XOR, r, r);
+ } else if (kind == AST_CALL) {
+ return gen_call(node);
+ } else if (kind == AST_INDEX) {
+ return gen_array_read(node);
+ } else if (kind == AST_FIELD) {
+ return gen_struct_read(node);
+ } else {
+ error("gen_expr cannot handle %s\n", ast_kind[node->kind]);
+ }
+ return 0;
+}
+
+void gen_while(Ast node) {
+ gen_trace("gen_while()");
+
+ // save branch targets
+ i32 old_loop_continue = loop_continue;
+ i32 old_loop_exit = loop_exit;
+
+ loop_continue = label_get();
+ loop_exit = label_get();
+
+ inst_label(loop_continue);
+
+ u32 r = gen_expr(node->c0);
+
+ // branch to exit if false
+ inst_br_cmpi(INS_BEQ, loop_exit, r, 0);
+
+ gen_block(node->c1);
+
+ inst_br(loop_continue);
+
+ // restore branch targets
+ loop_continue = old_loop_continue;
+ loop_exit = old_loop_exit;
+}
+
+void gen_if_else(Ast node) {
+ // fixups for jumps to the very end
+ FixupRec if_exit;
+ if_exit.next = nil;
+
+ gen_trace("gen_if()");
+ // IF contains one or more IFELSE nodes
+ node = node->c0;
+
+ // compute if expr
+ // branch ahead if false;
+ i32 r = gen_expr(node->c0);
+
+ i32 l0_br_false = inst_br_cmpi(INS_BEQ, label_get(), r, 0);
+
+ i32 l_exit = label_get();
+
+ // exec then block
+ gen_block(node->c1);
+
+ node = node->c2;
+ while (node != nil) {
+ // jump from end of 'then' block to end
+ inst_br(l_exit);
+
+ // false jump (over 'then' block) target is here
+ inst_label(l0_br_false);
+
+ if (node->kind == AST_IFELSE) { // ifelse ...
+ gen_trace("gen_ifelse()");
+ r = gen_expr(node->c0);
+
+ i32 l0_br_false = inst_br_cmpi(INS_BEQ, label_get(), r, 0);
+
+ gen_block(node->c1);
+ node = node->c2;
+ } else { // else ...
+ gen_trace("gen_else()");
+ gen_block(node);
+
+ // done, place exit label
+ inst_label(l_exit);
+ return;
+ }
+ }
+
+ // place final false label and exit label
+ inst_label(l0_br_false);
+ inst_label(l_exit);
+}
+
+void gen_block(Ast node);
+
+void gen_stmt(Ast node) {
+ err_ast = node;
+ gen_src_xref(node);
+ gen_trace("gen_stmt()\n");
+ u32 kind = node->kind;
+ if (kind == AST_EXPR) {
+ gen_expr(node->c0);
+ } else if (kind == AST_IF) {
+ gen_if_else(node);
+ } else if (kind == AST_WHILE) {
+ gen_while(node);
+ } else if (kind == AST_RETURN) {
+ if (node->c0) {
+ inst_movx(REG_R0, gen_expr(node->c0));
+ }
+ inst_br(func_exit);
+ } else if (kind == AST_BREAK) {
+ inst_br(loop_exit);
+ } else if (kind == AST_CONTINUE) {
+ inst_br(loop_continue);
+ } else if (kind == AST_BLOCK) {
+ gen_block(node);
+ } else {
+ error("gen_stmt cannot handle %s\n", ast_kind[kind]);
+ }
+}
+
+void gen_block(Ast node) {
+ gen_trace("gen_block()\n");
+ gen_src_xref(node);
+ node = node->c2;
+ while (node != nil) {
+ gen_stmt(node);
+ node = node->c2;
+ }
+}
+
+// before prologue after prologue
+// --------------- --------------
+// arg2 oldarg2
+// arg1 oldarg1
+// FP -> arg0 oldarg0
+// lrsave oldlr
+// fpsave oldfp <-+
+// loc0 oldloc0 |
+// loc1 oldloc1 |
+// ... ... |
+// locn oldlocn |
+// callarg2 arg2 |
+// callarg1 arg1 |
+// SP -> callarg0 arg0 |
+// lrsave |
+// FP -> fpsave ---+
+// loc0
+// loc1
+// ...
+// SP -> locn
+
+Inst gen_func(Ast node) {
+ err_last_func = node;
+ err_ast = node;
+
+ gen_trace("gen_func()\n");
+ gen_src_xref(node);
+
+ InstRec head;
+
+ // setup initial state
+ ins_last = &head;
+ reg_next = REG_VIRT_0;
+ label_next = 0;
+
+ inst_label_global(node->sym->name->text);
+ Inst first = ins_last;
+
+ // local space plus saved lr and fp
+ u32 x = node->sym->type->size + 8;
+
+ // generate prologue
+ inst_label(label_get());
+#if 0
+ node->sym->value = ctx.pc;
+ node->sym->flags |= SYM_IS_PLACED;
+#endif
+ inst_aluix(INS_SUB, REG_SP, REG_SP, x);
+ inst_stwi(REG_LR, REG_SP, x - 4);
+ inst_stwi(REG_FP, REG_SP, x - 8);
+ inst_aluix(INS_ADD, REG_FP, REG_SP, x - 8);
+
+ // save for use by return, etc
+ func_exit = label_get();
+
+ // generate body
+ gen_block(node->c0);
+
+ // generate epilogue
+ inst_label(func_exit);
+ inst_ldwix(REG_LR, REG_FP, 4);
+ inst_ldwix(REG_FP, REG_FP, 0);
+ inst_aluix(INS_ADD, REG_SP, REG_SP, x);
+ inst_ret(REG_LR);
+
+ return head.next;
+}
+
+void inst_disasm(FILE* fp, Inst ins);
+
+void gen_program(Ast node) {
+ gen_trace( "gen_risc5_simple()\n");
+
+ // TODO: program prologue
+
+ node = node->c2;
+ while (node != nil) {
+ if (node->kind == AST_FUNC) {
+ Inst ins = gen_func(node);
+ fprintf(stderr,"\n------------------\n");
+ while (ins != nil) {
+ inst_disasm(stderr, ins);
+ ins = ins->next;
+ }
+ }
+ node = node->c2;
+ }
+
+ err_last_func = nil;
+
+ // TODO: program epilogue, globals
+}
+
+void prreg(FILE* fp, i32 n) {
+ if ((n >= 0) && (n < REG_PHYS_COUNT)) {
+ fprintf(fp, "$%s", reg_phys_name[n]);
+ } else if (n < -1) {
+ fprintf(fp, "%%%u", (-n) + 1);
+ } else {
+ fprintf(fp, "<ERROR>");
+ }
+}
+
+void prarg(FILE* fp, i32 n, i32 op) {
+ if (op & INF_C_IMM) {
+ fprintf(fp, "%d", n);
+ return;
+ } else {
+ prreg(fp, n);
+ }
+}
+
+void prlabel(FILE* fp, i32 n) {
+ if (n >= 0) {
+ fprintf(fp, "L%u", n);
+ } else {
+ fprintf(fp, "@%s", global_names[-n]);
+ }
+}
+
+void inst_disasm(FILE* fp, Inst ins) {
+ u32 op = ins->op & INS_OP_MASK;
+ //fprintf(fp, "(%08x %08x %08x %08x)\n", ins->op, ins->a, ins->b, ins->c);
+ if (op == INS_LABEL) {
+ prlabel(fp, ins->a);
+ fprintf(fp, ":");
+ } else if (op == INS_MOV) {
+ fprintf(fp, "\tmov ");
+ prreg(fp, ins->a);
+ fprintf(fp, ", ");
+ prarg(fp, ins->c, ins->op);
+ } else if ((op == INS_LD) || (op == INS_ST)) {
+ str sz = isz_name[(ins->op & INF_SZ_MASK) >> 8];
+ fprintf(fp, "\t%s%s ", ins_name[op], sz);
+ prreg(fp, ins->a);
+ fprintf(fp, ", [");
+ prreg(fp, ins->b);
+ fprintf(fp, ", ");
+ prarg(fp, ins->c, ins->op);
+ fprintf(fp, "]");
+ } else if (op <= INS_PHI) {
+ fprintf(fp, "\t%s ", ins_name[op]);
+ prreg(fp, ins->a);
+ fprintf(fp, ", ");
+ prreg(fp, ins->b);
+ fprintf(fp, ", ");
+ prarg(fp, ins->c, ins->op);
+ } else if (op == INS_B) {
+ fprintf(fp, "\tb ");
+ prlabel(fp, ins->a);
+ } else if ((op >= INS_BEQ) && (op <= INS_BGE)) {
+ fprintf(fp, "\t%s ", ins_name[op]);
+ prlabel(fp, ins->a);
+ fprintf(fp, ", ");
+ prreg(fp, ins->b);
+ fprintf(fp, ", ");
+ prarg(fp, ins->c, ins->op);
+ } else if (op == INS_CALL) {
+ fprintf(stderr, "\tcall ");
+ prlabel(fp, ins->a);
+ } else if (op == INS_RET) {
+ fprintf(stderr, "\tret ");
+ prreg(fp, ins->a);
+ } else {
+ fprintf(fp, "\tinval 0x%x", ins->op);
+ }
+ fprintf(fp, "\n");
+}
+
+void binary_write(const char* outname) {
+}
+
+void listing_write(const char* listfn, const char* srcfn) {
+}
diff --git a/src/ir.h b/src/ir.h
@@ -0,0 +1,74 @@
+// Copyright 2021, Brian Swetland <swetland@frotz.net>
+// Licensed under the Apache License, Version 2.0.
+
+enum {
+ // arg c is imm, for ALU and Bcc ops
+ INF_C_IMM = 0x080,
+
+ // size flags for LD/ST ops
+ INF_SZ_U8 = 0x000,
+ INF_SZ_U16 = 0x100,
+ INF_SZ_U32 = 0x200,
+ INF_SZ_MASK = 0x300,
+};
+
+enum {
+ INS_ADD, // ADD Ra, Rb, Rc Ra = Rb + Rc
+ INS_SUB, // SUBI Ra, Rb, IMMc Ra = Rb + IMMc
+ INS_MUL, // ...
+ INS_UDIV,
+ INS_SDIV,
+ INS_UREM,
+ INS_SREM,
+
+ INS_LSL,
+ INS_LSR,
+ INS_ASR,
+ INS_AND,
+ INS_OR,
+ INS_XOR,
+
+ INS_PHI,
+
+ INS_MOV, // MOV Ra, Rc Ra = Rc
+
+ INS_LD, // LDW Ra, Rb, Rc Ra = MEM[Rb + Rc]
+ INS_ST, // STBI Ra, Rb, IMMc MEM[Rb + IMMc] = Ra
+
+ INS_LABEL, // La:
+
+ INS_B, // B La
+ INS_BEQ, // BEQ La, Ra, Rb BEQI La, Rb, IMMc
+ INS_BNE, // ...
+ INS_BLT,
+ INS_BLE,
+ INS_BGT,
+ INS_BGE,
+ INS_CALL, // CALL Global
+ INS_RET,
+
+ INS_COUNT, // total unique instructions
+
+ INS_OP_MASK = 0x7F,
+};
+
+str ins_name[INS_COUNT] = {
+ "add", "sub", "mul", "udiv", "sdiv", "urem", "srem",
+ "lsl", "lsr", "asr", "and", "or", "xor",
+ "phi", "mov", "ld", "st", "label",
+ "b", "beq", "bne", "blt", "ble", "bgt", "bge", "call", "ret",
+};
+str isz_name[4] = {
+ "b", "h", "w", "d"
+};
+
+typedef struct InstRec InstRec;
+typedef struct InstRec* Inst;
+
+struct InstRec {
+ Inst next;
+ i32 op;
+ i32 a;
+ i32 b;
+ i32 c;
+};