/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package target.mips;

import API.Instruction;
import IntermediaryCode.Block;
import IntermediaryCode.DataFrame;
import IntermediaryCode.OcorrenceFinderProcessor;
import ast.Node;
import compiler.IvannosysTargetArch;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * MipS tranSlation
 *
 * @author EUGENIO CARVALHO
 */
public class Gen extends API.Gen {

    protected HashMap<String, String> AddressCalculated = new HashMap<>();
    protected AllocatorMipsProcessor allocation = new AllocatorMipsProcessor();
    protected HashMap<String, String> comments = new HashMap<String, String>() {
        {
            put("beq", "branch if equals");
            put("bne", "branch if not equals");
            put("blez", "branch if register <= 0");
            put("bltz", "branch if register < 0");
            put("bgez", "branch if register >= 0");
            put("bgtz", "branch if register > 0");
        }
    };
    private ArrayList<String> returnRegisters;
    private HashMap<String, Boolean> ctrlDataUpdate = new HashMap<>();
    private int WORD_INC = 4;

    public Gen() {
        super("MIPS");
        Init();
    }

    public void Init() {
        BeforeTranslateBlock("live.var", new OcorrenceFinderProcessor());
        BeforeTranslateBlock("register.alloc", allocation);

        AfterTranslateBlock("otimization.1", new OtimizationMips());

        AfterTranslate("update.address", new UpdateAddressProcessor());
        getTarget()
                .Template(new TemplateMips())
                .Formats(new HashMap<String, String>() {
                    {
                        String base = "{[HEX(global.position,_addressLen,' ')]':  '}{[INSTDEC()]}{[T(1)]}{[inst]}' '",
                        end = "{[T(1)]}{'.'[tac.position]'.'}{'--'[comment]}";
                        put("S", base + end);

                        put("label", "'  '{[HEX(global.reference.position,8,0)]}{[INSTDEC()]}{' <'[label]'>:'");

                        // TemplateMips para instrucoes do tipo J
                        put("J", base + "{[rs]}{[L(label,'hex')]}" + end);

                        // TemplateMips para instrucoes do tipo R
                        put("R0", base + "{[rd]','}{[rs]','}{[rt]}" + end);
                        put("R1", base + "{[rs]','}{[rt]}" + end);
                        put("R2", base + "{[rs]}" + end);
                        put("R3", base + "{[rt]','}{[rs]}" + end);
                        put("R4", base + "{[rd]','}{[rt]','}{[sa]} " + end);
                        put("R5", base + "{[rd]}" + end);

                        // TemplateMips para instrucoes do tipo I
                        put("I0", base + "{[rt]','}{[rs]','}{[L(label,'hex')]|[offset]|[rd]}" + end); //[rs:sp,rt:7,rd:sp]  
                        // SW,LW
                        put("I1", base + "{[rt]','}{[OFFSET(offset,rs)]}" + end);
                        // Templates avulsos
                        put("PSEUDO", base);
                    }
                });
    }

    @Override
    public void TranslateLabel(Instruction inst) {
        Add(new Instruction()
                .S("type", "label")
                .S("format", "norender")
                //                .S("format", "label")
                .S("tac.position", inst.G("global.position"))
                .S("label", inst.G("label")));
        getTarget().RegisterLabelAddress(inst.G("label"));
    }

    @Override
    public void TranslateCopy(Instruction inst) throws Exception {

        //Se o valor deve ser armazenado em memoria
        if (inst.eq("reg.dst.store", "true")) {
            String dst = inst.G("dst");
            LoadParam(inst, "p1");
            StoreWord(inst.G("reg.p1"),
                    GlobalOrFrameRegister(dst),
                    getTarget().Block().Data().Offset(dst))
                    .S("tac.position", inst.G("global.position"))
                    .S("comment", "store content of " + inst.G("reg.p1") + " in " + dst);
        } else {
            Copy(inst, inst.G("reg.dst"))
                    .S("tac.position", inst.G("global.position"))
                    .S("comment", "copy " + inst.G("dst") + " ← " + inst.G("p1"));
        }

    }

