/* * This file is part of libFirm. * Copyright (C) 2012 University of Karlsruhe. */ /** * @file * @brief Chordal register allocation. * @author Sebastian Hack * @date 08.12.2004 */ #include "bechordal_t.h" #include "bechordal_common.h" #include "beinsn_t.h" #include "beirg.h" #include "belive.h" #include "bemodule.h" #include "besched.h" #include "bipartite.h" #include "debug.h" #include "hungarian.h" #include "irdump.h" #include "iredges_t.h" #include "util.h" #define USE_HUNGARIAN 0 DEBUG_ONLY(static firm_dbg_module_t *dbg = NULL;) static int get_next_free_reg(bitset_t *const available) { return bitset_next_set(available, 0); } static unsigned const *get_decisive_partner_regs(be_operand_t const *const o1, size_t const n_regs) { be_operand_t const *const o2 = o1->partner; if (!o2 || rbitset_contains(o1->regs, o2->regs, n_regs)) { return o1->regs; } else if (rbitset_contains(o2->regs, o1->regs, n_regs)) { return o2->regs; } else { return NULL; } } typedef struct pair_entry { be_operand_t *operand; int pos; bool is_def; } pair_entry_t; static unsigned n_regs; static int compare_entries(const void *a, const void *b) { pair_entry_t *a_entry = (pair_entry_t *)a; pair_entry_t *b_entry = (pair_entry_t *)b; be_operand_t *a_op = a_entry->operand; be_operand_t *b_op = b_entry->operand; const unsigned *a_regs = a_op->regs; const unsigned *b_regs = b_op->regs; int a_count = rbitset_popcount(a_regs, n_regs); int b_count = rbitset_popcount(b_regs, n_regs); if (a_count != b_count) { return a_count - b_count; } for (size_t i = 0, n = BITSET_SIZE_ELEMS(n_regs); i < n; ++i) { unsigned a_regs_i = a_regs[i]; unsigned b_regs_i = b_regs[i]; if (a_regs_i != b_regs_i) { return a_regs_i < b_regs_i ? -1 : 1; } } bool a_is_def = a_entry->is_def; bool b_is_def = b_entry->is_def; if (a_is_def != b_is_def) { return a_is_def - b_is_def; } return a_entry->pos - b_entry->pos; } static bool list_has_irn_else_add(ir_node **const list, size_t const n, ir_node *const irn) { for (ir_node *const *i = list; i != list + n; ++i) { if (*i == irn) return true; } list[n] = irn; return false; } static void pair_up_operands(be_chordal_env_t *const env, be_insn_t *const insn) { int n_ops = insn->n_ops; int use_start = insn->use_start; pair_entry_t *entries = OALLOCNZ(&env->obst, pair_entry_t, n_ops); /* Put definitions and uses into a single list. */ for (int i = 0; i < n_ops; ++i) { pair_entry_t *entry = &entries[i]; be_operand_t *op = &insn->ops[i]; entry->operand = op; entry->is_def = i < use_start; if (entry->is_def) { ir_node *carrier = op->carrier; if (is_Proj(carrier)) { entry->pos = get_Proj_num(carrier); } else { assert(i == 0); entry->pos = 0; } } else { entry->pos = i - use_start; } } /** * Only use most restricted node for each carrier. */ n_regs = env->cls->n_regs; QSORT(entries + use_start, n_ops - use_start, compare_entries); size_t n_uses = 0; ir_node **const uses = ALLOCAN(ir_node*, n_regs); for (int i = use_start; i < n_ops; ++i) { be_operand_t *op = entries[i].operand; if (list_has_irn_else_add(uses, n_uses, op->carrier)) { op->carrier = NULL; } else { ++n_uses; } } /** * Sort the list by register constraints (more restricted operands first). * Use a stable compare function that only depends on the graph structure. */ QSORT(entries, n_ops, compare_entries); /* Greedily pair definitions/uses. */ for (int i = 0; i < n_ops; ++i) { pair_entry_t *op_entry = &entries[i]; be_operand_t *op = op_entry->operand; bool op_is_def = op_entry->is_def; if (op->partner || !op->carrier || (!op_is_def && be_value_live_after(op->carrier, insn->irn))) { continue; } for (int j = i + 1; j < n_ops; ++j) { pair_entry_t *partner_entry = &entries[j]; be_operand_t *partner = partner_entry->operand; bool partner_is_def = partner_entry->is_def; if (!partner->partner && partner->carrier && op_is_def != partner_is_def && rbitsets_have_common(op->regs, partner->regs, n_regs) && (partner_is_def || !be_value_live_after(partner->carrier, insn->irn))) { op->partner = partner; partner->partner = op; break; } } } } static void handle_constraints(be_chordal_env_t *const env, ir_node *const irn) { void *const base = obstack_base(&env->obst); be_insn_t *insn = be_scan_insn(env, irn); /* Perms inserted before the constraint handling phase are considered to be * correctly precolored. These Perms arise during the ABI handling phase. */ if (!insn || is_Phi(irn)) goto end; /* Prepare the constraint handling of this node. * Perms are constructed and Copies are created for constrained values * interfering with the instruction. */ ir_node *const perm = pre_process_constraints(env, &insn); /* find suitable in operands to the out operands of the node. */ pair_up_operands(env, insn); /* Look at the in/out operands and add each operand (and its possible partner) * to a bipartite graph (left: nodes with partners, right: admissible colors). */ int n_alloc = 0; int const n_regs = env->cls->n_regs; ir_node **const alloc_nodes = ALLOCAN(ir_node*, n_regs); pmap *const partners = pmap_create(); #if USE_HUNGARIAN hungarian_problem_t *const bp = hungarian_new(n_regs, n_regs, HUNGARIAN_MATCH_PERFECT); #else bipartite_t *const bp = bipartite_new(n_regs, n_regs); #endif for (int i = 0, n_ops = insn->n_ops; i < n_ops; ++i) { /* If the operand has no partner or the partner has not been marked * for allocation, determine the admissible registers and mark it * for allocation by associating the node and its partner with the * set of admissible registers via a bipartite graph. */ be_operand_t *const op = &insn->ops[i]; if (!op->carrier) continue; if (op->partner && pmap_contains(partners, op->partner->carrier)) continue; ir_node *const partner = op->partner ? op->partner->carrier : NULL; pmap_insert(partners, op->carrier, partner); if (partner != NULL) pmap_insert(partners, partner, op->carrier); alloc_nodes[n_alloc] = op->carrier; DBG((dbg, LEVEL_2, "\tassociating %+F and %+F\n", op->carrier, partner)); unsigned const *const bs = get_decisive_partner_regs(op, n_regs); if (bs) { #ifdef DEBUG_libfirm DBG((dbg, LEVEL_2, "\tallowed registers for %+F:", op->carrier, bs[0])); rbitset_foreach(bs, n_regs, col) { arch_register_t const *const reg = arch_register_for_index(env->cls, col); DB((dbg, LEVEL_2, " %s", reg->name)); } DB((dbg, LEVEL_2, "\n")); #endif rbitset_foreach(bs, n_regs, col) { #if USE_HUNGARIAN hungarian_add(bp, n_alloc, col, 1); #else bipartite_add(bp, n_alloc, col); #endif } } else { DBG((dbg, LEVEL_2, "\tallowed registers for %+F: none\n", op->carrier)); } n_alloc++; } /* Put all nodes which live through the constrained instruction also to the * allocation bipartite graph. They are considered unconstrained. */ if (perm != NULL) { foreach_out_edge(perm, edge) { ir_node *const proj = get_edge_src_irn(edge); assert(is_Proj(proj)); /* Don't insert a node twice. */ if (pmap_contains(partners, proj)) continue; assert(n_alloc < n_regs); alloc_nodes[n_alloc] = proj; pmap_insert(partners, proj, NULL); bitset_foreach(env->allocatable_regs, col) { #if USE_HUNGARIAN hungarian_add(bp, n_alloc, col, 1); #else bipartite_add(bp, n_alloc, col); #endif } n_alloc++; } } /* Compute a valid register allocation. */ int *const assignment = ALLOCAN(int, n_regs); #if USE_HUNGARIAN hungarian_prepare_cost_matrix(bp, HUNGARIAN_MODE_MAXIMIZE_UTIL); int const match_res = hungarian_solve(bp, assignment, NULL, 1); assert(match_res == 0 && "matching failed"); #else bipartite_matching(bp, assignment); #endif /* Assign colors obtained from the matching. */ for (int i = 0; i < n_alloc; ++i) { assert(assignment[i] >= 0 && "there must have been a register assigned (node not register pressure faithful?)"); arch_register_t const *const reg = arch_register_for_index(env->cls, assignment[i]); ir_node *const irn = alloc_nodes[i]; arch_set_irn_register(irn, reg); DBG((dbg, LEVEL_2, "\tsetting %+F to register %s\n", irn, reg->name)); ir_node *const partner = pmap_get(ir_node, partners, irn); if (partner != NULL) { arch_set_irn_register(partner, reg); DBG((dbg, LEVEL_2, "\tsetting %+F to register %s\n", partner, reg->name)); } } #if USE_HUNGARIAN hungarian_free(bp); #else bipartite_free(bp); #endif pmap_destroy(partners); end: obstack_free(&env->obst, base); } /** * Handle constraint nodes in each basic block. * handle_constraints() inserts Perm nodes which perm * over all values live at the constrained node right in front * of the constrained node. These Perms signal a constrained node. * For further comments, refer to handle_constraints(). */ static void constraints(ir_node *const bl, void *const data) { be_chordal_env_t *const env = (be_chordal_env_t*)data; sched_foreach_safe(bl, irn) { handle_constraints(env, irn); } } static void assign(ir_node *const block, void *const env_ptr) { be_chordal_env_t *const env = (be_chordal_env_t*)env_ptr; struct list_head *const head = get_block_border_head(env, block); DBG((dbg, LEVEL_4, "Assigning colors for block %+F\n", block)); DBG((dbg, LEVEL_4, "\tusedef chain for block\n")); foreach_border_head(head, b) { DBG((dbg, LEVEL_4, "\t%s %+F/%d\n", b->is_def ? "def" : "use", b->irn, get_irn_idx(b->irn))); } bitset_t *const available = bitset_alloca(env->allocatable_regs->size); bitset_copy(available, env->allocatable_regs); /* Mind that the sequence of defs from back to front defines a perfect * elimination order. So, coloring the definitions from first to last * will work. */ foreach_border_head(head, b) { ir_node *const irn = b->irn; /* Assign a color, if it is a local def. Global defs already have a * color. */ if (!b->is_def) { /* Make the color available upon a use. */ arch_register_t const *const reg = arch_get_irn_register(irn); assert(reg && "Register must have been assigned"); bitset_set(available, reg->index); } else { arch_register_t const *const reg = arch_get_irn_register(irn); /* All live-ins must have a register assigned. (The dominators were * allocated before.) */ assert(b->is_real || reg); unsigned col; if (reg) { DBG((dbg, LEVEL_4, "%+F has reg %s\n", irn, reg->name)); col = reg->index; assert(bitset_is_set(available, col) && "pre-colored register must be free"); } else { assert(!arch_irn_is_ignore(irn)); col = get_next_free_reg(available); arch_set_irn_register_idx(irn, col); } bitset_clear(available, col); DBG((dbg, LEVEL_1, "\tassigning register %s(%d) to %+F\n", arch_get_irn_register(irn)->name, col, irn)); } } } static void be_ra_chordal_color(be_chordal_env_t *const chordal_env) { ir_graph *const irg = chordal_env->irg; assure_irg_properties(irg, IR_GRAPH_PROPERTY_CONSISTENT_DOMINANCE); be_assure_live_sets(irg); /* Handle register targeting constraints */ be_timer_push(T_CONSTR); dom_tree_walk_irg(irg, constraints, NULL, chordal_env); be_timer_pop(T_CONSTR); be_chordal_dump(BE_CH_DUMP_CONSTR, irg, chordal_env->cls, "constr"); /* First, determine the pressure */ dom_tree_walk_irg(irg, create_borders, NULL, chordal_env); /* Assign the colors */ dom_tree_walk_irg(irg, assign, NULL, chordal_env); } BE_REGISTER_MODULE_CONSTRUCTOR(be_init_chordal) void be_init_chordal(void) { static be_ra_chordal_coloring_t coloring = { be_ra_chordal_color }; FIRM_DBG_REGISTER(dbg, "firm.be.chordal"); be_register_chordal_coloring("default", &coloring); }