aboutsummaryrefslogblamecommitdiff
path: root/mips.c
blob: 0da5d4aae5fc8522fdbb3260a2b2d331183057dc (plain) (tree)
1
2
3
4

                   

                   















                                    
































                                                                                          











                                             
                             




















                                                                              
                             























                                                                          
                                                      


















                                                
                                    






                                                         
                                                 



















                                                
                                          







                                               
                                           












































                                                                  







                                                             





                                             






                                                 





                         



                                         








                                                      

                                   



































                                                                            
                                                     

                                                               





                                                              



















                                                                                         
                                                     
                                                               
                                      
                               




                                                              

















                                                                                         
                                                                            





                                                  
                                                                          














































































                                                                                              


                                                          





































                                                                    
#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;

void mips_prologue() {
    CVList_t v;
    CSList_t s;
    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);
        var->start = -1;
        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 %d\n", initr->ext.const_val);
                    break;
                case CCHAR:
                    printf("\t.byte %d\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));
    }
    for (s = cstrs; s; s = s->next)
    {
        printf("_str_%d:\n", s->id);
        printf("\t.asciiz \"%s\"\n", s->str);
    }
    /* pre-calc done */
    printf(".text\n");
}

void mips_load(int reg, COpr_t opr) {
    CVar_t var = cinterv_repr(opr)->info.var;
    CType_t type = opr->type;
    if (type->type == CSTRUCT ||
        type->type == CUNION ||
        type->type == CARR)
    {
        if (var->global)
            printf("\tla $%d, _%s\n", reg, var->name);
        else
            printf("\taddi $%d, $sp, %d\n", reg, var->start);
    }
    else
    {
        const char *l = type->type == CCHAR ? "lb" : "lw";
        if (var->global)
            printf("\t%s $%d, _%s\n", l, reg, var->name);
        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 = cinterv_repr(opr)->info.var;
    CType_t type = opr->type;
    const char *l = type->type == CCHAR ? "sb" : "sw";
    /* TODO: struct */
    if (var->global)
        printf("\t%s $%d, _%s\n", l, reg, var->name);
    else
        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)
    {
        printf("\tla $%d, _str_%d\n", reg0, opr->info.cstr->id);
        return reg0;
    }
    else if (opr->kind == IMMF)
    {
        printf("\tla $%d, %s\n", reg0, opr->info.str);
        return reg0;
    }
    if (opr->reg != -1) return cinterv_repr(opr)->reg;
    mips_load(reg0, opr);
    return reg0;
}