    @Override
    public void TranslateAssign(Instruction inst) throws Exception {

        String instruction = Mips.InstructionByOperator(inst);

        String rd = inst.G("reg.dst"),
                dst = inst.G("dst");

        Instruction ninst = new Instruction(instruction).S("tac.position", inst.G("global.position"));

        switch (Mips.Codops.get(instruction).G("type")) {
            //addi, addiu, slti, sltiu
            case "I":
                ninst.S("rt", rd);
//                System.out.println("Assign:" + inst);

                if (inst.eq("p2value", "true")) {
                    LoadParam(inst, "p1");
                    ninst.S("offset", inst.G("p2"))
                            .S("rs", inst.G("reg.p1"));
                } else {
                    LoadParam(inst, "p2");
                    ninst.S("offset", inst.G("p1"))
                            .S("rs", inst.G("reg.p2"));
                }

                break;
            // mult, divu, sgtu ,sltu 
            // sra, srl, sll
            case "R":
                LoadParam(inst);
                //
                if (ninst.in("inst", "sll,srl,sra".split(","))) {
                    rd = inst.G("reg.dst");
                    ninst.S("sa", inst.G("p2"))
                            .S("rt", inst.G("reg.p2"));
                } else {
                    ninst.S("rs", inst.G("reg.p1"))
                            .S("rt", inst.G("reg.p2"));
                }
                ninst.S("rd", rd);
//                System.out.println("inst["+rd+"]" + inst + ":" + ninst);
                break;
        }

        Add(ninst);

        // Verifica se a expr possui overflow
        String overflow = Mips.Instruction(instruction).G("overflow");

        if (!overflow.equals("")) {
            Add(new Instruction("R", overflow)
                    .S("rd", rd)
                    .S("tac.position", inst.G("global.position"))
            );
        }

        // Gravar na memoria?
        StoreResult(inst, rd, dst);
    }

    @Override
    public void TranslateJump(Instruction inst) {
        Add(new Instruction("J", "j")
                .copy("label", inst)
                .S("comment", inst.getText())
                .S("tac.position", inst.G("global.position"))
        );
    }

    @Override
    public void TranslateCall(Instruction inst) throws Exception {
//        allocation.registers.ResetArgs();
        // Before load params

        //        Copy(inst, returnRegisters.remove(2))
//                .S("tac.position", inst.G("global.position"))
//                .S("comment", "push param");
//        System.out.println("Call" + inst);
        ResetReturnArgs();
        try {
            Instruction retur;
            LinkedList<Instruction> intructions = getTac().Block().Instructions();
            int base = intructions.indexOf(inst);

            for (int i = inst.getInt("nump"); i > 0; i--) {
                retur = intructions.get(base - i);

                Copy(retur, returnRegisters.remove(2))
                        .S("tac.position", retur.G("global.position"))
                        .S("comment", "push param");

            }
        } catch (Exception ex) {
            Logger.getLogger(Gen.class.getName()).log(Level.SEVERE, null, ex);
        }

        Add(new Instruction("J", "jal")
                .S("label", inst.G("funcname"))
                .S("comment", "jump to <" + inst.G("funcname") + ">")
                .S("tac.position", inst.G("global.position"))
        );
        // After store results
        // Restaura o array de registradores de retorno para fazer o pop dos returns
        ResetReturnArgs();

    }

    @Override
    public void TranslatePushReturn(Instruction inst) {
        // Tratado no metodo 'TranslateReturn'
    }

