Tests pass if one edits the top build.lua to uncomment "qemuppc" from both vars.plats and vars.plats_with_tests, and one leaves mcg in plat/qemuppc/descr. Add or correct some EM instructions in treebuilder.c: - "lof", "stf": handle negative offsets in load() and store(). - "cuu": add using IR_FROMUI. - "lim", "sim": keep an entire word in ".ignmask", to be compatible with mach/powerpc/libem/trp.s and ncg. We also keep a word in ".ignmask" in ncg for both i386 and m68020. - "trp": pass trap number in register. See comment in helper_function_with_arg(). - "sig": push the old value of .trppc on the stack. - "and ?", "ior ?", "xor ?", "com ?", "cms ?", "set ?", "inn ?": connect to helper functions in libem. - "blm", "bls": drop call to memmove() and use new helper ".bls4", because tests/plat/structcopy_e.c can't call memmove(). - "xor s", "cms s": if s is large, fall back on helper function. - "rol", "ror": add by decomposing each rotate into 4 IR ops. - "rck s", "bls s": make fatal unless s is word size. - "loi": push multiple loads in the correct order. - "dup s", "exg s": if s is large, fall back on helper. - "dus": add using new helper ".dus4". - "lxl", "lxa": follow the static chain, not the dynamic chain. - "lor 1": materialise the stack before pushing the stack pointer. - "lor 2", "str 2": make fatal. - "los", "sts": drop calls to memcpy() and use helpers ".los4" and and ".sts4", so lang/m2/libm2/LtoUset.e starts working. - "gto": correctly read descriptor. Change mach/powerpc/mcg/table: - ANY.L: add for "asp -8". - LOAD.L: work around register corruption. - COMPAREUL.I: add for "cms 8".
		
			
				
	
	
		
			1758 lines
		
	
	
	
		
			44 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1758 lines
		
	
	
	
		
			44 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
#include "mcg.h"
 | 
						|
 | 
						|
static struct basicblock* current_bb;
 | 
						|
 | 
						|
static int stackptr;
 | 
						|
static struct ir* stack[64];
 | 
						|
 | 
						|
static struct ir* convert(struct ir* src, int srcsize, int destsize, int opcode);
 | 
						|
static struct ir* appendir(struct ir* ir);
 | 
						|
static void insn_ivalue(int opcode, arith value);
 | 
						|
 | 
						|
static void reset_stack(void)
 | 
						|
{
 | 
						|
    stackptr = 0;
 | 
						|
}
 | 
						|
 | 
						|
static void push(struct ir* ir)
 | 
						|
{
 | 
						|
    if (stackptr == sizeof(stack)/sizeof(*stack))
 | 
						|
        fatal("stack overflow");
 | 
						|
 | 
						|
#if 0
 | 
						|
    /* If we try to push something which is too small, convert it to a word
 | 
						|
     * first. */
 | 
						|
 | 
						|
    if (ir->size < EM_wordsize)
 | 
						|
        ir = convertu(ir, EM_wordsize);
 | 
						|
#endif
 | 
						|
 | 
						|
    stack[stackptr++] = ir;
 | 
						|
}
 | 
						|
 | 
						|
/* Returns the size of the top item on the stack. */
 | 
						|