void mips_space_alloc() {
    int local_size = func->rec.func.local_size;
    int arg_size = 0;
    int bret_size = 0;
    int tmp_size = 0;
    int offset = 0;
    int prev = 0;
    CBlock_t p;
    COList_t d;
    CVar_t v;
    for (d = defs; d; d = d->next)
    {
        COpr_t opr = d->opr;
        if (opr->kind == TMP && opr->par == opr)
        {
            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 = 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);
                }
            }
        }
    }
    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);
    /* adjust offset for spilled temporaries */
    for (d = defs; d; d = d->next)
    {
        COpr_t opr = d->opr;
        if (opr->kind == TMP && opr->par == opr)
            opr->info.var->start += prev;
    }
    prev += tmp_size;
    prev += align_shift(prev);
    for (p = 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 += 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() {
    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() {
    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_generate() {
    CBlock_t p;
    mips_space_alloc();
    if (strcmp(func->name, "main"))
        printf("_func_%s:\n",func->name);
    else
        printf("main:\n");
    mips_func_begin();
    for (p = entry; p; p = p->next)
    {
        if (p->ref) printf("_L%d:\n", p->id + gbbase);
        CInst_t i, ih = p->insts;
        const char *bop;
        for (i = ih->next; i != ih; i = i->next)
        {
            int flag = 1, swap;
            printf("# ");
            cinst_print(stdout, i);
            switch (i->op)
            {
                case LOAD:
                    if (i->dest->reg != -1)
                        mips_load(i->dest->reg, i->dest);
                    break;
                case MOVE:
                    {
                        /* TODO: struct */
                        int rs = mips_to_reg(i->src1, reg_v0);
                        int rd = i->dest->reg;
                        if (rd != -1)
                            printf("\tmove $%d $%d\n", rd, rs);
                        else
                            rd = rs;
                        if (i->dest->reg == -1 || i->dest->kind == VAR)
                            mips_store(rd, i->dest);
                    }
                    break;
                case BEQZ:
                    {
                        int rs = mips_to_reg(i->src1, reg_v0);
                        printf("\tbeqz $%d, _L%d\n", rs, i->dest->info.imm);
                    }
                    break;
                case BNEZ:
                    {
                        int rs = mips_to_reg(i->src1, reg_v0);
                        printf("\tbnez $%d, _L%d\n", rs, 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)
                        {
                            rd = i->dest->reg;
                            if (rd == -1) rd = reg_v1;
                            printf("\t%s $%d, %d($%d)\n", l, rd, i->src2->info.imm, arr);
                        }
                        else
                        {
                            int index = mips_to_reg(i->src2, reg_v1);
                            printf("\taddu $%d, $%d, $%d\n", index, arr, index);
                            rd = i->dest->reg;
                            if (rd == -1) rd = reg_v0;
                            printf("\t%s $%d, 0($%d)\n", l, rd, index);
                        }
                        if (i->dest->reg == -1 || i->dest->kind == VAR)
                            mips_store(rd, i->dest);
                    }
                    break;
                case WARR:
                    {
                        CType_t type = i->dest->type;
                        int arr = mips_to_reg(i->dest, reg_v0);
                        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 (i->src2->kind == IMM)
                        {
                            rs = mips_to_reg(i->src1, reg_v1);
                            printf("\t%s $%d, %d($%d)\n", s, rs, i->src2->info.imm, arr);
                        }
                        else
                        {
                            int index = mips_to_reg(i->src2, reg_v1);
                            printf("\taddu $%d, $%d, $%d\n", index, arr, index);
                            rs = mips_to_reg(i->src1, reg_v0);
                            printf("\t%s $%d, 0($%d)\n", s, rs, index);
                        }
                    }
                    break;
                case PUSH:
                    {
                        int rs = mips_to_reg(i->src1, reg_v0);
                        /* TODO: push struct */
                        printf("\tsw $%d, %d($sp) # push\n", rs, i->offset);
                    }
                    break;
                case CALL:
                    {
                        int rd = i->dest->reg;
                        if (i->src1->kind == IMMF)
                            printf("\tjal _func_%s\n", i->src1->info.str);
                        else
                            printf("\tjalr $%d\n", mips_to_reg(i->src1, reg_v0));
                        if (rd != -1) 
                            printf("\tmove $%d, $%d\n", rd, reg_v0);
                        else
                            rd = reg_v0;
                        if (i->dest->reg == -1 || i->dest->kind == VAR)
                            mips_store(reg_v0, i->dest);
                    }
                    break;
                case RET:
                    {
                        if (i->src1->reg != -1)
                            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 == -1) rd = reg_v0;
                        if (i->src1->kind == IMMF)
                            printf("\tla $%d, %s\n", rd, i->src1->info.str);
                        else
                        {
                            CVar_t var = i->src1->info.var;
                            if (var->global)
                                printf("\tla $%d, _%s\n", rd, var->name);
                            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 = 0;
            switch (i->op)
            {
                case MUL: bop = "mul"; break;
                case DIV: bop = "divu"; break;
                case MOD: bop = "rem"; break;
                case ADD: bop = "addu"; swap = 1; break;
                case SUB: bop = "subu"; break;
                case SHL: bop = "sllv"; break;
                case SHR: bop = "srlv"; break;
                case AND: bop = "and"; swap = 1; break;
                case XOR: bop = "xor"; swap = 1; break;
                case OR: bop = "or"; swap = 1; break;
                case NOR: bop = "nor"; break;
                case EQ: bop = "seq"; break;
                case NE: bop = "sne"; break;
                case LT: bop = "slt"; break;
                case GT: bop = "sgt"; break;
                case LE: bop = "sle"; break;
                case GE: bop = "sge"; break;
                default: flag = 0;
            }
            if (flag)
            {
                int rs, rt;
                int rd = i->dest->reg;
                if (rd == -1) rd = reg_v0;
                if (swap)
                {
                    COpr_t t;
                    if (i->src1->kind == IMM)
                    {
                        t = i->src1;
                        i->src1 = i->src2;
                        i->src2 = t;
                    }
                    if (i->src2->kind == IMM &&
                            i->src2->info.imm > -0x7fff &&
                            i->src2->info.imm < 0x8000)
                    {
                        switch (i->op)
                        {
                            case ADD: bop = "addiu"; break;
                            case AND: bop = "andi"; break;
                            case XOR: bop = "xori"; break;
                            case OR: bop = "ori"; 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 swap = 0;
                }
                if (!swap)
                {
                    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 == -1) 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();
}