    @Override
    public void TranslateReturn(Instruction inst) {
        // Restaura o array de registradores de retorno
        ResetReturnArgs();

        try {
            Instruction retur;
            LinkedList<Instruction> intructions = getTac().Block().Instructions();
            int base = intructions.indexOf(inst);

            for (int i = inst.getInt("p1"); i > 0; i--) {
                retur = intructions.get(base - i);

//                System.out.println("Return:" + retur);
                Copy(retur, returnRegisters.remove(0))
                        .S("tac.position", retur.G("global.position"))
                        .S("comment", "push return");

            }
        } catch (Exception ex) {
            Logger.getLogger(Gen.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    /**
     * Criar uma inStrucao que copia o valor ou conteudo do regiStrador para o
     * regiStrador 'r'
     *
     * @param inst
     * @param push
     * @param rt
     */
    protected Instruction Copy(Instruction inst, String rt) {
        Instruction c;
        if (inst.eq("p1value", "true")) {
            // andi Rt,Rs,immediate | Rt <-- [Rs] + ([I15]16 || [I15..0]);
            c = Copy(rt, Registers.R_ZERO, inst.G("p1"));

        } else {
            // addu Rd,Rs,Rt | Rd <-- [Rs] + [Rt];
            LoadParam(inst, "p1");
            c = CopyReg(inst.G("reg.p1"), rt);
        }
        return c.S("tac.position", inst.G("global.position"));
    }

    @Override
    public void TranslatePushParam(Instruction inst) {
        // Tratado no metodo 'TranslateCall'
    }

    @Override
    public void TranslatePopReturn(Instruction inst) {
        String p1 = inst.G("p1");
        StoreWord(returnRegisters.remove(0),
                GlobalOrFrameRegister(p1),
                getTarget().Block().Data().Offset(p1));
    }

    @Override
    public void TranslatePopParam(Instruction inst) {
        // todo - resetar args
//        System.out.println("PopParam:" + inst);
        String p1 = inst.G("p1");
        // O indice é 2 por conta dos dois primeiros registradores {v0, v1}
        StoreWord(returnRegisters.remove(2),
                GlobalOrFrameRegister(p1),
                getTarget().Block().Data().Offset(p1))
                .S("tac.position", inst.G("global.position"))
                .S("comment", "pop param");
    }

    @Override
    public void TranslateBranch(Instruction inst) throws Exception {

        String instruction = Mips.InstructionByOperator(inst);

        LoadParam(inst);

        String rs = inst.G("reg.p1");
        String rt = inst.G("reg.p2");

        Instruction I = new Instruction("I", instruction)
                .S("label", inst.G("funcname"));

        switch (instruction) {
            case "beq": // == {[inst]}' ' {[rt]},{[rs]},{immediate}
            case "bne": // != 
                I.S("rs", rs).S("rt", rt);
                break;
            case "blez": // <= 0{[inst]}' ' {[rs]},{immediate}
            case "bltz": // <  0
            case "bgez": // >= 0
            case "bgtz": // >  0

                Add(new Instruction("R", "subu")// Rd <-- [Rs] - [Rt];
                        .S("rs", rs)
                        .S("rt", rt)
                        .S("rd", Registers.R_V0)
                        .S("tac.position", inst.G("global.position"))
                        .S("comment", "")
                );

                I.S("rs", Registers.R_V0);
                break;
        }
        Add(I).S("label", inst.G("label"))
                .S("tac.position", inst.G("global.position"))
                .S("comment", comments.get(instruction));

    }

    @Override
    public void Prolog(String id) throws Exception {
        // Copia os dados para block target
        DataFrame data = getTarget().Block().Data(getTac().Block().Data());
        //Updata size in 4 bytes
        Node v;
        for (Map.Entry<String, Node> x : data.values().entrySet()) {
            v = x.getValue();
            if (!ctrlDataUpdate.containsKey(v.G("id"))) {
                ctrlDataUpdate.put(v.G("id"), Boolean.TRUE);
                v.S("size", v.getInt("size") * WORD_INC);
            }
        }
//        for (Map.Entry<String, Node> x : data.values().entrySet()) {
//            System.out.println("Copy:" + x.getKey() + x.getValue());
//        }
        // Adiciona o label
        Add(new Instruction()
                .S("type", "label")
                .S("format", "label")
                .S("label", id)
        );

        // Aloca o espaco da pilha
        Instruction alloc = Copy(Registers.R_SP, Registers.R_SP, "-" + data.Size())
                .S("comment", "p| push stack frame");

        if (!getTarget().Block().getName().equals("main")) {
            // Restaura o fp
            // Restaura o ra
            boolean call = getTac().Block().HasCall();
            int newvars = 0;
            for (String reg : new String[]{Registers.R_FP, Registers.R_RA}) {
                if (call) {
                    data.Add(reg, new Node().S("type", "int").set("size", 1 * WORD_INC));
                    newvars--;
                    StoreWord(reg, Registers.R_SP, data.Offset(reg))
                            .S("comment", "p| backup " + reg);
                }
            }
            alloc.somar("offset", newvars);
        }

        // Copia o sp para o fp
        CopyReg(Registers.R_SP, Registers.R_FP).Prepend("comment", "p|");
        ResetReturnArgs();
    }

    /**
     * Encerra a chamada de uma funcao
     *
     * @param id
     * @throws Exception
     */
    @Override
    public void Epilog(String id) throws Exception {
        DataFrame data = getTarget().Block().Data();
        if (!getTarget().Block().getName().equals("main")) {
            // Restaura o fp
            // Restaura o ra
            boolean call = getTac().Block().HasCall();
            for (String reg : new String[]{Registers.R_FP, Registers.R_RA}) {
                if (call) {
                    LoadWord(reg, Registers.R_SP, data.Offset(reg))
                            .S("comment", "e| restore " + reg);
                }
            }

            // Desaloca a pilha
            Copy(Registers.R_SP, Registers.R_SP, data.Size())
                    .S("comment", "e|pop stack frame");

//        if (getTac().Block().HasCall()) {
            Add(new Instruction("jr")
                    .S("rs", Registers.R_RA)
                    .S("comment", "e|return"));
//        }[] 
        } else {
            Add(new Instruction("stop").S("txt", "11111111111111111111111111111111").S("comment", "end of programa"));
        }
    }

    @Override
    public void TranslateIndexedAssignment(Instruction inst) throws Exception {
        //Fase de leitura
        IndexedRead(inst);
        //Fase de atribuicao
        IndexedDest(inst);
    }

    protected void IndexedRead(Instruction inst) {
        // Se a fonte é indexada
        if (inst.eq("src_indexed", "true")) {
            int offset = getTarget().Block().Data().Offset(inst.G("p1"));
            String p1 = inst.G("p1"),
                    rt = inst.G("reg.p1"),
                    rs = GlobalOrFrameRegister(p1);

//            System.out.println("Indexed read:" + inst);
            if (!inst.isNumber("p1.indice")) {
                String rd = inst.G("reg.p1.indice");
                if (!AddressCalculated.containsKey(p1)) {
                    Add(new Instruction("addu")
                            .S("rd", rd)
                            .S("rs", rs)
                            .S("rt", rd))
                            .S("tac.position", inst.G("global.position"));
                    AddressCalculated.put(p1, rs);
                }
                rs = rd;
            }

            // Carrega o conteudo enderecado em rt para rt
            LoadWord(rt, rs, offset)
                    .S("tac.position", inst.G("global.position"));

        } else {
            // Se não é indexada carrega o parametro normalmente
            LoadParam(inst);
        }
    }

    /**
     * FaSe de atribuicao
     *
     * @param inst
     */
    protected void IndexedDest(Instruction inst) {
//        System.out.println("Indexeds:" + inst);

        String regp1 = inst.G("reg.p1"),
                regdst = inst.G("reg.dst");

        // Valor atribuido é indexado
        // 1 - Verificar se deve ser armazenado ou copiado
        boolean dstIndexed = inst.eq("dst_indexed", "true");

        if (dstIndexed || inst.eq("reg.dst.store", "true")) { // deve ser armazenado

            String dst = inst.G("dst"),
                    rs = GlobalOrFrameRegister(dst),
                    comment;
            int offset = getTarget().Block().Data().Offset(dst);

            if (dstIndexed) {
                if (!inst.isNumber("dst.indice")) {
                    // Offset agora deve ser 0 pois o registrador conterá o endereco completo
//                    offset = 0;
                    String rd = inst.G("reg.dst.indice");
                    if (!AddressCalculated.containsKey(dst)) {
                        Add(new Instruction("addu")
                                .S("rd", rd)
                                .S("rs", rs)
                                .S("rt", rd)
                                .S("tac.position", inst.G("global.position"))
                        );
                        AddressCalculated.put(dst, rs);
                    }
                    rs = rd;
                }
                comment = "store in loaded address";
            } else {
                comment = "store in " + dst;
            }
//            System.out.println("Inst:" + inst);
            StoreWord(regp1, rs, offset)
                    .S("tac.position", inst.G("global.position"))
                    .S("comment", comment);

        } else { // deve ser copiado
            CopyReg(regp1, regdst)
                    .S("tac.position", inst.G("global.position"));
        }
    }

    /**
     * Verificar Se o valor da atribuição ja eSta em regiStrador. CaSo não
     * eSteja o valor é carregado e o valor retornado é o nome do regiStrador
     *
     * @param inst
     * @param param
     * @return
     */
    protected String LoadParam(Instruction inst) {
        return LoadParam(inst, "p1,p2");
    }

    protected String LoadParam(Instruction inst, String params) {
        return LoadParam(inst, params.split(","));
    }

    protected String LoadParam(Instruction inst, String[] params) {
        String pname;
        for (String param : params) {
            if (!inst.Has("reg." + param + ".load")) {
                continue;
            }
            Instruction Ninst = new Instruction();
            pname = inst.G(param);

            if (inst.eq(param + "value", "true")) {
                Ninst.S("inst", "addiu") // R[$rt] ← {(imm)[15:0], 0 × 16}
                        .S("rs", Registers.R_ZERO)
                        .S("rt", inst.G("reg." + param))
                        .S("offset", pname);
            } else {
                Ninst.S("inst", "lw") // R[$rt] ← Mem4B(R[$rs] + SignExt16b(imm))
                        .S("rt", inst.G("reg." + param))
                        .S("rs", GlobalOrFrameRegister(pname))
                        .S("comment", "load address stored in " + pname)
                        .set("offset", getTarget().Block().Data().Offset(pname));
            }

            Add(Ninst.S("tac.position", inst.G("global.position")));
//            x.S("reg." + param, reg);
        }

        return null;
    }

    /**
     * Copia o valor do regiStrador Src para o dSt
     *
     * @param src
     * @param dst
     * @return
     */
    protected Instruction CopyReg(String src, String dst) {
        return Add(new Instruction("addu")
                .S("rs", Registers.R_ZERO)
                .S("rt", src)
                .S("rd", dst)
                .S("comment", "copy " + dst + " ← " + src));
    }

    protected Instruction Copy(String rt, String rs, int offset) {
        return Copy(rt, rs, "" + offset);
    }

    protected Instruction Copy(String rt, String rs, String offset) {
        return Add(new Instruction("addiu") // lw $rt, imm($rs)
                .S("rt", rt)
                .S("rs", rs)
                .S("offset", offset)
                .S("comment", "copy " + rs + " ← " + rt)
        );
    }

    protected Instruction StoreWord(String rt, String rs, int offset) {
        return StoreWord(rt, rs, "" + offset);
    }

    protected Instruction StoreWord(String rt, String rs, String offset) {
        return Add(new Instruction("sw").S("rt", rt).S("rs", rs).S("offset", offset));
    }

    protected Instruction LoadWord(String rt, String rs, int offset) {
//        System.out.println("LoadWord:" + rt + ":" + rs + ":" + "off:" + offset);
        return LoadWord(rt, rs, "" + offset);
    }

    protected Instruction LoadWord(String rt, String rs, String offset) {
//        System.out.println("LoadWord:" + rt + ":" + rs + ":" + "off:" + offset);
        return Add(new Instruction("lw") // lw $rt, imm($rs)
                .S("rt", rt)
                .S("rs", rs)
                .S("offset", offset)
        );
    }

    protected void Noop() {
        Add(new Instruction("sll")
                .S("rs", "zero")
                .S("rt", "0")
                .S("rd", "zero"));
    }

    @Override
    public IvannosysTargetArch Export() {
        System.out.println(getTarget().GData());

        for (Map.Entry<String, Block> x : getTarget().stmts.entrySet()) {
            System.out.println(x.getValue().Data());
        }

        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }

    @Override
    public void TranslateUnary(Instruction inst) throws Exception {
        // Carrega o valor se nao esta em registrador
//        System.out.println("Translate Unary:" + inst);

        switch (inst.G("op")) {
            case "-": // Retorna o valor negado 10 -> -10

                Add(new Instruction("subu")// Rd <-- [Rs] - [Rt];
                        .S("rd", inst.G("reg.dst"))
                        .S("rs", Registers.R_ZERO)
                        .S("rt", inst.G("reg.p1"))
                        .S("comment", "negation arith")
                        .S("tac.position", inst.G("global.position"))
                );
                break;
            case "!": // XOR ( 0 1 -> 1) XOR( 1 1 -> 0)
                Add(new Instruction("xori") // Rt <-- [Rs] AND (016 || [I15..0]); ;
                        .S("rt", inst.G("reg.dst"))
                        .S("rs", inst.G("reg.p1"))
                        .S("offset", "1")
                        .S("comment", "negation bool")
                        .S("tac.position", inst.G("global.position"))
                );
                break;
//            case "*": // Copia do conteudo do ponteiro
//
//            case "&":  // Copia do endereco da variavel
//                break;//            case "*": // Copia do conteudo do ponteiro
//
//            case "&":  // Copia do endereco da variavel
//                break;//            case "*": // Copia do conteudo do ponteiro
//
//            case "&":  // Copia do endereco da variavel
//                break;//            case "*": // Copia do conteudo do ponteiro
//
//            case "&":  // Copia do endereco da variavel
//                break;//            case "*": // Copia do conteudo do ponteiro
//
//            case "&":  // Copia do endereco da variavel
//                break;//            case "*": // Copia do conteudo do ponteiro
//
//            case "&":  // Copia do endereco da variavel
//                break;//            case "*": // Copia do conteudo do ponteiro
//
//            case "&":  // Copia do endereco da variavel
//                break;//            case "*": // Copia do conteudo do ponteiro
//
//            case "&":  // Copia do endereco da variavel
//                break;
        }

    }

    /**
     * Traducao de intrucoeS do tipo
     *
     * - x = &a - copia do endereco de uma varaivel <br>
     * - x = *b - x recebe o conteudo da variavel referenciada por b<br>
     * - *x = y - conpia o conteudo de y para o endereco armazenado em x
     *
     * @param inst
     * @throws Exception
     */
    @Override
    public void TranslatePointerAssignment(Instruction inst) throws Exception {
        String p1 = inst.G("p1"),
                dst = inst.G("dst"),
                regdst = inst.G("reg.dst"),
                regp1 = inst.G("reg.p1");

        switch (inst.G("op")) {

            case "&": // Lendo o endereco de um ponteiro
                Copy(regp1,
                        GlobalOrFrameRegister(p1),
                        getTac().Block().Data().Offset(p1)
                )
                        .S("tac.position", inst.G("global.position"))
                        .S("comment", "copy address of " + p1);

                System.out.println("PointerAssi&:" + inst);
                // Se nao fez o store
                if (StoreResult(inst, regp1, dst) == null) {
                    CopyReg(regp1, regdst);
//                            .S("comment", "store content of " + regdst + " in " + dst);
                }
                break;
            case "*": // Lendo o conteudo de um ponteiro
                /**
                 * Carrega o valor contido no ponteiro<br>
                 * Utiliza o valor como endereço em um segundo load que carrega
                 * o dado<br>
                 * lw ${p1},12($fp)<br>
                 * lw ${dst},0(${p1}) $s<br>
                 */
                LoadWord(regp1,
                        GlobalOrFrameRegister(p1),
                        getTac().Block().Data().Offset(p1))
                        .S("tac.position", inst.G("global.position"))
                        .S("comment", "load address stored in " + p1);

                LoadWord(regdst,
                        regp1,
                        "0")
                        .S("tac.position", inst.G("global.position"))
                        .S("comment", "load content of address stored in " + regp1);
                /*Se for a ultima operacao de escrita nessa variavel salva em memoria*/
                Instruction store = StoreResult(inst, regdst, dst);
                if (store != null) {
                    store.S("comment", "store content of " + regdst + " in " + dst);
                }

                break;
            default: // Atribuicao a um ponteiro
                LoadParam(inst);

                /**
                 * Carrega o valor dentro do endereco do ponteiro <br>
                 * lw ${p1},12(${fp|gp})<br>
                 * lw ${dst},0(${p1}) $s<br>
                 * conteudo da variavel referenciada
                 */
                LoadWord(regdst,
                        GlobalOrFrameRegister(dst),
                        getTac().Block().Data().Offset(dst))
                        .S("tac.position", inst.G("global.position"))
                        .S("comment", "load address stored in " + dst);
                // Grava o valor do registrador na memoria
                StoreWord(regp1, regdst, "0")
                        .S("tac.position", inst.G("global.position"))
                        .S("comment", "store content of " + regp1 + " in *" + dst);
        }

    }

    @Override
    protected Instruction Add(Instruction inst) {
        try {
            if (!inst.eq("type", "label")) {
                getTarget().PositionInc();
                Instruction definiction = Mips.Instruction(inst.G("inst"));
                inst.Merge("sa,type,inst,codop,func,format", definiction);

            }

        } catch (Exception ex) {
            Logger.getLogger(Gen.class
                    .getName()).log(Level.SEVERE, null, ex);
        }

        return super.Add(inst); //To change body of generated methods, choose Tools | Templates.
    }

    protected String GlobalOrFrameRegister(String var) {
//        System.out.println("GlobalOrFrameRegister:" + var);
        return var.contains("_G") ? Registers.R_GP : Registers.R_FP;
    }

    protected Instruction StoreResult(Instruction inst, String rd, String dst) {
        if (inst.eq("reg.dst.store", "true")) {
            return StoreWord(rd,
                    GlobalOrFrameRegister(dst),
                    getTarget().Block().Data().Offset(dst)
            ).S("tac.position", inst.G("global.position"));
        }
        return null;
    }

    private void ResetReturnArgs() {
        returnRegisters = new ArrayList<String>() {
            {
                add("v0");
                add("v1");
                add("a0");
                add("a1");
                add("a2");
                add("a3");
            }
        };
    }

}
