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(®->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(®->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(®->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(®->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 )