static int peek(int delta)
 | 
						|
{
 | 
						|
    if (stackptr <= delta)
 | 
						|
        return EM_wordsize;
 | 
						|
    else
 | 
						|
    {
 | 
						|
        struct ir* ir = stack[stackptr-1-delta];
 | 
						|
        return ir->size;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static struct ir* pop(int size)
 | 
						|
{
 | 
						|
    if (size < EM_wordsize)
 | 
						|
        size = EM_wordsize;
 | 
						|
 | 
						|
    if (stackptr == 0)
 | 
						|
    {
 | 
						|
        /* Nothing in our fake stack, so we have to read from the real stack. */
 | 
						|
 | 
						|
        if (size < EM_wordsize)
 | 
						|
            size = EM_wordsize;
 | 
						|
        return
 | 
						|
            appendir(
 | 
						|
                new_ir0(
 | 
						|
                    IR_POP, size
 | 
						|
                )
 | 
						|
            );
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        struct ir* ir = stack[--stackptr];
 | 
						|
 | 
						|
#if 0
 | 
						|
        /* If we try to pop something which is smaller than a word, convert it first. */
 | 
						|
 | 
						|
        if (size < EM_wordsize)
 | 
						|
            ir = convertu(ir, size);
 | 
						|
#endif
 | 
						|
        if (ir->size != size)
 | 
						|
        {
 | 
						|
            if ((size == (EM_wordsize*2)) && (ir->size == EM_wordsize))
 | 
						|
            {
 | 
						|
                /* Tried to read a long, but we got an int. Assemble the long
 | 
						|
                 * out of two ints. Note that EM doesn't specify an order. */
 | 
						|
                return
 | 
						|
                    new_ir2(
 | 
						|
                        IR_FROMIPAIR, size,
 | 
						|
                        ir,
 | 
						|
                        pop(EM_wordsize)
 | 
						|
                    );
 | 
						|
            }
 | 
						|
            else if ((size == EM_wordsize) && (ir->size == (EM_wordsize*2)))
 | 
						|
            {
 | 
						|
                /* Tried to read an int, but we got a long. */
 | 
						|
                push(
 | 
						|
                    new_ir1(
 | 
						|
                        IR_FROML1, EM_wordsize,
 | 
						|
                        ir
 | 
						|
                    )
 | 
						|
                );
 | 
						|
 | 
						|
                return
 | 
						|
                    new_ir1(
 | 
						|
                        IR_FROML0, EM_wordsize,
 | 
						|
                        ir
 | 
						|
                    );
 | 
						|
            }
 | 
						|
            else
 | 
						|
                fatal("expected an item on stack of size %d, but got %d\n", size, ir->size);
 | 
						|
        }
 | 
						|
        return ir;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static void print_stack(void)
 | 
						|
{
 | 
						|
    int i;
 | 
						|
 | 
						|
    tracef('E', "E: stack:");
 | 
						|
    for (i=0; i<stackptr; i++)
 | 
						|
    {
 | 
						|
        struct ir* ir = stack[i];
 | 
						|
        tracef('E', " $%d.%d", ir->id, ir->size);
 | 
						|
    }
 | 
						|
    tracef('E', "  (top)\n");
 | 
						|
}
 | 
						|
 | 
						|
static struct ir* appendir(struct ir* ir)
 | 
						|
{
 | 
						|
    int i;
 | 
						|
 | 
						|
    assert(current_bb != NULL);
 | 
						|
    array_appendu(¤t_bb->irs, ir);
 | 
						|
 | 
						|
    ir_print('0', ir);
 | 
						|
    return ir;
 | 
						|
}
 | 
						|
 | 
						|
static void sequence_point(void)
 | 
						|
{
 | 
						|
    int i;
 | 
						|
 | 
						|
    /* Ensures that any partially-evaluated expressions on the stack are executed right
 | 
						|
     * now. This typically needs to happen before store operations, to prevents loads of
 | 
						|
     * the same address being delayed until after the store (at which point they'll
 | 
						|
     * return incorrect values).
 | 
						|
     */
 | 
						|
 | 
						|
    assert(current_bb != NULL);
 | 
						|
 | 
						|
    for (i=0; i<stackptr; i++)
 | 
						|
    {
 | 
						|
        struct ir* ir = stack[i];
 | 
						|
        array_appendu(¤t_bb->irs, ir);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static void materialise_stack(void)
 | 
						|
{
 | 
						|
    int i;
 | 
						|
 | 
						|
    for (i=0; i<stackptr; i++)
 | 
						|
    {
 | 
						|
        struct ir* ir = stack[i];
 | 
						|
        appendir(
 | 
						|
            new_ir1(
 | 
						|
                IR_PUSH, ir->size,
 | 
						|
                ir
 | 
						|
            )
 | 
						|
        );
 | 
						|
    }
 | 
						|
 | 
						|
    reset_stack();
 | 
						|
}
 | 
						|
 | 
						|
void tb_filestart(void)
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
void tb_fileend(void)
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
void tb_regvar(struct procedure* procedure, arith offset, int size, int type, int priority)
 | 
						|
{
 | 
						|
    struct local* local = calloc(1, sizeof(*local));
 | 
						|
    local->size = size;
 | 
						|
    local->offset = offset;
 | 
						|
    local->is_register = true;
 | 
						|
    imap_put(&procedure->locals, offset, local);
 | 
						|
}
 | 
						|
 | 
						|
static struct ir* address_of_external(const char* label, arith offset)
 | 
						|
{
 | 
						|
    if (offset != 0)
 | 
						|
        return
 | 
						|
            new_ir2(
 | 
						|
                IR_ADD, EM_pointersize,
 | 
						|
                new_labelir(label),
 | 
						|
                new_wordir(offset)
 | 
						|
            );
 | 
						|
    else
 | 
						|
        return
 | 
						|
            new_labelir(label);
 | 
						|
}
 | 
						|
 | 
						|
static struct ir* convert(struct ir* src, int srcsize, int destsize, int opcode)
 | 
						|
{
 | 
						|
    if (srcsize == 1)
 | 
						|
    {
 | 
						|
        if ((opcode == IR_FROMSI) || (opcode == IR_FROMSL))
 | 
						|
        {
 | 
						|
            src = new_ir1(
 | 
						|
                IR_EXTENDB, EM_wordsize,
 | 
						|
                src
 | 
						|
            );
 | 
						|
        }
 | 
						|
        srcsize = EM_wordsize;
 | 
						|
    }
 | 
						|
 | 
						|
    if ((srcsize == 2) && (srcsize != EM_wordsize))
 | 
						|
    {
 | 
						|
        if ((opcode == IR_FROMSI) || (opcode == IR_FROMSL))
 | 
						|
        {
 | 
						|
            src = new_ir1(
 | 
						|
                IR_EXTENDH, EM_wordsize,
 | 
						|
                src
 | 
						|
            );
 | 
						|
        }
 | 
						|
        srcsize = EM_wordsize;
 | 
						|
    }
 | 
						|
 | 
						|
    if (src->size == EM_wordsize)
 | 
						|
    {}
 | 
						|
    else if (src->size == (2*EM_wordsize))
 | 
						|
        opcode++;
 | 
						|
    else
 | 
						|
        fatal("can't convert from %d to %d", src->size, destsize);
 | 
						|
 | 
						|
    return
 | 
						|
        new_ir1(
 | 
						|
            opcode, destsize,
 | 
						|
            src
 | 
						|
        );
 | 
						|
}
 | 
						|
 | 
						|
static struct ir* compare(struct ir* left, struct ir* right,
 | 
						|
        int size, int opcode)
 | 
						|
{
 | 
						|
    if (size == EM_wordsize)
 | 
						|
    {}
 | 
						|
    else if (size == (2*EM_wordsize))
 | 
						|
        opcode++;
 | 
						|
    else
 | 
						|
        fatal("can't compare things of size %d", size);
 | 
						|
 | 
						|
    return
 | 
						|
        new_ir2(
 | 
						|
            opcode, EM_wordsize,
 | 
						|
            left, right
 | 
						|
        );
 | 
						|
}
 | 
						|
 | 
						|
static struct ir* store(int size, struct ir* address, int offset, struct ir* value)
 | 
						|
{
 | 
						|
    int opcode;
 | 
						|
 | 
						|
    sequence_point();
 | 
						|
 | 
						|
    if (size == 1)
 | 
						|
    {
 | 
						|
        opcode = IR_STOREB;
 | 
						|
        size = EM_wordsize;
 | 
						|
    }
 | 
						|
    else if ((size < EM_wordsize) && (size == 2))
 | 
						|
    {
 | 
						|
        opcode = IR_STOREH;
 | 
						|
        size = EM_wordsize;
 | 
						|
    }
 | 
						|
    else
 | 
						|
        opcode = IR_STORE;
 | 
						|
 | 
						|
    if (offset != 0)
 | 
						|
        address = new_ir2(
 | 
						|
            IR_ADD, EM_pointersize,
 | 
						|
            address, new_wordir(offset)
 | 
						|
        );
 | 
						|
 | 
						|
    return
 | 
						|
        new_ir2(
 | 
						|
            opcode, size,
 | 
						|
            address, value
 | 
						|
        );
 | 
						|
}
 | 
						|
 | 
						|
static struct ir* load(int size, struct ir* address, int offset)
 | 
						|
{
 | 
						|
    int opcode;
 | 
						|
 | 
						|
    if (size == 1)
 | 
						|
    {
 | 
						|
        opcode = IR_LOADB;
 | 
						|
        size = EM_wordsize;
 | 
						|
    }
 | 
						|
    else if ((size < EM_wordsize) && (size == 2))
 | 
						|
    {
 | 
						|
        opcode = IR_LOADH;
 | 
						|
        size = EM_wordsize;
 | 
						|
    }
 | 
						|
    else
 | 
						|
        opcode = IR_LOAD;
 | 
						|
 | 
						|
    if (offset != 0)
 | 
						|
        address = new_ir2(
 | 
						|
            IR_ADD, EM_pointersize,
 | 
						|
            address, new_wordir(offset)
 | 
						|
        );
 | 
						|
 | 
						|
    return
 | 
						|
        new_ir1(
 | 
						|
            opcode, size,
 | 
						|
            address
 | 
						|
        );
 | 
						|
}
 | 
						|
 | 
						|
static struct ir* tristate_compare(int size, int opcode)
 | 
						|
{
 | 
						|
    struct ir* right = pop(size);
 | 
						|
    struct ir* left = pop(size);
 | 
						|
 | 
						|
    return compare(left, right, size, opcode);
 | 
						|
}
 | 
						|
 | 
						|
static struct ir* tristate_compare0(int size, int opcode)
 | 
						|
{
 | 
						|
    struct ir* right = new_wordir(0);
 | 
						|
    struct ir* left = pop(size);
 | 
						|
 | 
						|
    return compare(left, right, size, opcode);
 | 
						|
}
 | 
						|
 | 
						|
static void simple_convert(int opcode)
 | 
						|
{
 | 
						|
    struct ir* destsize = pop(EM_wordsize);
 | 
						|
    struct ir* srcsize = pop(EM_wordsize);
 | 
						|
    struct ir* value;
 | 
						|
 | 
						|
    assert(srcsize->opcode == IR_CONST);
 | 
						|
    assert(destsize->opcode == IR_CONST);
 | 
						|
 | 
						|
    value = pop(srcsize->u.ivalue);
 | 
						|
    push(
 | 
						|
        convert(value, srcsize->u.ivalue, destsize->u.ivalue, opcode)
 | 
						|
    );
 | 
						|
}
 | 
						|
 | 
						|
static void simple_branch2(int opcode, int size,
 | 
						|
    struct basicblock* truebb, struct basicblock* falsebb,
 | 
						|
    int irop)
 | 
						|
{
 | 
						|
    struct ir* right = pop(size);
 | 
						|
    struct ir* left = pop(size);
 | 
						|
 | 
						|
    materialise_stack();
 | 
						|
    appendir(
 | 
						|
        new_ir2(
 | 
						|
            irop, 0,
 | 
						|
            compare(left, right, size, IR_COMPARESI),
 | 
						|
            new_ir2(
 | 
						|
                IR_PAIR, 0,
 | 
						|
                new_bbir(truebb),
 | 
						|
                new_bbir(falsebb)
 | 
						|
            )
 | 
						|
        )
 | 
						|
    );
 | 
						|
}
 | 
						|
 | 
						|
static void compare0_branch2(int opcode,
 | 
						|
    struct basicblock* truebb, struct basicblock* falsebb,
 | 
						|
    int irop)
 | 
						|
{
 | 
						|
    push(
 | 
						|
        new_wordir(0)
 | 
						|
    );
 | 
						|
 | 
						|
    simple_branch2(opcode, EM_wordsize, truebb, falsebb, irop);
 | 
						|
}
 | 
						|
 | 
						|
static void simple_test(int size, int irop)
 | 
						|
{
 | 
						|
    push(
 | 
						|
        new_ir1(
 | 
						|
            irop, EM_wordsize,
 | 
						|
            tristate_compare0(size, IR_COMPARESI)
 | 
						|
        )
 | 
						|
    );
 | 
						|
}
 | 
						|
 | 
						|
static void simple_test_neg(int size, int irop)
 | 
						|
{
 | 
						|
    simple_test(size, irop);
 | 
						|
 | 
						|
    push(
 | 
						|
        new_ir1(
 | 
						|
            IR_NOT, EM_wordsize,
 | 
						|
            pop(EM_wordsize)
 | 
						|
        )
 | 
						|
    );
 | 
						|
}
 | 
						|
 | 
						|
static void helper_function(const char* name)
 | 
						|
{
 | 
						|
    /* Delegates to a helper function; these leave their result on the stack
 | 
						|
     * rather than returning values through lfr. */
 | 
						|
 | 
						|
    materialise_stack();
 | 
						|
    appendir(
 | 
						|
        new_ir1(
 | 
						|
            IR_CALL, 0,
 | 
						|
            new_labelir(name)
 | 
						|
        )
 | 
						|
    );
 | 
						|
}
 | 
						|
 | 
						|
static void helper_function_with_arg(const char* name, struct ir* arg)
 | 
						|
{
 | 
						|
    /* Abuses IR_SETRET to set a register to pass one argument to a
 | 
						|
     * helper function.
 | 
						|
     *
 | 
						|
     * FIXME:  As of January 2018, mach/powerpc/libem takes an
 | 
						|
     * argument in register r3 only for ".los4", ".sts4", ".trp".
 | 
						|
     * This is an accident.  Should the argument be on the stack, or
 | 
						|
     * should other helpers use a register? */
 | 
						|
 | 
						|
    materialise_stack();
 | 
						|
    appendir(
 | 
						|
        new_ir1(
 | 
						|
            IR_SETRET, arg->size,
 | 
						|
            arg
 | 
						|
        )
 | 
						|
    );
 | 
						|
    appendir(
 | 
						|
        new_ir1(
 | 
						|
            IR_CALL, 0,
 | 
						|
            new_labelir(name)
 | 
						|
        )
 | 
						|
    );
 | 
						|
}
 | 
						|
 | 
						|
static void insn_simple(int opcode)
 | 
						|
{
 | 
						|
    switch (opcode)
 | 
						|
    {
 | 
						|
        case op_bra:
 | 
						|
        {
 | 
						|
            struct ir* dest = pop(EM_pointersize);
 | 
						|
 | 
						|
            materialise_stack();
 | 
						|
            appendir(
 | 
						|
                new_ir1(
 | 
						|
                    IR_JUMP, 0,
 | 
						|
                    dest
 | 
						|
                )
 | 
						|
            );
 | 
						|
            break;
 | 
						|
        }
 | 
						|
 | 
						|
        case op_cii: simple_convert(IR_FROMSI); break;
 | 
						|
        case op_ciu: simple_convert(IR_FROMSI); break;
 | 
						|
        case op_cui: simple_convert(IR_FROMUI); break;
 | 
						|
        case op_cuu: simple_convert(IR_FROMUI); break;
 | 
						|
        case op_cfu: simple_convert(IR_FROMUF); break;
 | 
						|
        case op_cfi: simple_convert(IR_FROMSF); break;
 | 
						|
        case op_cif: simple_convert(IR_FROMSI); break;
 | 
						|
        case op_cuf: simple_convert(IR_FROMUI); break;
 | 
						|
        case op_cff: simple_convert(IR_FROMSF); break;
 | 
						|
 | 
						|
        case op_cmp:
 | 
						|
            push(
 | 
						|
                tristate_compare(EM_pointersize, IR_COMPAREUI)
 | 
						|
            );
 | 
						|
            break;
 | 
						|
 | 
						|
        case op_teq: simple_test(    EM_wordsize, IR_IFEQ); break;
 | 
						|
        case op_tne: simple_test_neg(EM_wordsize, IR_IFEQ); break;
 | 
						|
        case op_tlt: simple_test(    EM_wordsize, IR_IFLT); break;
 | 
						|
        case op_tge: simple_test_neg(EM_wordsize, IR_IFLT); break;
 | 
						|
        case op_tle: simple_test(    EM_wordsize, IR_IFLE); break;
 | 
						|
        case op_tgt: simple_test_neg(EM_wordsize, IR_IFLE); break;
 | 
						|
 | 
						|
        case op_cai:
 | 
						|
        {
 | 
						|
            struct ir* dest = pop(EM_pointersize);
 | 
						|
 | 
						|
            materialise_stack();
 | 
						|
            appendir(
 | 
						|
                new_ir1(
 | 
						|
                    IR_CALL, 0,
 | 
						|
                    dest
 | 
						|
                )
 | 
						|
            );
 | 
						|
            break;
 | 
						|
        }
 | 
						|
 | 
						|
        case op_inc:
 | 
						|
        {
 | 
						|
            push(
 | 
						|
                new_ir2(
 | 
						|
                    IR_ADD, EM_wordsize,
 | 
						|
                    pop(EM_wordsize),
 | 
						|
                    new_wordir(1)
 | 
						|
                )
 | 
						|
            );
 | 
						|
            break;
 | 
						|
        }
 | 
						|
 | 
						|
        case op_dec:
 | 
						|
        {
 | 
						|
            push(
 | 
						|
                new_ir2(
 | 
						|
                    IR_SUB, EM_wordsize,
 | 
						|
                    pop(EM_wordsize),
 | 
						|
                    new_wordir(1)
 | 
						|
                )
 | 
						|
            );
 | 
						|
            break;
 | 
						|
        }
 | 
						|
 | 
						|
        case op_lim:
 | 
						|
        {
 | 
						|
            /* Traps use only 16 bits of .ignmask, but we keep an
 | 
						|
             * entire word, even if a word has more than 2 bytes. */
 | 
						|
            push(
 | 
						|
                load(
 | 
						|
                    EM_wordsize,
 | 
						|
                    new_labelir(".ignmask"), 0
 | 
						|
                )
 | 
						|
            );
 | 
						|
            break;
 | 
						|
        }
 | 
						|
 | 
						|
        case op_sim:
 | 
						|
        {
 | 
						|
            appendir(
 | 
						|
                store(
 | 
						|
                    EM_wordsize,
 | 
						|
                    new_labelir(".ignmask"), 0,
 | 
						|
                    pop(EM_wordsize)
 | 
						|
                )
 | 
						|
            );
 | 
						|
            break;
 | 
						|
        }
 | 
						|
 | 
						|
        case op_trp:
 | 
						|
            helper_function_with_arg(".trp", pop(EM_wordsize));
 | 
						|
            break;
 | 
						|
 | 
						|
        case op_sig:
 | 
						|
        {
 | 
						|
            struct ir* label = new_labelir(".trppc");
 | 
						|
            struct ir* value = pop(EM_pointersize);
 | 
						|
            push(
 | 
						|
                load(
 | 
						|
                    EM_pointersize,
 | 
						|
                    label, 0
 | 
						|
                )
 | 
						|
            );
 | 
						|
            appendir(
 | 
						|
                store(
 | 
						|
                    EM_pointersize,
 | 
						|
                    label, 0,
 | 
						|
                    value
 | 
						|
                )
 | 
						|
            );
 | 
						|
            break;
 | 
						|
        }
 | 
						|
 | 
						|
        case op_rtt:
 | 
						|
        {
 | 
						|
            insn_ivalue(op_ret, 0);
 | 
						|
            break;
 | 
						|
        }
 | 
						|
 | 
						|
        case op_and: helper_function(".and"); break;
 | 
						|
        case op_ior: helper_function(".ior"); break;
 | 
						|
        case op_xor: helper_function(".xor"); break;
 | 
						|
        case op_com: helper_function(".com"); break;
 | 
						|
        case op_cms: helper_function(".cms"); break;
 | 
						|
        case op_set: helper_function(".set"); break;
 | 
						|
        case op_inn: helper_function(".inn"); break;
 | 
						|
 | 
						|
        case op_dch:
 | 
						|
            push(
 | 
						|
                new_ir1(
 | 
						|
                    IR_CHAINFP, EM_pointersize,
 | 
						|
                    pop(EM_pointersize)
 | 
						|
                )
 | 
						|
            );
 | 
						|
            break;
 | 
						|
 | 
						|
        case op_lpb:
 | 
						|
            push(
 | 
						|
                new_ir1(
 | 
						|
                    IR_FPTOAB, EM_pointersize,
 | 
						|
                    pop(EM_pointersize)
 | 
						|
                )
 | 
						|
            );
 | 
						|
            break;
 | 
						|
 | 
						|
        case op_lni:
 | 
						|
        {
 | 
						|
            /* Increment line number --- ignore. */
 | 
						|
            break;
 | 
						|
        }
 | 
						|
 | 
						|
        default:
 | 
						|
            fatal("treebuilder: unknown simple instruction '%s'",
 | 
						|
                em_mnem[opcode - sp_fmnem]);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static void insn_bvalue(int opcode, struct basicblock* leftbb, struct basicblock* rightbb)
 | 
						|
{
 | 
						|
    switch (opcode)
 | 
						|
    {
 | 
						|
        case op_zeq: compare0_branch2(opcode, leftbb, rightbb, IR_CJUMPEQ); break;
 | 
						|
        case op_zlt: compare0_branch2(opcode, leftbb, rightbb, IR_CJUMPLT); break;
 | 
						|
        case op_zle: compare0_branch2(opcode, leftbb, rightbb, IR_CJUMPLE); break;
 | 
						|
 | 
						|
        case op_zne: compare0_branch2(opcode, rightbb, leftbb, IR_CJUMPEQ); break;
 | 
						|
        case op_zge: compare0_branch2(opcode, rightbb, leftbb, IR_CJUMPLT); break;
 | 
						|
        case op_zgt: compare0_branch2(opcode, rightbb, leftbb, IR_CJUMPLE); break;
 | 
						|
 | 
						|
        case op_beq: simple_branch2(opcode, EM_wordsize, leftbb, rightbb, IR_CJUMPEQ); break;
 | 
						|
        case op_blt: simple_branch2(opcode, EM_wordsize, leftbb, rightbb, IR_CJUMPLT); break;
 | 
						|
        case op_ble: simple_branch2(opcode, EM_wordsize, leftbb, rightbb, IR_CJUMPLE); break;
 | 
						|
 | 
						|
        case op_bne: simple_branch2(opcode, EM_wordsize, rightbb, leftbb, IR_CJUMPEQ); break;
 | 
						|
        case op_bge: simple_branch2(opcode, EM_wordsize, rightbb, leftbb, IR_CJUMPLT); break;
 | 
						|
        case op_bgt: simple_branch2(opcode, EM_wordsize, rightbb, leftbb, IR_CJUMPLE); break;
 | 
						|
 | 
						|
        case op_bra:
 | 
						|
        {
 | 
						|
            materialise_stack();
 | 
						|
 | 
						|
            appendir(
 | 
						|
                new_ir1(
 | 
						|
                    IR_JUMP, 0,
 | 
						|
                    new_bbir(leftbb)
 | 
						|
                )
 | 
						|
            );
 | 
						|
            break;
 | 
						|
        }
 | 
						|
 | 
						|
        case op_lae:
 | 
						|
            push(
 | 
						|
                new_bbir(leftbb)
 | 
						|
            );
 | 
						|
            break;
 | 
						|
 | 
						|
        default:
 | 
						|
            fatal("treebuilder: unknown bvalue instruction '%s'",
 | 
						|
                em_mnem[opcode - sp_fmnem]);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static void simple_alu1(int opcode, int size, int irop, const char* fallback)
 | 
						|
{
 | 
						|
    if (size > (2*EM_wordsize))
 | 
						|
    {
 | 
						|
        if (!fallback)
 | 
						|
            fatal("treebuilder: can't do opcode %s with size %d", em_mnem[opcode - sp_fmnem], size);
 | 
						|
        push(
 | 
						|
            new_wordir(size)
 | 
						|
        );
 | 
						|
        helper_function(fallback);
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        struct ir* val = pop(size);
 | 
						|
 | 
						|
        push(
 | 
						|
            new_ir1(
 | 
						|
                irop, size,
 | 
						|
                val
 | 
						|
            )
 | 
						|
        );
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static void simple_alu2(int opcode, int size, int irop, const char* fallback)
 | 
						|
{
 | 
						|
    if (size > (2*EM_wordsize))
 | 
						|
    {
 | 
						|
        if (!fallback)
 | 
						|
            fatal("treebuilder: can't do opcode %s with size %d", em_mnem[opcode - sp_fmnem], size);
 | 
						|
        push(
 | 
						|
            new_wordir(size)
 | 
						|
        );
 | 
						|
        helper_function(fallback);
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        struct ir* right = pop(size);
 | 
						|
        struct ir* left = pop(size);
 | 
						|
 | 
						|
        push(
 | 
						|
            new_ir2(
 | 
						|
                irop, size,
 | 
						|
                left, right
 | 
						|
            )
 | 
						|
        );
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static void rotate(int opcode, int size, int irop, int irop_reverse)
 | 
						|
{
 | 
						|
    if (size > (2*EM_wordsize))
 | 
						|
        fatal("treebuilder: can't do opcode %s with size %d", em_mnem[opcode - sp_fmnem], size);
 | 
						|
    else
 | 
						|
    {
 | 
						|
        struct ir* right = pop(size);
 | 
						|
        struct ir* left = pop(size);
 | 
						|
        struct ir* bits = new_wordir(8 * size);
 | 
						|
 | 
						|
        /* a rol b -> (a << b) | (a >> (32 - b)) */
 | 
						|
        push(
 | 
						|
            new_ir2(
 | 
						|
                IR_OR, size,
 | 
						|
                new_ir2(irop, size, left, right),
 | 
						|
                new_ir2(
 | 
						|
                    irop_reverse, size,
 | 
						|
                    left,
 | 
						|
                    new_ir2(IR_SUB, size, bits, right)
 | 
						|
                )
 | 
						|
            )
 | 
						|
        );
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static struct ir* extract_block_refs(struct basicblock* bb)
 | 
						|
{
 | 
						|
    struct ir* outir = NULL;
 | 
						|
    int i;
 | 
						|
 | 
						|
    for (i=0; i<bb->ems.count; i++)
 | 
						|
    {
 | 
						|
        struct em* em = bb->ems.item[i];
 | 
						|
        assert(em->opcode == op_bra);
 | 
						|
        assert(em->paramtype == PARAM_BVALUE);
 | 
						|
 | 
						|
        outir = new_ir2(
 | 
						|
            IR_PAIR, 0,
 | 
						|
            new_bbir(em->u.bvalue.left),
 | 
						|
            outir
 | 
						|
        );
 | 
						|
    }
 | 
						|
 | 
						|
    return outir;
 | 
						|
}
 | 
						|
 | 
						|
static void change_by(struct ir* address, int amount)
 | 
						|
{
 | 
						|
    appendir(
 | 
						|
        store(
 | 
						|
            EM_wordsize, address, 0,
 | 
						|
            new_ir2(
 | 
						|
                IR_ADD, EM_wordsize,
 | 
						|
                load(
 | 
						|
                    EM_wordsize, address, 0
 | 
						|
                ),
 | 
						|
                new_wordir(amount)
 | 
						|
            )
 | 
						|
        )
 | 
						|
    );
 | 
						|
}
 | 
						|
 | 
						|
static struct ir* ptradd(struct ir* address, int offset)
 | 
						|
{
 | 
						|
    if (offset == 0)
 | 
						|
        return address;
 | 
						|
 | 
						|
    return
 | 
						|
        new_ir2(
 | 
						|
            IR_ADD, EM_pointersize,
 | 
						|
            address,
 | 
						|
            new_wordir(offset)
 | 
						|
        );
 | 
						|
}
 | 
						|
 | 
						|
static struct ir* walk_static_chain(int level)
 | 
						|
{
 | 
						|
    struct ir* ir;
 | 
						|
 | 
						|
    /* The static chain, when it exists, is the first argument of each
 | 
						|
     * procedure.  The chain begins with the current frame at level 0,
 | 
						|
     * and continues until we reach the outermost procedure. */
 | 
						|
    ir = new_ir0(
 | 
						|
        IR_GETFP, EM_pointersize
 | 
						|
    );
 | 
						|
    while (level--)
 | 
						|
    {
 | 
						|
        /* Walk to the next frame pointer. */
 | 
						|
        ir = load(
 | 
						|
            EM_pointersize,
 | 
						|
            new_ir1(
 | 
						|
                IR_FPTOAB, EM_pointersize,
 | 
						|
                ir
 | 
						|
            ), 0
 | 
						|
        );
 | 
						|
    }
 | 
						|
    return ir;
 | 
						|
}
 | 
						|
 | 
						|
static void insn_ivalue(int opcode, arith value)
 | 
						|
{
 | 
						|
    switch (opcode)
 | 
						|
    {
 | 
						|
        case op_adi: simple_alu2(opcode, value, IR_ADD, NULL); break;
 | 
						|
        case op_sbi: simple_alu2(opcode, value, IR_SUB, NULL); break;
 | 
						|
        case op_mli: simple_alu2(opcode, value, IR_MUL, NULL); break;
 | 
						|
        case op_dvi: simple_alu2(opcode, value, IR_DIV, NULL); break;
 | 
						|
        case op_rmi: simple_alu2(opcode, value, IR_MOD, NULL); break;
 | 
						|
        case op_sli: simple_alu2(opcode, value, IR_ASL, NULL); break;
 | 
						|
        case op_sri: simple_alu2(opcode, value, IR_ASR, NULL); break;
 | 
						|
        case op_ngi: simple_alu1(opcode, value, IR_NEG, NULL); break;
 | 
						|
 | 
						|
        case op_adu: simple_alu2(opcode, value, IR_ADD, NULL); break;
 | 
						|
        case op_sbu: simple_alu2(opcode, value, IR_SUB, NULL); break;
 | 
						|
        case op_mlu: simple_alu2(opcode, value, IR_MUL, NULL); break;
 | 
						|
        case op_slu: simple_alu2(opcode, value, IR_LSL, NULL); break;
 | 
						|
        case op_sru: simple_alu2(opcode, value, IR_LSR, NULL); break;
 | 
						|
        case op_rmu: simple_alu2(opcode, value, IR_MODU, NULL); break;
 | 
						|
        case op_dvu: simple_alu2(opcode, value, IR_DIVU, NULL); break;
 | 
						|
 | 
						|
        case op_and: simple_alu2(opcode, value, IR_AND, ".and"); break;
 | 
						|
        case op_ior: simple_alu2(opcode, value, IR_OR, ".ior"); break;
 | 
						|
        case op_xor: simple_alu2(opcode, value, IR_EOR, ".xor"); break;
 | 
						|
        case op_com: simple_alu1(opcode, value, IR_NOT, ".com"); break;
 | 
						|
        case op_rol: rotate(opcode, value, IR_LSL, IR_LSR); break;
 | 
						|
        case op_ror: rotate(opcode, value, IR_LSR, IR_LSL); break;
 | 
						|
 | 
						|
        case op_adf: simple_alu2(opcode, value, IR_ADDF, NULL); break;
 | 
						|
        case op_sbf: simple_alu2(opcode, value, IR_SUBF, NULL); break;
 | 
						|
        case op_mlf: simple_alu2(opcode, value, IR_MULF, NULL); break;
 | 
						|
        case op_dvf: simple_alu2(opcode, value, IR_DIVF, NULL); break;
 | 
						|
        case op_ngf: simple_alu1(opcode, value, IR_NEGF, NULL); break;
 | 
						|
 | 
						|
        case op_cms:
 | 
						|
            if (value > (2*EM_wordsize))
 | 
						|
            {
 | 
						|
                push(new_wordir(value));
 | 
						|
                helper_function(".cms");
 | 
						|
                break;
 | 
						|
            }
 | 
						|
            /* fall through */
 | 
						|
        case op_cmu: push(tristate_compare(value, IR_COMPAREUI)); break;
 | 
						|
        case op_cmi: push(tristate_compare(value, IR_COMPARESI)); break;
 | 
						|
        case op_cmf: push(tristate_compare(value, IR_COMPAREF)); break;
 | 
						|
 | 
						|
        case op_rck:
 | 
						|
            if (value != EM_wordsize)
 | 
						|
                fatal("'rck %d' not supported", value);
 | 
						|
            helper_function(".rck");
 | 
						|
            break;
 | 
						|
        case op_set: push(new_wordir(value)); helper_function(".set"); break;
 | 
						|
        case op_inn: push(new_wordir(value)); helper_function(".inn"); break;
 | 
						|
 | 
						|
        case op_lol:
 | 
						|
            push(
 | 
						|
                load(
 | 
						|
                    EM_wordsize,
 | 
						|
                    new_localir(value), 0
 | 
						|
                )
 | 
						|
            );
 | 
						|
            break;
 | 
						|
 | 
						|
        case op_ldl:
 | 
						|
            push(
 | 
						|
                load(
 | 
						|
                    EM_wordsize*2,
 | 
						|
                    new_localir(value), 0
 | 
						|
                )
 | 
						|
            );
 | 
						|
            break;
 | 
						|
 | 
						|
        case op_stl:
 | 
						|
            appendir(
 | 
						|
                store(
 | 
						|
                    EM_wordsize,
 | 
						|
                    new_localir(value), 0,
 | 
						|
                    pop(EM_wordsize)
 | 
						|
                )
 | 
						|
            );
 | 
						|
            break;
 | 
						|
 | 
						|
        case op_sdl:
 | 
						|
            appendir(
 | 
						|
                store(
 | 
						|
                    EM_wordsize*2,
 | 
						|
                    new_localir(value), 0,
 | 
						|
                    pop(EM_wordsize*2)
 | 
						|
                )
 | 
						|
            );
 | 
						|
            break;
 | 
						|
 | 
						|
        case op_lal:
 | 
						|
            push(
 | 
						|
                new_localir(value)
 | 
						|
            );
 | 
						|
            break;
 | 
						|
 | 
						|
        case op_lil:
 | 
						|
            push(
 | 
						|
                load(
 | 
						|
                    EM_wordsize,
 | 
						|
                    load(
 | 
						|
                        EM_pointersize,
 | 
						|
                        new_localir(value), 0
 | 
						|
                    ), 0
 | 
						|
                )
 | 
						|
            );
 | 
						|
            break;
 | 
						|
 | 
						|
        case op_sil:
 | 
						|
            appendir(
 | 
						|
                store(
 | 
						|
                    EM_wordsize,
 | 
						|
                    load(
 | 
						|
                        EM_pointersize,
 | 
						|
                        new_localir(value), 0
 | 
						|
                    ), 0,
 | 
						|
                    pop(EM_wordsize)
 | 
						|
                )
 | 
						|
            );
 | 
						|
            break;
 | 
						|
 | 
						|
        case op_inl:
 | 
						|
            change_by(new_localir(value), 1);
 | 
						|
            break;
 | 
						|
 | 
						|
        case op_del:
 | 
						|
            change_by(new_localir(value), -1);
 | 
						|
            break;
 | 
						|
 | 
						|
        case op_zrl:
 | 
						|
            appendir(
 | 
						|
                store(
 | 
						|
                    EM_wordsize,
 | 
						|
                    new_localir(value), 0,
 | 
						|
                    new_wordir(0)
 | 
						|
                )
 | 
						|
            );
 | 
						|
            break;
 | 
						|
 | 
						|
        case op_zrf:
 | 
						|
        {
 | 
						|
            struct ir* ir = new_constir(value, 0);
 | 
						|
            ir->opcode = IR_CONST;
 | 
						|
            push(ir);
 | 
						|
            break;
 | 
						|
        }
 | 
						|
 | 
						|
        case op_loe:
 | 
						|
            push(
 | 
						|
                load(
 | 
						|
                    EM_wordsize,
 | 
						|
                    new_labelir(".hol0"), value
 | 
						|
                )
 | 
						|
            );
 | 
						|
            break;
 | 
						|
 | 
						|
        case op_lae:
 | 
						|
            push(
 | 
						|
                new_ir2(
 | 
						|
                    IR_ADD, EM_pointersize,
 | 
						|
                    new_labelir(".hol0"),
 | 
						|
                    new_wordir(value)
 | 
						|
                )
 | 
						|
            );
 | 
						|
            break;
 | 
						|
 | 
						|
        case op_ste:
 | 
						|
            appendir(
 | 
						|
                store(
 | 
						|
                    EM_wordsize,
 | 
						|
                    new_labelir(".hol0"), value,
 | 
						|
                    pop(EM_wordsize)
 | 
						|
                )
 | 
						|
            );
 | 
						|
            break;
 | 
						|
 | 
						|
        case op_zre:
 | 
						|
            appendir(
 | 
						|
                store(
 | 
						|
                    EM_wordsize,
 | 
						|
                    new_labelir(".hol0"), value,
 | 
						|
                    new_wordir(0)
 | 
						|
                )
 | 
						|
            );
 | 
						|
            break;
 | 
						|
 | 
						|
        case op_loc:
 | 
						|
            push(
 | 
						|
                new_wordir(value)
 | 
						|
            );
 | 
						|
            break;
 | 
						|
 | 
						|
        case op_loi:
 | 
						|
        {
 | 
						|
            struct ir* ptr = pop(EM_pointersize);
 | 
						|
            int offset = 0;
 | 
						|
 | 
						|
            if (value > (EM_wordsize*2))
 | 
						|
            {
 | 
						|
                /* We're going to need to do multiple loads; fix the address
 | 
						|
                 * so it'll go into a register and we can do maths on it. */
 | 
						|
                appendir(ptr);
 | 
						|
            }
 | 
						|
 | 
						|
            /* Stack grows down.  Load backwards. */
 | 
						|
            while (value > 0)
 | 
						|
            {
 | 
						|
                int s = EM_wordsize*2;
 | 
						|
                if (value < s)
 | 
						|
                    s = value;
 | 
						|
                value -= s;
 | 
						|
                push(
 | 
						|
                    load(
 | 
						|
                        s,
 | 
						|
                        ptr, value
 | 
						|
                    )
 | 
						|
                );
 | 
						|
            }
 | 
						|
 | 
						|
            assert(value == 0);
 | 
						|
            break;
 | 
						|
        }
 | 
						|
 | 
						|
        case op_lof:
 | 
						|
        {
 | 
						|
            struct ir* ptr = pop(EM_pointersize);
 | 
						|
 | 
						|
            push(
 | 
						|
                load(
 | 
						|
                    EM_wordsize,
 | 
						|
                    ptr, value
 | 
						|
                )
 | 
						|
            );
 | 
						|
            break;
 | 
						|
        }
 | 
						|
 | 
						|
        case op_ldf:
 | 
						|
        {
 | 
						|
            struct ir* ptr = pop(EM_pointersize);
 | 
						|
 | 
						|
            push(
 | 
						|
                load(
 | 
						|
                    EM_wordsize*2,
 | 
						|
                    ptr, value
 | 
						|
                )
 | 
						|
            );
 | 
						|
            break;
 | 
						|
        }
 | 
						|
 | 
						|
        case op_sti:
 | 
						|
        {
 | 
						|
            struct ir* ptr = pop(EM_pointersize);
 | 
						|
            int offset = 0;
 | 
						|
 | 
						|
            if (value > peek(0))
 | 
						|
            {
 | 
						|
                /* We're going to need to do multiple stores; fix the address
 | 
						|
                 * so it'll go into a register and we can do maths on it. */
 | 
						|
                appendir(ptr);
 | 
						|
            }
 | 
						|
 | 
						|
            while (value > 0)
 | 
						|
            {
 | 
						|
                struct ir* v = pop(peek(0));
 | 
						|
                int s = v->size;
 | 
						|
                if (value < s)
 | 
						|
                    s = value;
 | 
						|
 | 
						|
                appendir(
 | 
						|
                    store(
 | 
						|
                        s,
 | 
						|
                        ptr, offset,
 | 
						|
                        v
 | 
						|
                    )
 | 
						|
                );
 | 
						|
 | 
						|
                value -= s;
 | 
						|
                offset += s;
 | 
						|
            }
 | 
						|
 | 
						|
            assert(value == 0);
 | 
						|
            break;
 | 
						|
        }
 | 
						|
 | 
						|
        case op_stf:
 | 
						|
        {
 | 
						|
            struct ir* ptr = pop(EM_pointersize);
 | 
						|
            struct ir* val = pop(EM_wordsize);
 | 
						|
 | 
						|
            appendir(
 | 
						|
                store(
 | 
						|
                    EM_wordsize,
 | 
						|
                    ptr, value,
 | 
						|
                    val
 | 
						|
                )
 | 
						|
            );
 | 
						|
            break;
 | 
						|
        }
 | 
						|
 | 
						|
        case op_sdf:
 | 
						|
        {
 | 
						|
            struct ir* ptr = pop(EM_pointersize);
 | 
						|
            struct ir* val = pop(EM_wordsize*2);
 | 
						|
 | 
						|
            appendir(
 | 
						|
                store(
 | 
						|
                    EM_wordsize*2,
 | 
						|
                    ptr, value,
 | 
						|
                    val
 | 
						|
                )
 | 
						|
            );
 | 
						|
            break;
 | 
						|
        }
 | 
						|
 | 
						|
        case op_ads:
 | 
						|
        {
 | 
						|
            struct ir* off = pop(value);
 | 
						|
            struct ir* ptr = pop(EM_pointersize);
 | 
						|
 | 
						|
            if (value != EM_pointersize)
 | 
						|
                off = convert(off, value, EM_pointersize, IR_FROMUI);
 | 
						|
 | 
						|
            push(
 | 
						|
                new_ir2(
 | 
						|
                    IR_ADD, EM_pointersize,
 | 
						|
                    ptr, off
 | 
						|
                )
 | 
						|
            );
 | 
						|
            break;
 | 
						|
        }
 | 
						|
 | 
						|
        case op_adp:
 | 
						|
        {
 | 
						|
            struct ir* ptr = pop(EM_pointersize);
 | 
						|
 | 
						|
            push(
 | 
						|
                new_ir2(
 | 
						|
                    IR_ADD, EM_pointersize,
 | 
						|
                    ptr,
 | 
						|
                    new_wordir(value)
 | 
						|
                )
 | 
						|
            );
 | 
						|
            break;
 | 
						|
        }
 | 
						|
 | 
						|
        case op_sbs:
 | 
						|
        {
 | 
						|
            struct ir* right = pop(EM_pointersize);
 | 
						|
            struct ir* left = pop(EM_pointersize);
 | 
						|
 | 
						|
            struct ir* delta =
 | 
						|
                new_ir2(
 | 
						|
                    IR_SUB, EM_pointersize,
 | 
						|
                    left, right
 | 
						|
                );
 | 
						|
 | 
						|
            if (value != EM_pointersize)
 | 
						|
                delta = convert(delta, EM_pointersize, value, IR_FROMUI);
 | 
						|
 | 
						|
            push(delta);
 | 
						|
            break;
 | 
						|
        }
 | 
						|
 | 
						|
        case op_dup:
 | 
						|
        {
 | 
						|
            sequence_point();
 | 
						|
            if (value > (2*EM_wordsize))
 | 
						|
            {
 | 
						|
                push(new_wordir(value));
 | 
						|
                helper_function(".dus4");
 | 
						|
            }
 | 
						|
            else if ((value == (EM_wordsize*2)) && (peek(0) == EM_wordsize) && (peek(1) == EM_wordsize))
 | 
						|
            {
 | 
						|
                struct ir* v1 = pop(EM_wordsize);
 | 
						|
                struct ir* v2 = pop(EM_wordsize);
 | 
						|
                push(v2);
 | 
						|
                push(v1);
 | 
						|
                push(v2);
 | 
						|
                push(v1);
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                struct ir* v = pop(value);
 | 
						|
                push(v);
 | 
						|
                push(v);
 | 
						|
            }
 | 
						|
            break;
 | 
						|
        }
 | 
						|
 | 
						|
        case op_dus:
 | 
						|
        {
 | 
						|
            if (value != EM_wordsize)
 | 
						|
                fatal("'dus %d' not supported", value);
 | 
						|
            helper_function(".dus4");
 | 
						|
            break;
 | 
						|
        }
 | 
						|
 | 
						|
        case op_exg:
 | 
						|
        {
 | 
						|
            if (value > (2*EM_wordsize))
 | 
						|
            {
 | 
						|
                push(
 | 
						|
                    new_wordir(value)
 | 
						|
                );
 | 
						|
                helper_function(".exg");
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                struct ir* v1 = pop(value);
 | 
						|
                struct ir* v2 = pop(value);
 | 
						|
                push(v1);
 | 
						|
                push(v2);
 | 
						|
            }
 | 
						|
            break;
 | 
						|
        }
 | 
						|
 | 
						|
        case op_zer:
 | 
						|
        {
 | 
						|
            if (value <= EM_wordsize)
 | 
						|
                push(new_constir(value, 0));
 | 
						|
            else
 | 
						|
            {
 | 
						|
                push(new_wordir(value));
 | 
						|
                helper_function(".zer");
 | 
						|
            }
 | 
						|
            break;
 | 
						|
        }
 | 
						|
 | 
						|
        case op_asp:
 | 
						|
        {
 | 
						|
            switch (value)
 | 
						|
            {
 | 
						|
                case 0:
 | 
						|
                    break;
 | 
						|
 | 
						|
                case -1:
 | 
						|
                case -2:
 | 
						|
                case -4:
 | 
						|
                case -8:
 | 
						|
                    push(new_anyir(-value));
 | 
						|
                    break;
 | 
						|
 | 
						|
                default:
 | 
						|
                    while ((value > 0) && (stackptr > 0))
 | 
						|
                    {
 | 
						|
                        int s = peek(0);
 | 
						|
                        if (s > value)
 | 
						|
                            s = value;
 | 
						|
                        pop(s);
 | 
						|
                        value -= s;
 | 
						|
                    }
 | 
						|
 | 
						|
                    appendir(
 | 
						|
                        new_ir1(
 | 
						|
                            IR_STACKADJUST, EM_pointersize,
 | 
						|
                            new_wordir(value)
 | 
						|
                        )
 | 
						|
                    );
 | 
						|
                    break;
 | 
						|
            }
 | 
						|
            break;
 | 
						|
        }
 | 
						|
 | 
						|
        case op_ass:
 | 
						|
            appendir(
 | 
						|
                new_ir1(
 | 
						|
                    IR_STACKADJUST, EM_pointersize,
 | 
						|
                    pop(value)
 | 
						|
                )
 | 
						|
            );
 | 
						|
            break;
 | 
						|
 | 
						|
        case op_ret:
 | 
						|
        {
 | 
						|
            if (value > 0)
 | 
						|
            {
 | 
						|
                struct ir* retval = pop(value);
 | 
						|
                materialise_stack();
 | 
						|
                appendir(
 | 
						|
                    new_ir1(
 | 
						|
                        IR_SETRET, value,
 | 
						|
                        retval
 | 
						|
                    )
 | 
						|
                );
 | 
						|
            }
 | 
						|
 | 
						|
            if (!current_proc->exit)
 | 
						|
            {
 | 
						|
                current_proc->exit = bb_get(NULL);
 | 
						|
                array_append(¤t_proc->blocks, current_proc->exit);
 | 
						|
 | 
						|
                /* This is actually ignored --- the entire block gets special
 | 
						|
                 * treatment. But a lot of the rest of the code assumes that
 | 
						|
                 * all basic blocks have one instruction, so we insert one. */
 | 
						|
 | 
						|
                array_append(¤t_proc->exit->irs,
 | 
						|
                    new_ir0(
 | 
						|
                        IR_RET, 0
 | 
						|
                    )
 | 
						|
                );
 | 
						|
            }
 | 
						|
 | 
						|
            appendir(
 | 
						|
                new_ir1(
 | 
						|
                    IR_JUMP, 0,
 | 
						|
                    new_bbir(current_proc->exit)
 | 
						|
                )
 | 
						|
            );
 | 
						|
            break;
 | 
						|
        }
 | 
						|
 | 
						|
        case op_lfr:
 | 
						|
        {
 | 
						|
            push(
 | 
						|
                appendir(
 | 
						|
                    new_ir0(
 | 
						|
                        IR_GETRET, value
 | 
						|
                    )
 | 
						|
                )
 | 
						|
            );
 | 
						|
            break;
 | 
						|
        }
 | 
						|
 | 
						|
        case op_csa:
 | 
						|
        case op_csb:
 | 
						|
        {
 | 
						|
            const char* helper = aprintf(".%s",
 | 
						|
                (opcode == op_csa) ? "csa" : "csb");
 | 
						|
            struct ir* descriptor = pop(EM_pointersize);
 | 
						|
 | 
						|
            if (descriptor->opcode != IR_LABEL)
 | 
						|
                fatal("csa/csb are only supported if they refer "
 | 
						|
                    "directly to a descriptor block");
 | 
						|
 | 
						|
            push(descriptor);
 | 
						|
            materialise_stack();
 | 
						|
            appendir(
 | 
						|
                new_ir2(
 | 
						|
                    IR_FARJUMP, 0,
 | 
						|
                    new_labelir(helper),
 | 
						|
                    extract_block_refs(bb_get(descriptor->u.lvalue))
 | 
						|
                )
 | 
						|
            );
 | 
						|
            break;
 | 
						|
        }
 | 
						|
 | 
						|
        case op_sar:
 | 
						|
        case op_lar:
 | 
						|
        case op_aar:
 | 
						|
        {
 | 
						|
            const char* helper;
 | 
						|
            if (value != EM_wordsize)
 | 
						|
                fatal("sar/lar/aar are only supported when using "
 | 
						|
                    "word-size descriptors");
 | 
						|
 | 
						|
            switch (opcode)
 | 
						|
            {
 | 
						|
                case op_sar: helper = ".sar4"; break;
 | 
						|
                case op_lar: helper = ".lar4"; break;
 | 
						|
                case op_aar: helper = ".aar4"; break;
 | 
						|
            }
 | 
						|
 | 
						|
            materialise_stack();
 | 
						|
            /* No push here, because the helper function leaves the result on
 | 
						|
             * the physical stack (which is very dubious). */
 | 
						|
            appendir(
 | 
						|
                new_ir1(
 | 
						|
                    IR_CALL, 0,
 | 
						|
                    new_labelir(helper)
 | 
						|
                )
 | 
						|
            );
 | 
						|
            break;
 | 
						|
        }
 | 
						|
 | 
						|
        case op_lxl:
 | 
						|
            push(
 | 
						|
                walk_static_chain(value)
 | 
						|
            );
 | 
						|
            break;
 | 
						|
 | 
						|
        case op_lxa:
 | 
						|
            push(
 | 
						|
                new_ir1(
 | 
						|
                    IR_FPTOAB, EM_pointersize,
 | 
						|
                    walk_static_chain(value)
 | 
						|
                )
 | 
						|
            );
 | 
						|
            break;
 | 
						|
 | 
						|
        case op_fef:
 | 
						|
        {
 | 
						|
            struct ir* f = pop(value);
 | 
						|
 | 
						|
            /* fef is implemented by calling a helper function which then mutates
 | 
						|
             * the stack. We read the return values off the stack when retracting
 | 
						|
             * the stack pointer. */
 | 
						|
 | 
						|
            push(f);
 | 
						|
            push(new_wordir(0));
 | 
						|
 | 
						|
            materialise_stack();
 | 
						|
            appendir(
 | 
						|
                new_ir1(
 | 
						|
                    IR_CALL, 0,
 | 
						|
                    new_labelir((value == 4) ? ".fef4" : ".fef8")
 | 
						|
                )
 | 
						|
            );
 | 
						|
 | 
						|
            /* exit, leaving an int and then a float (or double) on the stack. */
 | 
						|
            break;
 | 
						|
        }
 | 
						|
 | 
						|
        case op_fif:
 | 
						|
        {
 | 
						|
            /* fif is implemented by calling a helper function which then mutates
 | 
						|
             * the stack. We read the return values off the stack when retracting
 | 
						|
             * the stack pointer. */
 | 
						|
 | 
						|
            /* We start with two floats on the stack. */
 | 
						|
 | 
						|
            materialise_stack();
 | 
						|
            appendir(
 | 
						|
                new_ir1(
 | 
						|
                    IR_CALL, 0,
 | 
						|
                    new_labelir((value == 4) ? ".fif4" : ".fif8")
 | 
						|
                )
 | 
						|
            );
 | 
						|
 | 
						|
            /* exit, leaving two floats (or doubles) on the stack. */
 | 
						|
            break;
 | 
						|
        }
 | 
						|
 | 
						|
        case op_lor:
 | 
						|
        {
 | 
						|
            switch (value)
 | 
						|
            {
 | 
						|
                case 0:
 | 
						|
                    push(
 | 
						|
                        appendir(
 | 
						|
                            new_ir1(
 | 
						|
                                IR_FPTOLB, EM_pointersize,
 | 
						|
                                new_ir0(
 | 
						|
                                    IR_GETFP, EM_pointersize
 | 
						|
                                )
 | 
						|
                            )
 | 
						|
                        )
 | 
						|
                    );
 | 
						|
                    break;
 | 
						|
 | 
						|
                case 1:
 | 
						|
                    materialise_stack();
 | 
						|
                    push(
 | 
						|
                        appendir(
 | 
						|
                            new_ir0(
 | 
						|
                                IR_GETSP, EM_pointersize
 | 
						|
                            )
 | 
						|
                        )
 | 
						|
                    );
 | 
						|
                    break;
 | 
						|
 | 
						|
                default:
 | 
						|
                    fatal("'lor %d' not supported", value);
 | 
						|
            }
 | 
						|
 | 
						|
            break;
 | 
						|
        }
 | 
						|
 | 
						|
        case op_str:
 | 
						|
        {
 | 
						|
            switch (value)
 | 
						|
            {
 | 
						|
                case 0:
 | 
						|
                    appendir(
 | 
						|
                        new_ir1(
 | 
						|
                            IR_SETFP, EM_pointersize,
 | 
						|
                            pop(EM_pointersize)
 | 
						|
                        )
 | 
						|
                    );
 | 
						|
                    break;
 | 
						|
 | 
						|
                case 1:
 | 
						|
                    appendir(
 | 
						|
                        new_ir1(
 | 
						|
                            IR_SETSP, EM_pointersize,
 | 
						|
                            pop(EM_pointersize)
 | 
						|
                        )
 | 
						|
                    );
 | 
						|
                    break;
 | 
						|
 | 
						|
                default:
 | 
						|
                    fatal("'str %d' not supported", value);
 | 
						|
            }
 | 
						|
 | 
						|
            break;
 | 
						|
        }
 | 
						|
 | 
						|
        case op_blm:
 | 
						|
            push(new_wordir(value));
 | 
						|
            helper_function(".bls4");
 | 
						|
            break;
 | 
						|
 | 
						|
        case op_bls:
 | 
						|
            if (value != EM_wordsize)
 | 
						|
                fatal("'bls %d' not supported", value);
 | 
						|
            helper_function(".bls4");
 | 
						|
            break;
 | 
						|
 | 
						|
        case op_los:
 | 
						|
            if (value != EM_wordsize)
 | 
						|
                fatal("'los %d' not supported", value);
 | 
						|
            helper_function_with_arg(".los4", pop(EM_wordsize));
 | 
						|
            break;
 | 
						|
 | 
						|
        case op_sts:
 | 
						|
            if (value != EM_wordsize)
 | 
						|
                fatal("'sts %d' not supported", value);
 | 
						|
            helper_function_with_arg(".sts4", pop(EM_wordsize));
 | 
						|
            break;
 | 
						|
 | 
						|
        case op_lin:
 | 
						|
        {
 | 
						|
            /* Set line number --- ignore. */
 | 
						|
            break;
 | 
						|
        }
 | 
						|
 | 
						|
        default:
 | 
						|
            fatal("treebuilder: unknown ivalue instruction '%s'",
 | 
						|
                em_mnem[opcode - sp_fmnem]);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static void insn_lvalue(int opcode, const char* label, arith offset)
 | 
						|
{
 | 
						|
    switch (opcode)
 | 
						|
    {
 | 
						|
        case op_lpi:
 | 
						|
        case op_lae:
 | 
						|
            push(
 | 
						|
                address_of_external(label, offset)
 | 
						|
            );
 | 
						|
            break;
 | 
						|
 | 
						|
        case op_loe:
 | 
						|
            push(
 | 
						|
                new_ir1(
 | 
						|
                    IR_LOAD, EM_wordsize,
 | 
						|
                    address_of_external(label, offset)
 | 
						|
                )
 | 
						|
            );
 | 
						|
            break;
 | 
						|
 | 
						|
        case op_lde:
 | 
						|
            push(
 | 
						|
                new_ir1(
 | 
						|
                    IR_LOAD, EM_wordsize*2,
 | 
						|
                    address_of_external(label, offset)
 | 
						|
                )
 | 
						|
            );
 | 
						|
            break;
 | 
						|
 | 
						|
        case op_ste:
 | 
						|
            sequence_point();
 | 
						|
            appendir(
 | 
						|
                new_ir2(
 | 
						|
                    IR_STORE, EM_wordsize,
 | 
						|
                    address_of_external(label, offset),
 | 
						|
                    pop(EM_wordsize)
 | 
						|
                )
 | 
						|
            );
 | 
						|
            break;
 | 
						|
 | 
						|
        case op_sde:
 | 
						|
            sequence_point();
 | 
						|
            appendir(
 | 
						|
                new_ir2(
 | 
						|
                    IR_STORE, EM_wordsize*2,
 | 
						|
                    address_of_external(label, offset),
 | 
						|
                    pop(EM_wordsize*2)
 | 
						|
                )
 | 
						|
            );
 | 
						|
            break;
 | 
						|
 | 
						|
        case op_zre:
 | 
						|
            sequence_point();
 | 
						|
            appendir(
 | 
						|
                new_ir2(
 | 
						|
                    IR_STORE, EM_wordsize,
 | 
						|
                    address_of_external(label, offset),
 | 
						|
                    new_wordir(0)
 | 
						|
                )
 | 
						|
            );
 | 
						|
            break;
 | 
						|
 | 
						|
        case op_ine:
 | 
						|
            sequence_point();
 | 
						|
            appendir(
 | 
						|
                new_ir2(
 | 
						|
                    IR_STORE, EM_wordsize,
 | 
						|
                    address_of_external(label, offset),
 | 
						|
                    new_ir2(
 | 
						|
                        IR_ADD, EM_wordsize,
 | 
						|
                        new_ir1(
 | 
						|
                            IR_LOAD, EM_wordsize,
 | 
						|
                            address_of_external(label, offset)
 | 
						|
                        ),
 | 
						|
                        new_wordir(1)
 | 
						|
                    )
 | 
						|
                )
 | 
						|
            );
 | 
						|
            break;
 | 
						|
 | 
						|
        case op_dee:
 | 
						|
            sequence_point();
 | 
						|
            appendir(
 | 
						|
                new_ir2(
 | 
						|
                    IR_STORE, EM_wordsize,
 | 
						|
                    address_of_external(label, offset),
 | 
						|
                    new_ir2(
 | 
						|
                        IR_ADD, EM_wordsize,
 | 
						|
                        new_ir1(
 | 
						|
                            IR_LOAD, EM_wordsize,
 | 
						|
                            address_of_external(label, offset)
 | 
						|
                        ),
 | 
						|
                        new_wordir(-1)
 | 
						|
                    )
 | 
						|
                )
 | 
						|
            );
 | 
						|
            break;
 | 
						|
 | 
						|
        case op_cal:
 | 
						|
            assert(offset == 0);
 | 
						|
            materialise_stack();
 | 
						|
            appendir(
 | 
						|
                new_ir1(
 | 
						|
                    IR_CALL, 0,
 | 
						|
                    new_labelir(label)
 | 
						|
                )
 | 
						|
            );
 | 
						|
            break;
 | 
						|
 | 
						|
        case op_bra:
 | 
						|
            assert(offset == 0);
 | 
						|
            materialise_stack();
 | 
						|
            appendir(
 | 
						|
                new_ir1(
 | 
						|
                    IR_JUMP, 0,
 | 
						|
                    new_labelir(label)
 | 
						|
                )
 | 
						|
            );
 | 
						|
            break;
 | 
						|
 | 
						|
        case op_gto:
 | 
						|
        {
 | 
						|
            struct ir* descriptor = address_of_external(label, offset);
 | 
						|
 | 
						|
            appendir(
 | 
						|
                new_ir1(
 | 
						|
                    IR_SETFP, EM_pointersize,
 | 
						|
                    load(EM_pointersize, descriptor, EM_pointersize*2)
 | 
						|
                )
 | 
						|
            );
 | 
						|
            appendir(
 | 
						|
                new_ir1(
 | 
						|
                    IR_SETSP, EM_pointersize,
 | 
						|
                    load(EM_pointersize, descriptor, EM_pointersize*1)
 | 
						|
                )
 | 
						|
            );
 | 
						|
            appendir(
 | 
						|
                new_ir1(
 | 
						|
                    IR_JUMP, 0,
 | 
						|
                    load(EM_pointersize, descriptor, EM_pointersize*0)
 | 
						|
                )
 | 
						|
            );
 | 
						|
            break;
 | 
						|
        }
 | 
						|
 | 
						|
        case op_fil:
 | 
						|
        {
 | 
						|
            /* Set filename --- ignore. */
 | 
						|
            break;
 | 
						|
        }
 | 
						|
 | 
						|
        default:
 | 
						|
            fatal("treebuilder: unknown lvalue instruction '%s'",
 | 
						|
                em_mnem[opcode - sp_fmnem]);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static void generate_tree(struct basicblock* bb)
 | 
						|
{
 | 
						|
    int i;
 | 
						|
 | 
						|
    tracef('0', "0: block %s\n", bb->name);
 | 
						|
 | 
						|
    current_bb = bb;
 | 
						|
    reset_stack();
 | 
						|
 | 
						|
    for (i=0; i<bb->ems.count; i++)
 | 
						|
    {
 | 
						|
        struct em* em = bb->ems.item[i];
 | 
						|
        tracef('E', "E: read %s ", em_mnem[em->opcode - sp_fmnem]);
 | 
						|
        switch (em->paramtype)
 | 
						|
        {
 | 
						|
            case PARAM_NONE:
 | 
						|
                tracef('E', "\n");
 | 
						|
                insn_simple(em->opcode);
 | 
						|
                break;
 | 
						|
 | 
						|
            case PARAM_IVALUE:
 | 
						|
                tracef('E', "value=%d\n", em->u.ivalue);
 | 
						|
                insn_ivalue(em->opcode, em->u.ivalue);
 | 
						|
                break;
 | 
						|
 | 
						|
            case PARAM_LVALUE:
 | 
						|
                tracef('E', "label=%s offset=%d\n",
 | 
						|
                    em->u.lvalue.label, em->u.lvalue.offset);
 | 
						|
                insn_lvalue(em->opcode, em->u.lvalue.label, em->u.lvalue.offset);
 | 
						|
                break;
 | 
						|
 | 
						|
            case PARAM_BVALUE:
 | 
						|
                tracef('E', "true=%s", em->u.bvalue.left->name);
 | 
						|
                if (em->u.bvalue.right)
 | 
						|
                    tracef('E', " false=%s", em->u.bvalue.right->name);
 | 
						|
                tracef('E', "\n");
 | 
						|
                insn_bvalue(em->opcode, em->u.bvalue.left, em->u.bvalue.right);
 | 
						|
                break;
 | 
						|
 | 
						|
            default:
 | 
						|
                assert(0);
 | 
						|
        }
 | 
						|
 | 
						|
        if (tracing('E'))
 | 
						|
            print_stack();
 | 
						|
    }
 | 
						|
 | 
						|
    /* Yes, we are allowed to leave stuff on the stack at the end of the procedure.
 | 
						|
     * It's discarded as part of the function return. */
 | 
						|
}
 | 
						|
 | 
						|
void tb_procedure(void)
 | 
						|
{
 | 
						|
    int i;
 | 
						|
 | 
						|
    for (i=0; i<current_proc->blocks.count; i++)
 | 
						|
        generate_tree(current_proc->blocks.item[i]);
 | 
						|
}
 | 
						|
 | 
						|
/* vim: set sw=4 ts=4 expandtab : */
 |