compiler

Unnamed Compiled Systems Language Project
git clone http://frotz.net/git/compiler.git
Log | Files | Refs

codegen-risc5-simple.c (19034B)


      1 // Copyright 2020, Brian Swetland <swetland@frotz.net>
      2 // Licensed under the Apache License, Version 2.0.
      3 
      4 // ------------------------------------------------------------------
      5 
      6 #include "risc5.h"
      7 
      8 // R0 is used for returns
      9 // R11 is for instruction combo temporary
     10 // FP, SB, SP, LR have fixed uses
     11 enum {
     12 	tmp_reg_count = 10,
     13 	tmp_reg_first = 1,  // 8,
     14 	tmp_reg_last  = 10, // 11,
     15 };
     16 
     17 bool is_tmp_reg(u32 n) {
     18 	return (n >= tmp_reg_first) && (n <= tmp_reg_last);
     19 }
     20 
     21 u32 regbits = 0;
     22 u32 regcount = 0;
     23 
     24 u32 get_reg_tmp() {
     25 	u32 n = tmp_reg_first;
     26 	while (n <= tmp_reg_last) {
     27 		if (!(regbits & (1 << n))) {
     28 			regbits |= (1 << n);
     29 			regcount++;
     30 			return n;
     31 		}
     32 		n++;
     33 	}
     34 	error("cannot allocate register");
     35 	return 0;
     36 }
     37 
     38 void put_reg(u32 r) {
     39 	if ((r < tmp_reg_first) || (r > tmp_reg_last)) {
     40 		// currently we don't strictly track r0..r7
     41 		// they are used for function calls and returns
     42 		return;
     43 	}
     44 	if (!(regbits & (1 << r))) {
     45 		error("freeing non-allocated register %u\n", r);
     46 	}
     47 	regbits = regbits & (~(1 << r));
     48 	regcount--;
     49 }
     50 
     51 bool is_reg_busy(u32 r) {
     52 	return regbits & (1 << r);
     53 }
     54 
     55 void emit(u32 ins) {
     56 	ctx.code[ctx.pc / 4] = ins;
     57 	//gen_trace_code("", ctx.pc);
     58 	ctx.pc = ctx.pc + 4;
     59 }
     60 
     61 enum {
     62 	R0 = 0, R1 = 1, R2 = 2, R3 = 3, R4 = 4, R5 = 5, R6 = 6, R7 = 7,
     63 	R8 = 9, R9 = 9, R10 = 10, R11 = 11, FP = 12, SB = 13, SP = 14, LR = 15,
     64 	TMP = 16
     65 };
     66 enum {
     67 	MOV = 0x0000, LSL = 0x0001, ASR = 0x0002, ROR = 0x0003,
     68 	AND = 0x0004, ANN = 0x0005, IOR = 0x0006, XOR = 0x0007,
     69 	ADD = 0x0008, SUB = 0x0009, MUL = 0x000A, DIV = 0x000B,
     70 	FAD = 0x000C, FSB = 0x000D, FML = 0x000E, FDV = 0x000F,
     71 	ADC = 0x2008, SBC = 0x2009, UML = 0x200A,
     72 	MHI = 0x2000,
     73 	MOV_H = 0x2000,
     74 	MOV_CC = 0x3000,
     75 	MOD = 0x001B, // fake op for plumbing (DIV+MOV_H)
     76 };
     77 
     78 void emit_op(u32 op, u32 a, u32 b, u32 c) {
     79 	if (op == MOD) {
     80 		emit_op(DIV, a, b, c);
     81 		emit_op(MOV_H, a, 0, 0);
     82 	} else {
     83 		emit((op << 16) | (a << 24) | (b << 20) | c);
     84 	}
     85 }
     86 void emit_opi_u16(u32 op, u32 a, u32 b, u32 n) {
     87 	if (op == MOD) {
     88 		emit_opi_u16(DIV, a, b, n);
     89 		emit_op(MOV_H, a, 0, 0);
     90 	} else {
     91 		emit(((0x4000 | op) << 16) | (a << 24) | (b << 20) | (n & 0xffff));
     92 	}
     93 }
     94 void emit_mov(u32 dst, u32 src) {
     95 	if (dst != src) {
     96 		emit_op(MOV, dst, 0, src);
     97 	}
     98 }
     99 
    100 // mov (load immediate) using the appropriate one or two
    101 // instruction form for the immediate argument
    102 void emit_movi(u32 a, u32 n) {
    103 	u32 m = n >> 16;
    104 	if (m == 0) {
    105 		emit_opi_u16(MOV, a, 0, n);
    106 	} else if (m == 0xFFFF) {
    107 		emit_opi_u16(MOV | 0x1000, a, 0, n);
    108 	} else {
    109 		emit_opi_u16(MHI, a, 0, m);
    110 		if ((n & 0xFFFF) != 0) {
    111 			emit_opi_u16(IOR, a, a, n);
    112 		}
    113 	}
    114 }
    115 
    116 // immediate op, using a temporary register and register op if the
    117 // immediate argument does not fit the single instruction form
    118 void emit_opi(u32 op, u32 a, u32 b, u32 n) {
    119 	u32 m = n >> 16;
    120 	if (m == 0) {
    121 		emit_opi_u16(op, a, b, n);
    122 	} else if (m == 0xFFFF) {
    123 		emit_opi_u16(op | 0x1000, a, b, n);
    124 	} else {
    125 		emit_opi_u16(MHI, R11, 0, m);
    126 		if ((n & 0xFFFF) != 0) {
    127 			emit_opi_u16(IOR, R11, R11, n);
    128 		}
    129 		emit_op(op, a, b, R11);
    130 	}
    131 }
    132 
    133 enum {
    134 	LDW = 8, LDB = 9, STW = 10, STB = 11
    135 };
    136 void emit_mem(u32 op, u32 a, u32 b, u32 off) {
    137 	emit((op << 28) | (a << 24) | (b << 20) | (off & 0xfffff));
    138 }
    139 
    140 enum {
    141 	MI = 0, EQ = 1, CS = 2,  VS = 3,  LS = 4,  LT = 5,  LE = 6,  AL = 7,
    142 	PL = 8, NE = 9, CC = 10, VC = 11, HI = 12, GE = 13, GT = 14, NV = 15,
    143 	L = 0x10,
    144 };
    145 void emit_br(u32 op, u32 r) {
    146 	emit(((0xC0 | op) << 24) | r);
    147 }
    148 void emit_bi(u32 op, u32 off) {
    149 	emit(((0xE0 | op) << 24) | (off & 0xffffff));
    150 }
    151 
    152 u8 rel_op_to_cc_tab[6] = { EQ, NE, LT, LE, GT, GE };
    153 u8 rel_op_to_inv_cc_tab[6] = { NE, EQ, GE, GT, LE, LT };
    154 u32 add_op_to_ins_tab[4] = { ADD, SUB, IOR, XOR };
    155 u32 mul_op_to_ins_tab[6] = { MUL, DIV, MOD, AND, LSL, ASR };
    156 
    157 // ------------------------------------------------------------------
    158 
    159 void gen_branch(u32 op, u32 addr) {
    160 	emit_bi(op, (addr - ctx.pc - 4) >> 2);
    161 }
    162 
    163 void gen_branch_fwd(u32 op, Fixup list) {
    164 	fixup_add_list(list, ctx.pc);
    165 	emit_bi(op, 0);
    166 }
    167 
    168 void gen_branch_sym(u32 op, Symbol sym) {
    169 	if (sym->flags & SYM_IS_PLACED) {
    170 		gen_branch(op, sym->value);
    171 	} else {
    172 		fixup_add_sym(sym, ctx.pc);
    173 		emit_bi(op, 0);
    174 	}
    175 }
    176 
    177 // patch branch instruction at addr to
    178 // branch to current pc
    179 void fixup_branch_fwd(u32 addr) {
    180 	u32 off = (ctx.pc - addr - 4) >> 2;
    181 	u32 ins = ctx.code[addr >> 2] & 0xFF000000;
    182 	ctx.code[addr >> 2] = ins | (off & 0x00FFFFFF);
    183 }
    184 
    185 void fixup_branches_fwd(Fixup fixup) {
    186 	while (fixup != nil) {
    187 		fixup_branch_fwd(fixup->addr);
    188 		fixup = fixup->next;
    189 	}
    190 }
    191 
    192 enum {
    193 	VAL_IMM    = 0x0001, // immediate value
    194 	VAL_REG    = 0x0002, // value in register
    195 	VAL_GLOBAL = 0x0010, // SB offset
    196 	VAL_PARAM  = 0x0020, // FP +offset
    197 	VAL_LOCAL  = 0x0040, // FP -offset
    198 	VAL_ADDR   = 0x0080, // address
    199 	VAL_RO     = 0x1000, // read only
    200 	VAL_LEFT   = 0x00F0, // asignable if non-ro
    201 };
    202 
    203 typedef struct ValRec ValRec;
    204 typedef struct ValRec* Val;
    205 
    206 struct ValRec {
    207 	u32 kind;
    208 	Type type;
    209 	u32 n;
    210 };
    211 
    212 u32 loop_continue = 0;
    213 Fixup loop_exit = nil;
    214 Fixup func_exit = nil;
    215 
    216 void gen_block(Ast node);
    217 u32 gen_expr(Ast node);
    218 
    219 Ast err_last_func = nil;
    220 Ast err_ast = nil;
    221 
    222 void gen_trace(str msg) {
    223 //	fprintf(stderr, "%p %p %s\n", err_last_func, err_ast, msg);
    224 }
    225 
    226 void gen_src_xref(Ast node) {
    227 	ctx.xref[ctx.pc/4] = node->srcloc;
    228 }
    229 
    230 void dump_error_ctxt() {
    231 	fprintf(stderr, "\n");
    232 	if (err_last_func) {
    233 		ast_dump(stderr, err_last_func, err_ast);
    234 	}
    235 	fprintf(stderr, "\n");
    236 }
    237 
    238 // obtain base register and offset
    239 // for the memory backing a Symbol
    240 void sym_get_loc(Symbol sym, u32* base, i32* offset) {
    241 	if (sym->kind == SYM_LOCAL) {
    242 		*base = FP;
    243 		*offset = -(4 + sym->value);
    244 	} else if (sym->kind == SYM_PARAM) {
    245 		*base = FP;
    246 		*offset = 8 + sym->value;
    247 	} else if (sym->kind == SYM_GLOBAL) {
    248 		*base = SB;
    249 		*offset = sym->value;
    250 	} else {
    251 		error("non-register-relative symbol");
    252 	}
    253 }
    254 
    255 u32 gen_addr_expr(Ast node) {
    256 	if (node->kind == AST_DEREF) {
    257 		u32 r = gen_addr_expr(node->c0);
    258 		emit_mem(LDW, r, r, 0);
    259 		return r;
    260 	} else if (node->kind == AST_INDEX) {
    261 		u32 esz = node->type->size;
    262 		u32 raddr = gen_addr_expr(node->c0);
    263 		u32 roff = gen_expr(node->c1);
    264 		if (esz > 1) {
    265 			emit_opi(MUL, roff, roff, esz);
    266 		}
    267 		emit_op(ADD, raddr, raddr, roff);
    268 		put_reg(roff);
    269 		return raddr;
    270 	} else if (node->kind == AST_FIELD) {
    271 		u32 raddr = gen_addr_expr(node->c0);
    272 		u32 off = node->c1->sym->value;
    273 		// HANDLE non-word-sized
    274 		emit_opi(ADD, raddr, raddr, off);
    275 		return raddr;
    276 	} else if (node->kind == AST_SYMBOL) {
    277 		u32 base;
    278 		i32 offset;
    279 		sym_get_loc(node->sym, &base, &offset);
    280 		u32 r = get_reg_tmp();
    281 		emit_opi(ADD, r, base, offset);
    282 		return r;
    283 	} else if (node->kind == AST_ADDROF) {
    284 		return gen_addr_expr(node->c0);
    285 	} else {
    286 		err_ast = node;
    287 		error("gen_addr_expr cannot handle %s", ast_kind[node->kind]);
    288 	}
    289 	return 0;
    290 }
    291 
    292 u32 gen_assign(Ast lhs, Ast expr) {
    293 	gen_trace("gen_assign()");
    294 
    295 	u32 raddr = gen_addr_expr(lhs);
    296 	u32 rval = gen_expr(expr);
    297 
    298 	if (lhs->type->size == 4) {
    299 		emit_mem(STW, rval, raddr, 0);
    300 	} else if (lhs->type->size == 1) {
    301 		emit_mem(STB, rval, raddr, 0);
    302 	} else {
    303 		err_ast = lhs;
    304 		error("unexpected size %u store", lhs->type->size);
    305 	}
    306 	put_reg(raddr);
    307 	return rval;
    308 }
    309 
    310 u32 reg_save(u32 base) {
    311 	u32 r = tmp_reg_first;
    312 	u32 n = 0;
    313 	while (r <= tmp_reg_last) {
    314 		if (regbits & (1 << r)) {
    315 			emit_mem(STW, r, SP, base + n);
    316 			n += 4;
    317 		}
    318 		r++;
    319 	}
    320 	u32 mask = regbits;
    321 	regbits = 0;
    322 	return mask;
    323 }
    324 
    325 void reg_restore(u32 base, u32 mask) {
    326 	if (regbits != 0) {
    327 		error("register restore collision");
    328 	}
    329 	regbits = mask;
    330 	u32 r = tmp_reg_first;
    331 	u32 n = 0;
    332 	while (r <= tmp_reg_last) {
    333 		if (regbits & (1 << r)) {
    334 			emit_mem(LDW, r, SP, base + n);
    335 			n += 4;
    336 		}
    337 		r++;
    338 	}
    339 }
    340 
    341 u32 gen_call(Ast node) {
    342 	gen_src_xref(node);
    343 	gen_trace("gen_call()");
    344 	Symbol sym = node->c0->sym;
    345 	Ast arg = node->c2;
    346 	Symbol param = sym->first;
    347 
    348 	if (sym->flags & SYM_IS_BUILTIN) {
    349 		u32 r = gen_expr(arg);
    350 		emit_movi(R11, 0xffff0000);
    351 		emit_mem(STW, r, R11, 0x100 + sym->value * 4);
    352 		put_reg(r);
    353 	} else {
    354 		u32 sizeregs = 4 * regcount;
    355 		if ((sym->type->len > 0) || (sizeregs > 0)) {
    356 			u32 sizeargs = 4 * sym->type->len;
    357 			emit_opi(SUB, SP, SP, sizeregs + sizeargs);
    358 			u32 mask = reg_save(sizeargs);
    359 			u32 n = 0;
    360 			while (arg != nil) {
    361 				u32 r;
    362 				if (param->type->kind == TYPE_POINTER) {
    363 					// XXX or ptr type?
    364 					r = gen_addr_expr(arg);
    365 				} else {
    366 					r = gen_expr(arg);
    367 				}
    368 				emit_mem(STW, r, SP, 4 * n);
    369 				put_reg(r);
    370 				arg = arg->c2;
    371 				param = param->next;
    372 				n = n + 1;
    373 			}
    374 			gen_branch_sym(AL|L, sym);
    375 			reg_restore(sizeargs, mask);
    376 			emit_opi(ADD, SP, SP, sizeregs + sizeargs);
    377 		} else {
    378 			// no args or temporaries to save
    379 			gen_branch_sym(AL|L, sym);
    380 		}
    381 	}
    382 	// return is in r0, if it exists
    383 	// stash it somewhere where it won't get stomped
    384 	// by other calls in this expr
    385 	u32 r = get_reg_tmp();
    386 	emit_mov(r, R0);
    387 	return r;
    388 }
    389 
    390 u32 gen_binop(Ast node, u32 op) {
    391 	gen_trace( "gen_binop()");
    392 	u32 left = gen_expr(node->c0);
    393 	u32 right = gen_expr(node->c1);
    394 	u32 res = get_reg_tmp();
    395 	emit_op(op, res, left, right);
    396 	put_reg(left);
    397 	put_reg(right);
    398 	return res;
    399 }
    400 
    401 u32 gen_relop(Ast node, u32 cc) {
    402 	gen_trace("gen_relop()");
    403 	u32 left = gen_expr(node->c0);
    404 	u32 right = gen_expr(node->c1);
    405 	u32 res = get_reg_tmp();
    406 	emit_movi(res, 1);
    407 	emit_op(SUB, left, left, right);
    408 	gen_branch(cc, ctx.pc + 8);
    409 	emit_movi(res, 0);
    410 	put_reg(left);
    411 	put_reg(right);
    412 	return res;
    413 }
    414 
    415 // returns address of branch to patch
    416 u32 gen_branch_if_expr_false(Ast node) {
    417 	if (ast_kind_is_relop(node->kind)) {
    418 		u32 cc = rel_op_to_inv_cc_tab[node->kind - AST_EQ];
    419 		u32 left = gen_expr(node->c0);
    420 		u32 right = gen_expr(node->c1);
    421 		emit_op(SUB, left, left, right);
    422 		put_reg(left);
    423 		put_reg(right);
    424 		u32 addr = ctx.pc;
    425 		emit_bi(cc, 0);
    426 		return addr;
    427 	} else {
    428 		i32 r = gen_expr(node);
    429 		emit_mov(R11, r); // set z flag
    430 		put_reg(r);
    431 		u32 addr = ctx.pc;
    432 		emit_bi(EQ, 0);
    433 		return addr;
    434 	}
    435 }
    436 
    437 u32 gen_short_circuit_op(Ast node, u32 cc, u32 sc) {
    438 	u32 r = gen_expr(node->c0);
    439 	emit_mov(R11, r); // set z flag
    440 	put_reg(r);
    441 	u32 l0_br_sc = ctx.pc;
    442 	emit_bi(cc, 0);
    443 	r = gen_expr(node->c1);
    444 	emit_mov(R11, r); // set z flag
    445 	put_reg(r);
    446 	u32 l1_br_sc = ctx.pc;
    447 	emit_bi(cc, 0);
    448 	r = get_reg_tmp();
    449 	emit_movi(r, !sc);
    450 	u32 l2_br_exit = ctx.pc;
    451 	emit_bi(AL, 0);
    452 	fixup_branch_fwd(l0_br_sc);
    453 	fixup_branch_fwd(l1_br_sc);
    454 	emit_movi(r, sc);
    455 	fixup_branch_fwd(l2_br_exit);
    456 	return r;
    457 }
    458 
    459 u32 gen_array_read(Ast node) {
    460 	u32 raddr = gen_addr_expr(node->c0);
    461 	u32 roff = gen_expr(node->c1);
    462 
    463 	u32 sz = node->c0->type->base->size;
    464 	if (sz > 1) {
    465 		emit_opi(MUL, roff, roff, sz);
    466 	}
    467 	emit_op(ADD, raddr, raddr, roff);
    468 	if (sz == 1) {
    469 		emit_mem(LDB, roff, raddr, 0);
    470 	} else {
    471 		emit_mem(LDW, roff, raddr, 0);
    472 	}
    473 	put_reg(raddr);
    474 	return roff;
    475 }
    476 
    477 u32 gen_struct_read(Ast node) {
    478 	u32 raddr = gen_addr_expr(node->c0);
    479 	u32 off = node->c1->sym->value;
    480 	u32 sz = node->c1->type->size;
    481 	if (sz == 1) {
    482 		emit_mem(LDB, raddr, raddr, off);
    483 	} else if (sz == 4) {
    484 		emit_mem(LDW, raddr, raddr, off);
    485 	} else {
    486 		err_ast = node;
    487 		error("unsupported field size");
    488 	}
    489 	return raddr;
    490 }
    491 
    492 u32 gen_expr(Ast node) {
    493 	err_ast = node;
    494 	gen_src_xref(node);
    495 	gen_trace("gen_expr()");
    496 	u32 kind = node->kind;
    497 	if (kind == AST_CONST) {
    498 		u32 r = get_reg_tmp();
    499 		emit_movi(r, node->ival);
    500 		return r;
    501 	} else if (kind == AST_SYMBOL) {
    502 		u32 r = get_reg_tmp();
    503 		// XXX type checking here or before
    504 		if (node->sym->kind == SYM_CONST) {
    505 			emit_movi(r, node->sym->value);
    506 		} else {
    507 			u32 base;
    508 			i32 offset;
    509 			sym_get_loc(node->sym, &base, &offset);
    510 			emit_mem(LDW, r, base, offset);
    511 		}
    512 		return r;
    513 	} else if (ast_kind_is_relop(kind)) {
    514 		return gen_relop(node, rel_op_to_cc_tab[kind - AST_EQ]);
    515 	} else if (ast_kind_is_addop(kind)) {
    516 		return gen_binop(node, add_op_to_ins_tab[kind - AST_ADD]);
    517 	} else if (ast_kind_is_mulop(kind)) {
    518 		return gen_binop(node, mul_op_to_ins_tab[kind - AST_MUL]);
    519 	} else if (kind == AST_BOOL_OR) {
    520 		return gen_short_circuit_op(node, NE, 1);
    521 	} else if (kind == AST_BOOL_AND) {
    522 		return gen_short_circuit_op(node, EQ, 0);
    523 	} else if (kind == AST_ASSIGN) {
    524 		return gen_assign(node->c0, node->c1);
    525 	} else if (kind == AST_NEG) {
    526 		u32 r = gen_expr(node->c0);
    527 		emit_movi(R11, 0);
    528 		emit_op(SUB, r, R11, r);
    529 		return r;
    530 	} else if (kind == AST_NOT) {
    531 		u32 r = gen_expr(node->c0);
    532 		emit_opi(XOR, r, r, 0xffffffff);
    533 		return r;
    534 	} else if (kind == AST_BOOL_NOT) {
    535 		u32 r = gen_expr(node->c0);
    536 		emit_opi(XOR, r, r, r);
    537 		return r;
    538 	} else if (kind == AST_CALL) {
    539 		return gen_call(node);
    540 	} else if (kind == AST_INDEX) {
    541 		return gen_array_read(node);
    542 	} else if (kind == AST_FIELD) {
    543 		return gen_struct_read(node);
    544 	} else {
    545 		error("gen_expr cannot handle %s\n", ast_kind[node->kind]);
    546 	}
    547 	return 0;
    548 }
    549 
    550 void gen_while(Ast node) {
    551 	gen_trace("gen_while()");
    552 	// save branch targets
    553 	u32 old_loop_continue = loop_continue;
    554 	Fixup old_loop_exit = loop_exit;
    555 
    556 	FixupRec list;
    557 	list.next = nil;
    558 	loop_exit = &list;
    559 
    560 	loop_continue = ctx.pc;
    561 
    562 	u32 br_false = gen_branch_if_expr_false(node->c0);
    563 
    564 	gen_block(node->c1);
    565 
    566 	gen_branch(AL, loop_continue);
    567 
    568 	// patch branches
    569 	fixup_branch_fwd(br_false);
    570 	fixup_branches_fwd(loop_exit->next);
    571 
    572 	// restore branch targets
    573 	loop_continue = old_loop_continue;
    574 	loop_exit = old_loop_exit;
    575 }
    576 
    577 void gen_if_else(Ast node) {
    578 	// fixups for jumps to the very end
    579 	FixupRec if_exit;
    580 	if_exit.next = nil;
    581 
    582 	gen_trace("gen_if()");
    583 	// IF contains one or more IFELSE nodes
    584 	node = node->c0;
    585 
    586 	// compute if expr
    587 	// branch ahead if false;
    588 	u32 l0_br_false = gen_branch_if_expr_false(node->c0);
    589 
    590 	// exec then block
    591 	gen_block(node->c1);
    592 
    593 	node = node->c2;
    594 	while (node != nil) {
    595 		// jump from end of 'then' block to end
    596 		gen_branch_fwd(AL, &if_exit);
    597 
    598 		// patch false jump (over 'then' block) to here
    599 		fixup_branch_fwd(l0_br_false);
    600 
    601 		if (node->kind == AST_IFELSE) { // ifelse ...
    602 			gen_trace("gen_ifelse()");
    603 			l0_br_false = gen_branch_if_expr_false(node->c0);
    604 			gen_block(node->c1);
    605 			node = node->c2;
    606 		} else { // else ...
    607 			gen_trace("gen_else()");
    608 			gen_block(node);
    609 
    610 			// done, patch up earlier jumps to exit
    611 			fixup_branches_fwd(if_exit.next);
    612 			return;
    613 		}
    614 	}
    615 
    616 	// patch false jump (over previous 'then' block) to here
    617 	fixup_branch_fwd(l0_br_false);
    618 
    619 	// done, patch up earlier jumps to exit
    620 	fixup_branches_fwd(if_exit.next);
    621 }
    622 
    623 void gen_block(Ast node);
    624 
    625 void gen_stmt(Ast node) {
    626 	err_ast = node;
    627 	gen_src_xref(node);
    628 	gen_trace("gen_stmt()\n");
    629 	u32 kind = node->kind;
    630 	if (kind == AST_EXPR) {
    631 		u32 r = gen_expr(node->c0);
    632 		put_reg(r);
    633 	} else if (kind == AST_IF) {
    634 		gen_if_else(node);
    635 	} else if (kind == AST_WHILE) {
    636 		gen_while(node);
    637 	} else if (kind == AST_RETURN) {
    638 		if (node->c0) {
    639 			u32 r = gen_expr(node->c0);
    640 			emit_mov(0, r);
    641 			put_reg(r);
    642 		}
    643 		gen_branch_fwd(AL, func_exit);
    644 	} else if (kind == AST_BREAK) {
    645 		gen_branch_fwd(AL, loop_exit);
    646 	} else if (kind == AST_CONTINUE) {
    647 		gen_branch(AL, loop_continue);
    648 	} else if (kind == AST_BLOCK) {
    649 		gen_block(node);
    650 	} else {
    651 		error("gen_stmt cannot handle %s\n", ast_kind[kind]);
    652 	}
    653 }
    654 
    655 void gen_block(Ast node) {
    656 	gen_trace("gen_block()\n");
    657 	gen_src_xref(node);
    658 	node = node->c2;
    659 	while (node != nil) {
    660 		gen_stmt(node);
    661 		node = node->c2;
    662 	}
    663 }
    664 
    665 // before prologue  after prologue
    666 // ---------------  --------------
    667 //       arg2             oldarg2
    668 //       arg1             oldarg1
    669 // FP -> arg0             oldarg0
    670 //       lrsave           oldlr
    671 //       fpsave           oldfp   <-+
    672 //       loc0             oldloc0   |
    673 //       loc1             oldloc1   |
    674 //       ...              ...       |
    675 //       locn             oldlocn   |
    676 //       callarg2         arg2      |
    677 //       callarg1         arg1      |
    678 // SP -> callarg0         arg0      |
    679 //                        lrsave    |
    680 //                  FP -> fpsave ---+
    681 //                        loc0
    682 //                        loc1
    683 //                        ...
    684 //                  SP -> locn
    685 
    686 void gen_func(Ast node) {
    687 	err_last_func = node;
    688 	err_ast = node;
    689 
    690 	gen_src_xref(node);
    691 	gen_trace("gen_func()\n");
    692 
    693 	// local space plus saved lr and fp
    694 	u32 x = node->sym->type->size + 8;
    695 
    696 	node->sym->value = ctx.pc;
    697 	node->sym->flags |= SYM_IS_PLACED;
    698 
    699 	// patch previous calls now that we have an entry address
    700 	fixup_branches_fwd(node->sym->fixups);
    701 	node->sym->fixups = nil; // XXX discard
    702 
    703 	// generate prologue
    704 	emit_opi(SUB, SP, SP, x);
    705 	emit_mem(STW, LR, SP, x - 4);
    706 	emit_mem(STW, FP, SP, x - 8);
    707 	emit_opi(ADD, FP, SP, x - 8);
    708 
    709 	// setup list of branches-to-epilogue
    710 	FixupRec list;
    711 	list.next = nil;
    712 	func_exit = &list;
    713 
    714 	// generate body
    715 	gen_block(node->c0);
    716 
    717 	// patch branches to epilogue
    718 	fixup_branches_fwd(list.next);
    719 
    720 	// generate epilogue
    721 	emit_mem(LDW, LR, FP, 4);
    722 	emit_mem(LDW, FP, FP, 0);
    723 	emit_opi(ADD, SP, SP, x);
    724 	emit_br(AL, LR);
    725 }
    726 
    727 void gen_program(Ast node) {
    728 	gen_trace( "gen_risc5_simple()\n");
    729 
    730 	emit_movi(SB, 0); // placeholder SB load
    731 	emit_bi(AL, 0);  // placeholder branch to init
    732 
    733 	node = node->c2;
    734 	while (node != nil) {
    735 		if (node->kind == AST_FUNC) {
    736 			gen_func(node);
    737 		}
    738 		node = node->c2;
    739 	}
    740 
    741 	err_last_func = nil;
    742 
    743 	Symbol sym = symbol_find(string_make("start", 5));
    744 	if (sym == nil) {
    745 		error("no 'start' function\n");
    746 	}
    747 	if (sym->type->kind != TYPE_FUNC) {
    748 		error("'start' is not a function\n");
    749 	}
    750 	if (sym->first != nil) {
    751 		error("'start' must have no parameters\n");
    752 	}
    753 
    754 	// patch static base load to after the last instruction
    755 	ctx.code[0] |= ctx.pc;
    756 	// patch branch-to-start
    757 	ctx.code[1] |= (sym->value - 8) >> 2;
    758 
    759 	// TODO: copy ro globals after code
    760 	// TODO: SB should neg-index into ro, pos-index into rw
    761 }
    762 
    763 // ================================================================
    764 
    765 void binary_write(const char* outname) {
    766 	int fd = open(outname, O_CREAT | O_TRUNC | O_WRONLY, 0644);
    767 	if (fd < 0) {
    768 		error("cannot open '%s' to write", outname);
    769 	}
    770 	u32 n = 0;
    771 	while (n < ctx.pc) {
    772 		if (write(fd, ctx.code + (n/4), sizeof(u32)) != sizeof(u32)) {
    773 			error("error writing '%s'", outname);
    774 		}
    775 		n += 4;
    776 	}
    777 	n = 0;
    778 	while (n < ctx.gp) {
    779 		if (write(fd, ctx.data + (n/4), sizeof(u32)) != sizeof(u32)) {
    780 			error("error writing '%s'", outname);
    781 		}
    782 		n += 4;
    783 	}
    784 	close(fd);
    785 }
    786 
    787 void listing_write(const char* listfn, const char* srcfn) {
    788 	FILE* fin = fopen(srcfn, "r");
    789 	if (fin == NULL) {
    790 		error("cannot re-read '%s'\n", srcfn);
    791 	}
    792 	FILE* fout = fopen(listfn, "w");
    793 	if (fout == NULL) {
    794 		error("cannot write '%s'\n", listfn);
    795 	}
    796 	u32 n = 0;
    797 	u32 line = 1;
    798 	char buf[1024];
    799 	while (n < ctx.pc) {
    800 		u32 ins = ctx.code[n/4];
    801 		if ((line < ctx.xref[n/4]) && fin) {
    802 			fprintf(fout, "\n");
    803 			while (line < ctx.xref[n/4]) {
    804 				if (fgets(buf, sizeof(buf), fin) == nil) {
    805 					fin = nil;
    806 					break;
    807 				}
    808 				u32 i = 0;
    809 				while (buf[i] != 0) {
    810 					if (buf[i] > ' ') {
    811 						fprintf(fout,"%s", buf);
    812 						break;
    813 					}
    814 					i++;
    815 				}
    816 				line++;
    817 			}
    818 			fprintf(fout, "\n");
    819 		}
    820 		risc5dis(n, ins, buf);
    821 		fprintf(fout, "%08x: %08x  %s\n", n, ins, buf);
    822 		n += 4;
    823 	}
    824 	n = 0;
    825 	while (n < ctx.gp) {
    826 		fprintf(fout, "%08x: %08x\n", ctx.pc + n, ctx.data[n >> 2]);
    827 		n += 4;
    828 	}
    829 	fclose(fout);
    830 	if (fin) {
    831 		fclose(fin);
    832 	}
    833 }