C-sparse

 view release on metacpan or  search on metacpan

src/sparse-0.4.4/example.c  view on Meta::CPAN

	[OP_OR_LIN] = "or",
	[OP_XOR_LIN] = "xor",
	[OP_AND_BOOL] = "and-bool",
	[OP_OR_BOOL] = "or-bool",

	/* Binary comparison */
	[OP_SET_EQ] = "seteq",
	[OP_SET_NE] = "setne",
	[OP_SET_LE] = "setle",
	[OP_SET_GE] = "setge",
	[OP_SET_LT] = "setlt",
	[OP_SET_GT] = "setgt",
	[OP_SET_B] = "setb",
	[OP_SET_A] = "seta",
	[OP_SET_BE] = "setbe",
	[OP_SET_AE] = "setae",

	/* Uni */
	[OP_NOT_LIN] = "not",
	[OP_NEG] = "neg",

	/* Special three-input */
	[OP_SEL] = "select",
	
	/* Memory */
	[OP_MALLOC] = "malloc",
	[OP_FREE] = "free",
	[OP_ALLOCA] = "alloca",
	[OP_LOAD] = "load",
	[OP_STORE] = "store",
	[OP_SETVAL] = "set",
	[OP_GET_ELEMENT_PTR] = "getelem",

	/* Other */
	[OP_PHI] = "phi",
	[OP_PHISOURCE] = "phisrc",
	[OP_COPY] = "copy",
	[OP_CAST] = "cast",
	[OP_SCAST] = "scast",
	[OP_FPCAST] = "fpcast",
	[OP_PTRCAST] = "ptrcast",
	[OP_CALL] = "call",
	[OP_VANEXT] = "va_next",
	[OP_VAARG] = "va_arg",
	[OP_SLICE] = "slice",
	[OP_SNOP] = "snop",
	[OP_LNOP] = "lnop",
	[OP_NOP] = "nop",
	[OP_DEATHNOTE] = "dead",
	[OP_ASM] = "asm",

	/* Sparse tagging (line numbers, context, whatever) */
	[OP_CONTEXT] = "context",
};

static int last_reg, stack_offset;

struct hardreg {
	const char *name;
	struct pseudo_list *contains;
	unsigned busy:16,
		 dead:8,
		 used:1;
};

#define TAG_DEAD 1
#define TAG_DIRTY 2

/* Our "switch" generation is very very stupid. */
#define SWITCH_REG (1)

static void output_bb(SCTX_ struct basic_block *bb, unsigned long generation);

/*
 * We only know about the caller-clobbered registers
 * right now.
 */
static struct hardreg hardregs[] = {
	{ .name = "%eax" },
	{ .name = "%edx" },
	{ .name = "%ecx" },
	{ .name = "%ebx" },
	{ .name = "%esi" },
	{ .name = "%edi" },

	{ .name = "%ebp" },
	{ .name = "%esp" },
};
#define REGNO 6
#define REG_EBP 6
#define REG_ESP 7

struct bb_state {
	struct position pos;
	struct storage_hash_list *inputs;
	struct storage_hash_list *outputs;
	struct storage_hash_list *internal;

	/* CC cache.. */
	int cc_opcode, cc_dead;
	pseudo_t cc_target;
};

enum optype {
	OP_UNDEF,
	OP_REG,
	OP_VAL,
	OP_MEM,
	OP_ADDR,
};

