483 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			483 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
#include "mcg.h"
 | 
						|
 | 
						|
static struct e_instr em;
 | 
						|
static struct basicblock* code_bb;
 | 
						|
static struct basicblock* data_bb;
 | 
						|
 | 
						|
static void queue_insn_label(int opcode, const char* label, arith offset);
 | 
						|
 | 
						|
static const char* type_to_str(int type)
 | 
						|
{
 | 
						|
	switch (type)
 | 
						|
	{
 | 
						|
		case EM_MNEM:       return "EM_MNEM";
 | 
						|
		case EM_PSEU:	    return "EM_PSEU";
 | 
						|
		case EM_STARTMES:   return "EM_STARTMES";
 | 
						|
		case EM_MESARG:     return "EM_MESARG";
 | 
						|
		case EM_ENDMES:     return "EM_ENDMES";
 | 
						|
		case EM_DEFILB:     return "EM_DEFILB";
 | 
						|
		case EM_DEFDLB:     return "EM_DEFDLB";
 | 
						|
		case EM_DEFDNAM:    return "EM_DEFDNAM";
 | 
						|
		case EM_ERROR:      return "EM_ERROR";
 | 
						|
		case EM_FATAL:      return "EM_FATAL";
 | 
						|
		case EM_EOF:        return "EM_EOF";
 | 
						|
	}
 | 
						|
 | 
						|
    assert(0 && "invalid EM type");
 | 
						|
}
 | 
						|
 | 
						|
static const char* argtype_to_str(int type)
 | 
						|
{
 | 
						|
    if (type == 0)        return "...";
 | 
						|
    if (type == ilb_ptyp) return "ilb";
 | 
						|
    if (type == nof_ptyp) return "nof";
 | 
						|
    if (type == sof_ptyp) return "sof";
 | 
						|
    if (type == cst_ptyp) return "cst";
 | 
						|
    if (type == pro_ptyp) return "pro";
 | 
						|
    if (type == str_ptyp) return "str";
 | 
						|
    if (type == ico_ptyp) return "ico";
 | 
						|
    if (type == uco_ptyp) return "uco";
 | 
						|
    if (type == fco_ptyp) return "fco";
 | 
						|
    return "???";
 | 
						|
}
 | 
						|
 | 
						|
static void unknown_type(const char* s)
 | 
						|
{
 | 
						|
    fatal("%s with unknown type '%s'",
 | 
						|
        s,
 | 
						|
        argtype_to_str(em.em_arg.ema_argtype));
 | 
						|
}
 | 
						|
 | 
						|
static const char* ilabel_to_str(label l)
 | 
						|
{
 | 
						|
    assert(current_proc != NULL);
 | 
						|
    return aprintf(".%s_I%d", current_proc->name, l);
 | 
						|
}
 | 
						|
 | 
						|
static const char* dlabel_to_str(label l)
 | 
						|
{
 | 
						|
    return aprintf(".D%d", l);
 | 
						|
}
 | 
						|
 | 
						|
static void terminate_block(void)
 | 
						|
{
 | 
						|
    code_bb->is_terminated = true;
 | 
						|
    code_bb = NULL;
 | 
						|
}
 | 
						|
 | 
						|
static struct em* new_insn(int opcode)
 | 
						|
{
 | 
						|
    struct em* em = calloc(sizeof(struct em), 1);
 | 
						|
    em->opcode = opcode;
 | 
						|
    return em;
 | 
						|
}
 | 
						|
 | 
						|
