#include "mcg.h"

static int next_id = 0;

struct ir* new_ir0(int opcode, int size)
{
	struct ir* ir = calloc(sizeof(struct ir), 1);
	ir->id = next_id++;
	ir->opcode = opcode;
	ir->size = size;
	return ir;
}

struct ir* new_ir1(int opcode, int size,
	struct ir* left)
{
	struct ir* ir = new_ir0(opcode, size);
	ir->left = left;
	return ir;
}

struct ir* new_ir2(int opcode, int size,
	struct ir* left, struct ir* right)
{
	struct ir* ir = new_ir0(opcode, size);
	ir->left = left;
	ir->right = right;
	return ir;
}

struct ir* new_labelir(const char* label)
{
	struct ir* ir = new_ir0(IR_LABEL, EM_pointersize);
	ir->u.lvalue = label;
	return ir;
}

struct ir* new_constir(int size, arith value)
{
	struct ir* ir = new_ir0(IR_CONST, size);
	ir->u.ivalue = value;
	return ir;
}

struct ir* new_wordir(arith value)
{
    return new_constir(EM_wordsize, value);
}

struct ir* new_bbir(struct basicblock* bb)
{
	struct ir* ir = new_ir0(IR_BLOCK, EM_pointersize);
	ir->u.bvalue = bb;
	return ir;
}

struct ir* new_anyir(int size)
{
	return new_ir0(IR_ANY, size);
}

struct ir* new_localir(int offset)
{
    struct ir* ir = new_ir0(IR_LOCAL, EM_pointersize);
    ir->u.ivalue = offset;
    return ir;
}

struct ir* ir_walk(struct ir* ir, ir_walker_t* cb, void* user)
{
    assert(ir->root);
    if (cb(ir, user))
        return ir;

    if (ir->left && (ir->left->root == ir->root))
    {
        struct ir* irr = ir_walk(ir->left, cb, user);
        if (irr)
            return irr;
    }

    if (ir->right && (ir->right->root == ir->root))
    {
        struct ir* irr = ir_walk(ir->right, cb, user);
        if (irr)
            return irr;
    }

    return NULL;
}

static bool finder_cb(struct ir* ir, void* user)
{
    int opcode = *(int*)user;
    if (ir->opcode == opcode)
        return true;
    return false;
}

struct ir* ir_find(struct ir* ir, int opcode)
{
    return ir_walk(ir, finder_cb, &opcode);
}

static void print_expr(char k, const struct ir* ir)
{
    tracef(k, "%s", ir_data[ir->opcode].name);
    if (ir->type)
        tracef(k, ".%c", ir->type);
    else if (ir->size)
        tracef(k, "%d", ir->size);
    tracef(k, "(");

	switch (ir->opcode)
	{
		case IR_CONST:
        case IR_LOCAL:
			tracef(k, "%d", ir->u.ivalue);
			break;

		case IR_LABEL:
			tracef(k, "%s", ir->u.lvalue);
			break;

		case IR_BLOCK:
			tracef(k, "%s", ir->u.bvalue->name);
			break;

        case IR_PHI:
        {
            int i;

            for (i=0; i<ir->u.phivalue.count; i++)
            {
                if (i > 0)
                    tracef(k, ", ");
                tracef(k, "%s=>$%d",
                    ir->u.phivalue.item[i].left->name,
                    ir->u.phivalue.item[i].right->id);
            }
            break;
        }

		default:
            if (ir->left)
            {
                if (ir->left->root != ir->root)
                    tracef(k, "$%d", ir->left->id);
                else
                    print_expr(k, ir->left);
            }
            if (ir->right)
            {
                tracef(k, ", ");
                if (ir->right->root != ir->root)
                    tracef(k, "$%d", ir->right->id);
                else
                    print_expr(k, ir->right);
            }
	}

	tracef(k, ")");
}

void ir_print(char k, const struct ir* ir)
{
	tracef(k, "%c: $%d = ", k, ir->id);
    print_expr(k, ir);
    tracef(k, "\n");
}

/* vim: set sw=4 ts=4 expandtab : */