struct operand {
	enum optype type;
	int size;
	union {
		struct hardreg *reg;
		long long value;
		struct /* OP_MEM and OP_ADDR */ {
			unsigned int offset;
			unsigned int scale;
			struct symbol *sym;

src/sparse-0.4.4/example.c  view on Meta::CPAN

static int can_regenerate(SCTX_ struct bb_state *state, pseudo_t pseudo)
{
	struct storage_hash *in;

	switch (pseudo->type) {
	case PSEUDO_VAL:
	case PSEUDO_SYM:
		return 1;

	default:
		in = find_storage_hash(sctx_ pseudo, state->inputs);
		if (in && in->storage->type != REG_REG)
			return 1;
		in = find_storage_hash(sctx_ pseudo, state->internal);
		if (in)
			return 1;
	}
	return 0;
}

static void flush_one_pseudo(SCTX_ struct bb_state *state, struct hardreg *hardreg, pseudo_t pseudo)
{
	struct storage_hash *out;
	struct storage *storage;

	if (can_regenerate(sctx_ state, pseudo))
		return;

	output_comment(sctx_ state, "flushing %s from %s", show_pseudo(sctx_ pseudo), hardreg->name);
	out = find_storage_hash(sctx_ pseudo, state->internal);
	if (!out) {
		out = find_storage_hash(sctx_ pseudo, state->outputs);
		if (!out)
			out = find_or_create_hash(sctx_ pseudo, &state->internal);
	}
	storage = out->storage;
	switch (storage->type) {
	default:
		/*
		 * Aieee - the next user wants it in a register, but we
		 * need to flush it to memory in between. Which means that
		 * we need to allocate an internal one, dammit..
		 */
		out = find_or_create_hash(sctx_ pseudo, &state->internal);
		storage = out->storage;
		/* Fall through */
	case REG_UDEF:
		alloc_stack(sctx_ state, storage);
		/* Fall through */
	case REG_STACK:
		output_insn(sctx_ state, "movl %s,%s", hardreg->name, show_memop(sctx_ storage));
		break;
	}
}

/* Flush a hardreg out to the storage it has.. */
static void flush_reg(SCTX_ struct bb_state *state, struct hardreg *reg)
{
	pseudo_t pseudo;

	if (reg->busy)
		output_comment(sctx_ state, "reg %s flushed while busy is %d!", reg->name, reg->busy);
	if (!reg->contains)
		return;
	reg->dead = 0;
	reg->used = 1;
	FOR_EACH_PTR(reg->contains, pseudo) {
		if (CURRENT_TAG(pseudo) & TAG_DEAD)
			continue;
		if (!(CURRENT_TAG(pseudo) & TAG_DIRTY))
			continue;
		flush_one_pseudo(sctx_ state, reg, pseudo);
	} END_FOR_EACH_PTR(pseudo);
	free_ptr_list(&reg->contains);
}

static struct storage_hash *find_pseudo_storage(SCTX_ struct bb_state *state, pseudo_t pseudo, struct hardreg *reg)
{
	struct storage_hash *src;

	src = find_storage_hash(sctx_ pseudo, state->internal);
	if (!src) {
		src = find_storage_hash(sctx_ pseudo, state->inputs);
		if (!src) {
			src = find_storage_hash(sctx_ pseudo, state->outputs);
			/* Undefined? Screw it! */
			if (!src)
				return NULL;

			/*
			 * If we found output storage, it had better be local stack
			 * that we flushed to earlier..
			 */
			if (src->storage->type != REG_STACK)
				return NULL;
		}
	}

	/*
	 * Incoming pseudo with out any pre-set storage allocation?
	 * We can make up our own, and obviously prefer to get it
	 * in the register we already selected (if it hasn't been
	 * used yet).
	 */
	if (src->storage->type == REG_UDEF) {
		if (reg && !reg->used) {
			src->storage->type = REG_REG;
			src->storage->regno = reg - hardregs;
			return NULL;
		}
		alloc_stack(sctx_ state, src->storage);
	}
	return src;
}

static void mark_reg_dead(SCTX_ struct bb_state *state, pseudo_t pseudo, struct hardreg *reg)
{
	pseudo_t p;

	FOR_EACH_PTR(reg->contains, p) {
		if (p != pseudo)
			continue;
		if (CURRENT_TAG(p) & TAG_DEAD)
			continue;
		output_comment(sctx_ state, "marking pseudo %s in reg %s dead", show_pseudo(sctx_ pseudo), reg->name);
		TAG_CURRENT(p, TAG_DEAD);
		reg->dead++;
	} END_FOR_EACH_PTR(p);
}

static void add_pseudo_reg(SCTX_ struct bb_state *state, pseudo_t pseudo, struct hardreg *reg)
{
	output_comment(sctx_ state, "added pseudo %s to reg %s", show_pseudo(sctx_ pseudo), reg->name);
	add_ptr_list_tag(&reg->contains, pseudo, TAG_DIRTY);
}

static struct hardreg *preferred_reg(SCTX_ struct bb_state *state, pseudo_t target)
{
	struct storage_hash *dst;

