diff options
author | kan <kan@FreeBSD.org> | 2003-07-11 03:40:53 +0000 |
---|---|---|
committer | kan <kan@FreeBSD.org> | 2003-07-11 03:40:53 +0000 |
commit | 0d580365624a5c68f9e5e130f0e54fe4c8b96822 (patch) | |
tree | c88ded260c2df3d4e9e8c72b19fd37a187d8c7af /contrib/gcc/ra-rewrite.c | |
parent | 9404c74f6c7843b012abf90228de7908fba1093a (diff) | |
parent | b2a8872fbe1ec1c49094559ac7b78e6ea4ab7180 (diff) | |
download | FreeBSD-src-0d580365624a5c68f9e5e130f0e54fe4c8b96822.zip FreeBSD-src-0d580365624a5c68f9e5e130f0e54fe4c8b96822.tar.gz |
This commit was generated by cvs2svn to compensate for changes in r117395,
which included commits to RCS files with non-trunk default branches.
Diffstat (limited to 'contrib/gcc/ra-rewrite.c')
-rw-r--r-- | contrib/gcc/ra-rewrite.c | 1987 |
1 files changed, 1987 insertions, 0 deletions
diff --git a/contrib/gcc/ra-rewrite.c b/contrib/gcc/ra-rewrite.c new file mode 100644 index 0000000..61645e2 --- /dev/null +++ b/contrib/gcc/ra-rewrite.c @@ -0,0 +1,1987 @@ +/* Graph coloring register allocator + Copyright (C) 2001, 2002 Free Software Foundation, Inc. + Contributed by Michael Matz <matz@suse.de> + and Daniel Berlin <dan@cgsoftware.com>. + + This file is part of GCC. + + GCC is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 2, or (at your option) any later version. + + GCC is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along + with GCC; see the file COPYING. If not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "config.h" +#include "system.h" +#include "rtl.h" +#include "tm_p.h" +#include "function.h" +#include "regs.h" +#include "hard-reg-set.h" +#include "basic-block.h" +#include "df.h" +#include "expr.h" +#include "output.h" +#include "except.h" +#include "ra.h" +#include "insn-config.h" +#include "reload.h" + +/* This file is part of the graph coloring register allocator, and + contains the functions to change the insn stream. I.e. it adds + spill code, rewrites insns to use the new registers after + coloring and deletes coalesced moves. */ + +struct rewrite_info; +struct rtx_list; + +static void spill_coalescing PARAMS ((sbitmap, sbitmap)); +static unsigned HOST_WIDE_INT spill_prop_savings PARAMS ((struct web *, + sbitmap)); +static void spill_prop_insert PARAMS ((struct web *, sbitmap, sbitmap)); +static int spill_propagation PARAMS ((sbitmap, sbitmap, sbitmap)); +static void spill_coalprop PARAMS ((void)); +static void allocate_spill_web PARAMS ((struct web *)); +static void choose_spill_colors PARAMS ((void)); +static void rewrite_program PARAMS ((bitmap)); +static void remember_slot PARAMS ((struct rtx_list **, rtx)); +static int slots_overlap_p PARAMS ((rtx, rtx)); +static void delete_overlapping_slots PARAMS ((struct rtx_list **, rtx)); +static int slot_member_p PARAMS ((struct rtx_list *, rtx)); +static void insert_stores PARAMS ((bitmap)); +static int spill_same_color_p PARAMS ((struct web *, struct web *)); +static bool is_partly_live_1 PARAMS ((sbitmap, struct web *)); +static void update_spill_colors PARAMS ((HARD_REG_SET *, struct web *, int)); +static int spill_is_free PARAMS ((HARD_REG_SET *, struct web *)); +static void emit_loads PARAMS ((struct rewrite_info *, int, rtx)); +static void reloads_to_loads PARAMS ((struct rewrite_info *, struct ref **, + unsigned int, struct web **)); +static void rewrite_program2 PARAMS ((bitmap)); +static void mark_refs_for_checking PARAMS ((struct web *, bitmap)); +static void detect_web_parts_to_rebuild PARAMS ((void)); +static void delete_useless_defs PARAMS ((void)); +static void detect_non_changed_webs PARAMS ((void)); +static void reset_changed_flag PARAMS ((void)); + +/* For tracking some statistics, we count the number (and cost) + of deleted move insns. */ +static unsigned int deleted_move_insns; +static unsigned HOST_WIDE_INT deleted_move_cost; + +/* This is the spill coalescing phase. In SPILLED the IDs of all + already spilled webs are noted. In COALESCED the IDs of webs still + to check for coalescing. This tries to coalesce two webs, which were + spilled, are connected by a move, and don't conflict. Greatly + reduces memory shuffling. */ + +static void +spill_coalescing (coalesce, spilled) + sbitmap coalesce, spilled; +{ + struct move_list *ml; + struct move *m; + for (ml = wl_moves; ml; ml = ml->next) + if ((m = ml->move) != NULL) + { + struct web *s = alias (m->source_web); + struct web *t = alias (m->target_web); + if ((TEST_BIT (spilled, s->id) && TEST_BIT (coalesce, t->id)) + || (TEST_BIT (spilled, t->id) && TEST_BIT (coalesce, s->id))) + { + struct conflict_link *wl; + if (TEST_BIT (sup_igraph, s->id * num_webs + t->id) + || TEST_BIT (sup_igraph, t->id * num_webs + s->id) + || s->pattern || t->pattern) + continue; + + deleted_move_insns++; + deleted_move_cost += BLOCK_FOR_INSN (m->insn)->frequency + 1; + PUT_CODE (m->insn, NOTE); + NOTE_LINE_NUMBER (m->insn) = NOTE_INSN_DELETED; + df_insn_modify (df, BLOCK_FOR_INSN (m->insn), m->insn); + + m->target_web->target_of_spilled_move = 1; + if (s == t) + /* May be, already coalesced due to a former move. */ + continue; + /* Merge the nodes S and T in the I-graph. Beware: the merging + of conflicts relies on the fact, that in the conflict list + of T all of it's conflicts are noted. This is currently not + the case if T would be the target of a coalesced web, because + then (in combine () above) only those conflicts were noted in + T from the web which was coalesced into T, which at the time + of combine() were not already on the SELECT stack or were + itself coalesced to something other. */ + if (t->type != SPILLED || s->type != SPILLED) + abort (); + remove_list (t->dlink, &WEBS(SPILLED)); + put_web (t, COALESCED); + t->alias = s; + s->is_coalesced = 1; + t->is_coalesced = 1; + merge_moves (s, t); + for (wl = t->conflict_list; wl; wl = wl->next) + { + struct web *pweb = wl->t; + if (wl->sub == NULL) + record_conflict (s, pweb); + else + { + struct sub_conflict *sl; + for (sl = wl->sub; sl; sl = sl->next) + { + struct web *sweb = NULL; + if (SUBWEB_P (sl->s)) + sweb = find_subweb (s, sl->s->orig_x); + if (!sweb) + sweb = s; + record_conflict (sweb, sl->t); + } + } + /* No decrement_degree here, because we already have colored + the graph, and don't want to insert pweb into any other + list. */ + pweb->num_conflicts -= 1 + t->add_hardregs; + } + } + } +} + +/* Returns the probable saving of coalescing WEB with webs from + SPILLED, in terms of removed move insn cost. */ + +static unsigned HOST_WIDE_INT +spill_prop_savings (web, spilled) + struct web *web; + sbitmap spilled; +{ + unsigned HOST_WIDE_INT savings = 0; + struct move_list *ml; + struct move *m; + unsigned int cost; + if (web->pattern) + return 0; + cost = 1 + MEMORY_MOVE_COST (GET_MODE (web->orig_x), web->regclass, 1); + cost += 1 + MEMORY_MOVE_COST (GET_MODE (web->orig_x), web->regclass, 0); + for (ml = wl_moves; ml; ml = ml->next) + if ((m = ml->move) != NULL) + { + struct web *s = alias (m->source_web); + struct web *t = alias (m->target_web); + if (s != web) + { + struct web *h = s; + s = t; + t = h; + } + if (s != web || !TEST_BIT (spilled, t->id) || t->pattern + || TEST_BIT (sup_igraph, s->id * num_webs + t->id) + || TEST_BIT (sup_igraph, t->id * num_webs + s->id)) + continue; + savings += BLOCK_FOR_INSN (m->insn)->frequency * cost; + } + return savings; +} + +/* This add all IDs of colored webs, which are connected to WEB by a move + to LIST and PROCESSED. */ + +static void +spill_prop_insert (web, list, processed) + struct web *web; + sbitmap list, processed; +{ + struct move_list *ml; + struct move *m; + for (ml = wl_moves; ml; ml = ml->next) + if ((m = ml->move) != NULL) + { + struct web *s = alias (m->source_web); + struct web *t = alias (m->target_web); + if (s != web) + { + struct web *h = s; + s = t; + t = h; + } + if (s != web || t->type != COLORED || TEST_BIT (processed, t->id)) + continue; + SET_BIT (list, t->id); + SET_BIT (processed, t->id); + } +} + +/* The spill propagation pass. If we have to spilled webs, the first + connected through a move to a colored one, and the second also connected + to that colored one, and this colored web is only used to connect both + spilled webs, it might be worthwhile to spill that colored one. + This is the case, if the cost of the removed copy insns (all three webs + could be placed into the same stack slot) is higher than the spill cost + of the web. + TO_PROP are the webs we try to propagate from (i.e. spilled ones), + SPILLED the set of all spilled webs so far and PROCESSED the set + of all webs processed so far, so we don't do work twice. */ + +static int +spill_propagation (to_prop, spilled, processed) + sbitmap to_prop, spilled, processed; +{ + int id; + int again = 0; + sbitmap list = sbitmap_alloc (num_webs); + sbitmap_zero (list); + + /* First insert colored move neighbors into the candidate list. */ + EXECUTE_IF_SET_IN_SBITMAP (to_prop, 0, id, + { + spill_prop_insert (ID2WEB (id), list, processed); + }); + sbitmap_zero (to_prop); + + /* For all candidates, see, if the savings are higher than it's + spill cost. */ + while ((id = sbitmap_first_set_bit (list)) >= 0) + { + struct web *web = ID2WEB (id); + RESET_BIT (list, id); + if (spill_prop_savings (web, spilled) >= web->spill_cost) + { + /* If so, we found a new spilled web. Insert it's colored + move neighbors again, and mark, that we need to repeat the + whole mainloop of spillprog/coalescing again. */ + remove_web_from_list (web); + web->color = -1; + put_web (web, SPILLED); + SET_BIT (spilled, id); + SET_BIT (to_prop, id); + spill_prop_insert (web, list, processed); + again = 1; + } + } + sbitmap_free (list); + return again; +} + +/* The main phase to improve spill costs. This repeatedly runs + spill coalescing and spill propagation, until nothing changes. */ + +static void +spill_coalprop () +{ + sbitmap spilled, processed, to_prop; + struct dlist *d; + int again; + spilled = sbitmap_alloc (num_webs); + processed = sbitmap_alloc (num_webs); + to_prop = sbitmap_alloc (num_webs); + sbitmap_zero (spilled); + for (d = WEBS(SPILLED); d; d = d->next) + SET_BIT (spilled, DLIST_WEB (d)->id); + sbitmap_copy (to_prop, spilled); + sbitmap_zero (processed); + do + { + spill_coalescing (to_prop, spilled); + /* XXX Currently (with optimistic coalescing) spill_propagation() + doesn't give better code, sometimes it gives worse (but not by much) + code. I believe this is because of slightly wrong cost + measurements. Anyway right now it isn't worth the time it takes, + so deactivate it for now. */ + again = 0 && spill_propagation (to_prop, spilled, processed); + } + while (again); + sbitmap_free (to_prop); + sbitmap_free (processed); + sbitmap_free (spilled); +} + +/* Allocate a spill slot for a WEB. Currently we spill to pseudo + registers, to be able to track also webs for "stack slots", and also + to possibly colorize them. These pseudos are sometimes handled + in a special way, where we know, that they also can represent + MEM references. */ + +static void +allocate_spill_web (web) + struct web *web; +{ + int regno = web->regno; + rtx slot; + if (web->stack_slot) + return; + slot = gen_reg_rtx (PSEUDO_REGNO_MODE (regno)); + web->stack_slot = slot; +} + +/* This chooses a color for all SPILLED webs for interference region + spilling. The heuristic isn't good in any way. */ + +static void +choose_spill_colors () +{ + struct dlist *d; + unsigned HOST_WIDE_INT *costs = (unsigned HOST_WIDE_INT *) + xmalloc (FIRST_PSEUDO_REGISTER * sizeof (costs[0])); + for (d = WEBS(SPILLED); d; d = d->next) + { + struct web *web = DLIST_WEB (d); + struct conflict_link *wl; + int bestc, c; + HARD_REG_SET avail; + memset (costs, 0, FIRST_PSEUDO_REGISTER * sizeof (costs[0])); + for (wl = web->conflict_list; wl; wl = wl->next) + { + struct web *pweb = wl->t; + if (pweb->type == COLORED || pweb->type == PRECOLORED) + costs[pweb->color] += pweb->spill_cost; + } + + COPY_HARD_REG_SET (avail, web->usable_regs); + if (web->crosses_call) + { + /* Add an arbitrary constant cost to colors not usable by + call-crossing webs without saves/loads. */ + for (c = 0; c < FIRST_PSEUDO_REGISTER; c++) + if (TEST_HARD_REG_BIT (call_used_reg_set, c)) + costs[c] += 1000; + } + bestc = -1; + for (c = 0; c < FIRST_PSEUDO_REGISTER; c++) + if ((bestc < 0 || costs[bestc] > costs[c]) + && TEST_HARD_REG_BIT (avail, c) + && HARD_REGNO_MODE_OK (c, PSEUDO_REGNO_MODE (web->regno))) + { + int i, size; + size = HARD_REGNO_NREGS (c, PSEUDO_REGNO_MODE (web->regno)); + for (i = 1; i < size + && TEST_HARD_REG_BIT (avail, c + i); i++); + if (i == size) + bestc = c; + } + web->color = bestc; + ra_debug_msg (DUMP_PROCESS, "choosing color %d for spilled web %d\n", + bestc, web->id); + } + + free (costs); +} + +/* For statistics sake we count the number and cost of all new loads, + stores and emitted rematerializations. */ +static unsigned int emitted_spill_loads; +static unsigned int emitted_spill_stores; +static unsigned int emitted_remat; +static unsigned HOST_WIDE_INT spill_load_cost; +static unsigned HOST_WIDE_INT spill_store_cost; +static unsigned HOST_WIDE_INT spill_remat_cost; + +/* In rewrite_program2() we detect if some def us useless, in the sense, + that the pseudo set is not live anymore at that point. The REF_IDs + of such defs are noted here. */ +static bitmap useless_defs; + +/* This is the simple and fast version of rewriting the program to + include spill code. It spills at every insn containing spilled + defs or uses. Loads are added only if flag_ra_spill_every_use is + nonzero, otherwise only stores will be added. This doesn't + support rematerialization. + NEW_DEATHS is filled with uids for insns, which probably contain + deaths. */ + +static void +rewrite_program (new_deaths) + bitmap new_deaths; +{ + unsigned int i; + struct dlist *d; + bitmap b = BITMAP_XMALLOC (); + + /* We walk over all webs, over all uses/defs. For all webs, we need + to look at spilled webs, and webs coalesced to spilled ones, in case + their alias isn't broken up, or they got spill coalesced. */ + for (i = 0; i < 2; i++) + for (d = (i == 0) ? WEBS(SPILLED) : WEBS(COALESCED); d; d = d->next) + { + struct web *web = DLIST_WEB (d); + struct web *aweb = alias (web); + unsigned int j; + rtx slot; + + /* Is trivially true for spilled webs, but not for coalesced ones. */ + if (aweb->type != SPILLED) + continue; + + /* First add loads before every use, if we have to. */ + if (flag_ra_spill_every_use) + { + bitmap_clear (b); + allocate_spill_web (aweb); + slot = aweb->stack_slot; + for (j = 0; j < web->num_uses; j++) + { + rtx insns, target, source; + rtx insn = DF_REF_INSN (web->uses[j]); + rtx prev = PREV_INSN (insn); + basic_block bb = BLOCK_FOR_INSN (insn); + /* Happens when spill_coalescing() deletes move insns. */ + if (!INSN_P (insn)) + continue; + + /* Check that we didn't already added a load for this web + and insn. Happens, when the an insn uses the same web + multiple times. */ + if (bitmap_bit_p (b, INSN_UID (insn))) + continue; + bitmap_set_bit (b, INSN_UID (insn)); + target = DF_REF_REG (web->uses[j]); + source = slot; + start_sequence (); + if (GET_CODE (target) == SUBREG) + source = simplify_gen_subreg (GET_MODE (target), source, + GET_MODE (source), + SUBREG_BYTE (target)); + ra_emit_move_insn (target, source); + insns = get_insns (); + end_sequence (); + emit_insn_before (insns, insn); + + if (bb->head == insn) + bb->head = NEXT_INSN (prev); + for (insn = PREV_INSN (insn); insn != prev; + insn = PREV_INSN (insn)) + { + set_block_for_insn (insn, bb); + df_insn_modify (df, bb, insn); + } + + emitted_spill_loads++; + spill_load_cost += bb->frequency + 1; + } + } + + /* Now emit the stores after each def. + If any uses were loaded from stackslots (compared to + rematerialized or not reloaded due to IR spilling), + aweb->stack_slot will be set. If not, we don't need to emit + any stack stores. */ + slot = aweb->stack_slot; + bitmap_clear (b); + if (slot) + for (j = 0; j < web->num_defs; j++) + { + rtx insns, source, dest; + rtx insn = DF_REF_INSN (web->defs[j]); + rtx following = NEXT_INSN (insn); + basic_block bb = BLOCK_FOR_INSN (insn); + /* Happens when spill_coalescing() deletes move insns. */ + if (!INSN_P (insn)) + continue; + if (bitmap_bit_p (b, INSN_UID (insn))) + continue; + bitmap_set_bit (b, INSN_UID (insn)); + start_sequence (); + source = DF_REF_REG (web->defs[j]); + dest = slot; + if (GET_CODE (source) == SUBREG) + dest = simplify_gen_subreg (GET_MODE (source), dest, + GET_MODE (dest), + SUBREG_BYTE (source)); + ra_emit_move_insn (dest, source); + + insns = get_insns (); + end_sequence (); + if (insns) + { + emit_insn_after (insns, insn); + if (bb->end == insn) + bb->end = PREV_INSN (following); + for (insn = insns; insn != following; insn = NEXT_INSN (insn)) + { + set_block_for_insn (insn, bb); + df_insn_modify (df, bb, insn); + } + } + else + df_insn_modify (df, bb, insn); + emitted_spill_stores++; + spill_store_cost += bb->frequency + 1; + /* XXX we should set new_deaths for all inserted stores + whose pseudo dies here. + Note, that this isn't the case for _all_ stores. */ + /* I.e. the next is wrong, and might cause some spilltemps + to be categorized as spilltemp2's (i.e. live over a death), + although they aren't. This might make them spill again, + which causes endlessness in the case, this insn is in fact + _no_ death. */ + bitmap_set_bit (new_deaths, INSN_UID (PREV_INSN (following))); + } + } + + BITMAP_XFREE (b); +} + +/* A simple list of rtx's. */ +struct rtx_list +{ + struct rtx_list *next; + rtx x; +}; + +/* Adds X to *LIST. */ + +static void +remember_slot (list, x) + struct rtx_list **list; + rtx x; +{ + struct rtx_list *l; + /* PRE: X is not already in LIST. */ + l = (struct rtx_list *) ra_alloc (sizeof (*l)); + l->next = *list; + l->x = x; + *list = l; +} + +/* Given two rtx' S1 and S2, either being REGs or MEMs (or SUBREGs + thereof), return nonzero, if they overlap. REGs and MEMs don't + overlap, and if they are MEMs they must have an easy address + (plus (basereg) (const_inst x)), otherwise they overlap. */ + +static int +slots_overlap_p (s1, s2) + rtx s1, s2; +{ + rtx base1, base2; + HOST_WIDE_INT ofs1 = 0, ofs2 = 0; + int size1 = GET_MODE_SIZE (GET_MODE (s1)); + int size2 = GET_MODE_SIZE (GET_MODE (s2)); + if (GET_CODE (s1) == SUBREG) + ofs1 = SUBREG_BYTE (s1), s1 = SUBREG_REG (s1); + if (GET_CODE (s2) == SUBREG) + ofs2 = SUBREG_BYTE (s2), s2 = SUBREG_REG (s2); + + if (s1 == s2) + return 1; + + if (GET_CODE (s1) != GET_CODE (s2)) + return 0; + + if (GET_CODE (s1) == REG && GET_CODE (s2) == REG) + { + if (REGNO (s1) != REGNO (s2)) + return 0; + if (ofs1 >= ofs2 + size2 || ofs2 >= ofs1 + size1) + return 0; + return 1; + } + if (GET_CODE (s1) != MEM || GET_CODE (s2) != MEM) + abort (); + s1 = XEXP (s1, 0); + s2 = XEXP (s2, 0); + if (GET_CODE (s1) != PLUS || GET_CODE (XEXP (s1, 0)) != REG + || GET_CODE (XEXP (s1, 1)) != CONST_INT) + return 1; + if (GET_CODE (s2) != PLUS || GET_CODE (XEXP (s2, 0)) != REG + || GET_CODE (XEXP (s2, 1)) != CONST_INT) + return 1; + base1 = XEXP (s1, 0); + base2 = XEXP (s2, 0); + if (!rtx_equal_p (base1, base2)) + return 1; + ofs1 += INTVAL (XEXP (s1, 1)); + ofs2 += INTVAL (XEXP (s2, 1)); + if (ofs1 >= ofs2 + size2 || ofs2 >= ofs1 + size1) + return 0; + return 1; +} + +/* This deletes from *LIST all rtx's which overlap with X in the sense + of slots_overlap_p(). */ + +static void +delete_overlapping_slots (list, x) + struct rtx_list **list; + rtx x; +{ + while (*list) + { + if (slots_overlap_p ((*list)->x, x)) + *list = (*list)->next; + else + list = &((*list)->next); + } +} + +/* Returns nonzero, of X is member of LIST. */ + +static int +slot_member_p (list, x) + struct rtx_list *list; + rtx x; +{ + for (;list; list = list->next) + if (rtx_equal_p (list->x, x)) + return 1; + return 0; +} + +/* A more sophisticated (and slower) method of adding the stores, than + rewrite_program(). This goes backward the insn stream, adding + stores as it goes, but only if it hasn't just added a store to the + same location. NEW_DEATHS is a bitmap filled with uids of insns + containing deaths. */ + +static void +insert_stores (new_deaths) + bitmap new_deaths; +{ + rtx insn; + rtx last_slot = NULL_RTX; + struct rtx_list *slots = NULL; + + /* We go simply backwards over basic block borders. */ + for (insn = get_last_insn (); insn; insn = PREV_INSN (insn)) + { + int uid = INSN_UID (insn); + + /* If we reach a basic block border, which has more than one + outgoing edge, we simply forget all already emitted stores. */ + if (GET_CODE (insn) == BARRIER + || JUMP_P (insn) || can_throw_internal (insn)) + { + last_slot = NULL_RTX; + slots = NULL; + } + if (!INSN_P (insn)) + continue; + + /* If this insn was not just added in this pass. */ + if (uid < insn_df_max_uid) + { + unsigned int n; + rtx following = NEXT_INSN (insn); + basic_block bb = BLOCK_FOR_INSN (insn); + struct ra_insn_info info; + + info = insn_df[uid]; + for (n = 0; n < info.num_defs; n++) + { + struct web *web = def2web[DF_REF_ID (info.defs[n])]; + struct web *aweb = alias (find_web_for_subweb (web)); + rtx slot, source; + if (aweb->type != SPILLED || !aweb->stack_slot) + continue; + slot = aweb->stack_slot; + source = DF_REF_REG (info.defs[n]); + /* adjust_address() might generate code. */ + start_sequence (); + if (GET_CODE (source) == SUBREG) + slot = simplify_gen_subreg (GET_MODE (source), slot, + GET_MODE (slot), + SUBREG_BYTE (source)); + /* If we have no info about emitted stores, or it didn't + contain the location we intend to use soon, then + add the store. */ + if ((!last_slot || !rtx_equal_p (slot, last_slot)) + && ! slot_member_p (slots, slot)) + { + rtx insns, ni; + last_slot = slot; + remember_slot (&slots, slot); + ra_emit_move_insn (slot, source); + insns = get_insns (); + end_sequence (); + if (insns) + { + emit_insn_after (insns, insn); + if (bb->end == insn) + bb->end = PREV_INSN (following); + for (ni = insns; ni != following; ni = NEXT_INSN (ni)) + { + set_block_for_insn (ni, bb); + df_insn_modify (df, bb, ni); + } + } + else + df_insn_modify (df, bb, insn); + emitted_spill_stores++; + spill_store_cost += bb->frequency + 1; + bitmap_set_bit (new_deaths, INSN_UID (PREV_INSN (following))); + } + else + { + /* Otherwise ignore insns from adjust_address() above. */ + end_sequence (); + } + } + } + /* If we look at a load generated by the allocator, forget + the last emitted slot, and additionally clear all slots + overlapping it's source (after all, we need it again). */ + /* XXX If we emit the stack-ref directly into the using insn the + following needs a change, because that is no new insn. Preferably + we would add some notes to the insn, what stackslots are needed + for it. */ + if (uid >= last_max_uid) + { + rtx set = single_set (insn); + last_slot = NULL_RTX; + /* If this was no simple set, give up, and forget everything. */ + if (!set) + slots = NULL; + else + { + if (1 || GET_CODE (SET_SRC (set)) == MEM) + delete_overlapping_slots (&slots, SET_SRC (set)); + } + } + } +} + +/* Returns 1 if both colored webs have some hardregs in common, even if + they are not the same width. */ + +static int +spill_same_color_p (web1, web2) + struct web *web1, *web2; +{ + int c1, size1, c2, size2; + if ((c1 = alias (web1)->color) < 0 || c1 == an_unusable_color) + return 0; + if ((c2 = alias (web2)->color) < 0 || c2 == an_unusable_color) + return 0; + + size1 = web1->type == PRECOLORED + ? 1 : HARD_REGNO_NREGS (c1, PSEUDO_REGNO_MODE (web1->regno)); + size2 = web2->type == PRECOLORED + ? 1 : HARD_REGNO_NREGS (c2, PSEUDO_REGNO_MODE (web2->regno)); + if (c1 >= c2 + size2 || c2 >= c1 + size1) + return 0; + return 1; +} + +/* Given the set of live web IDs LIVE, returns nonzero, if any of WEBs + subwebs (or WEB itself) is live. */ + +static bool +is_partly_live_1 (live, web) + sbitmap live; + struct web *web; +{ + do + if (TEST_BIT (live, web->id)) + return 1; + while ((web = web->subreg_next)); + return 0; +} + +/* Fast version in case WEB has no subwebs. */ +#define is_partly_live(live, web) ((!web->subreg_next) \ + ? TEST_BIT (live, web->id) \ + : is_partly_live_1 (live, web)) + +/* Change the set of currently IN_USE colors according to + WEB's color. Either add those colors to the hardreg set (if ADD + is nonzero), or remove them. */ + +static void +update_spill_colors (in_use, web, add) + HARD_REG_SET *in_use; + struct web *web; + int add; +{ + int c, size; + if ((c = alias (find_web_for_subweb (web))->color) < 0 + || c == an_unusable_color) + return; + size = HARD_REGNO_NREGS (c, GET_MODE (web->orig_x)); + if (SUBWEB_P (web)) + { + c += subreg_regno_offset (c, GET_MODE (SUBREG_REG (web->orig_x)), + SUBREG_BYTE (web->orig_x), + GET_MODE (web->orig_x)); + } + else if (web->type == PRECOLORED) + size = 1; + if (add) + for (; size--;) + SET_HARD_REG_BIT (*in_use, c + size); + else + for (; size--;) + CLEAR_HARD_REG_BIT (*in_use, c + size); +} + +/* Given a set of hardregs currently IN_USE and the color C of WEB, + return -1 if WEB has no color, 1 of it has the unusable color, + 0 if one of it's used hardregs are in use, and 1 otherwise. + Generally, if WEB can't be left colorized return 1. */ + +static int +spill_is_free (in_use, web) + HARD_REG_SET *in_use; + struct web *web; +{ + int c, size; + if ((c = alias (web)->color) < 0) + return -1; + if (c == an_unusable_color) + return 1; + size = web->type == PRECOLORED + ? 1 : HARD_REGNO_NREGS (c, PSEUDO_REGNO_MODE (web->regno)); + for (; size--;) + if (TEST_HARD_REG_BIT (*in_use, c + size)) + return 0; + return 1; +} + + +/* Structure for passing between rewrite_program2() and emit_loads(). */ +struct rewrite_info +{ + /* The web IDs which currently would need a reload. These are + currently live spilled webs, whose color was still free. */ + bitmap need_reload; + /* We need a scratch bitmap, but don't want to allocate one a zillion + times. */ + bitmap scratch; + /* Web IDs of currently live webs. This are the precise IDs, + not just those of the superwebs. If only on part is live, only + that ID is placed here. */ + sbitmap live; + /* An array of webs, which currently need a load added. + They will be emitted when seeing the first death. */ + struct web **needed_loads; + /* The current number of entries in needed_loads. */ + int nl_size; + /* The number of bits set in need_reload. */ + int num_reloads; + /* The current set of hardregs not available. */ + HARD_REG_SET colors_in_use; + /* Nonzero, if we just added some spill temps to need_reload or + needed_loads. In this case we don't wait for the next death + to emit their loads. */ + int any_spilltemps_spilled; + /* Nonzero, if we currently need to emit the loads. E.g. when we + saw an insn containing deaths. */ + int need_load; +}; + +/* The needed_loads list of RI contains some webs for which + we add the actual load insns here. They are added just before + their use last seen. NL_FIRST_RELOAD is the index of the first + load which is a converted reload, all other entries are normal + loads. LAST_BLOCK_INSN is the last insn of the current basic block. */ + +static void +emit_loads (ri, nl_first_reload, last_block_insn) + struct rewrite_info *ri; + int nl_first_reload; + rtx last_block_insn; +{ + int j; + for (j = ri->nl_size; j;) + { + struct web *web = ri->needed_loads[--j]; + struct web *supweb; + struct web *aweb; + rtx ni, slot, reg; + rtx before = NULL_RTX, after = NULL_RTX; + basic_block bb; + /* When spilltemps were spilled for the last insns, their + loads already are emitted, which is noted by setting + needed_loads[] for it to 0. */ + if (!web) + continue; + supweb = find_web_for_subweb (web); + if (supweb->regno >= max_normal_pseudo) + abort (); + /* Check for web being a spilltemp, if we only want to + load spilltemps. Also remember, that we emitted that + load, which we don't need to do when we have a death, + because then all of needed_loads[] is emptied. */ + if (!ri->need_load) + { + if (!supweb->spill_temp) + continue; + else + ri->needed_loads[j] = 0; + } + web->in_load = 0; + /* The adding of reloads doesn't depend on liveness. */ + if (j < nl_first_reload && !TEST_BIT (ri->live, web->id)) + continue; + aweb = alias (supweb); + aweb->changed = 1; + start_sequence (); + if (supweb->pattern) + { + /* XXX If we later allow non-constant sources for rematerialization + we must also disallow coalescing _to_ rematerialized webs + (at least then disallow spilling them, which we already ensure + when flag_ra_break_aliases), or not take the pattern but a + stackslot. */ + if (aweb != supweb) + abort (); + slot = copy_rtx (supweb->pattern); + reg = copy_rtx (supweb->orig_x); + /* Sanity check. orig_x should be a REG rtx, which should be + shared over all RTL, so copy_rtx should have no effect. */ + if (reg != supweb->orig_x) + abort (); + } + else + { + allocate_spill_web (aweb); + slot = aweb->stack_slot; + + /* If we don't copy the RTL there might be some SUBREG + rtx shared in the next iteration although being in + different webs, which leads to wrong code. */ + reg = copy_rtx (web->orig_x); + if (GET_CODE (reg) == SUBREG) + /*slot = adjust_address (slot, GET_MODE (reg), SUBREG_BYTE + (reg));*/ + slot = simplify_gen_subreg (GET_MODE (reg), slot, GET_MODE (slot), + SUBREG_BYTE (reg)); + } + ra_emit_move_insn (reg, slot); + ni = get_insns (); + end_sequence (); + before = web->last_use_insn; + web->last_use_insn = NULL_RTX; + if (!before) + { + if (JUMP_P (last_block_insn)) + before = last_block_insn; + else + after = last_block_insn; + } + if (after) + { + rtx foll = NEXT_INSN (after); + bb = BLOCK_FOR_INSN (after); + emit_insn_after (ni, after); + if (bb->end == after) + bb->end = PREV_INSN (foll); + for (ni = NEXT_INSN (after); ni != foll; ni = NEXT_INSN (ni)) + { + set_block_for_insn (ni, bb); + df_insn_modify (df, bb, ni); + } + } + else + { + rtx prev = PREV_INSN (before); + bb = BLOCK_FOR_INSN (before); + emit_insn_before (ni, before); + if (bb->head == before) + bb->head = NEXT_INSN (prev); + for (; ni != before; ni = NEXT_INSN (ni)) + { + set_block_for_insn (ni, bb); + df_insn_modify (df, bb, ni); + } + } + if (supweb->pattern) + { + emitted_remat++; + spill_remat_cost += bb->frequency + 1; + } + else + { + emitted_spill_loads++; + spill_load_cost += bb->frequency + 1; + } + RESET_BIT (ri->live, web->id); + /* In the special case documented above only emit the reloads and + one load. */ + if (ri->need_load == 2 && j < nl_first_reload) + break; + } + if (ri->need_load) + ri->nl_size = j; +} + +/* Given a set of reloads in RI, an array of NUM_REFS references (either + uses or defs) in REFS, and REF2WEB to translate ref IDs to webs + (either use2web or def2web) convert some reloads to loads. + This looks at the webs referenced, and how they change the set of + available colors. Now put all still live webs, which needed reloads, + and whose colors isn't free anymore, on the needed_loads list. */ + +static void +reloads_to_loads (ri, refs, num_refs, ref2web) + struct rewrite_info *ri; + struct ref **refs; + unsigned int num_refs; + struct web **ref2web; +{ + unsigned int n; + int num_reloads = ri->num_reloads; + for (n = 0; n < num_refs && num_reloads; n++) + { + struct web *web = ref2web[DF_REF_ID (refs[n])]; + struct web *supweb = find_web_for_subweb (web); + int is_death; + int j; + /* Only emit reloads when entering their interference + region. A use of a spilled web never opens an + interference region, independent of it's color. */ + if (alias (supweb)->type == SPILLED) + continue; + if (supweb->type == PRECOLORED + && TEST_HARD_REG_BIT (never_use_colors, supweb->color)) + continue; + /* Note, that if web (and supweb) are DEFs, we already cleared + the corresponding bits in live. I.e. is_death becomes true, which + is what we want. */ + is_death = !TEST_BIT (ri->live, supweb->id); + is_death &= !TEST_BIT (ri->live, web->id); + if (is_death) + { + int old_num_r = num_reloads; + bitmap_clear (ri->scratch); + EXECUTE_IF_SET_IN_BITMAP (ri->need_reload, 0, j, + { + struct web *web2 = ID2WEB (j); + struct web *aweb2 = alias (find_web_for_subweb (web2)); + if (spill_is_free (&(ri->colors_in_use), aweb2) == 0) + abort (); + if (spill_same_color_p (supweb, aweb2) + /* && interfere (web, web2) */) + { + if (!web2->in_load) + { + ri->needed_loads[ri->nl_size++] = web2; + web2->in_load = 1; + } + bitmap_set_bit (ri->scratch, j); + num_reloads--; + } + }); + if (num_reloads != old_num_r) + bitmap_operation (ri->need_reload, ri->need_reload, ri->scratch, + BITMAP_AND_COMPL); + } + } + ri->num_reloads = num_reloads; +} + +/* This adds loads for spilled webs to the program. It uses a kind of + interference region spilling. If flag_ra_ir_spilling is zero it + only uses improved chaitin spilling (adding loads only at insns + containing deaths). */ + +static void +rewrite_program2 (new_deaths) + bitmap new_deaths; +{ + basic_block bb; + int nl_first_reload; + struct rewrite_info ri; + rtx insn; + ri.needed_loads = (struct web **) xmalloc (num_webs * sizeof (struct web *)); + ri.need_reload = BITMAP_XMALLOC (); + ri.scratch = BITMAP_XMALLOC (); + ri.live = sbitmap_alloc (num_webs); + ri.nl_size = 0; + ri.num_reloads = 0; + for (insn = get_last_insn (); insn; insn = PREV_INSN (insn)) + { + basic_block last_bb = NULL; + rtx last_block_insn; + int i, j; + if (!INSN_P (insn)) + insn = prev_real_insn (insn); + while (insn && !(bb = BLOCK_FOR_INSN (insn))) + insn = prev_real_insn (insn); + if (!insn) + break; + i = bb->index + 2; + last_block_insn = insn; + + sbitmap_zero (ri.live); + CLEAR_HARD_REG_SET (ri.colors_in_use); + EXECUTE_IF_SET_IN_BITMAP (live_at_end[i - 2], 0, j, + { + struct web *web = use2web[j]; + struct web *aweb = alias (find_web_for_subweb (web)); + /* A web is only live at end, if it isn't spilled. If we wouldn't + check this, the last uses of spilled web per basic block + wouldn't be detected as deaths, although they are in the final + code. This would lead to cumulating many loads without need, + only increasing register pressure. */ + /* XXX do add also spilled webs which got a color for IR spilling. + Remember to not add to colors_in_use in that case. */ + if (aweb->type != SPILLED /*|| aweb->color >= 0*/) + { + SET_BIT (ri.live, web->id); + if (aweb->type != SPILLED) + update_spill_colors (&(ri.colors_in_use), web, 1); + } + }); + + bitmap_clear (ri.need_reload); + ri.num_reloads = 0; + ri.any_spilltemps_spilled = 0; + if (flag_ra_ir_spilling) + { + struct dlist *d; + int pass; + /* XXX If we don't add spilled nodes into live above, the following + becomes an empty loop. */ + for (pass = 0; pass < 2; pass++) + for (d = (pass) ? WEBS(SPILLED) : WEBS(COALESCED); d; d = d->next) + { + struct web *web = DLIST_WEB (d); + struct web *aweb = alias (web); + if (aweb->type != SPILLED) + continue; + if (is_partly_live (ri.live, web) + && spill_is_free (&(ri.colors_in_use), web) > 0) + { + ri.num_reloads++; + bitmap_set_bit (ri.need_reload, web->id); + /* Last using insn is somewhere in another block. */ + web->last_use_insn = NULL_RTX; + } + } + } + + last_bb = bb; + for (; insn; insn = PREV_INSN (insn)) + { + struct ra_insn_info info; + unsigned int n; + + if (INSN_P (insn) && BLOCK_FOR_INSN (insn) != last_bb) + { + int index = BLOCK_FOR_INSN (insn)->index + 2; + EXECUTE_IF_SET_IN_BITMAP (live_at_end[index - 2], 0, j, + { + struct web *web = use2web[j]; + struct web *aweb = alias (find_web_for_subweb (web)); + if (aweb->type != SPILLED) + { + SET_BIT (ri.live, web->id); + update_spill_colors (&(ri.colors_in_use), web, 1); + } + }); + bitmap_clear (ri.scratch); + EXECUTE_IF_SET_IN_BITMAP (ri.need_reload, 0, j, + { + struct web *web2 = ID2WEB (j); + struct web *supweb2 = find_web_for_subweb (web2); + struct web *aweb2 = alias (supweb2); + if (spill_is_free (&(ri.colors_in_use), aweb2) <= 0) + { + if (!web2->in_load) + { + ri.needed_loads[ri.nl_size++] = web2; + web2->in_load = 1; + } + bitmap_set_bit (ri.scratch, j); + ri.num_reloads--; + } + }); + bitmap_operation (ri.need_reload, ri.need_reload, ri.scratch, + BITMAP_AND_COMPL); + last_bb = BLOCK_FOR_INSN (insn); + last_block_insn = insn; + if (!INSN_P (last_block_insn)) + last_block_insn = prev_real_insn (last_block_insn); + } + + ri.need_load = 0; + if (INSN_P (insn)) + info = insn_df[INSN_UID (insn)]; + + if (INSN_P (insn)) + for (n = 0; n < info.num_defs; n++) + { + struct ref *ref = info.defs[n]; + struct web *web = def2web[DF_REF_ID (ref)]; + struct web *supweb = find_web_for_subweb (web); + int is_non_def = 0; + unsigned int n2; + + supweb = find_web_for_subweb (web); + /* Webs which are defined here, but also used in the same insn + are rmw webs, or this use isn't a death because of looping + constructs. In neither case makes this def available it's + resources. Reloads for it are still needed, it's still + live and it's colors don't become free. */ + for (n2 = 0; n2 < info.num_uses; n2++) + { + struct web *web2 = use2web[DF_REF_ID (info.uses[n2])]; + if (supweb == find_web_for_subweb (web2)) + { + is_non_def = 1; + break; + } + } + if (is_non_def) + continue; + + if (!is_partly_live (ri.live, supweb)) + bitmap_set_bit (useless_defs, DF_REF_ID (ref)); + + RESET_BIT (ri.live, web->id); + if (bitmap_bit_p (ri.need_reload, web->id)) + { + ri.num_reloads--; + bitmap_clear_bit (ri.need_reload, web->id); + } + if (web != supweb) + { + /* XXX subwebs aren't precisely tracked here. We have + everything we need (inverse webs), but the code isn't + yet written. We need to make all completely + overlapping web parts non-live here. */ + /* If by luck now the whole web isn't live anymore, no + reloads for it are needed. */ + if (!is_partly_live (ri.live, supweb) + && bitmap_bit_p (ri.need_reload, supweb->id)) + { + ri.num_reloads--; + bitmap_clear_bit (ri.need_reload, supweb->id); + } + } + else + { + struct web *sweb; + /* If the whole web is defined here, no parts of it are + live anymore and no reloads are needed for them. */ + for (sweb = supweb->subreg_next; sweb; + sweb = sweb->subreg_next) + { + RESET_BIT (ri.live, sweb->id); + if (bitmap_bit_p (ri.need_reload, sweb->id)) + { + ri.num_reloads--; + bitmap_clear_bit (ri.need_reload, sweb->id); + } + } + } + if (alias (supweb)->type != SPILLED) + update_spill_colors (&(ri.colors_in_use), web, 0); + } + + nl_first_reload = ri.nl_size; + + /* CALL_INSNs are not really deaths, but still more registers + are free after a call, than before. + XXX Note, that sometimes reload barfs when we emit insns between + a call and the insn which copies the return register into a + pseudo. */ + if (GET_CODE (insn) == CALL_INSN) + ri.need_load = 1; + else if (INSN_P (insn)) + for (n = 0; n < info.num_uses; n++) + { + struct web *web = use2web[DF_REF_ID (info.uses[n])]; + struct web *supweb = find_web_for_subweb (web); + int is_death; + if (supweb->type == PRECOLORED + && TEST_HARD_REG_BIT (never_use_colors, supweb->color)) + continue; + is_death = !TEST_BIT (ri.live, supweb->id); + is_death &= !TEST_BIT (ri.live, web->id); + if (is_death) + { + ri.need_load = 1; + bitmap_set_bit (new_deaths, INSN_UID (insn)); + break; + } + } + + if (INSN_P (insn) && ri.num_reloads) + { + int old_num_reloads = ri.num_reloads; + reloads_to_loads (&ri, info.uses, info.num_uses, use2web); + + /* If this insn sets a pseudo, which isn't used later + (i.e. wasn't live before) it is a dead store. We need + to emit all reloads which have the same color as this def. + We don't need to check for non-liveness here to detect + the deadness (it anyway is too late, as we already cleared + the liveness in the first loop over the defs), because if it + _would_ be live here, no reload could have that color, as + they would already have been converted to a load. */ + if (ri.num_reloads) + reloads_to_loads (&ri, info.defs, info.num_defs, def2web); + if (ri.num_reloads != old_num_reloads && !ri.need_load) + ri.need_load = 1; + } + + if (ri.nl_size && (ri.need_load || ri.any_spilltemps_spilled)) + emit_loads (&ri, nl_first_reload, last_block_insn); + + if (INSN_P (insn) && flag_ra_ir_spilling) + for (n = 0; n < info.num_uses; n++) + { + struct web *web = use2web[DF_REF_ID (info.uses[n])]; + struct web *aweb = alias (find_web_for_subweb (web)); + if (aweb->type != SPILLED) + update_spill_colors (&(ri.colors_in_use), web, 1); + } + + ri.any_spilltemps_spilled = 0; + if (INSN_P (insn)) + for (n = 0; n < info.num_uses; n++) + { + struct web *web = use2web[DF_REF_ID (info.uses[n])]; + struct web *supweb = find_web_for_subweb (web); + struct web *aweb = alias (supweb); + SET_BIT (ri.live, web->id); + if (aweb->type != SPILLED) + continue; + if (supweb->spill_temp) + ri.any_spilltemps_spilled = 1; + web->last_use_insn = insn; + if (!web->in_load) + { + if (spill_is_free (&(ri.colors_in_use), aweb) <= 0 + || !flag_ra_ir_spilling) + { + ri.needed_loads[ri.nl_size++] = web; + web->in_load = 1; + web->one_load = 1; + } + else if (!bitmap_bit_p (ri.need_reload, web->id)) + { + bitmap_set_bit (ri.need_reload, web->id); + ri.num_reloads++; + web->one_load = 1; + } + else + web->one_load = 0; + } + else + web->one_load = 0; + } + + if (GET_CODE (insn) == CODE_LABEL) + break; + } + + nl_first_reload = ri.nl_size; + if (ri.num_reloads) + { + int in_ir = 0; + edge e; + int num = 0; + HARD_REG_SET cum_colors, colors; + CLEAR_HARD_REG_SET (cum_colors); + for (e = bb->pred; e && num < 5; e = e->pred_next, num++) + { + int j; + CLEAR_HARD_REG_SET (colors); + EXECUTE_IF_SET_IN_BITMAP (live_at_end[e->src->index], 0, j, + { + struct web *web = use2web[j]; + struct web *aweb = alias (find_web_for_subweb (web)); + if (aweb->type != SPILLED) + update_spill_colors (&colors, web, 1); + }); + IOR_HARD_REG_SET (cum_colors, colors); + } + if (num == 5) + in_ir = 1; + + bitmap_clear (ri.scratch); + EXECUTE_IF_SET_IN_BITMAP (ri.need_reload, 0, j, + { + struct web *web2 = ID2WEB (j); + struct web *supweb2 = find_web_for_subweb (web2); + struct web *aweb2 = alias (supweb2); + /* block entry is IR boundary for aweb2? + Currently more some tries for good conditions. */ + if (((ra_pass > 0 || supweb2->target_of_spilled_move) + && (1 || in_ir || spill_is_free (&cum_colors, aweb2) <= 0)) + || (ra_pass == 1 + && (in_ir + || spill_is_free (&cum_colors, aweb2) <= 0))) + { + if (!web2->in_load) + { + ri.needed_loads[ri.nl_size++] = web2; + web2->in_load = 1; + } + bitmap_set_bit (ri.scratch, j); + ri.num_reloads--; + } + }); + bitmap_operation (ri.need_reload, ri.need_reload, ri.scratch, + BITMAP_AND_COMPL); + } + + ri.need_load = 1; + emit_loads (&ri, nl_first_reload, last_block_insn); + if (ri.nl_size != 0 /*|| ri.num_reloads != 0*/) + abort (); + if (!insn) + break; + } + free (ri.needed_loads); + sbitmap_free (ri.live); + BITMAP_XFREE (ri.scratch); + BITMAP_XFREE (ri.need_reload); +} + +/* WEBS is a web conflicting with a spilled one. Prepare it + to be able to rescan it in the next pass. Mark all it's uses + for checking, and clear the some members of their web parts + (of defs and uses). Notably don't clear the uplink. We don't + change the layout of this web, just it's conflicts. + Also remember all IDs of its uses in USES_AS_BITMAP. */ + +static void +mark_refs_for_checking (web, uses_as_bitmap) + struct web *web; + bitmap uses_as_bitmap; +{ + unsigned int i; + for (i = 0; i < web->num_uses; i++) + { + unsigned int id = DF_REF_ID (web->uses[i]); + SET_BIT (last_check_uses, id); + bitmap_set_bit (uses_as_bitmap, id); + web_parts[df->def_id + id].spanned_deaths = 0; + web_parts[df->def_id + id].crosses_call = 0; + } + for (i = 0; i < web->num_defs; i++) + { + unsigned int id = DF_REF_ID (web->defs[i]); + web_parts[id].spanned_deaths = 0; + web_parts[id].crosses_call = 0; + } +} + +/* The last step of the spill phase is to set up the structures for + incrementally rebuilding the interference graph. We break up + the web part structure of all spilled webs, mark their uses for + rechecking, look at their neighbors, and clean up some global + information, we will rebuild. */ + +static void +detect_web_parts_to_rebuild () +{ + bitmap uses_as_bitmap; + unsigned int i, pass; + struct dlist *d; + sbitmap already_webs = sbitmap_alloc (num_webs); + + uses_as_bitmap = BITMAP_XMALLOC (); + if (last_check_uses) + sbitmap_free (last_check_uses); + last_check_uses = sbitmap_alloc (df->use_id); + sbitmap_zero (last_check_uses); + sbitmap_zero (already_webs); + /* We need to recheck all uses of all webs involved in spilling (and the + uses added by spill insns, but those are not analyzed yet). + Those are the spilled webs themself, webs coalesced to spilled ones, + and webs conflicting with any of them. */ + for (pass = 0; pass < 2; pass++) + for (d = (pass == 0) ? WEBS(SPILLED) : WEBS(COALESCED); d; d = d->next) + { + struct web *web = DLIST_WEB (d); + struct conflict_link *wl; + unsigned int j; + /* This check is only needed for coalesced nodes, but hey. */ + if (alias (web)->type != SPILLED) + continue; + + /* For the spilled web itself we also need to clear it's + uplink, to be able to rebuild smaller webs. After all + spilling has split the web. */ + for (i = 0; i < web->num_uses; i++) + { + unsigned int id = DF_REF_ID (web->uses[i]); + SET_BIT (last_check_uses, id); + bitmap_set_bit (uses_as_bitmap, id); + web_parts[df->def_id + id].uplink = NULL; + web_parts[df->def_id + id].spanned_deaths = 0; + web_parts[df->def_id + id].crosses_call = 0; + } + for (i = 0; i < web->num_defs; i++) + { + unsigned int id = DF_REF_ID (web->defs[i]); + web_parts[id].uplink = NULL; + web_parts[id].spanned_deaths = 0; + web_parts[id].crosses_call = 0; + } + + /* Now look at all neighbors of this spilled web. */ + if (web->have_orig_conflicts) + wl = web->orig_conflict_list; + else + wl = web->conflict_list; + for (; wl; wl = wl->next) + { + if (TEST_BIT (already_webs, wl->t->id)) + continue; + SET_BIT (already_webs, wl->t->id); + mark_refs_for_checking (wl->t, uses_as_bitmap); + } + EXECUTE_IF_SET_IN_BITMAP (web->useless_conflicts, 0, j, + { + struct web *web2 = ID2WEB (j); + if (TEST_BIT (already_webs, web2->id)) + continue; + SET_BIT (already_webs, web2->id); + mark_refs_for_checking (web2, uses_as_bitmap); + }); + } + + /* We also recheck unconditionally all uses of any hardregs. This means + we _can_ delete all these uses from the live_at_end[] bitmaps. + And because we sometimes delete insn refering to hardregs (when + they became useless because they setup a rematerializable pseudo, which + then was rematerialized), some of those uses will go away with the next + df_analyse(). This means we even _must_ delete those uses from + the live_at_end[] bitmaps. For simplicity we simply delete + all of them. */ + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + if (!fixed_regs[i]) + { + struct df_link *link; + for (link = df->regs[i].uses; link; link = link->next) + if (link->ref) + bitmap_set_bit (uses_as_bitmap, DF_REF_ID (link->ref)); + } + + /* The information in live_at_end[] will be rebuild for all uses + we recheck, so clear it here (the uses of spilled webs, might + indeed not become member of it again). */ + live_at_end -= 2; + for (i = 0; i < (unsigned int) last_basic_block + 2; i++) + bitmap_operation (live_at_end[i], live_at_end[i], uses_as_bitmap, + BITMAP_AND_COMPL); + live_at_end += 2; + + if (rtl_dump_file && (debug_new_regalloc & DUMP_REBUILD) != 0) + { + ra_debug_msg (DUMP_REBUILD, "need to check these uses:\n"); + dump_sbitmap_file (rtl_dump_file, last_check_uses); + } + sbitmap_free (already_webs); + BITMAP_XFREE (uses_as_bitmap); +} + +/* Statistics about deleted insns, which are useless now. */ +static unsigned int deleted_def_insns; +static unsigned HOST_WIDE_INT deleted_def_cost; + +/* In rewrite_program2() we noticed, when a certain insn set a pseudo + which wasn't live. Try to delete all those insns. */ + +static void +delete_useless_defs () +{ + unsigned int i; + /* If the insn only sets the def without any sideeffect (besides + clobbers or uses), we can delete it. single_set() also tests + for INSN_P(insn). */ + EXECUTE_IF_SET_IN_BITMAP (useless_defs, 0, i, + { + rtx insn = DF_REF_INSN (df->defs[i]); + rtx set = single_set (insn); + struct web *web = find_web_for_subweb (def2web[i]); + if (set && web->type == SPILLED && web->stack_slot == NULL) + { + deleted_def_insns++; + deleted_def_cost += BLOCK_FOR_INSN (insn)->frequency + 1; + PUT_CODE (insn, NOTE); + NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED; + df_insn_modify (df, BLOCK_FOR_INSN (insn), insn); + } + }); +} + +/* Look for spilled webs, on whose behalf no insns were emitted. + We inversify (sp?) the changed flag of the webs, so after this function + a nonzero changed flag means, that this web was not spillable (at least + in this pass). */ + +static void +detect_non_changed_webs () +{ + struct dlist *d, *d_next; + for (d = WEBS(SPILLED); d; d = d_next) + { + struct web *web = DLIST_WEB (d); + d_next = d->next; + if (!web->changed) + { + ra_debug_msg (DUMP_PROCESS, "no insns emitted for spilled web %d\n", + web->id); + remove_web_from_list (web); + put_web (web, COLORED); + web->changed = 1; + } + else + web->changed = 0; + /* From now on web->changed is used as the opposite flag. + I.e. colored webs, which have changed set were formerly + spilled webs for which no insns were emitted. */ + } +} + +/* Before spilling we clear the changed flags for all spilled webs. */ + +static void +reset_changed_flag () +{ + struct dlist *d; + for (d = WEBS(SPILLED); d; d = d->next) + DLIST_WEB(d)->changed = 0; +} + +/* The toplevel function for this file. Given a colorized graph, + and lists of spilled, coalesced and colored webs, we add some + spill code. This also sets up the structures for incrementally + building the interference graph in the next pass. */ + +void +actual_spill () +{ + int i; + bitmap new_deaths = BITMAP_XMALLOC (); + reset_changed_flag (); + spill_coalprop (); + choose_spill_colors (); + useless_defs = BITMAP_XMALLOC (); + if (flag_ra_improved_spilling) + rewrite_program2 (new_deaths); + else + rewrite_program (new_deaths); + insert_stores (new_deaths); + delete_useless_defs (); + BITMAP_XFREE (useless_defs); + sbitmap_free (insns_with_deaths); + insns_with_deaths = sbitmap_alloc (get_max_uid ()); + death_insns_max_uid = get_max_uid (); + sbitmap_zero (insns_with_deaths); + EXECUTE_IF_SET_IN_BITMAP (new_deaths, 0, i, + { SET_BIT (insns_with_deaths, i);}); + detect_non_changed_webs (); + detect_web_parts_to_rebuild (); + BITMAP_XFREE (new_deaths); +} + +/* A bitmap of pseudo reg numbers which are coalesced directly + to a hardreg. Set in emit_colors(), used and freed in + remove_suspicious_death_notes(). */ +static bitmap regnos_coalesced_to_hardregs; + +/* Create new pseudos for each web we colored, change insns to + use those pseudos and set up ra_reg_renumber. */ + +void +emit_colors (df) + struct df *df; +{ + unsigned int i; + int si; + struct web *web; + int old_max_regno = max_reg_num (); + regset old_regs; + basic_block bb; + + /* This bitmap is freed in remove_suspicious_death_notes(), + which is also the user of it. */ + regnos_coalesced_to_hardregs = BITMAP_XMALLOC (); + /* First create the (REG xx) rtx's for all webs, as we need to know + the number, to make sure, flow has enough memory for them in the + various tables. */ + for (i = 0; i < num_webs - num_subwebs; i++) + { + web = ID2WEB (i); + if (web->type != COLORED && web->type != COALESCED) + continue; + if (web->type == COALESCED && alias (web)->type == COLORED) + continue; + if (web->reg_rtx || web->regno < FIRST_PSEUDO_REGISTER) + abort (); + + if (web->regno >= max_normal_pseudo) + { + rtx place; + if (web->color == an_unusable_color) + { + unsigned int inherent_size = PSEUDO_REGNO_BYTES (web->regno); + unsigned int total_size = MAX (inherent_size, 0); + place = assign_stack_local (PSEUDO_REGNO_MODE (web->regno), + total_size, + inherent_size == total_size ? 0 : -1); + RTX_UNCHANGING_P (place) = + RTX_UNCHANGING_P (regno_reg_rtx[web->regno]); + set_mem_alias_set (place, new_alias_set ()); + } + else + { + place = gen_reg_rtx (PSEUDO_REGNO_MODE (web->regno)); + } + web->reg_rtx = place; + } + else + { + /* Special case for i386 'fix_truncdi_nomemory' insn. + We must choose mode from insns not from PSEUDO_REGNO_MODE. + Actual only for clobbered register. */ + if (web->num_uses == 0 && web->num_defs == 1) + web->reg_rtx = gen_reg_rtx (GET_MODE (DF_REF_REAL_REG (web->defs[0]))); + else + web->reg_rtx = gen_reg_rtx (PSEUDO_REGNO_MODE (web->regno)); + /* Remember the different parts directly coalesced to a hardreg. */ + if (web->type == COALESCED) + bitmap_set_bit (regnos_coalesced_to_hardregs, REGNO (web->reg_rtx)); + } + } + ra_max_regno = max_regno = max_reg_num (); + allocate_reg_info (max_regno, FALSE, FALSE); + ra_reg_renumber = (short *) xmalloc (max_regno * sizeof (short)); + for (si = 0; si < max_regno; si++) + ra_reg_renumber[si] = -1; + + /* Then go through all references, and replace them by a new + pseudoreg for each web. All uses. */ + /* XXX + Beware: The order of replacements (first uses, then defs) matters only + for read-mod-write insns, where the RTL expression for the REG is + shared between def and use. For normal rmw insns we connected all such + webs, i.e. both the use and the def (which are the same memory) + there get the same new pseudo-reg, so order would not matter. + _However_ we did not connect webs, were the read cycle was an + uninitialized read. If we now would first replace the def reference + and then the use ref, we would initialize it with a REG rtx, which + gets never initialized, and yet more wrong, which would overwrite + the definition of the other REG rtx. So we must replace the defs last. + */ + for (i = 0; i < df->use_id; i++) + if (df->uses[i]) + { + regset rs = DF_REF_BB (df->uses[i])->global_live_at_start; + rtx regrtx; + web = use2web[i]; + web = find_web_for_subweb (web); + if (web->type != COLORED && web->type != COALESCED) + continue; + regrtx = alias (web)->reg_rtx; + if (!regrtx) + regrtx = web->reg_rtx; + *DF_REF_REAL_LOC (df->uses[i]) = regrtx; + if (REGNO_REG_SET_P (rs, web->regno) && REG_P (regrtx)) + { + /*CLEAR_REGNO_REG_SET (rs, web->regno);*/ + SET_REGNO_REG_SET (rs, REGNO (regrtx)); + } + } + + /* And all defs. */ + for (i = 0; i < df->def_id; i++) + { + regset rs; + rtx regrtx; + if (!df->defs[i]) + continue; + rs = DF_REF_BB (df->defs[i])->global_live_at_start; + web = def2web[i]; + web = find_web_for_subweb (web); + if (web->type != COLORED && web->type != COALESCED) + continue; + regrtx = alias (web)->reg_rtx; + if (!regrtx) + regrtx = web->reg_rtx; + *DF_REF_REAL_LOC (df->defs[i]) = regrtx; + if (REGNO_REG_SET_P (rs, web->regno) && REG_P (regrtx)) + { + /* Don't simply clear the current regno, as it might be + replaced by two webs. */ + /*CLEAR_REGNO_REG_SET (rs, web->regno);*/ + SET_REGNO_REG_SET (rs, REGNO (regrtx)); + } + } + + /* And now set up the ra_reg_renumber array for reload with all the new + pseudo-regs. */ + for (i = 0; i < num_webs - num_subwebs; i++) + { + web = ID2WEB (i); + if (web->reg_rtx && REG_P (web->reg_rtx)) + { + int r = REGNO (web->reg_rtx); + ra_reg_renumber[r] = web->color; + ra_debug_msg (DUMP_COLORIZE, "Renumber pseudo %d (== web %d) to %d\n", + r, web->id, ra_reg_renumber[r]); + } + } + + old_regs = BITMAP_XMALLOC (); + for (si = FIRST_PSEUDO_REGISTER; si < old_max_regno; si++) + SET_REGNO_REG_SET (old_regs, si); + FOR_EACH_BB (bb) + { + AND_COMPL_REG_SET (bb->global_live_at_start, old_regs); + AND_COMPL_REG_SET (bb->global_live_at_end, old_regs); + } + BITMAP_XFREE (old_regs); +} + +/* Delete some coalesced moves from the insn stream. */ + +void +delete_moves () +{ + struct move_list *ml; + struct web *s, *t; + /* XXX Beware: We normally would test here each copy insn, if + source and target got the same color (either by coalescing or by pure + luck), and then delete it. + This will currently not work. One problem is, that we don't color + the regs ourself, but instead defer to reload. So the colorization + is only a kind of suggestion, which reload doesn't have to follow. + For webs which are coalesced to a normal colored web, we only have one + new pseudo, so in this case we indeed can delete copy insns involving + those (because even if reload colors them different from our suggestion, + it still has to color them the same, as only one pseudo exists). But for + webs coalesced to precolored ones, we have not a single pseudo, but + instead one for each coalesced web. This means, that we can't delete + copy insns, where source and target are webs coalesced to precolored + ones, because then the connection between both webs is destroyed. Note + that this not only means copy insns, where one side is the precolored one + itself, but also those between webs which are coalesced to one color. + Also because reload we can't delete copy insns which involve any + precolored web at all. These often have also special meaning (e.g. + copying a return value of a call to a pseudo, or copying pseudo to the + return register), and the deletion would confuse reload in thinking the + pseudo isn't needed. One of those days reload will get away and we can + do everything we want. + In effect because of the later reload, we can't base our deletion on the + colors itself, but instead need to base them on the newly created + pseudos. */ + for (ml = wl_moves; ml; ml = ml->next) + /* The real condition we would ideally use is: s->color == t->color. + Additionally: s->type != PRECOLORED && t->type != PRECOLORED, in case + we want to prevent deletion of "special" copies. */ + if (ml->move + && (s = alias (ml->move->source_web))->reg_rtx + == (t = alias (ml->move->target_web))->reg_rtx + && s->type != PRECOLORED && t->type != PRECOLORED) + { + basic_block bb = BLOCK_FOR_INSN (ml->move->insn); + df_insn_delete (df, bb, ml->move->insn); + deleted_move_insns++; + deleted_move_cost += bb->frequency + 1; + } +} + +/* Due to resons documented elsewhere we create different pseudos + for all webs coalesced to hardregs. For these parts life_analysis() + might have added REG_DEAD notes without considering, that only this part + but not the whole coalesced web dies. The RTL is correct, there is no + coalescing yet. But if later reload's alter_reg() substitutes the + hardreg into the REG rtx it looks like that particular hardreg dies here, + although (due to coalescing) it still is live. This might make different + places of reload think, it can use that hardreg for reload regs, + accidentally overwriting it. So we need to remove those REG_DEAD notes. + (Or better teach life_analysis() and reload about our coalescing, but + that comes later) Bah. */ + +void +remove_suspicious_death_notes () +{ + rtx insn; + for (insn = get_insns(); insn; insn = NEXT_INSN (insn)) + if (INSN_P (insn)) + { + rtx *pnote = ®_NOTES (insn); + while (*pnote) + { + rtx note = *pnote; + if ((REG_NOTE_KIND (note) == REG_DEAD + || REG_NOTE_KIND (note) == REG_UNUSED) + && (GET_CODE (XEXP (note, 0)) == REG + && bitmap_bit_p (regnos_coalesced_to_hardregs, + REGNO (XEXP (note, 0))))) + *pnote = XEXP (note, 1); + else + pnote = &XEXP (*pnote, 1); + } + } + BITMAP_XFREE (regnos_coalesced_to_hardregs); + regnos_coalesced_to_hardregs = NULL; +} + +/* Allocate space for max_reg_num() pseudo registers, and + fill reg_renumber[] from ra_reg_renumber[]. If FREE_IT + is nonzero, also free ra_reg_renumber and reset ra_max_regno. */ + +void +setup_renumber (free_it) + int free_it; +{ + int i; + max_regno = max_reg_num (); + allocate_reg_info (max_regno, FALSE, TRUE); + for (i = 0; i < max_regno; i++) + { + reg_renumber[i] = (i < ra_max_regno) ? ra_reg_renumber[i] : -1; + } + if (free_it) + { + free (ra_reg_renumber); + ra_reg_renumber = NULL; + ra_max_regno = 0; + } +} + +/* Dump the costs and savings due to spilling, i.e. of added spill insns + and removed moves or useless defs. */ + +void +dump_cost (level) + unsigned int level; +{ + ra_debug_msg (level, "Instructions for spilling\n added:\n"); + ra_debug_msg (level, " loads =%d cost=", emitted_spill_loads); + ra_debug_msg (level, HOST_WIDE_INT_PRINT_UNSIGNED, spill_load_cost); + ra_debug_msg (level, "\n stores=%d cost=", emitted_spill_stores); + ra_debug_msg (level, HOST_WIDE_INT_PRINT_UNSIGNED, spill_store_cost); + ra_debug_msg (level, "\n remat =%d cost=", emitted_remat); + ra_debug_msg (level, HOST_WIDE_INT_PRINT_UNSIGNED, spill_remat_cost); + ra_debug_msg (level, "\n removed:\n moves =%d cost=", deleted_move_insns); + ra_debug_msg (level, HOST_WIDE_INT_PRINT_UNSIGNED, deleted_move_cost); + ra_debug_msg (level, "\n others=%d cost=", deleted_def_insns); + ra_debug_msg (level, HOST_WIDE_INT_PRINT_UNSIGNED, deleted_def_cost); + ra_debug_msg (level, "\n"); +} + +/* Initialization of the rewrite phase. */ + +void +ra_rewrite_init () +{ + emitted_spill_loads = 0; + emitted_spill_stores = 0; + emitted_remat = 0; + spill_load_cost = 0; + spill_store_cost = 0; + spill_remat_cost = 0; + deleted_move_insns = 0; + deleted_move_cost = 0; + deleted_def_insns = 0; + deleted_def_cost = 0; +} + +/* +vim:cinoptions={.5s,g0,p5,t0,(0,^-0.5s,n-0.5s:tw=78:cindent:sw=4: +*/ |