static void queue_insn_simple(int opcode)
 | 
						|
{
 | 
						|
    struct em* em = new_insn(opcode);
 | 
						|
    em->paramtype = PARAM_NONE;
 | 
						|
    array_append(&code_bb->ems, em);
 | 
						|
 | 
						|
    switch (opcode)
 | 
						|
    {
 | 
						|
        case op_bra:
 | 
						|
            terminate_block();
 | 
						|
            break;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static void queue_insn_value(int opcode, arith value)
 | 
						|
{
 | 
						|
    struct em* em = new_insn(opcode);
 | 
						|
    em->paramtype = PARAM_IVALUE;
 | 
						|
    em->u.ivalue = value;
 | 
						|
    array_append(&code_bb->ems, em);
 | 
						|
 | 
						|
    switch (opcode)
 | 
						|
    {
 | 
						|
        case op_csa:
 | 
						|
        case op_csb:
 | 
						|
        case op_ret:
 | 
						|
            terminate_block();
 | 
						|
            break;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static void queue_insn_label(int opcode, const char* label, arith offset)
 | 
						|
{
 | 
						|
    struct em* em = new_insn(opcode);
 | 
						|
    em->paramtype = PARAM_LVALUE;
 | 
						|
    em->u.lvalue.label = label;
 | 
						|
    em->u.lvalue.offset = offset;
 | 
						|
    array_append(&code_bb->ems, em);
 | 
						|
 | 
						|
    switch (opcode)
 | 
						|
    {
 | 
						|
        case op_bra:
 | 
						|
            terminate_block();
 | 
						|
            break;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static void queue_insn_block(int opcode, struct basicblock* left, struct basicblock* right)
 | 
						|
{
 | 
						|
    struct em* em = new_insn(opcode);
 | 
						|
    em->paramtype = PARAM_BVALUE;
 | 
						|
    em->u.bvalue.left = left;
 | 
						|
    em->u.bvalue.right = right;
 | 
						|
    array_append(&code_bb->ems, em);
 | 
						|
    
 | 
						|
    terminate_block();
 | 
						|
}
 | 
						|
 | 
						|
static void change_basicblock(struct basicblock* newbb)
 | 
						|
{
 | 
						|
    array_appendu(¤t_proc->blocks, newbb);
 | 
						|
 | 
						|
    if (code_bb && !code_bb->is_terminated)
 | 
						|
        queue_insn_block(op_bra, newbb, NULL);
 | 
						|
 | 
						|
    code_bb = newbb;
 | 
						|
}
 | 
						|
 | 
						|
static void queue_insn_ilabel(int opcode, int label)
 | 
						|
{
 | 
						|
    const char* name = ilabel_to_str(em.em_ilb);
 | 
						|
    struct basicblock* left = bb_get(name);
 | 
						|
 | 
						|
    switch (opcode)
 | 
						|
    {
 | 
						|
        case op_bra:
 | 
						|
            queue_insn_block(em.em_opcode, left, NULL);
 | 
						|
            break;
 | 
						|
 | 
						|
        case op_zeq:
 | 
						|
        case op_zne:
 | 
						|
        case op_zlt:
 | 
						|
        case op_zle:
 | 
						|
        case op_zgt:
 | 
						|
        case op_zge:
 | 
						|
        case op_beq:
 | 
						|
        case op_bne:
 | 
						|
        case op_blt:
 | 
						|
        case op_ble:
 | 
						|
        case op_bgt:
 | 
						|
        case op_bge:
 | 
						|
        {
 | 
						|
            struct basicblock* bb = bb_get(NULL);
 | 
						|
            queue_insn_block(em.em_opcode, left, bb);
 | 
						|
            change_basicblock(bb);
 | 
						|
            break;
 | 
						|
        }
 | 
						|
 | 
						|
        default:
 | 
						|
            fatal("parse_em: unhandled conditional '%s'", 
 | 
						|
                em_mnem[opcode - sp_fmnem]);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static void queue_ilabel(arith label)
 | 
						|
{
 | 
						|
    change_basicblock(bb_get(ilabel_to_str(label)));
 | 
						|
}
 | 
						|
 | 
						|
static void parse_pseu(void)
 | 
						|
{
 | 
						|
	switch (em.em_opcode)
 | 
						|
	{
 | 
						|
		case ps_exp: /* external proc */
 | 
						|
		case ps_exa: /* external array */
 | 
						|
		case ps_inp: /* internal proc */
 | 
						|
		case ps_ina: /* internal array */
 | 
						|
		{
 | 
						|
			bool export = (em.em_opcode == ps_exp) || (em.em_opcode == ps_exa);
 | 
						|
			bool proc = (em.em_opcode == ps_exp) || (em.em_opcode == ps_inp);
 | 
						|
 | 
						|
			switch (em.em_arg.ema_argtype)
 | 
						|
			{
 | 
						|
				case pro_ptyp:
 | 
						|
					symbol_declare(strdup(em.em_pnam), export, proc);
 | 
						|
					break;
 | 
						|
 | 
						|
				case sof_ptyp:
 | 
						|
                    assert(em.em_off == 0);
 | 
						|
					symbol_declare(strdup(em.em_dnam), export, proc);
 | 
						|
					break;
 | 
						|
 | 
						|
                case nof_ptyp:
 | 
						|
                    assert(em.em_off == 0);
 | 
						|
                    symbol_declare(dlabel_to_str(em.em_dlb), export, proc);
 | 
						|
                    break;
 | 
						|
 | 
						|
				default:
 | 
						|
                    unknown_type("exp, exa, inp, ina");
 | 
						|
			}
 | 
						|
			break;
 | 
						|
		}
 | 
						|
 | 
						|
		case ps_con: /* .data */
 | 
						|
		case ps_rom: /* .rom */
 | 
						|
        {
 | 
						|
            bool ro = (em.em_opcode == ps_rom);
 | 
						|
 | 
						|
			switch (em.em_arg.ema_argtype)
 | 
						|
			{
 | 
						|
				case ico_ptyp:
 | 
						|
				case uco_ptyp:
 | 
						|
                {
 | 
						|
                    arith val = atol(em.em_string);
 | 
						|
                    data_int(val, em.em_size, ro);
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
 | 
						|
                case fco_ptyp:
 | 
						|
                {
 | 
						|
                    data_float(em.em_string, em.em_size, ro);
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
 | 
						|
				case str_ptyp:
 | 
						|
                    data_block(strdup(em.em_string), em.em_size, ro);
 | 
						|
					break;
 | 
						|
 | 
						|
                case cst_ptyp:
 | 
						|
                    data_int(em.em_cst, EM_wordsize, ro);
 | 
						|
                    break;
 | 
						|
                    
 | 
						|
                case nof_ptyp:
 | 
						|
                    data_offset(dlabel_to_str(em.em_dlb), em.em_off, ro);
 | 
						|
                    break;
 | 
						|
 | 
						|
                case sof_ptyp:
 | 
						|
                case pro_ptyp:
 | 
						|
                    data_offset(strdup(em.em_dnam), em.em_off, ro);
 | 
						|
                    break;
 | 
						|
 | 
						|
                case ilb_ptyp:
 | 
						|
                {
 | 
						|
                    const char* label = ilabel_to_str(em.em_ilb);
 | 
						|
 | 
						|
                    /* This is really hacky; to handle basic block flow
 | 
						|
                     * descriptor blocks, we need to track which bbs a descriptor
 | 
						|
                     * can exit to. So we create fake bb objects for each
 | 
						|
                     * block, purely to track this.
 | 
						|
                     */
 | 
						|
 | 
						|
                    if (data_bb)
 | 
						|
                    {
 | 
						|
                        struct em* em = new_insn(op_bra);
 | 
						|
                        em->paramtype = PARAM_BVALUE;
 | 
						|
                        em->u.bvalue.left = bb_get(label);
 | 
						|
                        array_append(&data_bb->ems, em);
 | 
						|
                    }
 | 
						|
 | 
						|
                    data_offset(label, 0, ro);
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
 | 
						|
				default:
 | 
						|
                    unknown_type("con, rom");
 | 
						|
			}
 | 
						|
			break;
 | 
						|
        }
 | 
						|
 | 
						|
        case ps_bss:
 | 
						|
        {
 | 
						|
            switch (em.em_arg.ema_argtype)
 | 
						|
            {
 | 
						|
                case cst_ptyp:
 | 
						|
                    data_bss(EM_bsssize, em.em_cst);
 | 
						|
                    break;
 | 
						|
                    
 | 
						|
				case ico_ptyp:
 | 
						|
				case uco_ptyp:
 | 
						|
                {
 | 
						|
                    arith val = atol(em.em_string);
 | 
						|
                    data_int(val, em.em_size, false);
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
 | 
						|
                case sof_ptyp:
 | 
						|
                    data_offset(strdup(em.em_dnam), em.em_off, false);
 | 
						|
                    break;
 | 
						|
 | 
						|
                default:
 | 
						|
                    unknown_type("bss");
 | 
						|
            }
 | 
						|
            break;
 | 
						|
        }
 | 
						|
 | 
						|
		case ps_pro: /* procedure start */
 | 
						|
        {
 | 
						|
            struct symbol* symbol;
 | 
						|
 | 
						|
            current_proc = calloc(sizeof(struct procedure), 1);
 | 
						|
            current_proc->name = strdup(em.em_pnam);
 | 
						|
            current_proc->entry = bb_get(current_proc->name);
 | 
						|
            current_proc->locals_size = em.em_nlocals;
 | 
						|
            code_bb = current_proc->entry;
 | 
						|
            code_bb->is_root = true;
 | 
						|
            array_append(¤t_proc->blocks, code_bb);
 | 
						|
 | 
						|
            symbol = symbol_get(current_proc->name);
 | 
						|
            symbol->section = SECTION_TEXT;
 | 
						|
            symbol->proc = current_proc;
 | 
						|
            symbol->is_proc = true;
 | 
						|
            break;
 | 
						|
        }
 | 
						|
 | 
						|
		case ps_end: /* procedure end */
 | 
						|
            tb_procedure();
 | 
						|
 | 
						|
            current_proc = NULL;
 | 
						|
            code_bb = NULL;
 | 
						|
			break;
 | 
						|
 | 
						|
		default:
 | 
						|
            fatal("unknown pseudo with opcode %d\n", em.em_opcode);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static arith mes_get_cst(void)
 | 
						|
{
 | 
						|
    EM_getinstr(&em);
 | 
						|
    if (em.em_type != EM_MESARG)
 | 
						|
        fatal("malformed MES");
 | 
						|
    return em.em_cst;
 | 
						|
}
 | 
						|
 | 
						|
static void parse_mes(void)
 | 
						|
{
 | 
						|
    assert(em.em_arg.ema_argtype == cst_ptyp);
 | 
						|
    switch (em.em_cst)
 | 
						|
    {
 | 
						|
        case 0: /* error */
 | 
						|
            fatal("MES 0 received (explicit halt)");
 | 
						|
 | 
						|
        case 3: /* register variable */
 | 
						|
        {
 | 
						|
            /* ego will sometimes generate 'mes 3' pseudos with no actual
 | 
						|
             * parameters. Detect and ignore these. */
 | 
						|
            
 | 
						|
            EM_getinstr(&em);
 | 
						|
            if (em.em_type == EM_MESARG)
 | 
						|
            {
 | 
						|
                arith offset = em.em_cst;
 | 
						|
                int size = mes_get_cst();
 | 
						|
                int type = mes_get_cst();
 | 
						|
                int priority = mes_get_cst();
 | 
						|
                tb_regvar(current_proc, offset, size, type, priority);
 | 
						|
            }
 | 
						|
            break;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    while ((em.em_type == EM_STARTMES) || (em.em_type == EM_MESARG))
 | 
						|
        EM_getinstr(&em);
 | 
						|
 | 
						|
    if (em.em_type != EM_ENDMES)
 | 
						|
        fatal("malformed MES");
 | 
						|
}
 | 
						|
 | 
						|
static void create_data_label(const char* label)
 | 
						|
{
 | 
						|
    data_label(label);
 | 
						|
    if (current_proc)
 | 
						|
    {
 | 
						|
        data_bb = bb_get(label);
 | 
						|
        data_bb->is_fake = true;
 | 
						|
        array_append(¤t_proc->blocks, data_bb);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void parse_em(void)
 | 
						|
{
 | 
						|
    EM_getinstr(&em);
 | 
						|
	tb_filestart();
 | 
						|
 | 
						|
	while (em.em_type != EM_EOF)
 | 
						|
	{
 | 
						|
        switch (em.em_type)
 | 
						|
        {
 | 
						|
            case EM_PSEU:
 | 
						|
				parse_pseu();
 | 
						|
                break;
 | 
						|
 | 
						|
            case EM_DEFILB:
 | 
						|
                queue_ilabel(em.em_ilb);
 | 
						|
                break;
 | 
						|
 | 
						|
            case EM_DEFDLB:
 | 
						|
                create_data_label(dlabel_to_str(em.em_dlb));
 | 
						|
                break;
 | 
						|
 | 
						|
            case EM_DEFDNAM:
 | 
						|
                create_data_label(strdup(em.em_dnam));
 | 
						|
                break;
 | 
						|
 | 
						|
            case EM_STARTMES:
 | 
						|
                parse_mes();
 | 
						|
                break;
 | 
						|
 | 
						|
            case EM_MNEM:
 | 
						|
                if (code_bb)
 | 
						|
                {
 | 
						|
                    int flags = em_flag[em.em_opcode - sp_fmnem];
 | 
						|
 | 
						|
                    if (flags & EM_PAR)
 | 
						|
                    {
 | 
						|
                        switch (em.em_argtype)
 | 
						|
                        {
 | 
						|
                            case 0:
 | 
						|
                                /* This is an instruction which would normally
 | 
						|
                                 * take a size, but the size is provided on the
 | 
						|
                                 * stack. We hates them. */
 | 
						|
                                queue_insn_simple(em.em_opcode);
 | 
						|
                                break;
 | 
						|
                                
 | 
						|
                            case ilb_ptyp:
 | 
						|
                                queue_insn_ilabel(em.em_opcode, em.em_ilb);
 | 
						|
                                break;
 | 
						|
 | 
						|
                            case nof_ptyp:
 | 
						|
                                queue_insn_label(em.em_opcode,
 | 
						|
                                    dlabel_to_str(em.em_dlb), em.em_off);
 | 
						|
                                break;
 | 
						|
 | 
						|
                            case sof_ptyp:
 | 
						|
                                queue_insn_label(em.em_opcode,
 | 
						|
                                    strdup(em.em_dnam), em.em_off);
 | 
						|
                                break;
 | 
						|
 | 
						|
                            case pro_ptyp:
 | 
						|
                                queue_insn_label(em.em_opcode,
 | 
						|
                                    strdup(em.em_pnam), 0);
 | 
						|
                                break;
 | 
						|
 | 
						|
                            case cst_ptyp:
 | 
						|
                                if ((flags & EM_PAR) == PAR_B)
 | 
						|
                                    queue_insn_ilabel(em.em_opcode, em.em_ilb);
 | 
						|
                                else
 | 
						|
                                    queue_insn_value(em.em_opcode, em.em_cst);
 | 
						|
                                break;
 | 
						|
 | 
						|
                            default:
 | 
						|
                                unknown_type("instruction");
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                    else
 | 
						|
                        queue_insn_simple(em.em_opcode);
 | 
						|
                }
 | 
						|
                break;
 | 
						|
 | 
						|
            default:
 | 
						|
                fatal("unrecognised instruction type '%d'", em.em_type);
 | 
						|
        }
 | 
						|
 | 
						|
		EM_getinstr(&em);
 | 
						|
	}
 | 
						|
 | 
						|
	tb_fileend();
 | 
						|
}
 | 
						|
 | 
						|
/* vim: set sw=4 ts=4 expandtab : */
 |