	dst = find_storage_hash(sctx_ target, state->outputs);
	if (dst) {
		struct storage *storage = dst->storage;
		if (storage->type == REG_REG)
			return hardregs + storage->regno;
	}
	return NULL;
}

static struct hardreg *empty_reg(SCTX_ struct bb_state *state)
{
	int i;
	struct hardreg *reg = hardregs;

	for (i = 0; i < REGNO; i++, reg++) {
		if (!reg->contains)
			return reg;
	}
	return NULL;
}

static struct hardreg *target_reg(SCTX_ struct bb_state *state, pseudo_t pseudo, pseudo_t target)
{
	int i;
	int unable_to_find_reg = 0;
	struct hardreg *reg;

	/* First, see if we have a preferred target register.. */
	reg = preferred_reg(sctx_ state, target);
	if (reg && !reg->contains)
		goto found;

	reg = empty_reg(sctx_ state);
	if (reg)
		goto found;

	i = last_reg;
	do {
		i++;
		if (i >= REGNO)
			i = 0;
		reg = hardregs + i;
		if (!reg->busy) {
			flush_reg(sctx_ state, reg);
			last_reg = i;
			goto found;
		}
	} while (i != last_reg);
	assert(unable_to_find_reg);

found:
	add_pseudo_reg(sctx_ state, pseudo, reg);
	return reg;
}

static struct hardreg *find_in_reg(SCTX_ struct bb_state *state, pseudo_t pseudo)
{
	int i;
	struct hardreg *reg;

	for (i = 0; i < REGNO; i++) {
		pseudo_t p;

		reg = hardregs + i;
		FOR_EACH_PTR(reg->contains, p) {
			if (p == pseudo) {
				last_reg = i;
				output_comment(sctx_ state, "found pseudo %s in reg %s (busy=%d)", show_pseudo(sctx_ pseudo), reg->name, reg->busy);
				return reg;
			}
		} END_FOR_EACH_PTR(p);
	}
	return NULL;
}

static void flush_pseudo(SCTX_ struct bb_state *state, pseudo_t pseudo, struct storage *storage)
{
	struct hardreg *reg = find_in_reg(sctx_ state, pseudo);

	if (reg)
		flush_reg(sctx_ state, reg);
}

static void flush_cc_cache_to_reg(SCTX_ struct bb_state *state, pseudo_t pseudo, struct hardreg *reg)
{
	int opcode = state->cc_opcode;

	state->cc_opcode = 0;
	state->cc_target = NULL;
	output_insn(sctx_ state, "%s %s", opcodes[opcode], reg->name);
}

static void flush_cc_cache(SCTX_ struct bb_state *state)
{
	pseudo_t pseudo = state->cc_target;

	if (pseudo) {
		struct hardreg *dst;

		state->cc_target = NULL;

		if (!state->cc_dead) {
			dst = target_reg(sctx_ state, pseudo, pseudo);
			flush_cc_cache_to_reg(sctx_ state, pseudo, dst);
		}
	}
}

static void add_cc_cache(SCTX_ struct bb_state *state, int opcode, pseudo_t pseudo)
{
	assert(!state->cc_target);
	state->cc_target = pseudo;
	state->cc_opcode = opcode;
	state->cc_dead = 0;
	output_comment(sctx_ state, "caching %s", opcodes[opcode]);
}

/* Fill a hardreg with the pseudo it has */
static struct hardreg *fill_reg(SCTX_ struct bb_state *state, struct hardreg *hardreg, pseudo_t pseudo)
{
	struct storage_hash *src;
	struct instruction *def;

