diff options
Diffstat (limited to 'mips.c')
-rw-r--r-- | mips.c | 739 |
1 files changed, 739 insertions, 0 deletions
@@ -0,0 +1,739 @@ +#include <stdio.h> +#include <assert.h> +#include <string.h> +#include "ast.h" +#include "ssa.h" +#include "mips.h" + +int reg_v0 = 2; +int reg_v1 = 3; +int memcpy_cnt; +static int save_pos[32]; +static int used_reg[32]; +static CType_t func; + +void mips_prologue(void) { + CVList_t v; + CSList_t s; + int prev = 0; + printf(".data 0x10000000\n"); + for (v = gvars; v; v = v->next) + { + CVar_t var = v->var; + printf("\t.align 2\n"); + printf("_%s:\n", var->name); + prev += align_shift(prev); + var->start = prev; + prev += calc_size(var->type); + if (var->initr) + { + CNode *initr = var->initr->chd; + assert(var->initr->rec.subtype == INITR_NORM); + switch (initr->ext.type->type) + { + case CINT: + printf("\t.word %ld\n", initr->ext.const_val); + break; + case CCHAR: + printf("\t.byte %ld\n", initr->ext.const_val); + break; + case CPTR: + { + CType_t ref = initr->ext.type->rec.ref; + printf("\t.word "); + switch (ref->type) + { + case CFUNC: + printf("%s\n", initr->rec.strval); + break; + case CCHAR: + printf("_str_%d\n", ((CSList_t)initr->ext.const_val)->id); + break; + default: assert(0); + } + } + break; + default: assert(0); + } + } + else + printf("\t.space %d\n", calc_size(var->type)); + } + printf("\t.align 2\n"); + prev += align_shift(prev); + for (s = cstrs->next; s != cstrs; s = s->next) + { + int len = 1; + char *p = s->str; + printf("_str_%d:\n", s->id); + printf("\t.asciiz \"%s\"\n", s->str); + s->start = prev; + for (; *p != '\0'; p++) + { + len++; + if (*p == '\\') + { + switch (*(++p)) + { + case '0': p += 3; break; + case 'x': p += 2; break; + default: ; + } + } + } + prev += len; + } + /* pre-calc done */ + printf(".text\n"); +} + +#define IN_IMM(x) (-0x8000 <= (x) && (x) < 0x8000) + +void mips_global_addr(int reg, CVar_t var) { + int offset = var->start - 0x8000; + if (IN_IMM(offset)) + printf("\taddiu $%d, $gp, %d #%s\n", reg, offset, var->name); + else + printf("\tla $%d, _%s\n", reg, var->name); +} + +void mips_global(const char *l, int reg, CVar_t var) { + int offset = var->start - 0x8000; + if (IN_IMM(offset)) + printf("\t%s $%d, %d($gp) #%s\n", l, reg, offset, var->name); + else + printf("\t%s $%d, _%s\n", l, reg, var->name); +} + +void mips_load(int reg, COpr_t opr) { + CVar_t var = opr->spill->info.var; + CType_t type = opr->type; + if (opr->kind == VAR && + (type->type == CSTRUCT || + type->type == CUNION || + type->type == CARR)) + { + if (var->loc > 0) + mips_global_addr(reg, var); + else + printf("\taddiu $%d, $sp, %d\n", reg, var->start); + } + else + { + const char *l = type->type == CCHAR ? "lb" : "lw"; + if (var->loc > 0) + mips_global(l, reg, var); + else + printf("\t%s $%d, %d($sp)\t#%s\n", l, reg, var->start, var->name); + } +} + +void mips_store(int reg, COpr_t opr) { + CVar_t var = opr->spill->info.var; + CType_t type = opr->type; + const char *l = type->type == CCHAR ? "sb" : "sw"; + if (var->loc > 0) + mips_global(l, reg, var); + else if (opr->reg == -1) + printf("\t%s $%d, %d($sp)\t#%s\n", l, reg, var->start, var->name); +} + +int mips_to_reg(COpr_t opr, int reg0) { + if (opr->kind == IMM) + { + printf("\tli $%d, %d\n", reg0, opr->info.imm); + return reg0; + } + else if (opr->kind == IMMS) + { + CSList_t cstr = opr->info.cstr; + int offset = cstr->start - 0x8000; + if (IN_IMM(offset)) + printf("\taddiu $%d, $gp, %d # _str_%d\n", reg0, offset, cstr->id); + else + printf("\tla $%d, _str_%d\n", reg0, cstr->id); + return reg0; + } + else if (opr->kind == IMMF) + { + printf("\tla $%d, _func_%s\n", reg0, opr->info.str); + return reg0; + } + if (opr->reg != -1) + return opr->reg; + mips_load(reg0, opr); + return reg0; +} + +void mips_memcpy(void) { + printf("\tj _mem_cond%d\n", memcpy_cnt); + printf("_mem_loop%d:\n", memcpy_cnt); + printf("\tlw $5, 0($%d)\n", reg_v0); + printf("\tsw $5, 0($%d)\n", reg_v1); + printf("\taddiu $%d, $%d, 4\n", reg_v1, reg_v1); + printf("\taddiu $%d, $%d, 4\n", reg_v0, reg_v0); + printf("\taddiu $a0, $a0, -4\n"); + printf("_mem_cond%d:\n", memcpy_cnt); + printf("\tbnez $a0, _mem_loop%d\n", memcpy_cnt); + memcpy_cnt++; +} + +/* memcpy requires three arguments */ +#define RESERVED_CALL_SIZE 12 +void mips_space_alloc(void) { + int local_size = func->rec.func.local_size; + int arg_size = RESERVED_CALL_SIZE; + int bret_size = 0; + int tmp_size = 0; + int offset = 0; + int prev = 0; + int save_size = MAX_AVAIL_REGS * INT_SIZE; + CBlock_t p; + COList_t d; + CVar_t v; + for (d = func_ir->defs; d; d = d->next) + { + COpr_t opr = d->opr; + assert(opr->par == opr); + if (opr->kind == TMP && opr->reg == -1) + { + int t = opr->type->type; + tmp_size += align_shift(tmp_size); + opr->info.var->start = tmp_size; + if (t == CSTRUCT || t == CUNION || t == CARR) + tmp_size += PTR_SIZE; + else if (t == CVOID) + tmp_size += INT_SIZE; + else + tmp_size += calc_size(opr->type); + } + } + for (p = func_ir->entry; p; p = p->next) + { + CInst_t i, ih = p->insts; + for (i = ih->next; i != ih; i = i->next) + { + if (i->op == PUSH) + { + COpr_t arg = i->src1; + offset += align_shift(offset); + i->offset = offset; + if (arg->kind == IMM) + offset += INT_SIZE; + else if (arg->kind == IMMS) + offset += PTR_SIZE; + else if (arg->kind == IMMF) + offset += PTR_SIZE; + else + { + CType_t t = arg->type; + if (t->type == CARR) + offset += PTR_SIZE; + else + offset += calc_size(t); + } + } + else if (i->op == CALL) + { + CType_t rt = i->dest->type; + if (offset > arg_size) + arg_size = offset; + offset = 0; + if (rt->type == CSTRUCT || rt->type == CUNION) + { + bret_size += align_shift(bret_size); + i->bret = bret_size; + bret_size += calc_size(rt); + } + else i->bret = -1; + } + } + } + prev += arg_size; + prev += align_shift(prev); + /* adjust offset for local variables */ + for (v = func->rec.func.local; v; v = v->next) + v->start += prev; + prev += local_size; + prev += align_shift(prev); + { + int i; + for (i = 0; i < MAX_AVAIL_REGS; i++) + save_pos[avail_regs[i]] = prev + i * INT_SIZE; + save_pos[30] = prev + save_size; + save_size += INT_SIZE; + } + prev += save_size; + /* adjust offset for spilled temporaries */ + for (d = func_ir->defs; d; d = d->next) + { + COpr_t opr = d->opr; + assert(opr->par == opr); + if (opr->kind == TMP && opr->reg == -1) + opr->info.var->start += prev; + } + prev += tmp_size; + prev += align_shift(prev); + for (p = func_ir->entry; p; p = p->next) + { + CInst_t i, ih = p->insts; + for (i = ih->next; i != ih; i = i->next) + if (i->op == CALL && i->bret != -1) + i->bret += prev; + } + prev += bret_size; + prev += align_shift(prev); + prev += 4; /* return address */ + for (v = func->rec.func.params; v; v = v->next) + v->start += prev; /* skip the whole frame to reach args */ + func->rec.func.frame_size = prev; +} + +void mips_func_begin(void) { + int fsize = func->rec.func.frame_size; + if (fsize < 0x8000) + printf("\taddiu $sp, $sp, -%d\n", fsize); + else + { + printf("\tli $%d, %d\n", reg_v0, -fsize); + printf("\taddu $sp, $sp, $%d\n", reg_v0); + } + printf("\tsw $31, %d($sp) #%s\n", fsize - 4, func->name); +} + +void mips_func_end(void) { + int fsize = func->rec.func.frame_size; + printf("_ret_%s:\n", func->name); + printf("\tlw $31, %d($sp)\n", fsize - 4); + if (fsize < 0x8000) + printf("\taddiu $sp, $sp, %d\n", fsize); + else + { + printf("\tli $%d, %d\n", reg_v0, fsize); + printf("\taddu $sp, $sp, $%d\n", reg_v0); + } + printf("\tjr $31\n"); +} + +void mips_func(void) { + CBlock_t p; + CType_t rt; + func = func_ir->func; + rt = func->rec.func.ret; + /* int arg_cnt = 0; */ + mips_space_alloc(); + if (strcmp(func->name, "main")) + printf("_func_%s:\n",func->name); + else + printf("main:\n"); + mips_func_begin(); + for (p = func_ir->entry; p; p = p->next) + { + if (p->ref) printf("_L%d:\n", p->id + func_ir->gbbase); + CInst_t i, ih = p->insts; + const char *bop; + for (i = ih->next; i != ih; i = i->next) + { + int flag = 1, swap, rimm; + if (i->dest && i->dest->reg == -2 && i->dest->kind == TMP && i->op != CALL) + continue; + printf("# "); + cinst_print(stdout, i); + switch (i->op) + { + case LOAD: + if (i->dest->kind == VAR && + i->dest->reg > 0) /* && + i->dest->info.var->loc >= 0) */ + mips_load(i->dest->reg, i->dest); + break; + case MOVE: + { + int rs; + int rd = i->dest->reg; + CType_t type = i->dest->type; + if ((type->type == CSTRUCT || type->type == CUNION) && i->dest->kind == VAR) + { + rs = mips_to_reg(i->src1, reg_v0); + /* assert(i->dest->kind == VAR); */ + printf("\tsw $%d, 4($sp)\n", rs); + if (rd < 0) rd = reg_v0; + mips_load(rd, i->dest); + printf("\tsw $%d, 0($sp)\n", rd); + printf("\tli $%d, %d\n", reg_v0, calc_size(type)); + printf("\tsw $%d, 8($sp)\n", reg_v0); + /* don't need to save current registers because + * memcpy doesn't interfere with them */ + printf("\tjal _func_memcpy\n"); + } + else + { + if (i->dest->reg > 0 && i->src1->kind == IMM) + { + printf("\tli $%d %d\n", i->dest->reg, i->src1->info.imm); + } + else + { + rs = mips_to_reg(i->src1, reg_v0); + if (rd > 0) + { + if (rd != rs) printf("\tmove $%d $%d\n", rd, rs); + } + else + rd = rs; + } + if (i->dest->reg == -1 || + (i->dest->kind == VAR && + i->dest->info.var->loc > 0)) + mips_store(rd, i->dest); + } + } + break; + case BEQ: + case BNE: + { + int rs = mips_to_reg(i->src1, reg_v0); + int rt; + const char *b = i->op == BEQ ? "beq" : "bne"; + if (i->src2->kind == IMM) + printf("\t%s $%d, %d, _L%d\n", b, rs, i->src2->info.imm, + i->dest->info.imm); + else + { + rt = mips_to_reg(i->src2, reg_v1); + printf("\t%s $%d, $%d, _L%d\n", b, rs, rt, i->dest->info.imm); + } + } + break; + case GOTO: + printf("\tj _L%d\n", i->dest->info.imm); + break; + case ARR: + { + CType_t type = i->src1->type; + int arr = mips_to_reg(i->src1, reg_v0); + int rd; + const char *l; + if (type->type == CARR) + type = type->rec.arr.elem; + else + type = type->rec.ref; + l = type->type == CCHAR ? "lb" : "lw"; + if (i->src2->kind == IMM) + { + int index = i->src2->info.imm; + rd = i->dest->reg; + if (rd < 0) rd = reg_v1; + printf("\t%s $%d, %d($%d)\n", l, rd, index, arr); + } + else + { + int index = mips_to_reg(i->src2, reg_v1); + rd = i->dest->reg; + if (rd < 0) rd = reg_v0; + printf("\taddu $%d, $%d, $%d\n", reg_v1, arr, index); + printf("\t%s $%d, 0($%d)\n", l, rd, reg_v1); + } + if (i->dest->reg == -1 || i->dest->kind == VAR) + mips_store(rd, i->dest); + } + break; + case WARR: + { + CType_t type = i->wtype; + int arr = mips_to_reg(i->dest, reg_v1); + const char *s; + int rs; + /* + if (type->type == CARR) + type = type->rec.arr.elem; + else + type = type->rec.ref; + */ + s = type->type == CCHAR ? "sb" : "sw"; + + if (type->type == CSTRUCT || type->type == CUNION) + { + if (i->src2->kind == IMM) + printf("\taddiu $%d, $%d, %d\n", reg_v1, arr, i->src2->info.imm); + else + { + int index = mips_to_reg(i->src2, reg_v0); + printf("\taddu $%d, $%d, $%d\n", reg_v1, arr, index); + } + rs = mips_to_reg(i->src1, reg_v0); + printf("\tmove $%d, $%d\n", reg_v0, rs); + printf("\tli $a0, %d\n", calc_size(type)); + mips_memcpy(); + } + else + { + if (i->src2->kind == IMM) + { + rs = mips_to_reg(i->src1, reg_v0); + if (type->type == CSTRUCT || type->type == CUNION) + { + printf("\tmove $%d, $%d\n", reg_v0, rs); + printf("\taddiu $%d, $%d, $%d\n", reg_v1, arr, i->src2->info.imm); + printf("\tli $a0, %d\n", calc_size(type)); + mips_memcpy(); + } + else + printf("\t%s $%d, %d($%d)\n", s, rs, i->src2->info.imm, arr); + } + else + { + int index = mips_to_reg(i->src2, reg_v0); + printf("\taddu $%d, $%d, $%d\n", reg_v0, arr, index); + rs = mips_to_reg(i->src1, reg_v1); + printf("\t%s $%d, 0($%d)\n", s, rs, reg_v0); + } + } + } + break; + case PUSH: + if (!i->sysp) + { + int rs = mips_to_reg(i->src1, reg_v0); + /* + if (arg_cnt < 4) + { + printf("\tmove $%d, $%d\n", arg_cnt + 4, rs); + } + else + { + */ + CType_t type = i->src1->type; + if (type && (type->type == CSTRUCT || type->type == CUNION)) + { + printf("\taddiu $%d, $sp, %d\n", reg_v1, i->offset); + printf("\tmove $%d, $%d\n", reg_v0, rs); + printf("\tli $a0, %d\n", calc_size(type)); + mips_memcpy(); + } + else + printf("\tsw $%d, %d($sp) # push\n", rs, i->offset); + /* } + arg_cnt++; */ + } + break; + case CALL: + { + COList_t p; + int rd = i->dest->reg; + int j; + memset(used_reg, 0, sizeof used_reg); + /* NOTE: bad hack */ + if (i->prev->op == PUSH && i->prev->sysp) + { + char *fname = i->src1->info.str; + int flag = 0; + int rs = mips_to_reg(i->prev->src1, 4); + if (!strcmp(fname, "__print_int")) flag = 1; + else if (!strcmp(fname, "__print_char")) flag = 11; + else if (!strcmp(fname, "__print_string")) flag = 4; + else assert(0); + if (rs != 4) printf("\tmove $4, $%d\n", rs); + printf( "\tli $2, %d\n" + "\tsyscall\n", flag); + break; + } + if (rt->type == CSTRUCT || rt->type == CUNION) + used_reg[30] = 1; /* save $fp */ + for (p = func_ir->defs; p; p = p->next) + { + COpr_t opr = p->opr; + if (opr->reg != -1 && + (opr->kind == TMP || !(opr->info.var->loc > 0)) && + overlap_with_beg(opr, i->id)) + used_reg[opr->reg] = 1; + } + for (j = 0; j < 32; j++) + if (used_reg[j]) + printf("\tsw $%d, %d($sp) # save reg\n", j, save_pos[j]); + if (i->bret != -1) + { + if (i->dest->kind == TMP) + printf("\taddiu $fp, $sp, %d\n", i->bret); + else + mips_load(30, i->dest); + } + if (i->src1->kind == IMMF) + printf("\tjal %s%s\n", + strcmp(i->src1->info.str, "main") ? "_func_" : "", + i->src1->info.str); + else + printf("\tjalr $%d\n", mips_to_reg(i->src1, reg_v0)); + for (j = 0; j < 32; j++) + if (used_reg[j]) + printf("\tlw $%d, %d($sp) # load reg\n", j, save_pos[j]); + for (p = func_ir->defs; p; p = p->next) + { + COpr_t opr = p->opr; + if (opr->reg != -1 && + opr->kind == VAR && + opr->info.var->loc > 0 && + overlap_with_beg(opr, i->id)) + mips_load(opr->reg, opr); + } + if (rd != -2) + { + if (i->bret != -1 && i->dest->kind == VAR) + { + if (rd == -1) + rd = mips_to_reg(i->dest, reg_v1); + if (reg_v1 != rd) + printf("\tmove $%d, $%d\n", reg_v1, rd); + printf("\tli $a0, %d\n", calc_size(i->dest->type)); + mips_memcpy(); + } + else + { + if (rd != -1) + printf("\tmove $%d, $%d\n", rd, reg_v0); + if (i->dest->reg == -1 || i->dest->kind == VAR) + mips_store(reg_v0, i->dest); + } + } + } + break; + case RET: + { + if (i->src1) + { + if (rt->type == CSTRUCT || rt->type == CUNION) + { + int rs = mips_to_reg(i->src1, reg_v0); + assert(i->src1->kind == VAR || i->src1->kind == TMP); + printf("\tsw $%d, 4($sp)\n", rs); + printf("\tsw $fp, 0($sp)\n"); + printf("\tli $%d, %d\n", reg_v0, calc_size(rt)); + printf("\tsw $%d, 8($sp)\n", reg_v0); + printf("\tjal _func_memcpy\n"); + printf("\tmove $%d, $fp\n", reg_v0); + } + else + { + if (i->src1->reg != -1) + { + if (i->src1->kind == IMM) + printf("\tli $%d, %d\n", reg_v0, i->src1->info.imm); + else + printf("\tmove $%d, $%d\n", reg_v0, mips_to_reg(i->src1, reg_v1)); + } + else + { + mips_to_reg(i->src1, reg_v0); + } + } + } + printf("\tj _ret_%s\n", func->name); + } + break; + case ADDR: + { + assert(i->src1->kind == VAR || + i->src1->kind == IMMF); + int rd = i->dest->reg; + if (rd < 0) rd = reg_v0; + if (i->src1->kind == IMMF) + printf("\tla $%d, %s\n", rd, i->src1->info.str); + else + { + CVar_t var = i->src1->spill->info.var; + if (var->loc > 0) + mips_global_addr(rd, var); + else + printf("\taddiu $%d, $sp, %d\n", rd, var->start); + if (i->dest->reg == -1 || i->dest->kind == VAR) + mips_store(rd, i->dest); + } + } + break; + default: flag = 0; + } + if (flag) continue; + flag = 1; + swap = rimm = 0; + switch (i->op) + { + case MUL: bop = "mul"; rimm = 1; break; + case DIV: bop = "divu"; rimm = 1; break; + case MOD: bop = "rem"; rimm = 1; break; + case ADD: bop = "addu"; rimm = swap = 1; break; + case SUB: bop = "subu"; rimm = 1; break; + case SHL: bop = "sllv"; rimm = 1; break; + case SHR: bop = "srlv"; rimm = 1; break; + case AND: bop = "and"; rimm = swap = 1; break; + case XOR: bop = "xor"; rimm = swap = 1; break; + case OR: bop = "or"; rimm = swap = 1; break; + case NOR: bop = "nor"; rimm = 1; break; + case EQ: bop = "seq"; rimm = 1; break; + case NE: bop = "sne"; rimm = 1; break; + case LT: bop = "slt"; rimm = 1; break; + case GT: bop = "sgt"; rimm = 1; break; + case LE: bop = "sle"; rimm = 1; break; + case GE: bop = "sge"; rimm = 1; break; + default: flag = 0; + } + if (flag) + { + int rs, rt; + int rd = i->dest->reg; + if (rd < 0) rd = reg_v0; + if (rimm) + { + COpr_t t; + if (swap && i->src1->kind == IMM) + { + t = i->src1; + i->src1 = i->src2; + i->src2 = t; + } + if (i->src2->kind == IMM && IN_IMM(i->src2->info.imm)) + { + switch (i->op) + { + case ADD: bop = "addiu"; break; + case AND: bop = "andi"; break; + case XOR: bop = "xori"; break; + case OR: bop = "ori"; break; + case SHL: bop = "sll"; break; + case SHR: bop = "srl"; break; + default: ; + } + rs = mips_to_reg(i->src1, reg_v0); + printf("\t%s $%d, $%d, %d\n", + bop, rd, rs, i->src2->info.imm); + } + else rimm = 0; + } + if (!rimm) + { + rs = mips_to_reg(i->src1, reg_v0); + rt = mips_to_reg(i->src2, reg_v1); + printf("\t%s $%d, $%d, $%d\n", bop, rd, rs, rt); + } + if (i->dest->reg == -1 || i->dest->kind == VAR) + mips_store(rd, i->dest); + continue; + } + if (i->op == NEG) + { + int rs = mips_to_reg(i->src1, reg_v0); + int rd = i->dest->reg; + if (rd < 0) rd = reg_v0; + printf("\tnegu $%d, $%d\n", rd, rs); + if (i->dest->reg == -1 || i->dest->kind == VAR) + mips_store(rd, i->dest); + } + } + } + mips_func_end(); +} + +void mips_generate(void) { + mips_prologue(); + for (; func_ir; func_ir = func_ir->next) + mips_func(); +} |