From 61d811d40ca37ec171ceca4fdb8e4e171d28554f Mon Sep 17 00:00:00 2001 From: Anthony Pesch Date: Sun, 25 Jun 2017 01:00:31 -0400 Subject: [PATCH] removed extended basic block functionality from optimization passes --- src/jit/passes/load_store_elimination_pass.c | 151 +++--------- src/jit/passes/register_allocation_pass.c | 241 ++++--------------- 2 files changed, 82 insertions(+), 310 deletions(-) diff --git a/src/jit/passes/load_store_elimination_pass.c b/src/jit/passes/load_store_elimination_pass.c index 9542875a..6734d28b 100644 --- a/src/jit/passes/load_store_elimination_pass.c +++ b/src/jit/passes/load_store_elimination_pass.c @@ -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); } diff --git a/src/jit/passes/register_allocation_pass.c b/src/jit/passes/register_allocation_pass.c index 7620436e..7d2b8516 100644 --- a/src/jit/passes/register_allocation_pass.c +++ b/src/jit/passes/register_allocation_pass.c @@ -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; }