	if (state->cc_target == pseudo) {
		flush_cc_cache_to_reg(sctx_ state, pseudo, hardreg);
		return hardreg;
	}

src/sparse-0.4.4/example.c  view on Meta::CPAN

			/* Aiaiaiaiaii! Need to flush it to temporary memory */
			src = find_or_create_hash(sctx_ pseudo, &state->internal);
			/* Fall through */
		default:
			alloc_stack(sctx_ state, src->storage);
			/* Fall through */
		case REG_STACK:
		case REG_FRAME:
			flush_pseudo(sctx_ state, pseudo, src->storage);
			output_insn(sctx_ state, "leal %s,%s", show_memop(sctx_ src->storage), hardreg->name);
			break;
		}
		break;
	case PSEUDO_ARG:
	case PSEUDO_REG:
		def = pseudo->def;
		if (def && def->opcode == OP_SETVAL) {
			output_insn(sctx_ state, "movl $<%s>,%s", show_pseudo(sctx_ def->target), hardreg->name);
			break;
		}
		src = find_pseudo_storage(sctx_ state, pseudo, hardreg);
		if (!src)
			break;
		if (src->flags & TAG_DEAD)
			mark_reg_dead(sctx_ state, pseudo, hardreg);
		output_insn(sctx_ state, "mov.%d %s,%s", 32, show_memop(sctx_ src->storage), hardreg->name);
		break;
	default:
		output_insn(sctx_ state, "reload %s from %s", hardreg->name, show_pseudo(sctx_ pseudo));
		break;
	}
	return hardreg;
}

static struct hardreg *getreg(SCTX_ struct bb_state *state, pseudo_t pseudo, pseudo_t target)
{
	struct hardreg *reg;

	reg = find_in_reg(sctx_ state, pseudo);
	if (reg)
		return reg;
	reg = target_reg(sctx_ state, pseudo, target);
	return fill_reg(sctx_ state, reg, pseudo);
}

static void move_reg(SCTX_ struct bb_state *state, struct hardreg *src, struct hardreg *dst)
{
	output_insn(sctx_ state, "movl %s,%s", src->name, dst->name);
}

static struct hardreg *copy_reg(SCTX_ struct bb_state *state, struct hardreg *src, pseudo_t target)
{
	int i;
	struct hardreg *reg;

	/* If the container has been killed off, just re-use it */
	if (!src->contains)
		return src;

	/* If "src" only has one user, and the contents are dead, we can re-use it */
	if (src->busy == 1 && src->dead == 1)
		return src;

	reg = preferred_reg(sctx_ state, target);
	if (reg && !reg->contains) {
		output_comment(sctx_ state, "copying %s to preferred target %s", show_pseudo(sctx_ target), reg->name);
		move_reg(sctx_ state, src, reg);
		return reg;
	}

	for (i = 0; i < REGNO; i++) {
		reg = hardregs + i;
		if (!reg->contains) {
			output_comment(sctx_ state, "copying %s to %s", show_pseudo(sctx_ target), reg->name);
			output_insn(sctx_ state, "movl %s,%s", src->name, reg->name);
			return reg;
		}
	}

	flush_reg(sctx_ state, src);
	return src;
}

static void put_operand(SCTX_ struct bb_state *state, struct operand *op)
{
	switch (op->type) {
	case OP_REG:
		op->reg->busy--;
		break;
	case OP_ADDR:
	case OP_MEM:
		if (op->base)
			op->base->busy--;
		if (op->index)
			op->index->busy--;
		break;
	default:
		break;
	}
}

static struct operand *alloc_op(SCTX)
{
	struct operand *op = malloc(sizeof(*op));
	memset(op, 0, sizeof(*op));
	return op;
}

static struct operand *get_register_operand(SCTX_ struct bb_state *state, pseudo_t pseudo, pseudo_t target)
{
	struct operand *op = alloc_op(sctx);
	op->type = OP_REG;
	op->reg = getreg(sctx_ state, pseudo, target);
	op->reg->busy++;
	return op;
}

static int get_sym_frame_offset(SCTX_ struct bb_state *state, pseudo_t pseudo)
{
	int offset = pseudo->nr;
	if (offset < 0) {
		offset = alloc_stack_offset(sctx_ 4);
		pseudo->nr = offset;
	}
	return offset;
}

static struct operand *get_generic_operand(SCTX_ struct bb_state *state, pseudo_t pseudo)
{
	struct hardreg *reg;
	struct storage *src;
	struct storage_hash *hash;
	struct operand *op = malloc(sizeof(*op));

