mirror of https://github.com/inolen/redream.git
removed extended basic block functionality from optimization passes
This commit is contained in:
parent
35bb07e3d8
commit
61d811d40c
|
@ -8,67 +8,24 @@ DEFINE_STAT(stores_removed, "context stores eliminated");
|
|||
#define MAX_OFFSET 512
|
||||
|
||||
struct lse_entry {
|
||||
/* cache token when this entry was added */
|
||||
uint64_t token;
|
||||
|
||||
int offset;
|
||||
struct ir_value *value;
|
||||
};
|
||||
|
||||
struct lse_state {
|
||||
struct lse_entry available[MAX_OFFSET];
|
||||
struct list_node it;
|
||||
};
|
||||
|
||||
struct lse {
|
||||
struct list live_state;
|
||||
struct list free_state;
|
||||
/* current cache token */
|
||||
uint64_t token;
|
||||
|
||||
/* points to the available state at the top of the live stack */
|
||||
struct lse_entry *available;
|
||||
struct lse_entry available[MAX_OFFSET];
|
||||
};
|
||||
|
||||
static void lse_pop_state(struct lse *lse) {
|
||||
/* pop top state from live list */
|
||||
struct lse_state *state =
|
||||
list_last_entry(&lse->live_state, struct lse_state, it);
|
||||
CHECK_NOTNULL(state);
|
||||
list_remove(&lse->live_state, &state->it);
|
||||
|
||||
/* push state back to free list */
|
||||
list_add(&lse->free_state, &state->it);
|
||||
|
||||
/* cache off current state's available member */
|
||||
state = list_last_entry(&lse->live_state, struct lse_state, it);
|
||||
lse->available = state ? state->available : NULL;
|
||||
}
|
||||
|
||||
static void lse_push_state(struct lse *lse, int copy_from_prev) {
|
||||
struct lse_state *state =
|
||||
list_first_entry(&lse->free_state, struct lse_state, it);
|
||||
|
||||
if (state) {
|
||||
/* remove from the free list */
|
||||
list_remove(&lse->free_state, &state->it);
|
||||
} else {
|
||||
/* allocate new state if one wasn't available on the free list */
|
||||
state = calloc(1, sizeof(struct lse_state));
|
||||
}
|
||||
|
||||
/* push state to live list */
|
||||
list_add(&lse->live_state, &state->it);
|
||||
|
||||
/* copy previous state into new state */
|
||||
if (copy_from_prev && lse->available) {
|
||||
memcpy(state->available, lse->available, sizeof(state->available));
|
||||
} else {
|
||||
memset(state->available, 0, sizeof(state->available));
|
||||
}
|
||||
|
||||
/* cache off current state's available member */
|
||||
lse->available = state->available;
|
||||
}
|
||||
|
||||
static void lse_clear_available(struct lse *lse) {
|
||||
CHECK_NOTNULL(lse->available);
|
||||
memset(lse->available, 0, sizeof(struct lse_entry) * MAX_OFFSET);
|
||||
do {
|
||||
lse->token++;
|
||||
} while (lse->token == 0);
|
||||
}
|
||||
|
||||
static void lse_erase_available(struct lse *lse, int offset, int size) {
|
||||
|
@ -82,16 +39,17 @@ static void lse_erase_available(struct lse *lse, int offset, int size) {
|
|||
struct lse_entry *begin_entry = &lse->available[begin];
|
||||
struct lse_entry *end_entry = &lse->available[end];
|
||||
|
||||
if (begin_entry->value) {
|
||||
if (begin_entry->token == lse->token) {
|
||||
begin = begin_entry->offset;
|
||||
}
|
||||
|
||||
if (end_entry->value) {
|
||||
if (end_entry->token == lse->token) {
|
||||
end = end_entry->offset + ir_type_size(end_entry->value->type) - 1;
|
||||
}
|
||||
|
||||
for (; begin <= end; begin++) {
|
||||
struct lse_entry *entry = &lse->available[begin];
|
||||
entry->token = 0;
|
||||
entry->offset = 0;
|
||||
entry->value = NULL;
|
||||
}
|
||||
|
@ -102,6 +60,11 @@ static struct ir_value *lse_get_available(struct lse *lse, int offset) {
|
|||
|
||||
struct lse_entry *entry = &lse->available[offset];
|
||||
|
||||
/* make sure entry isn't stale */
|
||||
if (entry->token != lse->token) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* entries are added for the entire range of an available value to help with
|
||||
invalidation. if this entry doesn't start at the requested offset, it's
|
||||
not actually valid for reuse */
|
||||
|
@ -125,13 +88,16 @@ static void lse_set_available(struct lse *lse, int offset, struct ir_value *v) {
|
|||
entry where offset == entry.offset is valid for reuse */
|
||||
for (; begin <= end; begin++) {
|
||||
struct lse_entry *entry = &lse->available[begin];
|
||||
entry->token = lse->token;
|
||||
entry->offset = offset;
|
||||
entry->value = v;
|
||||
}
|
||||
}
|
||||
|
||||
static void lse_eliminate_loads_r(struct lse *lse, struct ir *ir,
|
||||
struct ir_block *block) {
|
||||
static void lse_eliminate_loads(struct lse *lse, struct ir *ir,
|
||||
struct ir_block *block) {
|
||||
lse_clear_available(lse);
|
||||
|
||||
list_for_each_entry_safe(instr, &block->instrs, struct ir_instr, it) {
|
||||
if (instr->op == OP_FALLBACK || instr->op == OP_CALL) {
|
||||
lse_clear_available(lse);
|
||||
|
@ -166,53 +132,11 @@ static void lse_eliminate_loads_r(struct lse *lse, struct ir *ir,
|
|||
lse_set_available(lse, offset, instr->arg[1]);
|
||||
}
|
||||
}
|
||||
|
||||
list_for_each_entry(edge, &block->outgoing, struct ir_edge, it) {
|
||||
lse_push_state(lse, 1);
|
||||
|
||||
lse_eliminate_loads_r(lse, ir, edge->dst);
|
||||
|
||||
lse_pop_state(lse);
|
||||
}
|
||||
}
|
||||
|
||||
static void lse_eliminate_loads(struct lse *lse, struct ir *ir) {
|
||||
lse_push_state(lse, 0);
|
||||
|
||||
struct ir_block *head_block =
|
||||
list_first_entry(&ir->blocks, struct ir_block, it);
|
||||
lse_eliminate_loads_r(lse, ir, head_block);
|
||||
|
||||
lse_pop_state(lse);
|
||||
|
||||
CHECK(list_empty(&lse->live_state));
|
||||
}
|
||||
|
||||
static void lse_eliminate_stores_r(struct lse *lse, struct ir *ir,
|
||||
struct ir_block *block) {
|
||||
struct lse_entry *parent_entries = lse->available;
|
||||
|
||||
list_for_each_entry(edge, &block->outgoing, struct ir_edge, it) {
|
||||
lse_push_state(lse, 0);
|
||||
|
||||
lse_eliminate_stores_r(lse, ir, edge->dst);
|
||||
|
||||
/* union results from children */
|
||||
int first = !list_prev_entry(edge, struct ir_edge, it);
|
||||
|
||||
for (int i = 0; i < MAX_OFFSET; i++) {
|
||||
struct lse_entry *existing = &parent_entries[i];
|
||||
struct lse_entry *child = &lse->available[i];
|
||||
|
||||
if (first) {
|
||||
*existing = *child;
|
||||
} else if (memcmp(existing, child, sizeof(*existing))) {
|
||||
memset(existing, 0, sizeof(*existing));
|
||||
}
|
||||
}
|
||||
|
||||
lse_pop_state(lse);
|
||||
}
|
||||
static void lse_eliminate_stores(struct lse *lse, struct ir *ir,
|
||||
struct ir_block *block) {
|
||||
lse_clear_available(lse);
|
||||
|
||||
list_for_each_entry_safe_reverse(instr, &block->instrs, struct ir_instr, it) {
|
||||
if (instr->op == OP_FALLBACK || instr->op == OP_CALL) {
|
||||
|
@ -249,28 +173,17 @@ static void lse_eliminate_stores_r(struct lse *lse, struct ir *ir,
|
|||
}
|
||||
}
|
||||
|
||||
static void lse_eliminate_stores(struct lse *lse, struct ir *ir) {
|
||||
lse_push_state(lse, 0);
|
||||
|
||||
struct ir_block *head_block =
|
||||
list_first_entry(&ir->blocks, struct ir_block, it);
|
||||
lse_eliminate_stores_r(lse, ir, head_block);
|
||||
|
||||
lse_pop_state(lse);
|
||||
}
|
||||
|
||||
void lse_run(struct lse *lse, struct ir *ir) {
|
||||
lse_eliminate_loads(lse, ir);
|
||||
lse_eliminate_stores(lse, ir);
|
||||
list_for_each_entry(block, &ir->blocks, struct ir_block, it) {
|
||||
lse_eliminate_loads(lse, ir, block);
|
||||
}
|
||||
|
||||
list_for_each_entry(block, &ir->blocks, struct ir_block, it) {
|
||||
lse_eliminate_stores(lse, ir, block);
|
||||
}
|
||||
}
|
||||
|
||||
void lse_destroy(struct lse *lse) {
|
||||
CHECK(list_empty(&lse->live_state));
|
||||
|
||||
list_for_each_entry_safe(state, &lse->free_state, struct lse_state, it) {
|
||||
free(state);
|
||||
}
|
||||
|
||||
free(lse);
|
||||
}
|
||||
|
||||
|
|
|
@ -57,51 +57,35 @@ struct ra_use {
|
|||
int next_idx;
|
||||
};
|
||||
|
||||
/* register allocation works over extended basic blocks by recursively iterating
|
||||
each path in the control flow tree, passing the output state of each block as
|
||||
the input state of each of its successor blocks. all state that is passed in
|
||||
this way is encapsulated inside of the ra_state struct, which is pushed and
|
||||
popped to a stack while iterating through ra_push_state / ra_pop_state */
|
||||
struct ra_state {
|
||||
struct ra {
|
||||
const struct jit_register *regs;
|
||||
int num_regs;
|
||||
|
||||
struct ra_bin *bins;
|
||||
|
||||
struct ra_tmp *tmps;
|
||||
int num_tmps;
|
||||
int max_tmps;
|
||||
|
||||
struct list_node it;
|
||||
};
|
||||
|
||||
struct ra {
|
||||
const struct jit_register *regs;
|
||||
int num_regs;
|
||||
|
||||
/* uses are constant throughout allocation, no need to push / pop as part of
|
||||
ra_state struct */
|
||||
struct ra_use *uses;
|
||||
int num_uses;
|
||||
int max_uses;
|
||||
|
||||
struct list live_state;
|
||||
struct list free_state;
|
||||
struct ra_state *state;
|
||||
};
|
||||
|
||||
#define NO_TMP -1
|
||||
#define NO_USE -1
|
||||
|
||||
#define ra_get_bin(i) &ra->state->bins[(i)]
|
||||
#define ra_get_bin(i) &ra->bins[(i)]
|
||||
|
||||
#define ra_get_ordinal(i) ((int)(i)->tag)
|
||||
#define ra_set_ordinal(i, ord) (i)->tag = (intptr_t)(ord)
|
||||
|
||||
#define ra_get_packed(b) \
|
||||
((b)->tmp_idx == NO_TMP ? NULL : &ra->state->tmps[(b)->tmp_idx])
|
||||
#define ra_set_packed(b, t) \
|
||||
(b)->tmp_idx = (t) ? (int)((t)-ra->state->tmps) : NO_TMP
|
||||
((b)->tmp_idx == NO_TMP ? NULL : &ra->tmps[(b)->tmp_idx])
|
||||
#define ra_set_packed(b, t) (b)->tmp_idx = (t) ? (int)((t)-ra->tmps) : NO_TMP
|
||||
|
||||
#define ra_get_tmp(v) (&ra->state->tmps[(v)->tag])
|
||||
#define ra_set_tmp(v, t) (v)->tag = (int)((t)-ra->state->tmps)
|
||||
#define ra_get_tmp(v) (&ra->tmps[(v)->tag])
|
||||
#define ra_set_tmp(v, t) (v)->tag = (int)((t)-ra->tmps)
|
||||
|
||||
static int ra_reg_can_store(const struct jit_register *reg,
|
||||
const struct ir_value *v) {
|
||||
|
@ -109,88 +93,6 @@ static int ra_reg_can_store(const struct jit_register *reg,
|
|||
return (reg->value_types & mask) == mask;
|
||||
}
|
||||
|
||||
static void ra_reset_state(struct ra *ra, struct ra_state *state) {
|
||||
for (int i = 0; i < ra->num_regs; i++) {
|
||||
struct ra_bin *bin = &state->bins[i];
|
||||
bin->tmp_idx = NO_TMP;
|
||||
}
|
||||
|
||||
state->num_tmps = 0;
|
||||
}
|
||||
|
||||
static void ra_copy_state(struct ra *ra, struct ra_state *dst,
|
||||
struct ra_state *src) {
|
||||
if (src->num_tmps > dst->max_tmps) {
|
||||
dst->max_tmps = src->max_tmps;
|
||||
dst->tmps = realloc(dst->tmps, dst->max_tmps * sizeof(struct ra_tmp));
|
||||
}
|
||||
|
||||
memcpy(dst->bins, src->bins, ra->num_regs * sizeof(struct ra_bin));
|
||||
memcpy(dst->tmps, src->tmps, src->num_tmps * sizeof(struct ra_tmp));
|
||||
dst->num_tmps = src->num_tmps;
|
||||
}
|
||||
|
||||
static void ra_destroy_state(struct ra *ra, struct ra_state *state) {
|
||||
free(state->tmps);
|
||||
free(state->bins);
|
||||
free(state);
|
||||
}
|
||||
|
||||
static struct ra_state *ra_create_state(struct ra *ra) {
|
||||
struct ra_state *state = calloc(1, sizeof(struct ra_state));
|
||||
|
||||
state->bins = calloc(ra->num_regs, sizeof(struct ra_bin));
|
||||
|
||||
for (int i = 0; i < ra->num_regs; i++) {
|
||||
struct ra_bin *bin = &state->bins[i];
|
||||
bin->reg = &ra->regs[i];
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
static void ra_pop_state(struct ra *ra) {
|
||||
/* pop top state from live list */
|
||||
struct ra_state *state =
|
||||
list_last_entry(&ra->live_state, struct ra_state, it);
|
||||
CHECK_NOTNULL(state);
|
||||
list_remove(&ra->live_state, &state->it);
|
||||
|
||||
/* push state back to free list */
|
||||
list_add(&ra->free_state, &state->it);
|
||||
|
||||
/* cache off latest state */
|
||||
state = list_last_entry(&ra->live_state, struct ra_state, it);
|
||||
|
||||
ra->state = state;
|
||||
}
|
||||
|
||||
static void ra_push_state(struct ra *ra) {
|
||||
struct ra_state *state =
|
||||
list_first_entry(&ra->free_state, struct ra_state, it);
|
||||
|
||||
if (state) {
|
||||
/* remove from the free list */
|
||||
list_remove(&ra->free_state, &state->it);
|
||||
} else {
|
||||
/* allocate new state if one wasn't available on the free list */
|
||||
state = ra_create_state(ra);
|
||||
}
|
||||
|
||||
/* push state to live list */
|
||||
list_add(&ra->live_state, &state->it);
|
||||
|
||||
/* copy previous state into new state */
|
||||
if (ra->state) {
|
||||
ra_copy_state(ra, state, ra->state);
|
||||
} else {
|
||||
ra_reset_state(ra, state);
|
||||
}
|
||||
|
||||
/* cache off latest state */
|
||||
ra->state = state;
|
||||
}
|
||||
|
||||
static void ra_add_use(struct ra *ra, struct ra_tmp *tmp, int ordinal) {
|
||||
if (ra->num_uses >= ra->max_uses) {
|
||||
/* grow array */
|
||||
|
@ -223,41 +125,34 @@ static void ra_add_use(struct ra *ra, struct ra_tmp *tmp, int ordinal) {
|
|||
}
|
||||
|
||||
static struct ra_tmp *ra_create_tmp(struct ra *ra, struct ir_value *value) {
|
||||
if (ra->state->num_tmps >= ra->state->max_tmps) {
|
||||
if (ra->num_tmps >= ra->max_tmps) {
|
||||
/* grow array */
|
||||
int old_max = ra->state->max_tmps;
|
||||
ra->state->max_tmps = MAX(32, ra->state->max_tmps * 2);
|
||||
ra->state->tmps =
|
||||
realloc(ra->state->tmps, ra->state->max_tmps * sizeof(struct ra_tmp));
|
||||
int old_max = ra->max_tmps;
|
||||
ra->max_tmps = MAX(32, ra->max_tmps * 2);
|
||||
ra->tmps = realloc(ra->tmps, ra->max_tmps * sizeof(struct ra_tmp));
|
||||
|
||||
/* initialize the new entries */
|
||||
memset(ra->state->tmps + old_max, 0,
|
||||
(ra->state->max_tmps - old_max) * sizeof(struct ra_tmp));
|
||||
memset(ra->tmps + old_max, 0,
|
||||
(ra->max_tmps - old_max) * sizeof(struct ra_tmp));
|
||||
}
|
||||
|
||||
/* reset the temporary's state, reusing the previously allocated uses array */
|
||||
struct ra_tmp *tmp = &ra->state->tmps[ra->state->num_tmps];
|
||||
struct ra_tmp *tmp = &ra->tmps[ra->num_tmps];
|
||||
tmp->next_use_idx = NO_USE;
|
||||
tmp->last_use_idx = NO_USE;
|
||||
tmp->value = NULL;
|
||||
tmp->slot = NULL;
|
||||
|
||||
/* assign the temporary to the value */
|
||||
value->tag = ra->state->num_tmps++;
|
||||
value->tag = ra->num_tmps++;
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static void ra_validate_r(struct ra *ra, struct ir *ir, struct ir_block *block,
|
||||
struct ir_value **active_in) {
|
||||
static void ra_validate(struct ra *ra, struct ir *ir, struct ir_block *block) {
|
||||
size_t active_size = sizeof(struct ir_value *) * ra->num_regs;
|
||||
struct ir_value **active = alloca(active_size);
|
||||
|
||||
if (active_in) {
|
||||
memcpy(active, active_in, active_size);
|
||||
} else {
|
||||
memset(active, 0, active_size);
|
||||
}
|
||||
memset(active, 0, active_size);
|
||||
|
||||
list_for_each_entry_safe(instr, &block->instrs, struct ir_instr, it) {
|
||||
for (int i = 0; i < MAX_INSTR_ARGS; i++) {
|
||||
|
@ -275,16 +170,6 @@ static void ra_validate_r(struct ra *ra, struct ir *ir, struct ir_block *block,
|
|||
active[instr->result->reg] = instr->result;
|
||||
}
|
||||
}
|
||||
|
||||
list_for_each_entry(edge, &block->outgoing, struct ir_edge, it) {
|
||||
ra_validate_r(ra, ir, edge->dst, active);
|
||||
}
|
||||
}
|
||||
|
||||
static void ra_validate(struct ra *ra, struct ir *ir) {
|
||||
struct ir_block *head_block =
|
||||
list_first_entry(&ir->blocks, struct ir_block, it);
|
||||
ra_validate_r(ra, ir, head_block, NULL);
|
||||
}
|
||||
|
||||
static void ra_pack_bin(struct ra *ra, struct ra_bin *bin,
|
||||
|
@ -506,7 +391,7 @@ static void ra_expire_tmps(struct ra *ra, struct ir *ir,
|
|||
}
|
||||
}
|
||||
|
||||
static void ra_visit_r(struct ra *ra, struct ir *ir, struct ir_block *block) {
|
||||
static void ra_visit(struct ra *ra, struct ir *ir, struct ir_block *block) {
|
||||
/* use safe iterator to avoid iterating over fills inserted
|
||||
when rewriting arguments */
|
||||
list_for_each_entry_safe(instr, &block->instrs, struct ir_instr, it) {
|
||||
|
@ -518,24 +403,10 @@ static void ra_visit_r(struct ra *ra, struct ir *ir, struct ir_block *block) {
|
|||
|
||||
ra_alloc(ra, ir, instr->result);
|
||||
}
|
||||
|
||||
list_for_each_entry(edge, &block->outgoing, struct ir_edge, it) {
|
||||
ra_push_state(ra);
|
||||
|
||||
ra_visit_r(ra, ir, edge->dst);
|
||||
|
||||
ra_pop_state(ra);
|
||||
}
|
||||
}
|
||||
|
||||
static void ra_visit(struct ra *ra, struct ir *ir) {
|
||||
struct ir_block *head_block =
|
||||
list_first_entry(&ir->blocks, struct ir_block, it);
|
||||
ra_visit_r(ra, ir, head_block);
|
||||
}
|
||||
|
||||
static void ra_create_temporaries_r(struct ra *ra, struct ir *ir,
|
||||
struct ir_block *block) {
|
||||
static void ra_create_temporaries(struct ra *ra, struct ir *ir,
|
||||
struct ir_block *block) {
|
||||
list_for_each_entry(instr, &block->instrs, struct ir_instr, it) {
|
||||
int ordinal = ra_get_ordinal(instr);
|
||||
|
||||
|
@ -555,69 +426,50 @@ static void ra_create_temporaries_r(struct ra *ra, struct ir *ir,
|
|||
ra_add_use(ra, tmp, ordinal);
|
||||
}
|
||||
}
|
||||
|
||||
list_for_each_entry(edge, &block->outgoing, struct ir_edge, it) {
|
||||
ra_create_temporaries_r(ra, ir, edge->dst);
|
||||
}
|
||||
}
|
||||
|
||||
static void ra_create_temporaries(struct ra *ra, struct ir *ir) {
|
||||
struct ir_block *head_block =
|
||||
list_first_entry(&ir->blocks, struct ir_block, it);
|
||||
ra_create_temporaries_r(ra, ir, head_block);
|
||||
}
|
||||
static void ra_assign_ordinals(struct ra *ra, struct ir *ir,
|
||||
struct ir_block *block) {
|
||||
int ordinal = 0;
|
||||
|
||||
static void ra_assign_ordinals_r(struct ra *ra, struct ir *ir,
|
||||
struct ir_block *block, int *ordinal) {
|
||||
/* assign each instruction an ordinal. these ordinals are used to describe
|
||||
the live range of a particular value */
|
||||
list_for_each_entry(instr, &block->instrs, struct ir_instr, it) {
|
||||
ra_set_ordinal(instr, *ordinal);
|
||||
ra_set_ordinal(instr, ordinal);
|
||||
|
||||
/* each instruction could fill up to MAX_INSTR_ARGS, space out ordinals
|
||||
enough to allow for this */
|
||||
(*ordinal) += 1 + MAX_INSTR_ARGS;
|
||||
ordinal += 1 + MAX_INSTR_ARGS;
|
||||
}
|
||||
|
||||
list_for_each_entry(edge, &block->outgoing, struct ir_edge, it) {
|
||||
ra_assign_ordinals_r(ra, ir, edge->dst, ordinal);
|
||||
}
|
||||
}
|
||||
|
||||
static void ra_assign_ordinals(struct ra *ra, struct ir *ir) {
|
||||
int ordinal = 0;
|
||||
struct ir_block *head_block =
|
||||
list_first_entry(&ir->blocks, struct ir_block, it);
|
||||
ra_assign_ordinals_r(ra, ir, head_block, &ordinal);
|
||||
}
|
||||
|
||||
static void ra_reset(struct ra *ra, struct ir *ir) {
|
||||
for (int i = 0; i < ra->num_regs; i++) {
|
||||
struct ra_bin *bin = &ra->bins[i];
|
||||
bin->tmp_idx = NO_TMP;
|
||||
}
|
||||
|
||||
ra->num_tmps = 0;
|
||||
ra->num_uses = 0;
|
||||
}
|
||||
|
||||
void ra_run(struct ra *ra, struct ir *ir) {
|
||||
ra_reset(ra, ir);
|
||||
list_for_each_entry(block, &ir->blocks, struct ir_block, it) {
|
||||
ra_reset(ra, ir);
|
||||
|
||||
ra_push_state(ra);
|
||||
|
||||
ra_assign_ordinals(ra, ir);
|
||||
ra_create_temporaries(ra, ir);
|
||||
ra_visit(ra, ir);
|
||||
#if 0
|
||||
ra_validate(ra, ir);
|
||||
ra_assign_ordinals(ra, ir, block);
|
||||
ra_create_temporaries(ra, ir, block);
|
||||
ra_visit(ra, ir, block);
|
||||
#if 1
|
||||
ra_validate(ra, ir, block);
|
||||
#endif
|
||||
|
||||
ra_pop_state(ra);
|
||||
}
|
||||
}
|
||||
|
||||
void ra_destroy(struct ra *ra) {
|
||||
CHECK(list_empty(&ra->live_state));
|
||||
|
||||
list_for_each_entry_safe(state, &ra->free_state, struct ra_state, it) {
|
||||
ra_destroy_state(ra, state);
|
||||
}
|
||||
|
||||
free(ra->uses);
|
||||
free(ra->tmps);
|
||||
free(ra->bins);
|
||||
free(ra);
|
||||
}
|
||||
|
||||
|
@ -627,5 +479,12 @@ struct ra *ra_create(const struct jit_register *regs, int num_regs) {
|
|||
ra->regs = regs;
|
||||
ra->num_regs = num_regs;
|
||||
|
||||
ra->bins = calloc(ra->num_regs, sizeof(struct ra_bin));
|
||||
|
||||
for (int i = 0; i < ra->num_regs; i++) {
|
||||
struct ra_bin *bin = &ra->bins[i];
|
||||
bin->reg = &ra->regs[i];
|
||||
}
|
||||
|
||||
return ra;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue