#include #include #include #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, %s%s\n", reg0, strcmp(opr->info.str, "main") ? "_func_" : "", 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(); }