	memset(op, 0, sizeof(*op));
	switch (pseudo->type) {
	case PSEUDO_VAL:
		op->type = OP_VAL;
		op->value = pseudo->value;
		break;

	case PSEUDO_SYM: {
		struct symbol *sym = pseudo->sym;
		op->type = OP_ADDR;
		if (sym->ctype.modifiers & MOD_NONLOCAL) {
			op->sym = sym;
			break;
		}
		op->base = hardregs + REG_EBP;
		op->offset = get_sym_frame_offset(sctx_ state, pseudo);
		break;
	}

	default:
		reg = find_in_reg(sctx_ state, pseudo);
		if (reg) {
			op->type = OP_REG;
			op->reg = reg;
			reg->busy++;
			break;
		}
		hash = find_pseudo_storage(sctx_ state, pseudo, NULL);
		if (!hash)
			break;
		src = hash->storage;
		switch (src->type) {
		case REG_REG:
			op->type = OP_REG;
			op->reg = hardregs + src->regno;
			op->reg->busy++;
			break;
		case REG_FRAME:
			op->type = OP_MEM;
			op->offset = src->offset;
			op->base = hardregs + REG_EBP;
			break;
		case REG_STACK:
			op->type = OP_MEM;
			op->offset = src->offset;
			op->base = hardregs + REG_ESP;
			break;
		default:
			break;
		}
	}
	return op;
}

/* Callers should be made to use the proper "operand" formats */
static const char *generic(SCTX_ struct bb_state *state, pseudo_t pseudo)
{
	struct hardreg *reg;
	struct operand *op = get_generic_operand(sctx_ state, pseudo);
	static char buf[100];
	const char *str;

	switch (op->type) {
	case OP_ADDR:
		if (!op->offset && op->base && !op->sym)
			return op->base->name;
		if (op->sym && !op->base) {
			int len = sprintf(buf, "$ %s", show_op(sctx_ state, op));
			if (op->offset)
				sprintf(buf + len, " + %d", op->offset);
			return buf;
		}
		str = show_op(sctx_ state, op);
		put_operand(sctx_ state, op);
		reg = target_reg(sctx_ state, pseudo, NULL);
		output_insn(sctx_ state, "lea %s,%s", show_op(sctx_ state, op), reg->name);
		return reg->name;		

	default:
		str = show_op(sctx_ state, op);
	}
	put_operand(sctx_ state, op);
	return str;
}

static struct operand *get_address_operand(SCTX_ struct bb_state *state, struct instruction *memop)
{
	struct hardreg *base;
	struct operand *op = get_generic_operand(sctx_ state, memop->src);

	switch (op->type) {
	case OP_ADDR:
		op->offset += memop->offset;
		break;
	default:
		put_operand(sctx_ state, op);
		base = getreg(sctx_ state, memop->src, NULL);
		op->type = OP_ADDR;
		op->base = base;
		base->busy++;
		op->offset = memop->offset;
		op->sym = NULL;
	}
	return op;
}

static const char *address(SCTX_ struct bb_state *state, struct instruction *memop)
{
	struct operand *op = get_address_operand(sctx_ state, memop);
	const char *str = show_op(sctx_ state, op);
	put_operand(sctx_ state, op);
	return str;
}

static const char *reg_or_imm(SCTX_ struct bb_state *state, pseudo_t pseudo)
{
	switch(pseudo->type) {
	case PSEUDO_VAL:
		return show_pseudo(sctx_ pseudo);
	default:
		return getreg(sctx_ state, pseudo, NULL)->name;
	}
}

static void kill_dead_reg(SCTX_ struct hardreg *reg)
{
	if (reg->dead) {
		pseudo_t p;
		
		FOR_EACH_PTR(reg->contains, p) {
			if (CURRENT_TAG(p) & TAG_DEAD) {
				DELETE_CURRENT_PTR(p);
				reg->dead--;
			}
		} END_FOR_EACH_PTR(p);
		PACK_PTR_LIST(&reg->contains);
		assert(!reg->dead);
	}
}

static struct hardreg *target_copy_reg(SCTX_ struct bb_state *state, struct hardreg *src, pseudo_t target)
{
	kill_dead_reg(sctx_ src);
	return copy_reg(sctx_ state, src, target);
}

static void do_binop(SCTX_ struct bb_state *state, struct instruction *insn, pseudo_t val1, pseudo_t val2)
{
	const char *op = opcodes[insn->opcode];
	struct operand *src = get_register_operand(sctx_ state, val1, insn->target);
	struct operand *src2 = get_generic_operand(sctx_ state, val2);
	struct hardreg *dst;

	dst = target_copy_reg(sctx_ state, src->reg, insn->target);
	output_insn(sctx_ state, "%s.%d %s,%s", op, insn->size, show_op(sctx_ state, src2), dst->name);
	put_operand(sctx_ state, src);
	put_operand(sctx_ state, src2);
	add_pseudo_reg(sctx_ state, insn->target, dst);
}

src/sparse-0.4.4/example.c  view on Meta::CPAN

 		generate_binop(sctx_ state, insn);
		break;

	case OP_BINCMP ... OP_BINCMP_END:
		generate_compare(sctx_ state, insn);
		break;

	case OP_CAST: case OP_SCAST: case OP_FPCAST: case OP_PTRCAST:
		generate_cast(sctx_ state, insn);
		break;

	case OP_SEL:
		generate_select(sctx_ state, insn);
		break;

	case OP_BR:
		generate_branch(sctx_ state, insn);
		break;

	case OP_SWITCH:
		generate_switch(sctx_ state, insn);
		break;

	case OP_CALL:
		generate_call(sctx_ state, insn);
		break;

	case OP_RET:
		generate_ret(sctx_ state, insn);
		break;

	case OP_ASM:
		generate_asm(sctx_ state, insn);
		break;

	case OP_PHI:
	case OP_PHISOURCE:
	default:
		output_insn(sctx_ state, "unimplemented: %s", show_instruction(sctx_ insn));
		break;
	}
	kill_dead_pseudos(sctx_ state);
}

#define VERY_BUSY 1000
#define REG_FIXED 2000

static void write_reg_to_storage(SCTX_ struct bb_state *state, struct hardreg *reg, pseudo_t pseudo, struct storage *storage)
{
	int i;
	struct hardreg *out;

	switch (storage->type) {
	case REG_REG:
		out = hardregs + storage->regno;
		if (reg == out)
			return;
		output_insn(sctx_ state, "movl %s,%s", reg->name, out->name);
		return;
	case REG_UDEF:
		if (reg->busy < VERY_BUSY) {
			storage->type = REG_REG;
			storage->regno = reg - hardregs;
			reg->busy = REG_FIXED;
			return;
		}

		/* Try to find a non-busy register.. */
		for (i = 0; i < REGNO; i++) {
			out = hardregs + i;
			if (out->contains)
				continue;
			output_insn(sctx_ state, "movl %s,%s", reg->name, out->name);
			storage->type = REG_REG;
			storage->regno = i;
			out->busy = REG_FIXED;
			return;
		}

		/* Fall back on stack allocation ... */
		alloc_stack(sctx_ state, storage);
		/* Fall through */
	default:
		output_insn(sctx_ state, "movl %s,%s", reg->name, show_memop(sctx_ storage));
		return;
	}
}

static void write_val_to_storage(SCTX_ struct bb_state *state, pseudo_t src, struct storage *storage)
{
	struct hardreg *out;

	switch (storage->type) {
	case REG_UDEF:
		alloc_stack(sctx_ state, storage);
	default:
		output_insn(sctx_ state, "movl %s,%s", show_pseudo(sctx_ src), show_memop(sctx_ storage));
		break;
	case REG_REG:
		out = hardregs + storage->regno;
		output_insn(sctx_ state, "movl %s,%s", show_pseudo(sctx_ src), out->name);
	}
}

static void fill_output(SCTX_ struct bb_state *state, pseudo_t pseudo, struct storage *out)
{
	int i;
	struct storage_hash *in;
	struct instruction *def;

	/* Is that pseudo a constant value? */
	switch (pseudo->type) {
	case PSEUDO_VAL:
		write_val_to_storage(sctx_ state, pseudo, out);
		return;
	case PSEUDO_REG:
		def = pseudo->def;
		if (def && def->opcode == OP_SETVAL) {
			write_val_to_storage(sctx_ state, pseudo, out);
			return;
		}
	default:
		break;
	}

	/* See if we have that pseudo in a register.. */
	for (i = 0; i < REGNO; i++) {
		struct hardreg *reg = hardregs + i;
		pseudo_t p;

		FOR_EACH_PTR(reg->contains, p) {
			if (p == pseudo) {
				write_reg_to_storage(sctx_ state, reg, pseudo, out);
				return;
			}
		} END_FOR_EACH_PTR(p);
	}

	/* Do we have it in another storage? */
	in = find_storage_hash(sctx_ pseudo, state->internal);
	if (!in) {
		in = find_storage_hash(sctx_ pseudo, state->inputs);
		/* Undefined? */
		if (!in)
			return;
	}
	switch (out->type) {
	case REG_UDEF:
		*out = *in->storage;
		break;
	case REG_REG:
		output_insn(sctx_ state, "movl %s,%s", show_memop(sctx_ in->storage), hardregs[out->regno].name);
		break;
	default:
		if (out == in->storage)
			break;
		if ((out->type == in->storage->type) && (out->regno == in->storage->regno))
			break;
		output_insn(sctx_ state, "movl %s,%s", show_memop(sctx_ in->storage), show_memop(sctx_ out));
		break;
	}
	return;
}

static int final_pseudo_flush(SCTX_ struct bb_state *state, pseudo_t pseudo, struct hardreg *reg)
{
	struct storage_hash *hash;
	struct storage *out;
	struct hardreg *dst;

	/*
	 * Since this pseudo is live at exit, we'd better have output 
	 * storage for it..
	 */
	hash = find_storage_hash(sctx_ pseudo, state->outputs);
	if (!hash)
		return 1;
	out = hash->storage;

	/* If the output is in a register, try to get it there.. */
	if (out->type == REG_REG) {
		dst = hardregs + out->regno;
		/*
		 * Two good cases: nobody is using the right register,
		 * or we've already set it aside for output..
		 */
		if (!dst->contains || dst->busy > VERY_BUSY)
			goto copy_to_dst;

		/* Aiee. Try to keep it in a register.. */
		dst = empty_reg(sctx_ state);
		if (dst)
			goto copy_to_dst;

		return 0;
	}

	/* If the output is undefined, let's see if we can put it in a register.. */
	if (out->type == REG_UDEF) {
		dst = empty_reg(sctx_ state);
		if (dst) {
			out->type = REG_REG;
			out->regno = dst - hardregs;
			goto copy_to_dst;
		}
		/* Uhhuh. Not so good. No empty registers right now */
		return 0;
	}

	/* If we know we need to flush it, just do so already .. */
	output_insn(sctx_ state, "movl %s,%s", reg->name, show_memop(sctx_ out));
	return 1;

copy_to_dst:
	if (reg == dst)
		return 1;
	output_insn(sctx_ state, "movl %s,%s", reg->name, dst->name);
	add_pseudo_reg(sctx_ state, pseudo, dst);
	return 1;
}

/*
 * This tries to make sure that we put all the pseudos that are
 * live on exit into the proper storage
 */
static void generate_output_storage(SCTX_ struct bb_state *state)
{
	struct storage_hash *entry;

	/* Go through the fixed outputs, making sure we have those regs free */
	FOR_EACH_PTR(state->outputs, entry) {
		struct storage *out = entry->storage;
		if (out->type == REG_REG) {
			struct hardreg *reg = hardregs + out->regno;
			pseudo_t p;
			int flushme = 0;

			reg->busy = REG_FIXED;
			FOR_EACH_PTR(reg->contains, p) {
				if (p == entry->pseudo) {
					flushme = -100;
					continue;
				}
				if (CURRENT_TAG(p) & TAG_DEAD)
					continue;

				/* Try to write back the pseudo to where it should go ... */
				if (final_pseudo_flush(sctx_ state, p, reg)) {
					DELETE_CURRENT_PTR(p);
					continue;
				}
				flushme++;
			} END_FOR_EACH_PTR(p);
			PACK_PTR_LIST(&reg->contains);
			if (flushme > 0)
				flush_reg(sctx_ state, reg);
		}
	} END_FOR_EACH_PTR(entry);

	FOR_EACH_PTR(state->outputs, entry) {
		fill_output(sctx_ state, entry->pseudo, entry->storage);
	} END_FOR_EACH_PTR(entry);
}

static void generate(SCTX_ struct basic_block *bb, struct bb_state *state)
{
	int i;
	struct storage_hash *entry;
	struct instruction *insn;

	for (i = 0; i < REGNO; i++) {
		free_ptr_list(&hardregs[i].contains);
		hardregs[i].busy = 0;
		hardregs[i].dead = 0;
		hardregs[i].used = 0;
	}

	FOR_EACH_PTR(state->inputs, entry) {
		struct storage *storage = entry->storage;
		const char *name = show_storage(sctx_ storage);
		output_comment(sctx_ state, "incoming %s in %s", show_pseudo(sctx_ entry->pseudo), name);
		if (storage->type == REG_REG) {
			int regno = storage->regno;
			add_pseudo_reg(sctx_ state, entry->pseudo, hardregs + regno);
			name = hardregs[regno].name;
		}
	} END_FOR_EACH_PTR(entry);

	output_label(sctx_ state, ".L%p", bb);
	FOR_EACH_PTR(bb->insns, insn) {
		if (!insn->bb)
			continue;
		generate_one_insn(sctx_ insn, state);
	} END_FOR_EACH_PTR(insn);

	if (sctxp verbose) {
		output_comment(sctx_ state, "--- in ---");
		FOR_EACH_PTR(state->inputs, entry) {
			output_comment(sctx_ state, "%s <- %s", show_pseudo(sctx_ entry->pseudo), show_storage(sctx_ entry->storage));
		} END_FOR_EACH_PTR(entry);
		output_comment(sctx_ state, "--- spill ---");
		FOR_EACH_PTR(state->internal, entry) {
			output_comment(sctx_ state, "%s <-> %s", show_pseudo(sctx_ entry->pseudo), show_storage(sctx_ entry->storage));
		} END_FOR_EACH_PTR(entry);
		output_comment(sctx_ state, "--- out ---");
		FOR_EACH_PTR(state->outputs, entry) {
			output_comment(sctx_ state, "%s -> %s", show_pseudo(sctx_ entry->pseudo), show_storage(sctx_ entry->storage));
		} END_FOR_EACH_PTR(entry);
	}
	printf("\n");
}

static void generate_list(SCTX_ struct basic_block_list *list, unsigned long generation)
{
	struct basic_block *bb;
	FOR_EACH_PTR(list, bb) {
		if (bb->generation == generation)
			continue;
		output_bb(sctx_ bb, generation);
	} END_FOR_EACH_PTR(bb);
}

/*
 * Mark all the output registers of all the parents
 * as being "used" - this does not mean that we cannot
 * re-use them, but it means that we cannot ask the
 * parents to pass in another pseudo in one of those
 * registers that it already uses for another child.
 */
static void mark_used_registers(SCTX_ struct basic_block *bb, struct bb_state *state)
{
	struct basic_block *parent;



( run in 1.091 second using v1.01-cache-2.11-cpan-39bf76dae61 )