| /* Code for RTL transformations to satisfy insn constraints. |
| Copyright (C) 2010-2024 Free Software Foundation, Inc. |
| Contributed by Vladimir Makarov <vmakarov@redhat.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 3, 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 COPYING3. If not see |
| <http://www.gnu.org/licenses/>. */ |
| |
| |
| /* This file contains code for 3 passes: constraint pass, |
| inheritance/split pass, and pass for undoing failed inheritance and |
| split. |
| |
| The major goal of constraint pass is to transform RTL to satisfy |
| insn and address constraints by: |
| o choosing insn alternatives; |
| o generating *reload insns* (or reloads in brief) and *reload |
| pseudos* which will get necessary hard registers later; |
| o substituting pseudos with equivalent values and removing the |
| instructions that initialized those pseudos. |
| |
| The constraint pass has biggest and most complicated code in LRA. |
| There are a lot of important details like: |
| o reuse of input reload pseudos to simplify reload pseudo |
| allocations; |
| o some heuristics to choose insn alternative to improve the |
| inheritance; |
| o early clobbers etc. |
| |
| The pass is mimicking former reload pass in alternative choosing |
| because the reload pass is oriented to current machine description |
| model. It might be changed if the machine description model is |
| changed. |
| |
| There is special code for preventing all LRA and this pass cycling |
| in case of bugs. |
| |
| On the first iteration of the pass we process every instruction and |
| choose an alternative for each one. On subsequent iterations we try |
| to avoid reprocessing instructions if we can be sure that the old |
| choice is still valid. |
| |
| The inheritance/spilt pass is to transform code to achieve |
| ineheritance and live range splitting. It is done on backward |
| traversal of EBBs. |
| |
| The inheritance optimization goal is to reuse values in hard |
| registers. There is analogous optimization in old reload pass. The |
| inheritance is achieved by following transformation: |
| |
| reload_p1 <- p reload_p1 <- p |
| ... new_p <- reload_p1 |
| ... => ... |
| reload_p2 <- p reload_p2 <- new_p |
| |
| where p is spilled and not changed between the insns. Reload_p1 is |
| also called *original pseudo* and new_p is called *inheritance |
| pseudo*. |
| |
| The subsequent assignment pass will try to assign the same (or |
| another if it is not possible) hard register to new_p as to |
| reload_p1 or reload_p2. |
| |
| If the assignment pass fails to assign a hard register to new_p, |
| this file will undo the inheritance and restore the original code. |
| This is because implementing the above sequence with a spilled |
| new_p would make the code much worse. The inheritance is done in |
| EBB scope. The above is just a simplified example to get an idea |
| of the inheritance as the inheritance is also done for non-reload |
| insns. |
| |
| Splitting (transformation) is also done in EBB scope on the same |
| pass as the inheritance: |
| |
| r <- ... or ... <- r r <- ... or ... <- r |
| ... s <- r (new insn -- save) |
| ... => |
| ... r <- s (new insn -- restore) |
| ... <- r ... <- r |
| |
| The *split pseudo* s is assigned to the hard register of the |
| original pseudo or hard register r. |
| |
| Splitting is done: |
| o In EBBs with high register pressure for global pseudos (living |
| in at least 2 BBs) and assigned to hard registers when there |
| are more one reloads needing the hard registers; |
| o for pseudos needing save/restore code around calls. |
| |
| If the split pseudo still has the same hard register as the |
| original pseudo after the subsequent assignment pass or the |
| original pseudo was split, the opposite transformation is done on |
| the same pass for undoing inheritance. */ |
| |
| #undef REG_OK_STRICT |
| |
| #include "config.h" |
| #include "system.h" |
| #include "coretypes.h" |
| #include "backend.h" |
| #include "hooks.h" |
| #include "target.h" |
| #include "rtl.h" |
| #include "tree.h" |
| #include "predict.h" |
| #include "df.h" |
| #include "memmodel.h" |
| #include "tm_p.h" |
| #include "expmed.h" |
| #include "optabs.h" |
| #include "regs.h" |
| #include "ira.h" |
| #include "recog.h" |
| #include "output.h" |
| #include "addresses.h" |
| #include "expr.h" |
| #include "cfgrtl.h" |
| #include "rtl-error.h" |
| #include "lra.h" |
| #include "lra-int.h" |
| #include "print-rtl.h" |
| #include "function-abi.h" |
| #include "rtl-iter.h" |
| |
| /* Value of LRA_CURR_RELOAD_NUM at the beginning of BB of the current |
| insn. Remember that LRA_CURR_RELOAD_NUM is the number of emitted |
| reload insns. */ |
| static int bb_reload_num; |
| |
| /* The current insn being processed and corresponding its single set |
| (NULL otherwise), its data (basic block, the insn data, the insn |
| static data, and the mode of each operand). */ |
| static rtx_insn *curr_insn; |
| static rtx curr_insn_set; |
| static basic_block curr_bb; |
| static lra_insn_recog_data_t curr_id; |
| static struct lra_static_insn_data *curr_static_id; |
| static machine_mode curr_operand_mode[MAX_RECOG_OPERANDS]; |
| /* Mode of the register substituted by its equivalence with VOIDmode |
| (e.g. constant) and whose subreg is given operand of the current |
| insn. VOIDmode in all other cases. */ |
| static machine_mode original_subreg_reg_mode[MAX_RECOG_OPERANDS]; |
| |
| |
| |
| /* Start numbers for new registers and insns at the current constraints |
| pass start. */ |
| static int new_regno_start; |
| static int new_insn_uid_start; |
| |
| /* If LOC is nonnull, strip any outer subreg from it. */ |
| static inline rtx * |
| strip_subreg (rtx *loc) |
| { |
| return loc && GET_CODE (*loc) == SUBREG ? &SUBREG_REG (*loc) : loc; |
| } |
| |
| /* Return hard regno of REGNO or if it is was not assigned to a hard |
| register, use a hard register from its allocno class. */ |
| static int |
| get_try_hard_regno (int regno) |
| { |
| int hard_regno; |
| enum reg_class rclass; |
| |
| if ((hard_regno = regno) >= FIRST_PSEUDO_REGISTER) |
| hard_regno = lra_get_regno_hard_regno (regno); |
| if (hard_regno >= 0) |
| return hard_regno; |
| rclass = lra_get_allocno_class (regno); |
| if (rclass == NO_REGS) |
| return -1; |
| return ira_class_hard_regs[rclass][0]; |
| } |
| |
| /* Return the hard regno of X after removing its subreg. If X is not a |
| register or a subreg of a register, return -1. If X is a pseudo, use its |
| assignment. If X is a hard regno, return the final hard regno which will be |
| after elimination. */ |
| static int |
| get_hard_regno (rtx x) |
| { |
| rtx reg; |
| int hard_regno; |
| |
| reg = x; |
| if (SUBREG_P (x)) |
| reg = SUBREG_REG (x); |
| if (! REG_P (reg)) |
| return -1; |
| if (! HARD_REGISTER_NUM_P (hard_regno = REGNO (reg))) |
| hard_regno = lra_get_regno_hard_regno (hard_regno); |
| if (hard_regno < 0) |
| return -1; |
| if (HARD_REGISTER_NUM_P (REGNO (reg))) |
| hard_regno = lra_get_elimination_hard_regno (hard_regno); |
| if (SUBREG_P (x)) |
| hard_regno += subreg_regno_offset (hard_regno, GET_MODE (reg), |
| SUBREG_BYTE (x), GET_MODE (x)); |
| return hard_regno; |
| } |
| |
| /* If REGNO is a hard register or has been allocated a hard register, |
| return the class of that register. If REGNO is a reload pseudo |
| created by the current constraints pass, return its allocno class. |
| Return NO_REGS otherwise. */ |
| static enum reg_class |
| get_reg_class (int regno) |
| { |
| int hard_regno; |
| |
| if (! HARD_REGISTER_NUM_P (hard_regno = regno)) |
| hard_regno = lra_get_regno_hard_regno (regno); |
| if (hard_regno >= 0) |
| { |
| hard_regno = lra_get_elimination_hard_regno (hard_regno); |
| return REGNO_REG_CLASS (hard_regno); |
| } |
| if (regno >= new_regno_start) |
| return lra_get_allocno_class (regno); |
| return NO_REGS; |
| } |
| |
| /* Return true if REG_CLASS has enough allocatable hard regs to keep value of |
| REG_MODE. */ |
| static bool |
| enough_allocatable_hard_regs_p (enum reg_class reg_class, |
| enum machine_mode reg_mode) |
| { |
| int i, j, hard_regno, class_size, nregs; |
| |
| if (hard_reg_set_subset_p (reg_class_contents[reg_class], lra_no_alloc_regs)) |
| return false; |
| class_size = ira_class_hard_regs_num[reg_class]; |
| for (i = 0; i < class_size; i++) |
| { |
| hard_regno = ira_class_hard_regs[reg_class][i]; |
| nregs = hard_regno_nregs (hard_regno, reg_mode); |
| if (nregs == 1) |
| return true; |
| for (j = 0; j < nregs; j++) |
| if (TEST_HARD_REG_BIT (lra_no_alloc_regs, hard_regno + j) |
| || ! TEST_HARD_REG_BIT (reg_class_contents[reg_class], |
| hard_regno + j)) |
| break; |
| if (j >= nregs) |
| return true; |
| } |
| return false; |
| } |
| |
| /* True if C is a non-empty register class that has too few registers |
| to be safely used as a reload target class. */ |
| #define SMALL_REGISTER_CLASS_P(C) \ |
| (ira_class_hard_regs_num [(C)] == 1 \ |
| || (ira_class_hard_regs_num [(C)] >= 1 \ |
| && targetm.class_likely_spilled_p (C))) |
| |
| /* Return true if REG satisfies (or will satisfy) reg class constraint |
| CL. Use elimination first if REG is a hard register. If REG is a |
| reload pseudo created by this constraints pass, assume that it will |
| be allocated a hard register from its allocno class, but allow that |
| class to be narrowed to CL if it is currently a superset of CL and |
| if either: |
| |
| - ALLOW_ALL_RELOAD_CLASS_CHANGES_P is true or |
| - the instruction we're processing is not a reload move. |
| |
| If NEW_CLASS is nonnull, set *NEW_CLASS to the new allocno class of |
| REGNO (reg), or NO_REGS if no change in its class was needed. */ |
| static bool |
| in_class_p (rtx reg, enum reg_class cl, enum reg_class *new_class, |
| bool allow_all_reload_class_changes_p = false) |
| { |
| enum reg_class rclass, common_class; |
| machine_mode reg_mode; |
| rtx src; |
| int regno = REGNO (reg); |
| |
| if (new_class != NULL) |
| *new_class = NO_REGS; |
| if (regno < FIRST_PSEUDO_REGISTER) |
| { |
| rtx final_reg = reg; |
| rtx *final_loc = &final_reg; |
| |
| lra_eliminate_reg_if_possible (final_loc); |
| return TEST_HARD_REG_BIT (reg_class_contents[cl], REGNO (*final_loc)); |
| } |
| reg_mode = GET_MODE (reg); |
| rclass = get_reg_class (regno); |
| src = curr_insn_set != NULL ? SET_SRC (curr_insn_set) : NULL; |
| if (regno < new_regno_start |
| /* Do not allow the constraints for reload instructions to |
| influence the classes of new pseudos. These reloads are |
| typically moves that have many alternatives, and restricting |
| reload pseudos for one alternative may lead to situations |
| where other reload pseudos are no longer allocatable. */ |
| || (!allow_all_reload_class_changes_p |
| && INSN_UID (curr_insn) >= new_insn_uid_start |
| && src != NULL |
| && ((REG_P (src) || MEM_P (src)) |
| || (GET_CODE (src) == SUBREG |
| && (REG_P (SUBREG_REG (src)) || MEM_P (SUBREG_REG (src))))))) |
| /* When we don't know what class will be used finally for reload |
| pseudos, we use ALL_REGS. */ |
| return ((regno >= new_regno_start && rclass == ALL_REGS) |
| || (rclass != NO_REGS && ira_class_subset_p[rclass][cl] |
| && ! hard_reg_set_subset_p (reg_class_contents[cl], |
| lra_no_alloc_regs))); |
| else |
| { |
| common_class = ira_reg_class_subset[rclass][cl]; |
| if (new_class != NULL) |
| *new_class = common_class; |
| return (enough_allocatable_hard_regs_p (common_class, reg_mode) |
| /* Do not permit reload insn operand matching (new_class == NULL |
| case) if the new class is too small. */ |
| && (new_class != NULL || common_class == rclass |
| || !SMALL_REGISTER_CLASS_P (common_class))); |
| } |
| } |
| |
| /* Return true if REGNO satisfies a memory constraint. */ |
| static bool |
| in_mem_p (int regno) |
| { |
| return get_reg_class (regno) == NO_REGS; |
| } |
| |
| /* Return true if ADDR is a valid memory address for mode MODE in address |
| space AS, and check that each pseudo has the proper kind of hard |
| reg. */ |
| static bool |
| valid_address_p (machine_mode mode ATTRIBUTE_UNUSED, |
| rtx addr, addr_space_t as) |
| { |
| #ifdef GO_IF_LEGITIMATE_ADDRESS |
| lra_assert (ADDR_SPACE_GENERIC_P (as)); |
| GO_IF_LEGITIMATE_ADDRESS (mode, addr, win); |
| return false; |
| |
| win: |
| return true; |
| #else |
| return targetm.addr_space.legitimate_address_p (mode, addr, 0, as, |
| ERROR_MARK); |
| #endif |
| } |
| |
| namespace { |
| /* Temporarily eliminates registers in an address (for the lifetime of |
| the object). */ |
| class address_eliminator { |
| public: |
| address_eliminator (struct address_info *ad); |
| ~address_eliminator (); |
| |
| private: |
| struct address_info *m_ad; |
| rtx *m_base_loc; |
| rtx m_base_reg; |
| rtx *m_index_loc; |
| rtx m_index_reg; |
| }; |
| } |
| |
| address_eliminator::address_eliminator (struct address_info *ad) |
| : m_ad (ad), |
| m_base_loc (strip_subreg (ad->base_term)), |
| m_base_reg (NULL_RTX), |
| m_index_loc (strip_subreg (ad->index_term)), |
| m_index_reg (NULL_RTX) |
| { |
| if (m_base_loc != NULL) |
| { |
| m_base_reg = *m_base_loc; |
| /* If we have non-legitimate address which is decomposed not in |
| the way we expected, don't do elimination here. In such case |
| the address will be reloaded and elimination will be done in |
| reload insn finally. */ |
| if (REG_P (m_base_reg)) |
| lra_eliminate_reg_if_possible (m_base_loc); |
| if (m_ad->base_term2 != NULL) |
| *m_ad->base_term2 = *m_ad->base_term; |
| } |
| if (m_index_loc != NULL) |
| { |
| m_index_reg = *m_index_loc; |
| if (REG_P (m_index_reg)) |
| lra_eliminate_reg_if_possible (m_index_loc); |
| } |
| } |
| |
| address_eliminator::~address_eliminator () |
| { |
| if (m_base_loc && *m_base_loc != m_base_reg) |
| { |
| *m_base_loc = m_base_reg; |
| if (m_ad->base_term2 != NULL) |
| *m_ad->base_term2 = *m_ad->base_term; |
| } |
| if (m_index_loc && *m_index_loc != m_index_reg) |
| *m_index_loc = m_index_reg; |
| } |
| |
| /* Return true if the eliminated form of AD is a legitimate target address. |
| If OP is a MEM, AD is the address within OP, otherwise OP should be |
| ignored. CONSTRAINT is one constraint that the operand may need |
| to meet. */ |
| static bool |
| valid_address_p (rtx op, struct address_info *ad, |
| enum constraint_num constraint) |
| { |
| address_eliminator eliminator (ad); |
| |
| /* Allow a memory OP if it matches CONSTRAINT, even if CONSTRAINT is more |
| forgiving than "m". |
| Need to extract memory from op for special memory constraint, |
| i.e. bcst_mem_operand in i386 backend. */ |
| if (MEM_P (extract_mem_from_operand (op)) |
| && insn_extra_relaxed_memory_constraint (constraint) |
| && constraint_satisfied_p (op, constraint)) |
| return true; |
| |
| return valid_address_p (ad->mode, *ad->outer, ad->as); |
| } |
| |
| /* For special_memory_operand, it could be false for MEM_P (op), |
| i.e. bcst_mem_operand in i386 backend. |
| Extract and return real memory operand or op. */ |
| rtx |
| extract_mem_from_operand (rtx op) |
| { |
| for (rtx x = op;; x = XEXP (x, 0)) |
| { |
| if (MEM_P (x)) |
| return x; |
| if (GET_RTX_LENGTH (GET_CODE (x)) != 1 |
| || GET_RTX_FORMAT (GET_CODE (x))[0] != 'e') |
| break; |
| } |
| return op; |
| } |
| |
| /* Return true if the eliminated form of memory reference OP satisfies |
| extra (special) memory constraint CONSTRAINT. */ |
| static bool |
| satisfies_memory_constraint_p (rtx op, enum constraint_num constraint) |
| { |
| struct address_info ad; |
| rtx mem = extract_mem_from_operand (op); |
| if (!MEM_P (mem)) |
| return false; |
| |
| decompose_mem_address (&ad, mem); |
| address_eliminator eliminator (&ad); |
| return constraint_satisfied_p (op, constraint); |
| } |
| |
| /* Return true if the eliminated form of address AD satisfies extra |
| address constraint CONSTRAINT. */ |
| static bool |
| satisfies_address_constraint_p (struct address_info *ad, |
| enum constraint_num constraint) |
| { |
| address_eliminator eliminator (ad); |
| return constraint_satisfied_p (*ad->outer, constraint); |
| } |
| |
| /* Return true if the eliminated form of address OP satisfies extra |
| address constraint CONSTRAINT. */ |
| static bool |
| satisfies_address_constraint_p (rtx op, enum constraint_num constraint) |
| { |
| struct address_info ad; |
| |
| decompose_lea_address (&ad, &op); |
| return satisfies_address_constraint_p (&ad, constraint); |
| } |
| |
| /* Initiate equivalences for LRA. As we keep original equivalences |
| before any elimination, we need to make copies otherwise any change |
| in insns might change the equivalences. */ |
| void |
| lra_init_equiv (void) |
| { |
| ira_expand_reg_equiv (); |
| for (int i = FIRST_PSEUDO_REGISTER; i < max_reg_num (); i++) |
| { |
| rtx res; |
| |
| if ((res = ira_reg_equiv[i].memory) != NULL_RTX) |
| ira_reg_equiv[i].memory = copy_rtx (res); |
| if ((res = ira_reg_equiv[i].invariant) != NULL_RTX) |
| ira_reg_equiv[i].invariant = copy_rtx (res); |
| } |
| } |
| |
| static rtx loc_equivalence_callback (rtx, const_rtx, void *); |
| |
| /* Update equivalence for REGNO. We need to this as the equivalence |
| might contain other pseudos which are changed by their |
| equivalences. */ |
| static void |
| update_equiv (int regno) |
| { |
| rtx x; |
| |
| if ((x = ira_reg_equiv[regno].memory) != NULL_RTX) |
| ira_reg_equiv[regno].memory |
| = simplify_replace_fn_rtx (x, NULL_RTX, loc_equivalence_callback, |
| NULL_RTX); |
| if ((x = ira_reg_equiv[regno].invariant) != NULL_RTX) |
| ira_reg_equiv[regno].invariant |
| = simplify_replace_fn_rtx (x, NULL_RTX, loc_equivalence_callback, |
| NULL_RTX); |
| } |
| |
| /* If we have decided to substitute X with another value, return that |
| value, otherwise return X. */ |
| static rtx |
| get_equiv (rtx x) |
| { |
| int regno; |
| rtx res; |
| |
| if (! REG_P (x) || (regno = REGNO (x)) < FIRST_PSEUDO_REGISTER |
| || ! ira_reg_equiv[regno].defined_p |
| || ! ira_reg_equiv[regno].profitable_p |
| || lra_get_regno_hard_regno (regno) >= 0) |
| return x; |
| if ((res = ira_reg_equiv[regno].memory) != NULL_RTX) |
| { |
| if (targetm.cannot_substitute_mem_equiv_p (res)) |
| return x; |
| return res; |
| } |
| if ((res = ira_reg_equiv[regno].constant) != NULL_RTX) |
| return res; |
| if ((res = ira_reg_equiv[regno].invariant) != NULL_RTX) |
| return res; |
| gcc_unreachable (); |
| } |
| |
| /* If we have decided to substitute X with the equivalent value, |
| return that value after elimination for INSN, otherwise return |
| X. */ |
| static rtx |
| get_equiv_with_elimination (rtx x, rtx_insn *insn) |
| { |
| rtx res = get_equiv (x); |
| |
| if (x == res || CONSTANT_P (res)) |
| return res; |
| return lra_eliminate_regs_1 (insn, res, GET_MODE (res), |
| false, false, 0, true); |
| } |
| |
| /* Set up curr_operand_mode. */ |
| static void |
| init_curr_operand_mode (void) |
| { |
| int nop = curr_static_id->n_operands; |
| for (int i = 0; i < nop; i++) |
| { |
| machine_mode mode = GET_MODE (*curr_id->operand_loc[i]); |
| if (mode == VOIDmode) |
| { |
| /* The .md mode for address operands is the mode of the |
| addressed value rather than the mode of the address itself. */ |
| if (curr_id->icode >= 0 && curr_static_id->operand[i].is_address) |
| mode = Pmode; |
| else |
| mode = curr_static_id->operand[i].mode; |
| } |
| curr_operand_mode[i] = mode; |
| } |
| } |
| |
| |
| |
| /* The page contains code to reuse input reloads. */ |
| |
| /* Structure describes input reload of the current insns. */ |
| struct input_reload |
| { |
| /* True for input reload of matched operands. */ |
| bool match_p; |
| /* True for input reload of inout earlyclobber operand. */ |
| bool early_clobber_p; |
| /* Reloaded value. */ |
| rtx input; |
| /* Reload pseudo used. */ |
| rtx reg; |
| }; |
| |
| /* The number of elements in the following array. */ |
| static int curr_insn_input_reloads_num; |
| /* Array containing info about input reloads. It is used to find the |
| same input reload and reuse the reload pseudo in this case. */ |
| static struct input_reload curr_insn_input_reloads[LRA_MAX_INSN_RELOADS]; |
| |
| /* Initiate data concerning reuse of input reloads for the current |
| insn. */ |
| static void |
| init_curr_insn_input_reloads (void) |
| { |
| curr_insn_input_reloads_num = 0; |
| } |
| |
| /* The canonical form of an rtx inside a MEM is not necessarily the same as the |
| canonical form of the rtx outside the MEM. Fix this up in the case that |
| we're reloading an address (and therefore pulling it outside a MEM). */ |
| static rtx |
| canonicalize_reload_addr (rtx addr) |
| { |
| subrtx_var_iterator::array_type array; |
| FOR_EACH_SUBRTX_VAR (iter, array, addr, NONCONST) |
| { |
| rtx x = *iter; |
| if (GET_CODE (x) == MULT && CONST_INT_P (XEXP (x, 1))) |
| { |
| const HOST_WIDE_INT ci = INTVAL (XEXP (x, 1)); |
| const int pwr2 = exact_log2 (ci); |
| if (pwr2 > 0) |
| { |
| /* Rewrite this to use a shift instead, which is canonical when |
| outside of a MEM. */ |
| PUT_CODE (x, ASHIFT); |
| XEXP (x, 1) = GEN_INT (pwr2); |
| } |
| } |
| } |
| |
| return addr; |
| } |
| |
| /* Create a new pseudo using MODE, RCLASS, EXCLUDE_START_HARD_REGS, ORIGINAL or |
| reuse an existing reload pseudo. Don't reuse an existing reload pseudo if |
| IN_SUBREG_P is true and the reused pseudo should be wrapped up in a SUBREG. |
| EARLY_CLOBBER_P is true for input reload of inout early clobber operand. |
| The result pseudo is returned through RESULT_REG. Return TRUE if we created |
| a new pseudo, FALSE if we reused an existing reload pseudo. Use TITLE to |
| describe new registers for debug purposes. */ |
| static bool |
| get_reload_reg (enum op_type type, machine_mode mode, rtx original, |
| enum reg_class rclass, HARD_REG_SET *exclude_start_hard_regs, |
| bool in_subreg_p, bool early_clobber_p, |
| const char *title, rtx *result_reg) |
| { |
| int i, regno; |
| enum reg_class new_class; |
| |
| if (type == OP_OUT) |
| { |
| /* Output reload registers tend to start out with a conservative |
| choice of register class. Usually this is ALL_REGS, although |
| a target might narrow it (for performance reasons) through |
| targetm.preferred_reload_class. It's therefore quite common |
| for a reload instruction to require a more restrictive class |
| than the class that was originally assigned to the reload register. |
| |
| In these situations, it's more efficient to refine the choice |
| of register class rather than create a second reload register. |
| This also helps to avoid cycling for registers that are only |
| used by reload instructions. */ |
| if (REG_P (original) |
| && (int) REGNO (original) >= new_regno_start |
| && INSN_UID (curr_insn) >= new_insn_uid_start |
| && in_class_p (original, rclass, &new_class, true)) |
| { |
| unsigned int regno = REGNO (original); |
| if (lra_dump_file != NULL) |
| { |
| fprintf (lra_dump_file, " Reuse r%d for output ", regno); |
| dump_value_slim (lra_dump_file, original, 1); |
| } |
| if (new_class != lra_get_allocno_class (regno)) |
| lra_change_class (regno, new_class, ", change to", false); |
| if (lra_dump_file != NULL) |
| fprintf (lra_dump_file, "\n"); |
| *result_reg = original; |
| return false; |
| } |
| *result_reg |
| = lra_create_new_reg_with_unique_value (mode, original, rclass, |
| exclude_start_hard_regs, title); |
| return true; |
| } |
| |
| bool unique_p = early_clobber_p; |
| /* Prevent reuse value of expression with side effects, |
| e.g. volatile memory. */ |
| if (! side_effects_p (original)) |
| for (i = 0; i < curr_insn_input_reloads_num; i++) |
| { |
| if (! curr_insn_input_reloads[i].match_p |
| && ! curr_insn_input_reloads[i].early_clobber_p |
| && rtx_equal_p (curr_insn_input_reloads[i].input, original) |
| && in_class_p (curr_insn_input_reloads[i].reg, rclass, &new_class)) |
| { |
| rtx reg = curr_insn_input_reloads[i].reg; |
| regno = REGNO (reg); |
| /* If input is equal to original and both are VOIDmode, |
| GET_MODE (reg) might be still different from mode. |
| Ensure we don't return *result_reg with wrong mode. */ |
| if (GET_MODE (reg) != mode) |
| { |
| if (in_subreg_p) |
| continue; |
| if (maybe_lt (GET_MODE_SIZE (GET_MODE (reg)), |
| GET_MODE_SIZE (mode))) |
| continue; |
| reg = lowpart_subreg (mode, reg, GET_MODE (reg)); |
| if (reg == NULL_RTX || GET_CODE (reg) != SUBREG) |
| continue; |
| } |
| *result_reg = reg; |
| if (lra_dump_file != NULL) |
| { |
| fprintf (lra_dump_file, " Reuse r%d for reload ", regno); |
| dump_value_slim (lra_dump_file, original, 1); |
| } |
| if (new_class != lra_get_allocno_class (regno)) |
| lra_change_class (regno, new_class, ", change to", false); |
| if (lra_dump_file != NULL) |
| fprintf (lra_dump_file, "\n"); |
| return false; |
| } |
| /* If we have an input reload with a different mode, make sure it |
| will get a different hard reg. */ |
| else if (REG_P (original) |
| && REG_P (curr_insn_input_reloads[i].input) |
| && REGNO (original) == REGNO (curr_insn_input_reloads[i].input) |
| && (GET_MODE (original) |
| != GET_MODE (curr_insn_input_reloads[i].input))) |
| unique_p = true; |
| } |
| *result_reg = (unique_p |
| ? lra_create_new_reg_with_unique_value |
| : lra_create_new_reg) (mode, original, rclass, |
| exclude_start_hard_regs, title); |
| lra_assert (curr_insn_input_reloads_num < LRA_MAX_INSN_RELOADS); |
| curr_insn_input_reloads[curr_insn_input_reloads_num].input = original; |
| curr_insn_input_reloads[curr_insn_input_reloads_num].match_p = false; |
| curr_insn_input_reloads[curr_insn_input_reloads_num].early_clobber_p |
| = early_clobber_p; |
| curr_insn_input_reloads[curr_insn_input_reloads_num++].reg = *result_reg; |
| return true; |
| } |
| |
| |
| /* The page contains major code to choose the current insn alternative |
| and generate reloads for it. */ |
| |
| /* Return the offset from REGNO of the least significant register |
| in (reg:MODE REGNO). |
| |
| This function is used to tell whether two registers satisfy |
| a matching constraint. (reg:MODE1 REGNO1) matches (reg:MODE2 REGNO2) if: |
| |
| REGNO1 + lra_constraint_offset (REGNO1, MODE1) |
| == REGNO2 + lra_constraint_offset (REGNO2, MODE2) */ |
| int |
| lra_constraint_offset (int regno, machine_mode mode) |
| { |
| lra_assert (regno < FIRST_PSEUDO_REGISTER); |
| |
| scalar_int_mode int_mode; |
| if (WORDS_BIG_ENDIAN |
| && is_a <scalar_int_mode> (mode, &int_mode) |
| && GET_MODE_SIZE (int_mode) > UNITS_PER_WORD) |
| return hard_regno_nregs (regno, mode) - 1; |
| return 0; |
| } |
| |
| /* Like rtx_equal_p except that it allows a REG and a SUBREG to match |
| if they are the same hard reg, and has special hacks for |
| auto-increment and auto-decrement. This is specifically intended for |
| process_alt_operands to use in determining whether two operands |
| match. X is the operand whose number is the lower of the two. |
| |
| It is supposed that X is the output operand and Y is the input |
| operand. Y_HARD_REGNO is the final hard regno of register Y or |
| register in subreg Y as we know it now. Otherwise, it is a |
| negative value. */ |
| static bool |
| operands_match_p (rtx x, rtx y, int y_hard_regno) |
| { |
| int i; |
| RTX_CODE code = GET_CODE (x); |
| const char *fmt; |
| |
| if (x == y) |
| return true; |
| if ((code == REG || (code == SUBREG && REG_P (SUBREG_REG (x)))) |
| && (REG_P (y) || (GET_CODE (y) == SUBREG && REG_P (SUBREG_REG (y))))) |
| { |
| int j; |
| |
| i = get_hard_regno (x); |
| if (i < 0) |
| goto slow; |
| |
| if ((j = y_hard_regno) < 0) |
| goto slow; |
| |
| i += lra_constraint_offset (i, GET_MODE (x)); |
| j += lra_constraint_offset (j, GET_MODE (y)); |
| |
| return i == j; |
| } |
| |
| /* If two operands must match, because they are really a single |
| operand of an assembler insn, then two post-increments are invalid |
| because the assembler insn would increment only once. On the |
| other hand, a post-increment matches ordinary indexing if the |
| post-increment is the output operand. */ |
| if (code == POST_DEC || code == POST_INC || code == POST_MODIFY) |
| return operands_match_p (XEXP (x, 0), y, y_hard_regno); |
| |
| /* Two pre-increments are invalid because the assembler insn would |
| increment only once. On the other hand, a pre-increment matches |
| ordinary indexing if the pre-increment is the input operand. */ |
| if (GET_CODE (y) == PRE_DEC || GET_CODE (y) == PRE_INC |
| || GET_CODE (y) == PRE_MODIFY) |
| return operands_match_p (x, XEXP (y, 0), -1); |
| |
| slow: |
| |
| if (code == REG && REG_P (y)) |
| return REGNO (x) == REGNO (y); |
| |
| if (code == REG && GET_CODE (y) == SUBREG && REG_P (SUBREG_REG (y)) |
| && x == SUBREG_REG (y)) |
| return true; |
| if (GET_CODE (y) == REG && code == SUBREG && REG_P (SUBREG_REG (x)) |
| && SUBREG_REG (x) == y) |
| return true; |
| |
| /* Now we have disposed of all the cases in which different rtx |
| codes can match. */ |
| if (code != GET_CODE (y)) |
| return false; |
| |
| /* (MULT:SI x y) and (MULT:HI x y) are NOT equivalent. */ |
| if (GET_MODE (x) != GET_MODE (y)) |
| return false; |
| |
| switch (code) |
| { |
| CASE_CONST_UNIQUE: |
| return false; |
| |
| case CONST_VECTOR: |
| if (!same_vector_encodings_p (x, y)) |
| return false; |
| break; |
| |
| case LABEL_REF: |
| return label_ref_label (x) == label_ref_label (y); |
| case SYMBOL_REF: |
| return XSTR (x, 0) == XSTR (y, 0); |
| |
| default: |
| break; |
| } |
| |
| /* Compare the elements. If any pair of corresponding elements fail |
| to match, return false for the whole things. */ |
| |
| fmt = GET_RTX_FORMAT (code); |
| for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) |
| { |
| int val, j; |
| switch (fmt[i]) |
| { |
| case 'w': |
| if (XWINT (x, i) != XWINT (y, i)) |
| return false; |
| break; |
| |
| case 'i': |
| if (XINT (x, i) != XINT (y, i)) |
| return false; |
| break; |
| |
| case 'p': |
| if (maybe_ne (SUBREG_BYTE (x), SUBREG_BYTE (y))) |
| return false; |
| break; |
| |
| case 'e': |
| val = operands_match_p (XEXP (x, i), XEXP (y, i), -1); |
| if (val == 0) |
| return false; |
| break; |
| |
| case '0': |
| break; |
| |
| case 'E': |
| if (XVECLEN (x, i) != XVECLEN (y, i)) |
| return false; |
| for (j = XVECLEN (x, i) - 1; j >= 0; --j) |
| { |
| val = operands_match_p (XVECEXP (x, i, j), XVECEXP (y, i, j), -1); |
| if (val == 0) |
| return false; |
| } |
| break; |
| |
| /* It is believed that rtx's at this level will never |
| contain anything but integers and other rtx's, except for |
| within LABEL_REFs and SYMBOL_REFs. */ |
| default: |
| gcc_unreachable (); |
| } |
| } |
| return true; |
| } |
| |
| /* True if X is a constant that can be forced into the constant pool. |
| MODE is the mode of the operand, or VOIDmode if not known. */ |
| #define CONST_POOL_OK_P(MODE, X) \ |
| ((MODE) != VOIDmode \ |
| && CONSTANT_P (X) \ |
| && GET_CODE (X) != HIGH \ |
| && GET_MODE_SIZE (MODE).is_constant () \ |
| && !targetm.cannot_force_const_mem (MODE, X)) |
| |
| /* If REG is a reload pseudo, try to make its class satisfying CL. */ |
| static void |
| narrow_reload_pseudo_class (rtx reg, enum reg_class cl) |
| { |
| enum reg_class rclass; |
| |
| /* Do not make more accurate class from reloads generated. They are |
| mostly moves with a lot of constraints. Making more accurate |
| class may results in very narrow class and impossibility of find |
| registers for several reloads of one insn. */ |
| if (INSN_UID (curr_insn) >= new_insn_uid_start) |
| return; |
| if (GET_CODE (reg) == SUBREG) |
| reg = SUBREG_REG (reg); |
| if (! REG_P (reg) || (int) REGNO (reg) < new_regno_start) |
| return; |
| if (in_class_p (reg, cl, &rclass) && rclass != cl) |
| lra_change_class (REGNO (reg), rclass, " Change to", true); |
| } |
| |
| /* Searches X for any reference to a reg with the same value as REGNO, |
| returning the rtx of the reference found if any. Otherwise, |
| returns NULL_RTX. */ |
| static rtx |
| regno_val_use_in (unsigned int regno, rtx x) |
| { |
| const char *fmt; |
| int i, j; |
| rtx tem; |
| |
| if (REG_P (x) && lra_reg_info[REGNO (x)].val == lra_reg_info[regno].val) |
| return x; |
| |
| fmt = GET_RTX_FORMAT (GET_CODE (x)); |
| for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--) |
| { |
| if (fmt[i] == 'e') |
| { |
| if ((tem = regno_val_use_in (regno, XEXP (x, i)))) |
| return tem; |
| } |
| else if (fmt[i] == 'E') |
| for (j = XVECLEN (x, i) - 1; j >= 0; j--) |
| if ((tem = regno_val_use_in (regno , XVECEXP (x, i, j)))) |
| return tem; |
| } |
| |
| return NULL_RTX; |
| } |
| |
| /* Return true if all current insn non-output operands except INS (it |
| has a negaitve end marker) do not use pseudos with the same value |
| as REGNO. */ |
| static bool |
| check_conflict_input_operands (int regno, signed char *ins) |
| { |
| int in; |
| int n_operands = curr_static_id->n_operands; |
| |
| for (int nop = 0; nop < n_operands; nop++) |
| if (! curr_static_id->operand[nop].is_operator |
| && curr_static_id->operand[nop].type != OP_OUT) |
| { |
| for (int i = 0; (in = ins[i]) >= 0; i++) |
| if (in == nop) |
| break; |
| if (in < 0 |
| && regno_val_use_in (regno, *curr_id->operand_loc[nop]) != NULL_RTX) |
| return false; |
| } |
| return true; |
| } |
| |
| /* Generate reloads for matching OUT and INS (array of input operand numbers |
| with end marker -1) with reg class GOAL_CLASS and EXCLUDE_START_HARD_REGS, |
| considering output operands OUTS (similar array to INS) needing to be in |
| different registers. Add input and output reloads correspondingly to the |
| lists *BEFORE and *AFTER. OUT might be negative. In this case we generate |
| input reloads for matched input operands INS. EARLY_CLOBBER_P is a flag |
| that the output operand is early clobbered for chosen alternative. */ |
| static void |
| match_reload (signed char out, signed char *ins, signed char *outs, |
| enum reg_class goal_class, HARD_REG_SET *exclude_start_hard_regs, |
| rtx_insn **before, rtx_insn **after, bool early_clobber_p) |
| { |
| bool out_conflict; |
| int i, in; |
| rtx new_in_reg, new_out_reg, reg; |
| machine_mode inmode, outmode; |
| rtx in_rtx = *curr_id->operand_loc[ins[0]]; |
| rtx out_rtx = out < 0 ? in_rtx : *curr_id->operand_loc[out]; |
| |
| inmode = curr_operand_mode[ins[0]]; |
| outmode = out < 0 ? inmode : curr_operand_mode[out]; |
| push_to_sequence (*before); |
| if (inmode != outmode) |
| { |
| /* process_alt_operands has already checked that the mode sizes |
| are ordered. */ |
| if (partial_subreg_p (outmode, inmode)) |
| { |
| bool asm_p = asm_noperands (PATTERN (curr_insn)) >= 0; |
| int hr; |
| HARD_REG_SET temp_hard_reg_set; |
| |
| if (asm_p && (hr = get_hard_regno (out_rtx)) >= 0 |
| && hard_regno_nregs (hr, inmode) > 1) |
| { |
| /* See gcc.c-torture/execute/20030222-1.c. |
| Consider the code for 32-bit (e.g. BE) target: |
| int i, v; long x; x = v; asm ("" : "=r" (i) : "0" (x)); |
| We generate the following RTL with reload insns: |
| 1. subreg:si(x:di, 0) = 0; |
| 2. subreg:si(x:di, 4) = v:si; |
| 3. t:di = x:di, dead x; |
| 4. asm ("" : "=r" (subreg:si(t:di,4)) : "0" (t:di)) |
| 5. i:si = subreg:si(t:di,4); |
| If we assign hard reg of x to t, dead code elimination |
| will remove insn #2 and we will use unitialized hard reg. |
| So exclude the hard reg of x for t. We could ignore this |
| problem for non-empty asm using all x value but it is hard to |
| check that the asm are expanded into insn realy using x |
| and setting r. */ |
| CLEAR_HARD_REG_SET (temp_hard_reg_set); |
| if (exclude_start_hard_regs != NULL) |
| temp_hard_reg_set = *exclude_start_hard_regs; |
| SET_HARD_REG_BIT (temp_hard_reg_set, hr); |
| exclude_start_hard_regs = &temp_hard_reg_set; |
| } |
| reg = new_in_reg |
| = lra_create_new_reg_with_unique_value (inmode, in_rtx, goal_class, |
| exclude_start_hard_regs, |
| ""); |
| new_out_reg = gen_lowpart_SUBREG (outmode, reg); |
| LRA_SUBREG_P (new_out_reg) = 1; |
| /* If the input reg is dying here, we can use the same hard |
| register for REG and IN_RTX. We do it only for original |
| pseudos as reload pseudos can die although original |
| pseudos still live where reload pseudos dies. */ |
| if (REG_P (in_rtx) && (int) REGNO (in_rtx) < lra_new_regno_start |
| && find_regno_note (curr_insn, REG_DEAD, REGNO (in_rtx)) |
| && (!early_clobber_p |
| || check_conflict_input_operands(REGNO (in_rtx), ins))) |
| lra_assign_reg_val (REGNO (in_rtx), REGNO (reg)); |
| } |
| else |
| { |
| reg = new_out_reg |
| = lra_create_new_reg_with_unique_value (outmode, out_rtx, |
| goal_class, |
| exclude_start_hard_regs, |
| ""); |
| new_in_reg = gen_lowpart_SUBREG (inmode, reg); |
| /* NEW_IN_REG is non-paradoxical subreg. We don't want |
| NEW_OUT_REG living above. We add clobber clause for |
| this. This is just a temporary clobber. We can remove |
| it at the end of LRA work. */ |
| rtx_insn *clobber = emit_clobber (new_out_reg); |
| LRA_TEMP_CLOBBER_P (PATTERN (clobber)) = 1; |
| LRA_SUBREG_P (new_in_reg) = 1; |
| if (GET_CODE (in_rtx) == SUBREG) |
| { |
| rtx subreg_reg = SUBREG_REG (in_rtx); |
| |
| /* If SUBREG_REG is dying here and sub-registers IN_RTX |
| and NEW_IN_REG are similar, we can use the same hard |
| register for REG and SUBREG_REG. */ |
| if (REG_P (subreg_reg) |
| && (int) REGNO (subreg_reg) < lra_new_regno_start |
| && GET_MODE (subreg_reg) == outmode |
| && known_eq (SUBREG_BYTE (in_rtx), SUBREG_BYTE (new_in_reg)) |
| && find_regno_note (curr_insn, REG_DEAD, REGNO (subreg_reg)) |
| && (! early_clobber_p |
| || check_conflict_input_operands (REGNO (subreg_reg), |
| ins))) |
| lra_assign_reg_val (REGNO (subreg_reg), REGNO (reg)); |
| } |
| } |
| } |
| else |
| { |
| /* Pseudos have values -- see comments for lra_reg_info. |
| Different pseudos with the same value do not conflict even if |
| they live in the same place. When we create a pseudo we |
| assign value of original pseudo (if any) from which we |
| created the new pseudo. If we create the pseudo from the |
| input pseudo, the new pseudo will have no conflict with the |
| input pseudo which is wrong when the input pseudo lives after |
| the insn and as the new pseudo value is changed by the insn |
| output. Therefore we create the new pseudo from the output |
| except the case when we have single matched dying input |
| pseudo. |
| |
| We cannot reuse the current output register because we might |
| have a situation like "a <- a op b", where the constraints |
| force the second input operand ("b") to match the output |
| operand ("a"). "b" must then be copied into a new register |
| so that it doesn't clobber the current value of "a". |
| |
| We cannot use the same value if the output pseudo is |
| early clobbered or the input pseudo is mentioned in the |
| output, e.g. as an address part in memory, because |
| output reload will actually extend the pseudo liveness. |
| We don't care about eliminable hard regs here as we are |
| interesting only in pseudos. */ |
| |
| /* Matching input's register value is the same as one of the other |
| output operand. Output operands in a parallel insn must be in |
| different registers. */ |
| out_conflict = false; |
| if (REG_P (in_rtx)) |
| { |
| for (i = 0; outs[i] >= 0; i++) |
| { |
| rtx other_out_rtx = *curr_id->operand_loc[outs[i]]; |
| if (outs[i] != out && REG_P (other_out_rtx) |
| && (regno_val_use_in (REGNO (in_rtx), other_out_rtx) |
| != NULL_RTX)) |
| { |
| out_conflict = true; |
| break; |
| } |
| } |
| } |
| |
| new_in_reg = new_out_reg |
| = (! early_clobber_p && ins[1] < 0 && REG_P (in_rtx) |
| && (int) REGNO (in_rtx) < lra_new_regno_start |
| && find_regno_note (curr_insn, REG_DEAD, REGNO (in_rtx)) |
| && (! early_clobber_p |
| || check_conflict_input_operands (REGNO (in_rtx), ins)) |
| && (out < 0 |
| || regno_val_use_in (REGNO (in_rtx), out_rtx) == NULL_RTX) |
| && !out_conflict |
| ? lra_create_new_reg (inmode, in_rtx, goal_class, |
| exclude_start_hard_regs, "") |
| : lra_create_new_reg_with_unique_value (outmode, out_rtx, goal_class, |
| exclude_start_hard_regs, |
| "")); |
| } |
| /* In operand can be got from transformations before processing insn |
| constraints. One example of such transformations is subreg |
| reloading (see function simplify_operand_subreg). The new |
| pseudos created by the transformations might have inaccurate |
| class (ALL_REGS) and we should make their classes more |
| accurate. */ |
| narrow_reload_pseudo_class (in_rtx, goal_class); |
| lra_emit_move (copy_rtx (new_in_reg), in_rtx); |
| *before = get_insns (); |
| end_sequence (); |
| /* Add the new pseudo to consider values of subsequent input reload |
| pseudos. */ |
| lra_assert (curr_insn_input_reloads_num < LRA_MAX_INSN_RELOADS); |
| curr_insn_input_reloads[curr_insn_input_reloads_num].input = in_rtx; |
| curr_insn_input_reloads[curr_insn_input_reloads_num].match_p = true; |
| curr_insn_input_reloads[curr_insn_input_reloads_num].early_clobber_p = false; |
| curr_insn_input_reloads[curr_insn_input_reloads_num++].reg = new_in_reg; |
| for (i = 0; (in = ins[i]) >= 0; i++) |
| if (GET_MODE (*curr_id->operand_loc[in]) == VOIDmode |
| || GET_MODE (new_in_reg) == GET_MODE (*curr_id->operand_loc[in])) |
| *curr_id->operand_loc[in] = new_in_reg; |
| else |
| { |
| lra_assert |
| (GET_MODE (new_out_reg) == GET_MODE (*curr_id->operand_loc[in])); |
| *curr_id->operand_loc[in] = new_out_reg; |
| } |
| lra_update_dups (curr_id, ins); |
| if (out < 0) |
| return; |
| /* See a comment for the input operand above. */ |
| narrow_reload_pseudo_class (out_rtx, goal_class); |
| if (find_reg_note (curr_insn, REG_UNUSED, out_rtx) == NULL_RTX) |
| { |
| reg = SUBREG_P (out_rtx) ? SUBREG_REG (out_rtx) : out_rtx; |
| start_sequence (); |
| /* If we had strict_low_part, use it also in reload to keep other |
| parts unchanged but do it only for regs as strict_low_part |
| has no sense for memory and probably there is no insn pattern |
| to match the reload insn in memory case. */ |
| if (out >= 0 && curr_static_id->operand[out].strict_low && REG_P (reg)) |
| out_rtx = gen_rtx_STRICT_LOW_PART (VOIDmode, out_rtx); |
| lra_emit_move (out_rtx, copy_rtx (new_out_reg)); |
| emit_insn (*after); |
| *after = get_insns (); |
| end_sequence (); |
| } |
| *curr_id->operand_loc[out] = new_out_reg; |
| lra_update_dup (curr_id, out); |
| } |
| |
| /* Return register class which is union of all reg classes in insn |
| constraint alternative string starting with P. */ |
| static enum reg_class |
| reg_class_from_constraints (const char *p) |
| { |
| int c, len; |
| enum reg_class op_class = NO_REGS; |
| |
| do |
| switch ((c = *p, len = CONSTRAINT_LEN (c, p)), c) |
| { |
| case '#': |
| case ',': |
| return op_class; |
| |
| case 'g': |
| op_class = reg_class_subunion[op_class][GENERAL_REGS]; |
| break; |
| |
| default: |
| enum constraint_num cn = lookup_constraint (p); |
| enum reg_class cl = reg_class_for_constraint (cn); |
| if (cl == NO_REGS) |
| { |
| if (insn_extra_address_constraint (cn)) |
| op_class |
| = (reg_class_subunion |
| [op_class][base_reg_class (VOIDmode, ADDR_SPACE_GENERIC, |
| ADDRESS, SCRATCH)]); |
| break; |
| } |
| |
| op_class = reg_class_subunion[op_class][cl]; |
| break; |
| } |
| while ((p += len), c); |
| return op_class; |
| } |
| |
| /* If OP is a register, return the class of the register as per |
| get_reg_class, otherwise return NO_REGS. */ |
| static inline enum reg_class |
| get_op_class (rtx op) |
| { |
| return REG_P (op) ? get_reg_class (REGNO (op)) : NO_REGS; |
| } |
| |
| /* Return generated insn mem_pseudo:=val if TO_P or val:=mem_pseudo |
| otherwise. If modes of MEM_PSEUDO and VAL are different, use |
| SUBREG for VAL to make them equal. */ |
| static rtx_insn * |
| emit_spill_move (bool to_p, rtx mem_pseudo, rtx val) |
| { |
| if (GET_MODE (mem_pseudo) != GET_MODE (val)) |
| { |
| /* Usually size of mem_pseudo is greater than val size but in |
| rare cases it can be less as it can be defined by target |
| dependent macro HARD_REGNO_CALLER_SAVE_MODE. */ |
| if (! MEM_P (val)) |
| { |
| val = gen_lowpart_SUBREG (GET_MODE (mem_pseudo), |
| GET_CODE (val) == SUBREG |
| ? SUBREG_REG (val) : val); |
| LRA_SUBREG_P (val) = 1; |
| } |
| else |
| { |
| mem_pseudo = gen_lowpart_SUBREG (GET_MODE (val), mem_pseudo); |
| LRA_SUBREG_P (mem_pseudo) = 1; |
| } |
| } |
| return to_p ? gen_move_insn (mem_pseudo, val) |
| : gen_move_insn (val, mem_pseudo); |
| } |
| |
| /* Process a special case insn (register move), return true if we |
| don't need to process it anymore. INSN should be a single set |
| insn. Set up that RTL was changed through CHANGE_P and that hook |
| TARGET_SECONDARY_MEMORY_NEEDED says to use secondary memory through |
| SEC_MEM_P. */ |
| static bool |
| check_and_process_move (bool *change_p, bool *sec_mem_p ATTRIBUTE_UNUSED) |
| { |
| int sregno, dregno; |
| rtx dest, src, dreg, sreg, new_reg, scratch_reg; |
| rtx_insn *before; |
| enum reg_class dclass, sclass, secondary_class; |
| secondary_reload_info sri; |
| |
| lra_assert (curr_insn_set != NULL_RTX); |
| dreg = dest = SET_DEST (curr_insn_set); |
| sreg = src = SET_SRC (curr_insn_set); |
| if (GET_CODE (dest) == SUBREG) |
| dreg = SUBREG_REG (dest); |
| if (GET_CODE (src) == SUBREG) |
| sreg = SUBREG_REG (src); |
| if (! (REG_P (dreg) || MEM_P (dreg)) || ! (REG_P (sreg) || MEM_P (sreg))) |
| return false; |
| sclass = dclass = NO_REGS; |
| if (REG_P (dreg)) |
| dclass = get_reg_class (REGNO (dreg)); |
| gcc_assert (dclass < LIM_REG_CLASSES && dclass >= NO_REGS); |
| if (dclass == ALL_REGS) |
| /* ALL_REGS is used for new pseudos created by transformations |
| like reload of SUBREG_REG (see function |
| simplify_operand_subreg). We don't know their class yet. We |
| should figure out the class from processing the insn |
| constraints not in this fast path function. Even if ALL_REGS |
| were a right class for the pseudo, secondary_... hooks usually |
| are not define for ALL_REGS. */ |
| return false; |
| if (REG_P (sreg)) |
| sclass = get_reg_class (REGNO (sreg)); |
| gcc_assert (sclass < LIM_REG_CLASSES && sclass >= NO_REGS); |
| if (sclass == ALL_REGS) |
| /* See comments above. */ |
| return false; |
| if (sclass == NO_REGS && dclass == NO_REGS) |
| return false; |
| if (targetm.secondary_memory_needed (GET_MODE (src), sclass, dclass) |
| && ((sclass != NO_REGS && dclass != NO_REGS) |
| || (GET_MODE (src) |
| != targetm.secondary_memory_needed_mode (GET_MODE (src))))) |
| { |
| *sec_mem_p = true; |
| return false; |
| } |
| if (! REG_P (dreg) || ! REG_P (sreg)) |
| return false; |
| sri.prev_sri = NULL; |
| sri.icode = CODE_FOR_nothing; |
| sri.extra_cost = 0; |
| secondary_class = NO_REGS; |
| /* Set up hard register for a reload pseudo for hook |
| secondary_reload because some targets just ignore unassigned |
| pseudos in the hook. */ |
| if (dclass != NO_REGS && lra_get_regno_hard_regno (REGNO (dreg)) < 0) |
| { |
| dregno = REGNO (dreg); |
| reg_renumber[dregno] = ira_class_hard_regs[dclass][0]; |
| } |
| else |
| dregno = -1; |
| if (sclass != NO_REGS && lra_get_regno_hard_regno (REGNO (sreg)) < 0) |
| { |
| sregno = REGNO (sreg); |
| reg_renumber[sregno] = ira_class_hard_regs[sclass][0]; |
| } |
| else |
| sregno = -1; |
| if (sclass != NO_REGS) |
| secondary_class |
| = (enum reg_class) targetm.secondary_reload (false, dest, |
| (reg_class_t) sclass, |
| GET_MODE (src), &sri); |
| if (sclass == NO_REGS |
| || ((secondary_class != NO_REGS || sri.icode != CODE_FOR_nothing) |
| && dclass != NO_REGS)) |
| { |
| enum reg_class old_sclass = secondary_class; |
| secondary_reload_info old_sri = sri; |
| |
| sri.prev_sri = NULL; |
| sri.icode = CODE_FOR_nothing; |
| sri.extra_cost = 0; |
| secondary_class |
| = (enum reg_class) targetm.secondary_reload (true, src, |
| (reg_class_t) dclass, |
| GET_MODE (src), &sri); |
| /* Check the target hook consistency. */ |
| lra_assert |
| ((secondary_class == NO_REGS && sri.icode == CODE_FOR_nothing) |
| || (old_sclass == NO_REGS && old_sri.icode == CODE_FOR_nothing) |
| || (secondary_class == old_sclass && sri.icode == old_sri.icode)); |
| } |
| if (sregno >= 0) |
| reg_renumber [sregno] = -1; |
| if (dregno >= 0) |
| reg_renumber [dregno] = -1; |
| if (secondary_class == NO_REGS && sri.icode == CODE_FOR_nothing) |
| return false; |
| *change_p = true; |
| new_reg = NULL_RTX; |
| if (secondary_class != NO_REGS) |
| new_reg = lra_create_new_reg_with_unique_value (GET_MODE (src), NULL_RTX, |
| secondary_class, NULL, |
| "secondary"); |
| start_sequence (); |
| if (sri.icode == CODE_FOR_nothing) |
| lra_emit_move (new_reg, src); |
| else |
| { |
| enum reg_class scratch_class; |
| |
| scratch_class = (reg_class_from_constraints |
| (insn_data[sri.icode].operand[2].constraint)); |
| scratch_reg = (lra_create_new_reg_with_unique_value |
| (insn_data[sri.icode].operand[2].mode, NULL_RTX, |
| scratch_class, NULL, "scratch")); |
| emit_insn (GEN_FCN (sri.icode) (new_reg != NULL_RTX ? new_reg : dest, |
| src, scratch_reg)); |
| } |
| before = get_insns (); |
| end_sequence (); |
| lra_process_new_insns (curr_insn, before, NULL, "Inserting the move"); |
| if (new_reg != NULL_RTX) |
| SET_SRC (curr_insn_set) = new_reg; |
| else |
| { |
| if (lra_dump_file != NULL) |
| { |
| fprintf (lra_dump_file, "Deleting move %u\n", INSN_UID (curr_insn)); |
| dump_insn_slim (lra_dump_file, curr_insn); |
| } |
| lra_set_insn_deleted (curr_insn); |
| return true; |
| } |
| return false; |
| } |
| |
| /* The following data describe the result of process_alt_operands. |
| The data are used in curr_insn_transform to generate reloads. */ |
| |
| /* The chosen reg classes which should be used for the corresponding |
| operands. */ |
| static enum reg_class goal_alt[MAX_RECOG_OPERANDS]; |
| /* Hard registers which cannot be a start hard register for the corresponding |
| operands. */ |
| static HARD_REG_SET goal_alt_exclude_start_hard_regs[MAX_RECOG_OPERANDS]; |
| /* True if the operand should be the same as another operand and that |
| other operand does not need a reload. */ |
| static bool goal_alt_match_win[MAX_RECOG_OPERANDS]; |
| /* True if the operand does not need a reload. */ |
| static bool goal_alt_win[MAX_RECOG_OPERANDS]; |
| /* True if the operand can be offsetable memory. */ |
| static bool goal_alt_offmemok[MAX_RECOG_OPERANDS]; |
| /* The number of an operand to which given operand can be matched to. */ |
| static int goal_alt_matches[MAX_RECOG_OPERANDS]; |
| /* The number of elements in the following array. */ |
| static int goal_alt_dont_inherit_ops_num; |
| /* Numbers of operands whose reload pseudos should not be inherited. */ |
| static int goal_alt_dont_inherit_ops[MAX_RECOG_OPERANDS]; |
| /* True if we should try only this alternative for the next constraint sub-pass |
| to speed up the sub-pass. */ |
| static bool goal_reuse_alt_p; |
| /* True if the insn commutative operands should be swapped. */ |
| static bool goal_alt_swapped; |
| /* The chosen insn alternative. */ |
| static int goal_alt_number; |
| /* True if output reload of the stack pointer should be generated. */ |
| static bool goal_alt_out_sp_reload_p; |
| |
| /* True if the corresponding operand is the result of an equivalence |
| substitution. */ |
| static bool equiv_substition_p[MAX_RECOG_OPERANDS]; |
| |
| /* The following five variables are used to choose the best insn |
| alternative. They reflect final characteristics of the best |
| alternative. */ |
| |
| /* Number of necessary reloads and overall cost reflecting the |
| previous value and other unpleasantness of the best alternative. */ |
| static int best_losers, best_overall; |
| /* Overall number hard registers used for reloads. For example, on |
| some targets we need 2 general registers to reload DFmode and only |
| one floating point register. */ |
| static int best_reload_nregs; |
| /* Overall number reflecting distances of previous reloading the same |
| value. The distances are counted from the current BB start. It is |
| used to improve inheritance chances. */ |
| static int best_reload_sum; |
| |
| /* True if the current insn should have no correspondingly input or |
| output reloads. */ |
| static bool no_input_reloads_p, no_output_reloads_p; |
| |
| /* True if we swapped the commutative operands in the current |
| insn. */ |
| static int curr_swapped; |
| |
| /* if CHECK_ONLY_P is false, arrange for address element *LOC to be a |
| register of class CL. Add any input reloads to list BEFORE. AFTER |
| is nonnull if *LOC is an automodified value; handle that case by |
| adding the required output reloads to list AFTER. Return true if |
| the RTL was changed. |
| |
| if CHECK_ONLY_P is true, check that the *LOC is a correct address |
| register. Return false if the address register is correct. */ |
| static bool |
| process_addr_reg (rtx *loc, bool check_only_p, rtx_insn **before, rtx_insn **after, |
| enum reg_class cl) |
| { |
| int regno; |
| enum reg_class rclass, new_class; |
| rtx reg; |
| rtx new_reg; |
| machine_mode mode; |
| bool subreg_p, before_p = false; |
| |
| subreg_p = GET_CODE (*loc) == SUBREG; |
| if (subreg_p) |
| { |
| reg = SUBREG_REG (*loc); |
| mode = GET_MODE (reg); |
| |
| /* For mode with size bigger than ptr_mode, there unlikely to be "mov" |
| between two registers with different classes, but there normally will |
| be "mov" which transfers element of vector register into the general |
| register, and this normally will be a subreg which should be reloaded |
| as a whole. This is particularly likely to be triggered when |
| -fno-split-wide-types specified. */ |
| if (!REG_P (reg) |
| || in_class_p (reg, cl, &new_class) |
| || known_le (GET_MODE_SIZE (mode), GET_MODE_SIZE (ptr_mode))) |
| loc = &SUBREG_REG (*loc); |
| } |
| |
| reg = *loc; |
| mode = GET_MODE (reg); |
| if (! REG_P (reg)) |
| { |
| if (check_only_p) |
| return true; |
| /* Always reload memory in an address even if the target supports |
| such addresses. */ |
| new_reg = lra_create_new_reg_with_unique_value (mode, reg, cl, NULL, |
| "address"); |
| before_p = true; |
| } |
| else |
| { |
| regno = REGNO (reg); |
| rclass = get_reg_class (regno); |
| if (! check_only_p |
| && (*loc = get_equiv_with_elimination (reg, curr_insn)) != reg) |
| { |
| if (lra_dump_file != NULL) |
| { |
| fprintf (lra_dump_file, |
| "Changing pseudo %d in address of insn %u on equiv ", |
| REGNO (reg), INSN_UID (curr_insn)); |
| dump_value_slim (lra_dump_file, *loc, 1); |
| fprintf (lra_dump_file, "\n"); |
| } |
| *loc = copy_rtx (*loc); |
| } |
| if (*loc != reg || ! in_class_p (reg, cl, &new_class)) |
| { |
| if (check_only_p) |
| return true; |
| reg = *loc; |
| if (get_reload_reg (after == NULL ? OP_IN : OP_INOUT, |
| mode, reg, cl, NULL, |
| subreg_p, false, "address", &new_reg)) |
| before_p = true; |
| } |
| else if (new_class != NO_REGS && rclass != new_class) |
| { |
| if (check_only_p) |
| return true; |
| lra_change_class (regno, new_class, " Change to", true); |
| return false; |
| } |
| else |
| return false; |
| } |
| if (before_p) |
| { |
| push_to_sequence (*before); |
| lra_emit_move (new_reg, reg); |
| *before = get_insns (); |
| end_sequence (); |
| } |
| *loc = new_reg; |
| if (after != NULL) |
| { |
| start_sequence (); |
| lra_emit_move (before_p ? copy_rtx (reg) : reg, new_reg); |
| emit_insn (*after); |
| *after = get_insns (); |
| end_sequence (); |
| } |
| return true; |
| } |
| |
| /* Insert move insn in simplify_operand_subreg. BEFORE returns |
| the insn to be inserted before curr insn. AFTER returns the |
| the insn to be inserted after curr insn. ORIGREG and NEWREG |
| are the original reg and new reg for reload. */ |
| static void |
| insert_move_for_subreg (rtx_insn **before, rtx_insn **after, rtx origreg, |
| rtx newreg) |
| { |
| if (before) |
| { |
| push_to_sequence (*before); |
| lra_emit_move (newreg, origreg); |
| *before = get_insns (); |
| end_sequence (); |
| } |
| if (after) |
| { |
| start_sequence (); |
| lra_emit_move (origreg, newreg); |
| emit_insn (*after); |
| *after = get_insns (); |
| end_sequence (); |
| } |
| } |
| |
| static bool valid_address_p (machine_mode mode, rtx addr, addr_space_t as); |
| static bool process_address (int, bool, rtx_insn **, rtx_insn **); |
| |
| /* Make reloads for subreg in operand NOP with internal subreg mode |
| REG_MODE, add new reloads for further processing. Return true if |
| any change was done. */ |
| static bool |
| simplify_operand_subreg (int nop, machine_mode reg_mode) |
| { |
| int hard_regno, inner_hard_regno; |
| rtx_insn *before, *after; |
| machine_mode mode, innermode; |
| rtx reg, new_reg; |
| rtx operand = *curr_id->operand_loc[nop]; |
| enum reg_class regclass; |
| enum op_type type; |
| |
| before = after = NULL; |
| |
| if (GET_CODE (operand) != SUBREG) |
| return false; |
| |
| mode = GET_MODE (operand); |
| reg = SUBREG_REG (operand); |
| innermode = GET_MODE (reg); |
| type = curr_static_id->operand[nop].type; |
| if (MEM_P (reg)) |
| { |
| const bool addr_was_valid |
| = valid_address_p (innermode, XEXP (reg, 0), MEM_ADDR_SPACE (reg)); |
| alter_subreg (curr_id->operand_loc[nop], false); |
| rtx subst = *curr_id->operand_loc[nop]; |
| lra_assert (MEM_P (subst)); |
| const bool addr_is_valid = valid_address_p (GET_MODE (subst), |
| XEXP (subst, 0), |
| MEM_ADDR_SPACE (subst)); |
| if (!addr_was_valid |
| || addr_is_valid |
| || ((get_constraint_type (lookup_constraint |
| (curr_static_id->operand[nop].constraint)) |
| != CT_SPECIAL_MEMORY) |
| /* We still can reload address and if the address is |
| valid, we can remove subreg without reloading its |
| inner memory. */ |
| && valid_address_p (GET_MODE (subst), |
| regno_reg_rtx |
| [ira_class_hard_regs |
| [base_reg_class (GET_MODE (subst), |
| MEM_ADDR_SPACE (subst), |
| ADDRESS, SCRATCH)][0]], |
| MEM_ADDR_SPACE (subst)))) |
| { |
| /* If we change the address for a paradoxical subreg of memory, the |
| new address might violate the necessary alignment or the access |
| might be slow; take this into consideration. We need not worry |
| about accesses beyond allocated memory for paradoxical memory |
| subregs as we don't substitute such equiv memory (see processing |
| equivalences in function lra_constraints) and because for spilled |
| pseudos we allocate stack memory enough for the biggest |
| corresponding paradoxical subreg. |
| |
| However, do not blindly simplify a (subreg (mem ...)) for |
| WORD_REGISTER_OPERATIONS targets as this may lead to loading junk |
| data into a register when the inner is narrower than outer or |
| missing important data from memory when the inner is wider than |
| outer. This rule only applies to modes that are no wider than |
| a word. |
| |
| If valid memory becomes invalid after subreg elimination |
| and address might be different we still have to reload |
| memory. |
| */ |
| if ((! addr_was_valid |
| || addr_is_valid |
| || known_eq (GET_MODE_SIZE (mode), GET_MODE_SIZE (innermode))) |
| && !(maybe_ne (GET_MODE_PRECISION (mode), |
| GET_MODE_PRECISION (innermode)) |
| && known_le (GET_MODE_SIZE (mode), UNITS_PER_WORD) |
| && known_le (GET_MODE_SIZE (innermode), UNITS_PER_WORD) |
| && WORD_REGISTER_OPERATIONS) |
| && (!(MEM_ALIGN (subst) < GET_MODE_ALIGNMENT (mode) |
| && targetm.slow_unaligned_access (mode, MEM_ALIGN (subst))) |
| || (MEM_ALIGN (reg) < GET_MODE_ALIGNMENT (innermode) |
| && targetm.slow_unaligned_access (innermode, |
| MEM_ALIGN (reg))))) |
| return true; |
| |
| *curr_id->operand_loc[nop] = operand; |
| |
| /* But if the address was not valid, we cannot reload the MEM without |
| reloading the address first. */ |
| if (!addr_was_valid) |
| process_address (nop, false, &before, &after); |
| |
| /* INNERMODE is fast, MODE slow. Reload the mem in INNERMODE. */ |
| enum reg_class rclass |
| = (enum reg_class) targetm.preferred_reload_class (reg, ALL_REGS); |
| if (get_reload_reg (curr_static_id->operand[nop].type, innermode, |
| reg, rclass, NULL, |
| true, false, "slow/invalid mem", &new_reg)) |
| { |
| bool insert_before, insert_after; |
| bitmap_set_bit (&lra_subreg_reload_pseudos, REGNO (new_reg)); |
| |
| insert_before = (type != OP_OUT |
| || partial_subreg_p (mode, innermode)); |
| insert_after = type != OP_IN; |
| insert_move_for_subreg (insert_before ? &before : NULL, |
| insert_after ? &after : NULL, |
| reg, new_reg); |
| } |
| SUBREG_REG (operand) = new_reg; |
| |
| /* Convert to MODE. */ |
| reg = operand; |
| rclass |
| = (enum reg_class) targetm.preferred_reload_class (reg, ALL_REGS); |
| if (get_reload_reg (curr_static_id->operand[nop].type, mode, reg, |
| rclass, NULL, |
| true, false, "slow/invalid mem", &new_reg)) |
| { |
| bool insert_before, insert_after; |
| bitmap_set_bit (&lra_subreg_reload_pseudos, REGNO (new_reg)); |
| |
| insert_before = type != OP_OUT; |
| insert_after = type != OP_IN; |
| insert_move_for_subreg (insert_before ? &before : NULL, |
| insert_after ? &after : NULL, |
| reg, new_reg); |
| } |
| *curr_id->operand_loc[nop] = new_reg; |
| lra_process_new_insns (curr_insn, before, after, |
| "Inserting slow/invalid mem reload"); |
| return true; |
| } |
| |
| /* If the address was valid and became invalid, prefer to reload |
| the memory. Typical case is when the index scale should |
| correspond the memory. */ |
| *curr_id->operand_loc[nop] = operand; |
| /* Do not return false here as the MEM_P (reg) will be processed |
| later in this function. */ |
| } |
| else if (REG_P (reg) && REGNO (reg) < FIRST_PSEUDO_REGISTER) |
| { |
| alter_subreg (curr_id->operand_loc[nop], false); |
| return true; |
| } |
| else if (CONSTANT_P (reg)) |
| { |
| /* Try to simplify subreg of constant. It is usually result of |
| equivalence substitution. */ |
| if (innermode == VOIDmode |
| && (innermode = original_subreg_reg_mode[nop]) == VOIDmode) |
| innermode = curr_static_id->operand[nop].mode; |
| if ((new_reg = simplify_subreg (mode, reg, innermode, |
| SUBREG_BYTE (operand))) != NULL_RTX) |
| { |
| *curr_id->operand_loc[nop] = new_reg; |
| return true; |
| } |
| } |
| /* Put constant into memory when we have mixed modes. It generates |
| a better code in most cases as it does not need a secondary |
| reload memory. It also prevents LRA looping when LRA is using |
| secondary reload memory again and again. */ |
| if (CONSTANT_P (reg) && CONST_POOL_OK_P (reg_mode, reg) |
| && SCALAR_INT_MODE_P (reg_mode) != SCALAR_INT_MODE_P (mode)) |
| { |
| SUBREG_REG (operand) = force_const_mem (reg_mode, reg); |
| alter_subreg (curr_id->operand_loc[nop], false); |
| return true; |
| } |
| auto fp_subreg_can_be_simplified_after_reload_p = [] (machine_mode innermode, |
| poly_uint64 offset, |
| machine_mode mode) { |
| reload_completed = 1; |
| bool res = simplify_subreg_regno (FRAME_POINTER_REGNUM, |
| innermode, |
| offset, mode) >= 0; |
| reload_completed = 0; |
| return res; |
| }; |
| /* Force a reload of the SUBREG_REG if this is a constant or PLUS or |
| if there may be a problem accessing OPERAND in the outer |
| mode. */ |
| if ((REG_P (reg) |
| && REGNO (reg) >= FIRST_PSEUDO_REGISTER |
| && (hard_regno = lra_get_regno_hard_regno (REGNO (reg))) >= 0 |
| /* Don't reload paradoxical subregs because we could be looping |
| having repeatedly final regno out of hard regs range. */ |
| && (hard_regno_nregs (hard_regno, innermode) |
| >= hard_regno_nregs (hard_regno, mode)) |
| && simplify_subreg_regno (hard_regno, innermode, |
| SUBREG_BYTE (operand), mode) < 0 |
| /* Exclude reloading of frame pointer in subreg if frame pointer can not |
| be simplified here only because the reload is not finished yet. */ |
| && (hard_regno != FRAME_POINTER_REGNUM |
| || !fp_subreg_can_be_simplified_after_reload_p (innermode, |
| SUBREG_BYTE (operand), |
| mode)) |
| /* Don't reload subreg for matching reload. It is actually |
| valid subreg in LRA. */ |
| && ! LRA_SUBREG_P (operand)) |
| || CONSTANT_P (reg) || GET_CODE (reg) == PLUS || MEM_P (reg)) |
| { |
| enum reg_class rclass; |
| |
| if (REG_P (reg)) |
| /* There is a big probability that we will get the same class |
| for the new pseudo and we will get the same insn which |
| means infinite looping. So spill the new pseudo. */ |
| rclass = NO_REGS; |
| else |
| /* The class will be defined later in curr_insn_transform. */ |
| rclass |
| = (enum reg_class) targetm.preferred_reload_class (reg, ALL_REGS); |
| |
| if (get_reload_reg (curr_static_id->operand[nop].type, reg_mode, reg, |
| rclass, NULL, |
| true, false, "subreg reg", &new_reg)) |
| { |
| bool insert_before, insert_after; |
| bitmap_set_bit (&lra_subreg_reload_pseudos, REGNO (new_reg)); |
| |
| insert_before = (type != OP_OUT |
| || read_modify_subreg_p (operand)); |
| insert_after = (type != OP_IN); |
| insert_move_for_subreg (insert_before ? &before : NULL, |
| insert_after ? &after : NULL, |
| reg, new_reg); |
| } |
| SUBREG_REG (operand) = new_reg; |
| lra_process_new_insns (curr_insn, before, after, |
| "Inserting subreg reload"); |
| return true; |
| } |
| /* Force a reload for a paradoxical subreg. For paradoxical subreg, |
| IRA allocates hardreg to the inner pseudo reg according to its mode |
| instead of the outermode, so the size of the hardreg may not be enough |
| to contain the outermode operand, in that case we may need to insert |
| reload for the reg. For the following two types of paradoxical subreg, |
| we need to insert reload: |
| 1. If the op_type is OP_IN, and the hardreg could not be paired with |
| other hardreg to contain the outermode operand |
| (checked by in_hard_reg_set_p), we need to insert the reload. |
| 2. If the op_type is OP_OUT or OP_INOUT. |
| |
| Here is a paradoxical subreg example showing how the reload is generated: |
| |
| (insn 5 4 7 2 (set (reg:TI 106 [ __comp ]) |
| (subreg:TI (reg:DI 107 [ __comp ]) 0)) {*movti_internal_rex64} |
| |
| In IRA, reg107 is allocated to a DImode hardreg. We use x86-64 as example |
| here, if reg107 is assigned to hardreg R15, because R15 is the last |
| hardreg, compiler cannot find another hardreg to pair with R15 to |
| contain TImode data. So we insert a TImode reload reg180 for it. |
| After reload is inserted: |
| |
| (insn 283 0 0 (set (subreg:DI (reg:TI 180 [orig:107 __comp ] [107]) 0) |
| (reg:DI 107 [ __comp ])) -1 |
| (insn 5 4 7 2 (set (reg:TI 106 [ __comp ]) |
| (subreg:TI (reg:TI 180 [orig:107 __comp ] [107]) 0)) {*movti_internal_rex64} |
| |
| Two reload hard registers will be allocated to reg180 to save TImode data |
| in LRA_assign. |
| |
| For LRA pseudos this should normally be handled by the biggest_mode |
| mechanism. However, it's possible for new uses of an LRA pseudo |
| to be introduced after we've allocated it, such as when undoing |
| inheritance, and the allocated register might not then be appropriate |
| for the new uses. */ |
| else if (REG_P (reg) |
| && REGNO (reg) >= FIRST_PSEUDO_REGISTER |
| && paradoxical_subreg_p (operand) |
| && (inner_hard_regno = lra_get_regno_hard_regno (REGNO (reg))) >= 0 |
| && ((hard_regno |
| = simplify_subreg_regno (inner_hard_regno, innermode, |
| SUBREG_BYTE (operand), mode)) < 0 |
| || ((hard_regno_nregs (inner_hard_regno, innermode) |
| < hard_regno_nregs (hard_regno, mode)) |
| && (regclass = lra_get_allocno_class (REGNO (reg))) |
| && (type != OP_IN |
| || !in_hard_reg_set_p (reg_class_contents[regclass], |
| mode, hard_regno) |
| || overlaps_hard_reg_set_p (lra_no_alloc_regs, |
| mode, hard_regno))))) |
| { |
| /* The class will be defined later in curr_insn_transform. */ |
| enum reg_class rclass |
| = (enum reg_class) targetm.preferred_reload_class (reg, ALL_REGS); |
| |
| if (get_reload_reg (curr_static_id->operand[nop].type, mode, reg, |
| rclass, NULL, |
| true, false, "paradoxical subreg", &new_reg)) |
| { |
| rtx subreg; |
| bool insert_before, insert_after; |
| |
| PUT_MODE (new_reg, mode); |
| subreg = gen_lowpart_SUBREG (innermode, new_reg); |
| bitmap_set_bit (&lra_subreg_reload_pseudos, REGNO (new_reg)); |
| |
| insert_before = (type != OP_OUT); |
| insert_after = (type != OP_IN); |
| insert_move_for_subreg (insert_before ? &before : NULL, |
| insert_after ? &after : NULL, |
| reg, subreg); |
| } |
| SUBREG_REG (operand) = new_reg; |
| lra_process_new_insns (curr_insn, before, after, |
| "Inserting paradoxical subreg reload"); |
| return true; |
| } |
| return false; |
| } |
| |
| /* Return TRUE if X refers for a hard register from SET. */ |
| static bool |
| uses_hard_regs_p (rtx x, HARD_REG_SET set) |
| { |
| int i, j, x_hard_regno; |
| machine_mode mode; |
| const char *fmt; |
| enum rtx_code code; |
| |
| if (x == NULL_RTX) |
| return false; |
| code = GET_CODE (x); |
| mode = GET_MODE (x); |
| |
| if (code == SUBREG) |
| { |
| /* For all SUBREGs we want to check whether the full multi-register |
| overlaps the set. For normal SUBREGs this means 'get_hard_regno' of |
| the inner register, for paradoxical SUBREGs this means the |
| 'get_hard_regno' of the full SUBREG and for complete SUBREGs either is |
| fine. Use the wider mode for all cases. */ |
| rtx subreg = SUBREG_REG (x); |
| mode = wider_subreg_mode (x); |
| if (mode == GET_MODE (subreg)) |
| { |
| x = subreg; |
| code = GET_CODE (x); |
| } |
| } |
| |
| if (REG_P (x) || SUBREG_P (x)) |
| { |
| x_hard_regno = get_hard_regno (x); |
| return (x_hard_regno >= 0 |
| && overlaps_hard_reg_set_p (set, mode, x_hard_regno)); |
| } |
| fmt = GET_RTX_FORMAT (code); |
| for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) |
| { |
| if (fmt[i] == 'e') |
| { |
| if (uses_hard_regs_p (XEXP (x, i), set)) |
| return true; |
| } |
| else if (fmt[i] == 'E') |
| { |
| for (j = XVECLEN (x, i) - 1; j >= 0; j--) |
| if (uses_hard_regs_p (XVECEXP (x, i, j), set)) |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /* Return true if OP is a spilled pseudo. */ |
| static inline bool |
| spilled_pseudo_p (rtx op) |
| { |
| return (REG_P (op) |
| && REGNO (op) >= FIRST_PSEUDO_REGISTER && in_mem_p (REGNO (op))); |
| } |
| |
| /* Return true if X is a general constant. */ |
| static inline bool |
| general_constant_p (rtx x) |
| { |
| return CONSTANT_P (x) && (! flag_pic || LEGITIMATE_PIC_OPERAND_P (x)); |
| } |
| |
| static bool |
| reg_in_class_p (rtx reg, enum reg_class cl) |
| { |
| if (cl == NO_REGS) |
| return get_reg_class (REGNO (reg)) == NO_REGS; |
| return in_class_p (reg, cl, NULL); |
| } |
| |
| /* Return true if SET of RCLASS contains no hard regs which can be |
| used in MODE. */ |
| static bool |
| prohibited_class_reg_set_mode_p (enum reg_class rclass, |
| HARD_REG_SET &set, |
| machine_mode mode) |
| { |
| HARD_REG_SET temp; |
| |
| lra_assert (hard_reg_set_subset_p (reg_class_contents[rclass], set)); |
| temp = set & ~lra_no_alloc_regs; |
| return (hard_reg_set_subset_p |
| (temp, ira_prohibited_class_mode_regs[rclass][mode])); |
| } |
| |
| |
| /* Used to check validity info about small class input operands. It |
| should be incremented at start of processing an insn |
| alternative. */ |
| static unsigned int curr_small_class_check = 0; |
| |
| /* Update number of used inputs of class OP_CLASS for operand NOP |
| of alternative NALT. Return true if we have more such class operands |
| than the number of available regs. */ |
| static bool |
| update_and_check_small_class_inputs (int nop, int nalt, |
| enum reg_class op_class) |
| { |
| static unsigned int small_class_check[LIM_REG_CLASSES]; |
| static int small_class_input_nums[LIM_REG_CLASSES]; |
| |
| if (SMALL_REGISTER_CLASS_P (op_class) |
| /* We are interesting in classes became small because of fixing |
| some hard regs, e.g. by an user through GCC options. */ |
| && hard_reg_set_intersect_p (reg_class_contents[op_class], |
| ira_no_alloc_regs) |
| && (curr_static_id->operand[nop].type != OP_OUT |
| || TEST_BIT (curr_static_id->operand[nop].early_clobber_alts, nalt))) |
| { |
| if (small_class_check[op_class] == curr_small_class_check) |
| small_class_input_nums[op_class]++; |
| else |
| { |
| small_class_check[op_class] = curr_small_class_check; |
| small_class_input_nums[op_class] = 1; |
| } |
| if (small_class_input_nums[op_class] > ira_class_hard_regs_num[op_class]) |
| return true; |
| } |
| return false; |
| } |
| |
| /* Print operand constraints for alternative ALT_NUMBER of the current |
| insn. */ |
| static void |
| print_curr_insn_alt (int alt_number) |
| { |
| for (int i = 0; i < curr_static_id->n_operands; i++) |
| { |
| const char *p = (curr_static_id->operand_alternative |
| [alt_number * curr_static_id->n_operands + i].constraint); |
| if (*p == '\0') |
| continue; |
| fprintf (lra_dump_file, " (%d) ", i); |
| for (; *p != '\0' && *p != ',' && *p != '#'; p++) |
| fputc (*p, lra_dump_file); |
| } |
| } |
| |
| /* Major function to choose the current insn alternative and what |
| operands should be reloaded and how. If ONLY_ALTERNATIVE is not |
| negative we should consider only this alternative. Return false if |
| we cannot choose the alternative or find how to reload the |
| operands. */ |
| static bool |
| process_alt_operands (int only_alternative) |
| { |
| bool ok_p = false; |
| int nop, overall, nalt; |
| int n_alternatives = curr_static_id->n_alternatives; |
| int n_operands = curr_static_id->n_operands; |
| /* LOSERS counts the operands that don't fit this alternative and |
| would require loading. */ |
| int losers; |
| int addr_losers; |
| /* REJECT is a count of how undesirable this alternative says it is |
| if any reloading is required. If the alternative matches exactly |
| then REJECT is ignored, but otherwise it gets this much counted |
| against it in addition to the reloading needed. */ |
| int reject; |
| /* This is defined by '!' or '?' alternative constraint and added to |
| reject. But in some cases it can be ignored. */ |
| int static_reject; |
| int op_reject; |
| /* The number of elements in the following array. */ |
| int early_clobbered_regs_num; |
| /* Numbers of operands which are early clobber registers. */ |
| int early_clobbered_nops[MAX_RECOG_OPERANDS]; |
| enum reg_class curr_alt[MAX_RECOG_OPERANDS]; |
| HARD_REG_SET curr_alt_set[MAX_RECOG_OPERANDS]; |
| HARD_REG_SET curr_alt_exclude_start_hard_regs[MAX_RECOG_OPERANDS]; |
| bool curr_alt_match_win[MAX_RECOG_OPERANDS]; |
| bool curr_alt_win[MAX_RECOG_OPERANDS]; |
| bool curr_alt_offmemok[MAX_RECOG_OPERANDS]; |
| int curr_alt_matches[MAX_RECOG_OPERANDS]; |
| /* The number of elements in the following array. */ |
| int curr_alt_dont_inherit_ops_num; |
| /* Numbers of operands whose reload pseudos should not be inherited. */ |
| int curr_alt_dont_inherit_ops[MAX_RECOG_OPERANDS]; |
| bool curr_reuse_alt_p; |
| /* True if output stack pointer reload should be generated for the current |
| alternative. */ |
| bool curr_alt_out_sp_reload_p; |
| bool curr_alt_class_change_p; |
| rtx op; |
| /* The register when the operand is a subreg of register, otherwise the |
| operand itself. */ |
| rtx no_subreg_reg_operand[MAX_RECOG_OPERANDS]; |
| /* The register if the operand is a register or subreg of register, |
| otherwise NULL. */ |
| rtx operand_reg[MAX_RECOG_OPERANDS]; |
| int hard_regno[MAX_RECOG_OPERANDS]; |
| machine_mode biggest_mode[MAX_RECOG_OPERANDS]; |
| int reload_nregs, reload_sum; |
| bool costly_p; |
| enum reg_class cl; |
| const HARD_REG_SET *cl_filter; |
| |
| /* Calculate some data common for all alternatives to speed up the |
| function. */ |
| for (nop = 0; nop < n_operands; nop++) |
| { |
| rtx reg; |
| |
| op = no_subreg_reg_operand[nop] = *curr_id->operand_loc[nop]; |
| /* The real hard regno of the operand after the allocation. */ |
| hard_regno[nop] = get_hard_regno (op); |
| |
| operand_reg[nop] = reg = op; |
| biggest_mode[nop] = GET_MODE (op); |
| if (GET_CODE (op) == SUBREG) |
| { |
| biggest_mode[nop] = wider_subreg_mode (op); |
| operand_reg[nop] = reg = SUBREG_REG (op); |
| } |
| if (! REG_P (reg)) |
| operand_reg[nop] = NULL_RTX; |
| else if (REGNO (reg) >= FIRST_PSEUDO_REGISTER |
| || ((int) REGNO (reg) |
| == lra_get_elimination_hard_regno (REGNO (reg)))) |
| no_subreg_reg_operand[nop] = reg; |
| else |
| operand_reg[nop] = no_subreg_reg_operand[nop] |
| /* Just use natural mode for elimination result. It should |
| be enough for extra constraints hooks. */ |
| = regno_reg_rtx[hard_regno[nop]]; |
| } |
| |
| /* The constraints are made of several alternatives. Each operand's |
| constraint looks like foo,bar,... with commas separating the |
| alternatives. The first alternatives for all operands go |
| together, the second alternatives go together, etc. |
| |
| First loop over alternatives. */ |
| alternative_mask preferred = curr_id->preferred_alternatives; |
| if (only_alternative >= 0) |
| preferred &= ALTERNATIVE_BIT (only_alternative); |
| |
| for (nalt = 0; nalt < n_alternatives; nalt++) |
| { |
| /* Loop over operands for one constraint alternative. */ |
| if (!TEST_BIT (preferred, nalt)) |
| continue; |
| |
| if (lra_dump_file != NULL) |
| { |
| fprintf (lra_dump_file, " Considering alt=%d of insn %d: ", |
| nalt, INSN_UID (curr_insn)); |
| print_curr_insn_alt (nalt); |
| fprintf (lra_dump_file, "\n"); |
| } |
| |
| bool matching_early_clobber[MAX_RECOG_OPERANDS]; |
| curr_small_class_check++; |
| overall = losers = addr_losers = 0; |
| static_reject = reject = reload_nregs = reload_sum = 0; |
| for (nop = 0; nop < n_operands; nop++) |
| { |
| int inc = (curr_static_id |
| ->operand_alternative[nalt * n_operands + nop].reject); |
| if (lra_dump_file != NULL && inc != 0) |
| fprintf (lra_dump_file, |
| " Staticly defined alt reject+=%d\n", inc); |
| static_reject += inc; |
| matching_early_clobber[nop] = 0; |
| } |
| reject += static_reject; |
| early_clobbered_regs_num = 0; |
| curr_alt_out_sp_reload_p = false; |
| curr_reuse_alt_p = true; |
| curr_alt_class_change_p = false; |
| |
| for (nop = 0; nop < n_operands; nop++) |
| { |
| const char *p; |
| char *end; |
| int len, c, m, i, opalt_num, this_alternative_matches; |
| bool win, did_match, offmemok, early_clobber_p; |
| /* false => this operand can be reloaded somehow for this |
| alternative. */ |
| bool badop; |
| /* true => this operand can be reloaded if the alternative |
| allows regs. */ |
| bool winreg; |
| /* True if a constant forced into memory would be OK for |
| this operand. */ |
| bool constmemok; |
| enum reg_class this_alternative, this_costly_alternative; |
| HARD_REG_SET this_alternative_set, this_costly_alternative_set; |
| HARD_REG_SET this_alternative_exclude_start_hard_regs; |
| bool this_alternative_match_win, this_alternative_win; |
| bool this_alternative_offmemok; |
| bool scratch_p; |
| machine_mode mode; |
| enum constraint_num cn; |
| bool class_change_p = false; |
| |
| opalt_num = nalt * n_operands + nop; |
| if (curr_static_id->operand_alternative[opalt_num].anything_ok) |
| { |
| /* Fast track for no constraints at all. */ |
| curr_alt[nop] = NO_REGS; |
| CLEAR_HARD_REG_SET (curr_alt_set[nop]); |
| curr_alt_win[nop] = true; |
| curr_alt_match_win[nop] = false; |
| curr_alt_offmemok[nop] = false; |
| curr_alt_matches[nop] = -1; |
| continue; |
| } |
| |
| op = no_subreg_reg_operand[nop]; |
| mode = curr_operand_mode[nop]; |
| |
| win = did_match = winreg = offmemok = constmemok = false; |
| badop = true; |
| |
| early_clobber_p = false; |
| p = curr_static_id->operand_alternative[opalt_num].constraint; |
| |
| this_costly_alternative = this_alternative = NO_REGS; |
| /* We update set of possible hard regs besides its class |
| because reg class might be inaccurate. For example, |
| union of LO_REGS (l), HI_REGS(h), and STACK_REG(k) in ARM |
| is translated in HI_REGS because classes are merged by |
| pairs and there is no accurate intermediate class. */ |
| CLEAR_HARD_REG_SET (this_alternative_set); |
| CLEAR_HARD_REG_SET (this_costly_alternative_set); |
| CLEAR_HARD_REG_SET (this_alternative_exclude_start_hard_regs); |
| this_alternative_win = false; |
| this_alternative_match_win = false; |
| this_alternative_offmemok = false; |
| this_alternative_matches = -1; |
| |
| /* An empty constraint should be excluded by the fast |
| track. */ |
| lra_assert (*p != 0 && *p != ','); |
| |
| op_reject = 0; |
| /* Scan this alternative's specs for this operand; set WIN |
| if the operand fits any letter in this alternative. |
| Otherwise, clear BADOP if this operand could fit some |
| letter after reloads, or set WINREG if this operand could |
| fit after reloads provided the constraint allows some |
| registers. */ |
| costly_p = false; |
| do |
| { |
| switch ((c = *p, len = CONSTRAINT_LEN (c, p)), c) |
| { |
| case '\0': |
| len = 0; |
| break; |
| case ',': |
| c = '\0'; |
| break; |
| |
| case '&': |
| early_clobber_p = true; |
| break; |
| |
| case '$': |
| op_reject += LRA_MAX_REJECT; |
| break; |
| case '^': |
| op_reject += LRA_LOSER_COST_FACTOR; |
| break; |
| |
| case '#': |
| /* Ignore rest of this alternative. */ |
| c = '\0'; |
| break; |
| |
| case '0': case '1': case '2': case '3': case '4': |
| case '5': case '6': case '7': case '8': case '9': |
| { |
| int m_hregno; |
| bool match_p; |
| |
| m = strtoul (p, &end, 10); |
| p = end; |
| len = 0; |
| lra_assert (nop > m); |
| |
| /* Reject matches if we don't know which operand is |
| bigger. This situation would arguably be a bug in |
| an .md pattern, but could also occur in a user asm. */ |
| if (!ordered_p (GET_MODE_SIZE (biggest_mode[m]), |
| GET_MODE_SIZE (biggest_mode[nop]))) |
| break; |
| |
| /* Don't match wrong asm insn operands for proper |
| diagnostic later. */ |
| if (INSN_CODE (curr_insn) < 0 |
| && (curr_operand_mode[m] == BLKmode |
| || curr_operand_mode[nop] == BLKmode) |
| && curr_operand_mode[m] != curr_operand_mode[nop]) |
| break; |
| |
| m_hregno = get_hard_regno (*curr_id->operand_loc[m]); |
| /* We are supposed to match a previous operand. |
| If we do, we win if that one did. If we do |
| not, count both of the operands as losers. |
| (This is too conservative, since most of the |
| time only a single reload insn will be needed |
| to make the two operands win. As a result, |
| this alternative may be rejected when it is |
| actually desirable.) */ |
| match_p = false; |
| if (operands_match_p (*curr_id->operand_loc[nop], |
| *curr_id->operand_loc[m], m_hregno)) |
| { |
| /* We should reject matching of an early |
| clobber operand if the matching operand is |
| not dying in the insn. */ |
| if (!TEST_BIT (curr_static_id->operand[m] |
| .early_clobber_alts, nalt) |
| || operand_reg[nop] == NULL_RTX |
| || (find_regno_note (curr_insn, REG_DEAD, |
| REGNO (op)) |
| || REGNO (op) == REGNO (operand_reg[m]))) |
| match_p = true; |
| } |
| if (match_p) |
| { |
| /* If we are matching a non-offsettable |
| address where an offsettable address was |
| expected, then we must reject this |
| combination, because we can't reload |
| it. */ |
| if (curr_alt_offmemok[m] |
| && MEM_P (*curr_id->operand_loc[m]) |
| && curr_alt[m] == NO_REGS && ! curr_alt_win[m]) |
| continue; |
| } |
| else |
| { |
| /* If the operands do not match and one |
| operand is INOUT, we can not match them. |
| Try other possibilities, e.g. other |
| alternatives or commutative operand |
| exchange. */ |
| if (curr_static_id->operand[nop].type == OP_INOUT |
| || curr_static_id->operand[m].type == OP_INOUT) |
| break; |
| /* Operands don't match. If the operands are |
| different user defined explicit hard |
| registers, then we cannot make them match |
| when one is early clobber operand. */ |
| if ((REG_P (*curr_id->operand_loc[nop]) |
| || SUBREG_P (*curr_id->operand_loc[nop])) |
| && (REG_P (*curr_id->operand_loc[m]) |
| || SUBREG_P (*curr_id->operand_loc[m]))) |
| { |
| rtx nop_reg = *curr_id->operand_loc[nop]; |
| if (SUBREG_P (nop_reg)) |
| nop_reg = SUBREG_REG (nop_reg); |
| rtx m_reg = *curr_id->operand_loc[m]; |
| if (SUBREG_P (m_reg)) |
| m_reg = SUBREG_REG (m_reg); |
| |
| if (REG_P (nop_reg) |
| && HARD_REGISTER_P (nop_reg) |
| && REG_USERVAR_P (nop_reg) |
| && REG_P (m_reg) |
| && HARD_REGISTER_P (m_reg) |
| && REG_USERVAR_P (m_reg)) |
| { |
| int i; |
| |
| for (i = 0; i < early_clobbered_regs_num; i++) |
| if (m == early_clobbered_nops[i]) |
| break; |
| if (i < early_clobbered_regs_num |
| || early_clobber_p) |
| break; |
| } |
| } |
| /* Both operands must allow a reload register, |
| otherwise we cannot make them match. */ |
| if (curr_alt[m] == NO_REGS) |
| break; |
| /* Retroactively mark the operand we had to |
| match as a loser, if it wasn't already and |
| it wasn't matched to a register constraint |
| (e.g it might be matched by memory). */ |
| if (curr_alt_win[m] |
| && (operand_reg[m] == NULL_RTX |
| || hard_regno[m] < 0)) |
| { |
| losers++; |
| reload_nregs |
| += (ira_reg_class_max_nregs[curr_alt[m]] |
| [GET_MODE (*curr_id->operand_loc[m])]); |
| } |
| |
| /* Prefer matching earlyclobber alternative as |
| it results in less hard regs required for |
| the insn than a non-matching earlyclobber |
| alternative. */ |
| if (TEST_BIT (curr_static_id->operand[m] |
| .early_clobber_alts, nalt)) |
| { |
| if (lra_dump_file != NULL) |
| fprintf |
| (lra_dump_file, |
| " %d Matching earlyclobber alt:" |
| " reject--\n", |
| nop); |
| if (!matching_early_clobber[m]) |
| { |
| reject--; |
| matching_early_clobber[m] = 1; |
| } |
| } |
| /* Otherwise we prefer no matching |
| alternatives because it gives more freedom |
| in RA. */ |
| else if (operand_reg[nop] == NULL_RTX |
| || (find_regno_note (curr_insn, REG_DEAD, |
| REGNO (operand_reg[nop])) |
| == NULL_RTX)) |
| { |
| if (lra_dump_file != NULL) |
| fprintf |
| (lra_dump_file, |
| " %d Matching alt: reject+=2\n", |
| nop); |
| reject += 2; |
| } |
| } |
| /* If we have to reload this operand and some |
| previous operand also had to match the same |
| thing as this operand, we don't know how to do |
| that. */ |
| if (!match_p || !curr_alt_win[m]) |
| { |
| for (i = 0; i < nop; i++) |
| if (curr_alt_matches[i] == m) |
| break; |
| if (i < nop) |
| break; |
| } |
| else |
| did_match = true; |
| |
| this_alternative_matches = m; |
| /* This can be fixed with reloads if the operand |
| we are supposed to match can be fixed with |
| reloads. */ |
| badop = false; |
| this_alternative = curr_alt[m]; |
| this_alternative_set = curr_alt_set[m]; |
| this_alternative_exclude_start_hard_regs |
| = curr_alt_exclude_start_hard_regs[m]; |
| winreg = this_alternative != NO_REGS; |
| break; |
| } |
| |
| case 'g': |
| if (MEM_P (op) |
| || general_constant_p (op) |
| || spilled_pseudo_p (op)) |
| win = true; |
| cl = GENERAL_REGS; |
| cl_filter = nullptr; |
| goto reg; |
| |
| default: |
| cn = lookup_constraint (p); |
| switch (get_constraint_type (cn)) |
| { |
| case CT_REGISTER: |
| cl = reg_class_for_constraint (cn); |
| if (cl != NO_REGS) |
| { |
| cl_filter = get_register_filter (cn); |
| goto reg; |
| } |
| break; |
| |
| case CT_CONST_INT: |
| if (CONST_INT_P (op) |
| && insn_const_int_ok_for_constraint (INTVAL (op), cn)) |
| win = true; |
| break; |
| |
| case CT_MEMORY: |
| case CT_RELAXED_MEMORY: |
| if (MEM_P (op) |
| && satisfies_memory_constraint_p (op, cn)) |
| win = true; |
| else if (spilled_pseudo_p (op)) |
| win = true; |
| |
| /* If we didn't already win, we can reload constants |
| via force_const_mem or put the pseudo value into |
| memory, or make other memory by reloading the |
| address like for 'o'. */ |
| if (CONST_POOL_OK_P (mode, op) |
| || MEM_P (op) || REG_P (op) |
| /* We can restore the equiv insn by a |
| reload. */ |
| || equiv_substition_p[nop]) |
| badop = false; |
| constmemok = true; |
| offmemok = true; |
| break; |
| |
| case CT_ADDRESS: |
| /* An asm operand with an address constraint |
| that doesn't satisfy address_operand has |
| is_address cleared, so that we don't try to |
| make a non-address fit. */ |
| if (!curr_static_id->operand[nop].is_address) |
| break; |
| /* If we didn't already win, we can reload the address |
| into a base register. */ |
| if (satisfies_address_constraint_p (op, cn)) |
| win = true; |
| cl = base_reg_class (VOIDmode, ADDR_SPACE_GENERIC, |
| ADDRESS, SCRATCH); |
| cl_filter = nullptr; |
| badop = false; |
| goto reg; |
| |
| case CT_FIXED_FORM: |
| if (constraint_satisfied_p (op, cn)) |
| win = true; |
| break; |
| |
| case CT_SPECIAL_MEMORY: |
| if (satisfies_memory_constraint_p (op, cn)) |
| win = true; |
| else if (spilled_pseudo_p (op)) |
| { |
| curr_reuse_alt_p = false; |
| win = true; |
| } |
| break; |
| } |
| break; |
| |
| reg: |
| if (mode == BLKmode) |
| break; |
| this_alternative = reg_class_subunion[this_alternative][cl]; |
| if (hard_reg_set_subset_p (this_alternative_set, |
| reg_class_contents[cl])) |
| this_alternative_exclude_start_hard_regs |
| = ira_exclude_class_mode_regs[cl][mode]; |
| else if (!hard_reg_set_subset_p (reg_class_contents[cl], |
| this_alternative_set)) |
| this_alternative_exclude_start_hard_regs |
| |= ira_exclude_class_mode_regs[cl][mode]; |
| this_alternative_set |= reg_class_contents[cl]; |
| if (cl_filter) |
| this_alternative_exclude_start_hard_regs |= ~*cl_filter; |
| if (costly_p) |
| { |
| this_costly_alternative |
| = reg_class_subunion[this_costly_alternative][cl]; |
| this_costly_alternative_set |= reg_class_contents[cl]; |
| } |
| winreg = true; |
| if (REG_P (op)) |
| { |
| tree decl; |
| if (hard_regno[nop] >= 0 |
| && in_hard_reg_set_p (this_alternative_set, |
| mode, hard_regno[nop]) |
| && (!cl_filter |
| || TEST_HARD_REG_BIT (*cl_filter, |
| hard_regno[nop])) |
| && ((REG_ATTRS (op) && (decl = REG_EXPR (op)) != NULL |
| && VAR_P (decl) && DECL_HARD_REGISTER (decl)) |
| || !(TEST_HARD_REG_BIT |
| (this_alternative_exclude_start_hard_regs, |
| hard_regno[nop])))) |
| win = true; |
| else if (hard_regno[nop] < 0) |
| { |
| if (in_class_p (op, this_alternative, NULL)) |
| win = true; |
| else if (in_class_p (op, this_alternative, NULL, true)) |
| { |
| class_change_p = true; |
| win = true; |
| } |
| } |
| } |
| break; |
| } |
| if (c != ' ' && c != '\t') |
| costly_p = c == '*'; |
| } |
| while ((p += len), c); |
| |
| scratch_p = (operand_reg[nop] != NULL_RTX |
| && ira_former_scratch_p (REGNO (operand_reg[nop]))); |
| /* Record which operands fit this alternative. */ |
| if (win) |
| { |
| this_alternative_win = true; |
| if (class_change_p) |
| { |
| curr_alt_class_change_p = true; |
| if (lra_dump_file != NULL) |
| fprintf (lra_dump_file, |
| " %d Narrowing class: reject+=3\n", |
| nop); |
| reject += 3; |
| } |
| if (operand_reg[nop] != NULL_RTX) |
| { |
| if (hard_regno[nop] >= 0) |
| { |
| if (in_hard_reg_set_p (this_costly_alternative_set, |
| mode, hard_regno[nop])) |
| { |
| if (lra_dump_file != NULL) |
| fprintf (lra_dump_file, |
| " %d Costly set: reject++\n", |
| nop); |
| reject++; |
| } |
| } |
| else |
| { |
| /* Prefer won reg to spilled pseudo under other |
| equal conditions for possibe inheritance. */ |
| if (! scratch_p) |
| { |
| if (lra_dump_file != NULL) |
| fprintf |
| (lra_dump_file, |
| " %d Non pseudo reload: reject++\n", |
| nop); |
| reject++; |
| } |
| if (in_class_p (operand_reg[nop], |
| this_costly_alternative, NULL, true)) |
| { |
| if (lra_dump_file != NULL) |
| fprintf |
| (lra_dump_file, |
| " %d Non pseudo costly reload:" |
| " reject++\n", |
| nop); |
| reject++; |
| } |
| } |
| /* We simulate the behavior of old reload here. |
| Although scratches need hard registers and it |
| might result in spilling other pseudos, no reload |
| insns are generated for the scratches. So it |
| might cost something but probably less than old |
| reload pass believes. */ |
| if (scratch_p) |
| { |
| if (lra_dump_file != NULL) |
| fprintf (lra_dump_file, |
| " %d Scratch win: reject+=2\n", |
| nop); |
| reject += 2; |
| } |
| } |
| } |
| else if (did_match) |
| this_alternative_match_win = true; |
| else |
| { |
| int const_to_mem = 0; |
| bool no_regs_p; |
| |
| reject += op_reject; |
| /* Mark output reload of the stack pointer. */ |
| if (op == stack_pointer_rtx |
| && curr_static_id->operand[nop].type != OP_IN) |
| curr_alt_out_sp_reload_p = true; |
| |
| /* If this alternative asks for a specific reg class, see if there |
| is at least one allocatable register in that class. */ |
| no_regs_p |
| = (this_alternative == NO_REGS |
| || (hard_reg_set_subset_p |
| (reg_class_contents[this_alternative], |
| lra_no_alloc_regs))); |
| |
| /* For asms, verify that the class for this alternative is possible |
| for the mode that is specified. */ |
| if (!no_regs_p && INSN_CODE (curr_insn) < 0) |
| { |
| int i; |
| for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) |
| if (targetm.hard_regno_mode_ok (i, mode) |
| && in_hard_reg_set_p (reg_class_contents[this_alternative], |
| mode, i)) |
| break; |
| if (i == FIRST_PSEUDO_REGISTER) |
| winreg = false; |
| } |
| |
| /* If this operand accepts a register, and if the |
| register class has at least one allocatable register, |
| then this operand can be reloaded. */ |
| if (winreg && !no_regs_p) |
| badop = false; |
| |
| if (badop) |
| { |
| if (lra_dump_file != NULL) |
| fprintf (lra_dump_file, |
| " Bad operand -- refuse\n"); |
| goto fail; |
| } |
| |
| if (this_alternative != NO_REGS) |
| { |
| HARD_REG_SET available_regs |
| = (reg_class_contents[this_alternative] |
| & ~((ira_prohibited_class_mode_regs |
| [this_alternative][mode]) |
| | lra_no_alloc_regs)); |
| if (hard_reg_set_empty_p (available_regs)) |
| { |
| /* There are no hard regs holding a value of given |
| mode. */ |
| if (offmemok) |
| { |
| this_alternative = NO_REGS; |
| if (lra_dump_file != NULL) |
| fprintf (lra_dump_file, |
| " %d Using memory because of" |
| " a bad mode: reject+=2\n", |
| nop); |
| reject += 2; |
| } |
| else |
| { |
| if (lra_dump_file != NULL) |
| fprintf (lra_dump_file, |
| " Wrong mode -- refuse\n"); |
| goto fail; |
| } |
| } |
| } |
| |
| /* If not assigned pseudo has a class which a subset of |
| required reg class, it is a less costly alternative |
| as the pseudo still can get a hard reg of necessary |
| class. */ |
| if (! no_regs_p && REG_P (op) && hard_regno[nop] < 0 |
| && (cl = get_reg_class (REGNO (op))) != NO_REGS |
| && ira_class_subset_p[this_alternative][cl]) |
| { |
| if (lra_dump_file != NULL) |
| fprintf |
| (lra_dump_file, |
| " %d Super set class reg: reject-=3\n", nop); |
| reject -= 3; |
| } |
| |
| this_alternative_offmemok = offmemok; |
| if (this_costly_alternative != NO_REGS) |
| { |
| if (lra_dump_file != NULL) |
| fprintf (lra_dump_file, |
| " %d Costly loser: reject++\n", nop); |
| reject++; |
| } |
| /* If the operand is dying, has a matching constraint, |
| and satisfies constraints of the matched operand |
| which failed to satisfy the own constraints, most probably |
| the reload for this operand will be gone. */ |
| if (this_alternative_matches >= 0 |
| && !curr_alt_win[this_alternative_matches] |
| && REG_P (op) |
| && find_regno_note (curr_insn, REG_DEAD, REGNO (op)) |
| && (hard_regno[nop] >= 0 |
| ? in_hard_reg_set_p (this_alternative_set, |
| mode, hard_regno[nop]) |
| : in_class_p (op, this_alternative, NULL))) |
| { |
| if (lra_dump_file != NULL) |
| fprintf |
| (lra_dump_file, |
| " %d Dying matched operand reload: reject++\n", |
| nop); |
| reject++; |
| } |
| else |
| { |
| /* Strict_low_part requires to reload the register |
| not the sub-register. In this case we should |
| check that a final reload hard reg can hold the |
| value mode. */ |
| if (curr_static_id->operand[nop].strict_low |
| && REG_P (op) |
| && hard_regno[nop] < 0 |
| && GET_CODE (*curr_id->operand_loc[nop]) == SUBREG |
| && ira_class_hard_regs_num[this_alternative] > 0 |
| && (!targetm.hard_regno_mode_ok |
| (ira_class_hard_regs[this_alternative][0], |
| GET_MODE (*curr_id->operand_loc[nop])))) |
| { |
| if (lra_dump_file != NULL) |
| fprintf |
| (lra_dump_file, |
| " Strict low subreg reload -- refuse\n"); |
| goto fail; |
| } |
| losers++; |
| } |
| if (operand_reg[nop] != NULL_RTX |
| /* Output operands and matched input operands are |
| not inherited. The following conditions do not |
| exactly describe the previous statement but they |
| are pretty close. */ |
| && curr_static_id->operand[nop].type != OP_OUT |
| && (this_alternative_matches < 0 |
| || curr_static_id->operand[nop].type != OP_IN)) |
| { |
| int last_reload = (lra_reg_info[ORIGINAL_REGNO |
| (operand_reg[nop])] |
| .last_reload); |
| |
| /* The value of reload_sum has sense only if we |
| process insns in their order. It happens only on |
| the first constraints sub-pass when we do most of |
| reload work. */ |
| if (lra_constraint_iter == 1 && last_reload > bb_reload_num) |
| reload_sum += last_reload - bb_reload_num; |
| } |
| /* If this is a constant that is reloaded into the |
| desired class by copying it to memory first, count |
| that as another reload. This is consistent with |
| other code and is required to avoid choosing another |
| alternative when the constant is moved into memory. |
| Note that the test here is precisely the same as in |
| the code below that calls force_const_mem. */ |
| if (CONST_POOL_OK_P (mode, op) |
| && ((targetm.preferred_reload_class |
| (op, this_alternative) == NO_REGS) |
| || no_input_reloads_p)) |
| { |
| const_to_mem = 1; |
| if (! no_regs_p) |
| losers++; |
| } |
| |
| /* Alternative loses if it requires a type of reload not |
| permitted for this insn. We can always reload |
| objects with a REG_UNUSED note. */ |
| if ((curr_static_id->operand[nop].type != OP_IN |
| && no_output_reloads_p |
| && ! find_reg_note (curr_insn, REG_UNUSED, op)) |
| || (curr_static_id->operand[nop].type != OP_OUT |
| && no_input_reloads_p && ! const_to_mem) |
| || (this_alternative_matches >= 0 |
| && (no_input_reloads_p |
| || (no_output_reloads_p |
| && (curr_static_id->operand |
| [this_alternative_matches].type != OP_IN) |
| && ! find_reg_note (curr_insn, REG_UNUSED, |
| no_subreg_reg_operand |
| [this_alternative_matches]))))) |
| { |
| if (lra_dump_file != NULL) |
| fprintf |
| (lra_dump_file, |
| " No input/output reload -- refuse\n"); |
| goto fail; |
| } |
| |
| /* Alternative loses if it required class pseudo cannot |
| hold value of required mode. Such insns can be |
| described by insn definitions with mode iterators. */ |
| if (GET_MODE (*curr_id->operand_loc[nop]) != VOIDmode |
| && ! hard_reg_set_empty_p (this_alternative_set) |
| /* It is common practice for constraints to use a |
| class which does not have actually enough regs to |
| hold the value (e.g. x86 AREG for mode requiring |
| more one general reg). Therefore we have 2 |
| conditions to check that the reload pseudo cannot |
| hold the mode value. */ |
| && (!targetm.hard_regno_mode_ok |
| (ira_class_hard_regs[this_alternative][0], |
| GET_MODE (*curr_id->operand_loc[nop]))) |
| /* The above condition is not enough as the first |
| reg in ira_class_hard_regs can be not aligned for |
| multi-words mode values. */ |
| && (prohibited_class_reg_set_mode_p |
| (this_alternative, this_alternative_set, |
| GET_MODE (*curr_id->operand_loc[nop])))) |
| { |
| if (lra_dump_file != NULL) |
| fprintf (lra_dump_file, |
| " reload pseudo for op %d " |
| "cannot hold the mode value -- refuse\n", |
| nop); |
| goto fail; |
| } |
| |
| /* Check strong discouragement of reload of non-constant |
| into class THIS_ALTERNATIVE. */ |
| if (! CONSTANT_P (op) && ! no_regs_p |
| && (targetm.preferred_reload_class |
| (op, this_alternative) == NO_REGS |
| || (curr_static_id->operand[nop].type == OP_OUT |
| && (targetm.preferred_output_reload_class |
| (op, this_alternative) == NO_REGS)))) |
| { |
| if (offmemok && REG_P (op)) |
| { |
| if (lra_dump_file != NULL) |
| fprintf |
| (lra_dump_file, |
| " %d Spill pseudo into memory: reject+=3\n", |
| nop); |
| reject += 3; |
| } |
| else |
| { |
| if (lra_dump_file != NULL) |
| fprintf |
| (lra_dump_file, |
| " %d Non-prefered reload: reject+=%d\n", |
| nop, LRA_MAX_REJECT); |
| reject += LRA_MAX_REJECT; |
| } |
| } |
| |
| if (! (MEM_P (op) && offmemok) |
| && ! (const_to_mem && constmemok)) |
| { |
| /* We prefer to reload pseudos over reloading other |
| things, since such reloads may be able to be |
| eliminated later. So bump REJECT in other cases. |
| Don't do this in the case where we are forcing a |
| constant into memory and it will then win since |
| we don't want to have a different alternative |
| match then. */ |
| if (! (REG_P (op) && REGNO (op) >= FIRST_PSEUDO_REGISTER)) |
| { |
| if (lra_dump_file != NULL) |
| fprintf |
| (lra_dump_file, |
| " %d Non-pseudo reload: reject+=2\n", |
| nop); |
| reject += 2; |
| } |
| |
| if (! no_regs_p) |
| reload_nregs |
| += ira_reg_class_max_nregs[this_alternative][mode]; |
| |
| if (SMALL_REGISTER_CLASS_P (this_alternative)) |
| { |
| if (lra_dump_file != NULL) |
| fprintf |
| (lra_dump_file, |
| " %d Small class reload: reject+=%d\n", |
| nop, LRA_LOSER_COST_FACTOR / 2); |
| reject += LRA_LOSER_COST_FACTOR / 2; |
| } |
| } |
| |
| /* We are trying to spill pseudo into memory. It is |
| usually more costly than moving to a hard register |
| although it might takes the same number of |
| reloads. |
| |
| Non-pseudo spill may happen also. Suppose a target allows both |
| register and memory in the operand constraint alternatives, |
| then it's typical that an eliminable register has a substition |
| of "base + offset" which can either be reloaded by a simple |
| "new_reg <= base + offset" which will match the register |
| constraint, or a similar reg addition followed by further spill |
| to and reload from memory which will match the memory |
| constraint, but this memory spill will be much more costly |
| usually. |
| |
| Code below increases the reject for both pseudo and non-pseudo |
| spill. */ |
| if (no_regs_p |
| && !(MEM_P (op) && offmemok) |
| && !(REG_P (op) && hard_regno[nop] < 0)) |
| { |
| if (lra_dump_file != NULL) |
| fprintf |
| (lra_dump_file, |
| " %d Spill %spseudo into memory: reject+=3\n", |
| nop, REG_P (op) ? "" : "Non-"); |
| reject += 3; |
| if (VECTOR_MODE_P (mode)) |
| { |
| /* Spilling vectors into memory is usually more |
| costly as they contain big values. */ |
| if (lra_dump_file != NULL) |
| fprintf |
| (lra_dump_file, |
| " %d Spill vector pseudo: reject+=2\n", |
| nop); |
| reject += 2; |
| } |
| } |
| |
| /* When we use an operand requiring memory in given |
| alternative, the insn should write *and* read the |
| value to/from memory it is costly in comparison with |
| an insn alternative which does not use memory |
| (e.g. register or immediate operand). We exclude |
| memory operand for such case as we can satisfy the |
| memory constraints by reloading address. */ |
| if (no_regs_p && offmemok && !MEM_P (op)) |
| { |
| if (lra_dump_file != NULL) |
| fprintf |
| (lra_dump_file, |
| " Using memory insn operand %d: reject+=3\n", |
| nop); |
| reject += 3; |
| } |
| |
| /* If reload requires moving value through secondary |
| memory, it will need one more insn at least. */ |
| if (this_alternative != NO_REGS |
| && REG_P (op) && (cl = get_reg_class (REGNO (op))) != NO_REGS |
| && ((curr_static_id->operand[nop].type != OP_OUT |
| && targetm.secondary_memory_needed (GET_MODE (op), cl, |
| this_alternative)) |
| || (curr_static_id->operand[nop].type != OP_IN |
| && (targetm.secondary_memory_needed |
| (GET_MODE (op), this_alternative, cl))))) |
| losers++; |
| |
| if (MEM_P (op) && offmemok) |
| addr_losers++; |
| else |
| { |
| /* Input reloads can be inherited more often than |
| output reloads can be removed, so penalize output |
| reloads. */ |
| if (!REG_P (op) || curr_static_id->operand[nop].type != OP_IN) |
| { |
| if (lra_dump_file != NULL) |
| fprintf |
| (lra_dump_file, |
| " %d Non input pseudo reload: reject++\n", |
| nop); |
| reject++; |
| } |
| |
| if (curr_static_id->operand[nop].type == OP_INOUT) |
| { |
| if (lra_dump_file != NULL) |
| fprintf |
| (lra_dump_file, |
| " %d Input/Output reload: reject+=%d\n", |
| nop, LRA_LOSER_COST_FACTOR); |
| reject += LRA_LOSER_COST_FACTOR; |
| } |
| } |
| } |
| |
| if (early_clobber_p && ! scratch_p) |
| { |
| if (lra_dump_file != NULL) |
| fprintf (lra_dump_file, |
| " %d Early clobber: reject++\n", nop); |
| reject++; |
| } |
| /* ??? We check early clobbers after processing all operands |
| (see loop below) and there we update the costs more. |
| Should we update the cost (may be approximately) here |
| because of early clobber register reloads or it is a rare |
| or non-important thing to be worth to do it. */ |
| overall = (losers * LRA_LOSER_COST_FACTOR + reject |
| - (addr_losers == losers ? static_reject : 0)); |
| if ((best_losers == 0 || losers != 0) && best_overall < overall) |
| { |
| if (lra_dump_file != NULL) |
| fprintf (lra_dump_file, |
| " overall=%d,losers=%d -- refuse\n", |
| overall, losers); |
| goto fail; |
| } |
| |
| if (update_and_check_small_class_inputs (nop, nalt, |
| this_alternative)) |
| { |
| if (lra_dump_file != NULL) |
| fprintf (lra_dump_file, |
| " not enough small class regs -- refuse\n"); |
| goto fail; |
| } |
| curr_alt[nop] = this_alternative; |
| curr_alt_set[nop] = this_alternative_set; |
| curr_alt_exclude_start_hard_regs[nop] |
| = this_alternative_exclude_start_hard_regs; |
| curr_alt_win[nop] = this_alternative_win; |
| curr_alt_match_win[nop] = this_alternative_match_win; |
| curr_alt_offmemok[nop] = this_alternative_offmemok; |
| curr_alt_matches[nop] = this_alternative_matches; |
| |
| if (this_alternative_matches >= 0 |
| && !did_match && !this_alternative_win) |
| curr_alt_win[this_alternative_matches] = false; |
| |
| if (early_clobber_p && operand_reg[nop] != NULL_RTX) |
| early_clobbered_nops[early_clobbered_regs_num++] = nop; |
| } |
| |
| if (curr_insn_set != NULL_RTX && n_operands == 2 |
| /* Prevent processing non-move insns. */ |
| && (GET_CODE (SET_SRC (curr_insn_set)) == SUBREG |
| || SET_SRC (curr_insn_set) == no_subreg_reg_operand[1]) |
| && ((! curr_alt_win[0] && ! curr_alt_win[1] |
| && REG_P (no_subreg_reg_operand[0]) |
| && REG_P (no_subreg_reg_operand[1]) |
| && (reg_in_class_p (no_subreg_reg_operand[0], curr_alt[1]) |
| || reg_in_class_p (no_subreg_reg_operand[1], curr_alt[0]))) |
| || (! curr_alt_win[0] && curr_alt_win[1] |
| && REG_P (no_subreg_reg_operand[1]) |
| /* Check that we reload memory not the memory |
| address. */ |
| && ! (curr_alt_offmemok[0] |
| && MEM_P (no_subreg_reg_operand[0])) |
| && reg_in_class_p (no_subreg_reg_operand[1], curr_alt[0])) |
| || (curr_alt_win[0] && ! curr_alt_win[1] |
| && REG_P (no_subreg_reg_operand[0]) |
| /* Check that we reload memory not the memory |
| address. */ |
| && ! (curr_alt_offmemok[1] |
| && MEM_P (no_subreg_reg_operand[1])) |
| && reg_in_class_p (no_subreg_reg_operand[0], curr_alt[1]) |
| && (! CONST_POOL_OK_P (curr_operand_mode[1], |
| no_subreg_reg_operand[1]) |
| || (targetm.preferred_reload_class |
| (no_subreg_reg_operand[1], |
| (enum reg_class) curr_alt[1]) != NO_REGS)) |
| /* If it is a result of recent elimination in move |
| insn we can transform it into an add still by |
| using this alternative. */ |
| && GET_CODE (no_subreg_reg_operand[1]) != PLUS |
| /* Likewise if the source has been replaced with an |
| equivalent value. This only happens once -- the reload |
| will use the equivalent value instead of the register it |
| replaces -- so there should be no danger of cycling. */ |
| && !equiv_substition_p[1]))) |
| { |
| /* We have a move insn and a new reload insn will be similar |
| to the current insn. We should avoid such situation as |
| it results in LRA cycling. */ |
| if (lra_dump_file != NULL) |
| fprintf (lra_dump_file, |
| " Cycle danger: overall += LRA_MAX_REJECT\n"); |
| overall += LRA_MAX_REJECT; |
| } |
| ok_p = true; |
| curr_alt_dont_inherit_ops_num = 0; |
| for (nop = 0; nop < early_clobbered_regs_num; nop++) |
| { |
| int i, j, clobbered_hard_regno, first_conflict_j, last_conflict_j; |
| HARD_REG_SET temp_set; |
| |
| i = early_clobbered_nops[nop]; |
| if ((! curr_alt_win[i] && ! curr_alt_match_win[i]) |
| || hard_regno[i] < 0) |
| continue; |
| lra_assert (operand_reg[i] != NULL_RTX); |
| clobbered_hard_regno = hard_regno[i]; |
| CLEAR_HARD_REG_SET (temp_set); |
| add_to_hard_reg_set (&temp_set, GET_MODE (*curr_id->operand_loc[i]), |
| clobbered_hard_regno); |
| first_conflict_j = last_conflict_j = -1; |
| for (j = 0; j < n_operands; j++) |
| if (j == i |
| /* We don't want process insides of match_operator and |
| match_parallel because otherwise we would process |
| their operands once again generating a wrong |
| code. */ |
| || curr_static_id->operand[j].is_operator) |
| continue; |
| else if ((curr_alt_matches[j] == i && curr_alt_match_win[j]) |
| || (curr_alt_matches[i] == j && curr_alt_match_win[i])) |
| continue; |
| /* If we don't reload j-th operand, check conflicts. */ |
| else if ((curr_alt_win[j] || curr_alt_match_win[j]) |
| && uses_hard_regs_p (*curr_id->operand_loc[j], temp_set)) |
| { |
| if (first_conflict_j < 0) |
| first_conflict_j = j; |
| last_conflict_j = j; |
| /* Both the earlyclobber operand and conflicting operand |
| cannot both be user defined hard registers. */ |
| if (HARD_REGISTER_P (operand_reg[i]) |
| && REG_USERVAR_P (operand_reg[i]) |
| && operand_reg[j] != NULL_RTX |
| && HARD_REGISTER_P (operand_reg[j]) |
| && REG_USERVAR_P (operand_reg[j])) |
| { |
| /* For asm, let curr_insn_transform diagnose it. */ |
| if (INSN_CODE (curr_insn) < 0) |
| return false; |
| fatal_insn ("unable to generate reloads for " |
| "impossible constraints:", curr_insn); |
| } |
| } |
| if (last_conflict_j < 0) |
| continue; |
| |
| /* If an earlyclobber operand conflicts with another non-matching |
| operand (ie, they have been assigned the same hard register), |
| then it is better to reload the other operand, as there may |
| exist yet another operand with a matching constraint associated |
| with the earlyclobber operand. However, if one of the operands |
| is an explicit use of a hard register, then we must reload the |
| other non-hard register operand. */ |
| if (HARD_REGISTER_P (operand_reg[i]) |
| || (first_conflict_j == last_conflict_j |
| && operand_reg[last_conflict_j] != NULL_RTX |
| && !curr_alt_match_win[last_conflict_j] |
| && !HARD_REGISTER_P (operand_reg[last_conflict_j]))) |
| { |
| curr_alt_win[last_conflict_j] = false; |
| curr_alt_dont_inherit_ops[curr_alt_dont_inherit_ops_num++] |
| = last_conflict_j; |
| losers++; |
| if (lra_dump_file != NULL) |
| fprintf |
| (lra_dump_file, |
| " %d Conflict early clobber reload: reject--\n", |
| i); |
| } |
| else |
| { |
| /* We need to reload early clobbered register and the |
| matched registers. */ |
| for (j = 0; j < n_operands; j++) |
| if (curr_alt_matches[j] == i) |
| { |
| curr_alt_match_win[j] = false; |
| losers++; |
| overall += LRA_LOSER_COST_FACTOR; |
| } |
| if (! curr_alt_match_win[i]) |
| curr_alt_dont_inherit_ops[curr_alt_dont_inherit_ops_num++] = i; |
| else |
| { |
| /* Remember pseudos used for match reloads are never |
| inherited. */ |
| lra_assert (curr_alt_matches[i] >= 0); |
| curr_alt_win[curr_alt_matches[i]] = false; |
| } |
| curr_alt_win[i] = curr_alt_match_win[i] = false; |
| losers++; |
| if (lra_dump_file != NULL) |
| fprintf |
| (lra_dump_file, |
| " %d Matched conflict early clobber reloads: " |
| "reject--\n", |
| i); |
| } |
| /* Early clobber was already reflected in REJECT. */ |
| if (!matching_early_clobber[i]) |
| { |
| lra_assert (reject > 0); |
| reject--; |
| matching_early_clobber[i] = 1; |
| } |
| overall += LRA_LOSER_COST_FACTOR - 1; |
| } |
| if (lra_dump_file != NULL) |
| fprintf (lra_dump_file, " overall=%d,losers=%d,rld_nregs=%d\n", |
| overall, losers, reload_nregs); |
| |
| /* If this alternative can be made to work by reloading, and it |
| needs less reloading than the others checked so far, record |
| it as the chosen goal for reloading. */ |
| if ((best_losers != 0 && losers == 0) |
| || (((best_losers == 0 && losers == 0) |
| || (best_losers != 0 && losers != 0)) |
| && (best_overall > overall |
| || (best_overall == overall |
| /* If the cost of the reloads is the same, |
| prefer alternative which requires minimal |
| number of reload regs. */ |
| && (reload_nregs < best_reload_nregs |
| || (reload_nregs == best_reload_nregs |
| && (best_reload_sum < reload_sum |
| || (best_reload_sum == reload_sum |
| && nalt < goal_alt_number)))))))) |
| { |
| for (nop = 0; nop < n_operands; nop++) |
| { |
| goal_alt_win[nop] = curr_alt_win[nop]; |
| goal_alt_match_win[nop] = curr_alt_match_win[nop]; |
| goal_alt_matches[nop] = curr_alt_matches[nop]; |
| goal_alt[nop] = curr_alt[nop]; |
| goal_alt_exclude_start_hard_regs[nop] |
| = curr_alt_exclude_start_hard_regs[nop]; |
| goal_alt_offmemok[nop] = curr_alt_offmemok[nop]; |
| } |
| goal_alt_dont_inherit_ops_num = curr_alt_dont_inherit_ops_num; |
| goal_reuse_alt_p = curr_reuse_alt_p; |
| for (nop = 0; nop < curr_alt_dont_inherit_ops_num; nop++) |
| goal_alt_dont_inherit_ops[nop] = curr_alt_dont_inherit_ops[nop]; |
| goal_alt_swapped = curr_swapped; |
| goal_alt_out_sp_reload_p = curr_alt_out_sp_reload_p; |
| best_overall = overall; |
| best_losers = losers; |
| best_reload_nregs = reload_nregs; |
| best_reload_sum = reload_sum; |
| goal_alt_number = nalt; |
| } |
| if (losers == 0 && !curr_alt_class_change_p) |
| /* Everything is satisfied. Do not process alternatives |
| anymore. */ |
| break; |
| fail: |
| ; |
| } |
| return ok_p; |
| } |
| |
| /* Make reload base reg from address AD. */ |
| static rtx |
| base_to_reg (struct address_info *ad) |
| { |
| enum reg_class cl; |
| int code = -1; |
| rtx new_inner = NULL_RTX; |
| rtx new_reg = NULL_RTX; |
| rtx_insn *insn; |
| rtx_insn *last_insn = get_last_insn(); |
| |
| lra_assert (ad->disp == ad->disp_term); |
| cl = base_reg_class (ad->mode, ad->as, ad->base_outer_code, |
| get_index_code (ad)); |
| new_reg = lra_create_new_reg (GET_MODE (*ad->base), NULL_RTX, cl, NULL, |
| "base"); |
| new_inner = simplify_gen_binary (PLUS, GET_MODE (new_reg), new_reg, |
| ad->disp_term == NULL |
| ? const0_rtx |
| : *ad->disp_term); |
| if (!valid_address_p (ad->mode, new_inner, ad->as)) |
| return NULL_RTX; |
| insn = emit_insn (gen_rtx_SET (new_reg, *ad->base)); |
| code = recog_memoized (insn); |
| if (code < 0) |
| { |
| delete_insns_since (last_insn); |
| return NULL_RTX; |
| } |
| |
| return new_inner; |
| } |
| |
| /* Make reload base reg + DISP from address AD. Return the new pseudo. */ |
| static rtx |
| base_plus_disp_to_reg (struct address_info *ad, rtx disp) |
| { |
| enum reg_class cl; |
| rtx new_reg; |
| |
| lra_assert (ad->base == ad->base_term); |
| cl = base_reg_class (ad->mode, ad->as, ad->base_outer_code, |
| get_index_code (ad)); |
| new_reg = lra_create_new_reg (GET_MODE (*ad->base_term), NULL_RTX, cl, NULL, |
| "base + disp"); |
| lra_emit_add (new_reg, *ad->base_term, disp); |
| return new_reg; |
| } |
| |
| /* Make reload of index part of address AD. Return the new |
| pseudo. */ |
| static rtx |
| index_part_to_reg (struct address_info *ad, enum reg_class index_class) |
| { |
| rtx new_reg; |
| |
| new_reg = lra_create_new_reg (GET_MODE (*ad->index), NULL_RTX, |
| index_class, NULL, "index term"); |
| expand_mult (GET_MODE (*ad->index), *ad->index_term, |
| GEN_INT (get_index_scale (ad)), new_reg, 1); |
| return new_reg; |
| } |
| |
| /* Return true if we can add a displacement to address AD, even if that |
| makes the address invalid. The fix-up code requires any new address |
| to be the sum of the BASE_TERM, INDEX and DISP_TERM fields. */ |
| static bool |
| can_add_disp_p (struct address_info *ad) |
| { |
| return (!ad->autoinc_p |
| && ad->segment == NULL |
| && ad->base == ad->base_term |
| && ad->disp == ad->disp_term); |
| } |
| |
| /* Make equiv substitution in address AD. Return true if a substitution |
| was made. */ |
| static bool |
| equiv_address_substitution (struct address_info *ad) |
| { |
| rtx base_reg, new_base_reg, index_reg, new_index_reg, *base_term, *index_term; |
| poly_int64 disp; |
| HOST_WIDE_INT scale; |
| bool change_p; |
| |
| base_term = strip_subreg (ad->base_term); |
| if (base_term == NULL) |
| base_reg = new_base_reg = NULL_RTX; |
| else |
| { |
| base_reg = *base_term; |
| new_base_reg = get_equiv_with_elimination (base_reg, curr_insn); |
| } |
| index_term = strip_subreg (ad->index_term); |
| if (index_term == NULL) |
| index_reg = new_index_reg = NULL_RTX; |
| else |
| { |
| index_reg = *index_term; |
| new_index_reg = get_equiv_with_elimination (index_reg, curr_insn); |
| } |
| if (base_reg == new_base_reg && index_reg == new_index_reg) |
| return false; |
| disp = 0; |
| change_p = false; |
| if (lra_dump_file != NULL) |
| { |
| fprintf (lra_dump_file, "Changing address in insn %d ", |
| INSN_UID (curr_insn)); |
| dump_value_slim (lra_dump_file, *ad->outer, 1); |
| } |
| if (base_reg != new_base_reg) |
| { |
| poly_int64 offset; |
| if (REG_P (new_base_reg)) |
| { |
| *base_term = new_base_reg; |
| change_p = true; |
| } |
| else if (GET_CODE (new_base_reg) == PLUS |
| && REG_P (XEXP (new_base_reg, 0)) |
| && poly_int_rtx_p (XEXP (new_base_reg, 1), &offset) |
| && can_add_disp_p (ad)) |
| { |
| disp += offset; |
| *base_term = XEXP (new_base_reg, 0); |
| change_p = true; |
| } |
| if (ad->base_term2 != NULL) |
| *ad->base_term2 = *ad->base_term; |
| } |
| if (index_reg != new_index_reg) |
| { |
| poly_int64 offset; |
| if (REG_P (new_index_reg)) |
| { |
| *index_term = new_index_reg; |
| change_p = true; |
| } |
| else if (GET_CODE (new_index_reg) == PLUS |
| && REG_P (XEXP (new_index_reg, 0)) |
| && poly_int_rtx_p (XEXP (new_index_reg, 1), &offset) |
| && can_add_disp_p (ad) |
| && (scale = get_index_scale (ad))) |
| { |
| disp += offset * scale; |
| *index_term = XEXP (new_index_reg, 0); |
| change_p = true; |
| } |
| } |
| if (maybe_ne (disp, 0)) |
| { |
| if (ad->disp != NULL) |
| *ad->disp = plus_constant (GET_MODE (*ad->inner), *ad->disp, disp); |
| else |
| { |
| *ad->inner = plus_constant (GET_MODE (*ad->inner), *ad->inner, disp); |
| update_address (ad); |
| } |
| change_p = true; |
| } |
| if (lra_dump_file != NULL) |
| { |
| if (! change_p) |
| fprintf (lra_dump_file, " -- no change\n"); |
| else |
| { |
| fprintf (lra_dump_file, " on equiv "); |
| dump_value_slim (lra_dump_file, *ad->outer, 1); |
| fprintf (lra_dump_file, "\n"); |
| } |
| } |
| return change_p; |
| } |
| |
| /* Skip all modifiers and whitespaces in constraint STR and return the |
| result. */ |
| static const char * |
| skip_constraint_modifiers (const char *str) |
| { |
| for (;;str++) |
| switch (*str) |
| { |
| case '+': case '&' : case '=': case '*': case ' ': case '\t': |
| case '$': case '^' : case '%': case '?': case '!': |
| break; |
| default: return str; |
| } |
| } |
| |
| /* Takes a string of 0 or more comma-separated constraints. When more |
| than one constraint is present, evaluate whether they all correspond |
| to a single, repeated constraint (e.g. "r,r") or whether we have |
| more than one distinct constraints (e.g. "r,m"). */ |
| static bool |
| constraint_unique (const char *cstr) |
| { |
| enum constraint_num ca, cb; |
| ca = CONSTRAINT__UNKNOWN; |
| for (;;) |
| { |
| cstr = skip_constraint_modifiers (cstr); |
| if (*cstr == '\0' || *cstr == ',') |
| cb = CONSTRAINT_X; |
| else |
| { |
| cb = lookup_constraint (cstr); |
| if (cb == CONSTRAINT__UNKNOWN) |
| return false; |
| cstr += CONSTRAINT_LEN (cstr[0], cstr); |
| } |
| /* Handle the first iteration of the loop. */ |
| if (ca == CONSTRAINT__UNKNOWN) |
| ca = cb; |
| /* Handle the general case of comparing ca with subsequent |
| constraints. */ |
| else if (ca != cb) |
| return false; |
| if (*cstr == '\0') |
| return true; |
| if (*cstr == ',') |
| cstr += 1; |
| } |
| } |
| |
| /* Major function to make reloads for an address in operand NOP or |
| check its correctness (If CHECK_ONLY_P is true). The supported |
| cases are: |
| |
| 1) an address that existed before LRA started, at which point it |
| must have been valid. These addresses are subject to elimination |
| and may have become invalid due to the elimination offset being out |
| of range. |
| |
| 2) an address created by forcing a constant to memory |
| (force_const_to_mem). The initial form of these addresses might |
| not be valid, and it is this function's job to make them valid. |
| |
| 3) a frame address formed from a register and a (possibly zero) |
| constant offset. As above, these addresses might not be valid and |
| this function must make them so. |
| |
| Add reloads to the lists *BEFORE and *AFTER. We might need to add |
| reloads to *AFTER because of inc/dec, {pre, post} modify in the |
| address. Return true for any RTL change. |
| |
| The function is a helper function which does not produce all |
| transformations (when CHECK_ONLY_P is false) which can be |
| necessary. It does just basic steps. To do all necessary |
| transformations use function process_address. */ |
| static bool |
| process_address_1 (int nop, bool check_only_p, |
| rtx_insn **before, rtx_insn **after) |
| { |
| struct address_info ad; |
| rtx new_reg; |
| HOST_WIDE_INT scale; |
| rtx op = *curr_id->operand_loc[nop]; |
| rtx mem = extract_mem_from_operand (op); |
| const char *constraint; |
| enum constraint_num cn; |
| bool change_p = false; |
| |
| if (MEM_P (mem) |
| && GET_MODE (mem) == BLKmode |
| && GET_CODE (XEXP (mem, 0)) == SCRATCH) |
| return false; |
| |
| constraint |
| = skip_constraint_modifiers (curr_static_id->operand[nop].constraint); |
| if (IN_RANGE (constraint[0], '0', '9')) |
| { |
| char *end; |
| unsigned long dup = strtoul (constraint, &end, 10); |
| constraint |
| = skip_constraint_modifiers (curr_static_id->operand[dup].constraint); |
| } |
| cn = lookup_constraint (*constraint == '\0' ? "X" : constraint); |
| /* If we have several alternatives or/and several constraints in an |
| alternative and we can not say at this stage what constraint will be used, |
| use unknown constraint. The exception is an address constraint. If |
| operand has one address constraint, probably all others constraints are |
| address ones. */ |
| if (constraint[0] != '\0' && get_constraint_type (cn) != CT_ADDRESS |
| && !constraint_unique (constraint)) |
| cn = CONSTRAINT__UNKNOWN; |
| if (insn_extra_address_constraint (cn) |
| /* When we find an asm operand with an address constraint that |
| doesn't satisfy address_operand to begin with, we clear |
| is_address, so that we don't try to make a non-address fit. |
| If the asm statement got this far, it's because other |
| constraints are available, and we'll use them, disregarding |
| the unsatisfiable address ones. */ |
| && curr_static_id->operand[nop].is_address) |
| decompose_lea_address (&ad, curr_id->operand_loc[nop]); |
| /* Do not attempt to decompose arbitrary addresses generated by combine |
| for asm operands with loose constraints, e.g 'X'. |
| Need to extract memory from op for special memory constraint, |
| i.e. bcst_mem_operand in i386 backend. */ |
| else if (MEM_P (mem) |
| && !(INSN_CODE (curr_insn) < 0 |
| && get_constraint_type (cn) == CT_FIXED_FORM |
| && constraint_satisfied_p (op, cn))) |
| decompose_mem_address (&ad, mem); |
| else if (GET_CODE (op) == SUBREG |
| && MEM_P (SUBREG_REG (op))) |
| decompose_mem_address (&ad, SUBREG_REG (op)); |
| else |
| return false; |
| /* If INDEX_REG_CLASS is assigned to base_term already and isn't to |
| index_term, swap them so to avoid assigning INDEX_REG_CLASS to both |
| when INDEX_REG_CLASS is a single register class. */ |
| enum reg_class index_cl = index_reg_class (curr_insn); |
| if (ad.base_term != NULL |
| && ad.index_term != NULL |
| && ira_class_hard_regs_num[index_cl] == 1 |
| && REG_P (*ad.base_term) |
| && REG_P (*ad.index_term) |
| && in_class_p (*ad.base_term, index_cl, NULL) |
| && ! in_class_p (*ad.index_term, index_cl, NULL)) |
| { |
| std::swap (ad.base, ad.index); |
| std::swap (ad.base_term, ad.index_term); |
| } |
| if (! check_only_p) |
| change_p = equiv_address_substitution (&ad); |
| if (ad.base_term != NULL |
| && (process_addr_reg |
| (ad.base_term, check_only_p, before, |
| (ad.autoinc_p |
| && !(REG_P (*ad.base_term) |
| && find_regno_note (curr_insn, REG_DEAD, |
| REGNO (*ad.base_term)) != NULL_RTX) |
| ? after : NULL), |
| base_reg_class (ad.mode, ad.as, ad.base_outer_code, |
| get_index_code (&ad), curr_insn)))) |
| { |
| change_p = true; |
| if (ad.base_term2 != NULL) |
| *ad.base_term2 = *ad.base_term; |
| } |
| if (ad.index_term != NULL |
| && process_addr_reg (ad.index_term, check_only_p, |
| before, NULL, index_cl)) |
| change_p = true; |
| |
| /* Target hooks sometimes don't treat extra-constraint addresses as |
| legitimate address_operands, so handle them specially. */ |
| if (insn_extra_address_constraint (cn) |
| && satisfies_address_constraint_p (&ad, cn)) |
| return change_p; |
| |
| if (check_only_p) |
| return change_p; |
| |
| /* There are three cases where the shape of *AD.INNER may now be invalid: |
| |
| 1) the original address was valid, but either elimination or |
| equiv_address_substitution was applied and that made |
| the address invalid. |
| |
| 2) the address is an invalid symbolic address created by |
| force_const_to_mem. |
| |
| 3) the address is a frame address with an invalid offset. |
| |
| 4) the address is a frame address with an invalid base. |
| |
| All these cases involve a non-autoinc address, so there is no |
| point revalidating other types. */ |
| if (ad.autoinc_p || valid_address_p (op, &ad, cn)) |
| return change_p; |
| |
| /* Any index existed before LRA started, so we can assume that the |
| presence and shape of the index is valid. */ |
| push_to_sequence (*before); |
| lra_assert (ad.disp == ad.disp_term); |
| if (ad.base == NULL) |
| { |
| if (ad.index == NULL) |
| { |
| rtx_insn *insn; |
| rtx_insn *last = get_last_insn (); |
| int code = -1; |
| enum reg_class cl = base_reg_class (ad.mode, ad.as, |
| SCRATCH, SCRATCH, |
| curr_insn); |
| rtx addr = *ad.inner; |
| |
| new_reg = lra_create_new_reg (Pmode, NULL_RTX, cl, NULL, "addr"); |
| if (HAVE_lo_sum) |
| { |
| /* addr => lo_sum (new_base, addr), case (2) above. */ |
| insn = emit_insn (gen_rtx_SET |
| (new_reg, |
| gen_rtx_HIGH (Pmode, copy_rtx (addr)))); |
| code = recog_memoized (insn); |
| if (code >= 0) |
| { |
| *ad.inner = gen_rtx_LO_SUM (Pmode, new_reg, addr); |
| if (!valid_address_p (op, &ad, cn)) |
| { |
| /* Try to put lo_sum into register. */ |
| insn = emit_insn (gen_rtx_SET |
| (new_reg, |
| gen_rtx_LO_SUM (Pmode, new_reg, addr))); |
| code = recog_memoized (insn); |
| if (code >= 0) |
| { |
| *ad.inner = new_reg; |
| if (!valid_address_p (op, &ad, cn)) |
| { |
| *ad.inner = addr; |
| code = -1; |
| } |
| } |
| |
| } |
| } |
| if (code < 0) |
| delete_insns_since (last); |
| } |
| |
| if (code < 0) |
| { |
| /* addr => new_base, case (2) above. */ |
| lra_emit_move (new_reg, addr); |
| |
| for (insn = last == NULL_RTX ? get_insns () : NEXT_INSN (last); |
| insn != NULL_RTX; |
| insn = NEXT_INSN (insn)) |
| if (recog_memoized (insn) < 0) |
| break; |
| if (insn != NULL_RTX) |
| { |
| /* Do nothing if we cannot generate right insns. |
| This is analogous to reload pass behavior. */ |
| delete_insns_since (last); |
| end_sequence (); |
| return false; |
| } |
| *ad.inner = new_reg; |
| } |
| } |
| else |
| { |
| /* index * scale + disp => new base + index * scale, |
| case (1) above. */ |
| enum reg_class cl = base_reg_class (ad.mode, ad.as, PLUS, |
| GET_CODE (*ad.index), |
| curr_insn); |
| |
| lra_assert (index_cl != NO_REGS); |
| new_reg = lra_create_new_reg (Pmode, NULL_RTX, cl, NULL, "disp"); |
| lra_emit_move (new_reg, *ad.disp); |
| *ad.inner = simplify_gen_binary (PLUS, GET_MODE (new_reg), |
| new_reg, *ad.index); |
| } |
| } |
| else if (ad.index == NULL) |
| { |
| int regno; |
| enum reg_class cl; |
| rtx set; |
| rtx_insn *insns, *last_insn; |
| /* Try to reload base into register only if the base is invalid |
| for the address but with valid offset, case (4) above. */ |
| start_sequence (); |
| new_reg = base_to_reg (&ad); |
| |
| /* base + disp => new base, cases (1) and (3) above. */ |
| /* Another option would be to reload the displacement into an |
| index register. However, postreload has code to optimize |
| address reloads that have the same base and different |
| displacements, so reloading into an index register would |
| not necessarily be a win. */ |
| if (new_reg == NULL_RTX) |
| { |
| /* See if the target can split the displacement into a |
| legitimate new displacement from a local anchor. */ |
| gcc_assert (ad.disp == ad.disp_term); |
| poly_int64 orig_offset; |
| rtx offset1, offset2; |
| if (poly_int_rtx_p (*ad.disp, &orig_offset) |
| && targetm.legitimize_address_displacement (&offset1, &offset2, |
| orig_offset, |
| ad.mode)) |
| { |
| new_reg = base_plus_disp_to_reg (&ad, offset1); |
| new_reg = gen_rtx_PLUS (GET_MODE (new_reg), new_reg, offset2); |
| } |
| else |
| new_reg = base_plus_disp_to_reg (&ad, *ad.disp); |
| } |
| insns = get_insns (); |
| last_insn = get_last_insn (); |
| /* If we generated at least two insns, try last insn source as |
| an address. If we succeed, we generate one less insn. */ |
| if (REG_P (new_reg) |
| && last_insn != insns |
| && (set = single_set (last_insn)) != NULL_RTX |
| && GET_CODE (SET_SRC (set)) == PLUS |
| && REG_P (XEXP (SET_SRC (set), 0)) |
| && CONSTANT_P (XEXP (SET_SRC (set), 1))) |
| { |
| *ad.inner = SET_SRC (set); |
| if (valid_address_p (op, &ad, cn)) |
| { |
| *ad.base_term = XEXP (SET_SRC (set), 0); |
| *ad.disp_term = XEXP (SET_SRC (set), 1); |
| cl = base_reg_class (ad.mode, ad.as, ad.base_outer_code, |
| get_index_code (&ad), curr_insn); |
| regno = REGNO (*ad.base_term); |
| if (regno >= FIRST_PSEUDO_REGISTER |
| && cl != lra_get_allocno_class (regno)) |
| lra_change_class (regno, cl, " Change to", true); |
| new_reg = SET_SRC (set); |
| delete_insns_since (PREV_INSN (last_insn)); |
| } |
| } |
| end_sequence (); |
| emit_insn (insns); |
| *ad.inner = new_reg; |
| } |
| else if (ad.disp_term != NULL) |
| { |
| /* base + scale * index + disp => new base + scale * index, |
| case (1) above. */ |
| gcc_assert (ad.disp == ad.disp_term); |
| new_reg = base_plus_disp_to_reg (&ad, *ad.disp); |
| *ad.inner = simplify_gen_binary (PLUS, GET_MODE (new_reg), |
| new_reg, *ad.index); |
| } |
| else if ((scale = get_index_scale (&ad)) == 1) |
| { |
| /* The last transformation to one reg will be made in |
| curr_insn_transform function. */ |
| end_sequence (); |
| return false; |
| } |
| else if (scale != 0) |
| { |
| /* base + scale * index => base + new_reg, |
| case (1) above. |
| Index part of address may become invalid. For example, we |
| changed pseudo on the equivalent memory and a subreg of the |
| pseudo onto the memory of different mode for which the scale is |
| prohibitted. */ |
| new_reg = index_part_to_reg (&ad, index_cl); |
| *ad.inner = simplify_gen_binary (PLUS, GET_MODE (new_reg), |
| *ad.base_term, new_reg); |
| } |
| else |
| { |
| enum reg_class cl = base_reg_class (ad.mode, ad.as, |
| SCRATCH, SCRATCH, |
| curr_insn); |
| rtx addr = *ad.inner; |
| |
| new_reg = lra_create_new_reg (Pmode, NULL_RTX, cl, NULL, "addr"); |
| /* addr => new_base. */ |
| lra_emit_move (new_reg, addr); |
| *ad.inner = new_reg; |
| } |
| *before = get_insns (); |
| end_sequence (); |
| return true; |
| } |
| |
| /* If CHECK_ONLY_P is false, do address reloads until it is necessary. |
| Use process_address_1 as a helper function. Return true for any |
| RTL changes. |
| |
| If CHECK_ONLY_P is true, just check address correctness. Return |
| false if the address correct. */ |
| static bool |
| process_address (int nop, bool check_only_p, |
| rtx_insn **before, rtx_insn **after) |
| { |
| bool res = false; |
| |
| while (process_address_1 (nop, check_only_p, before, after)) |
| { |
| if (check_only_p) |
| return true; |
| res = true; |
| } |
| return res; |
| } |
| |
| /* Override the generic address_reload_context in order to |
| control the creation of reload pseudos. */ |
| class lra_autoinc_reload_context : public address_reload_context |
| { |
| machine_mode mode; |
| enum reg_class rclass; |
| |
| public: |
| lra_autoinc_reload_context (machine_mode mode, enum reg_class new_rclass) |
| : mode (mode), rclass (new_rclass) {} |
| |
| rtx get_reload_reg () const override final |
| { |
| return lra_create_new_reg (mode, NULL_RTX, rclass, NULL, "INC/DEC result"); |
| } |
| }; |
| |
| /* Emit insns to reload VALUE into a new register. VALUE is an |
| auto-increment or auto-decrement RTX whose operand is a register or |
| memory location; so reloading involves incrementing that location. |
| |
| INC_AMOUNT is the number to increment or decrement by (always |
| positive and ignored for POST_MODIFY/PRE_MODIFY). |
| |
| Return a pseudo containing the result. */ |
| static rtx |
| emit_inc (enum reg_class new_rclass, rtx value, poly_int64 inc_amount) |
| { |
| lra_autoinc_reload_context context (GET_MODE (value), new_rclass); |
| return context.emit_autoinc (value, inc_amount); |
| } |
| |
| /* Return true if the current move insn does not need processing as we |
| already know that it satisfies its constraints. */ |
| static bool |
| simple_move_p (void) |
| { |
| rtx dest, src; |
| enum reg_class dclass, sclass; |
| |
| lra_assert (curr_insn_set != NULL_RTX); |
| dest = SET_DEST (curr_insn_set); |
| src = SET_SRC (curr_insn_set); |
| |
| /* If the instruction has multiple sets we need to process it even if it |
| is single_set. This can happen if one or more of the SETs are dead. |
| See PR73650. */ |
| if (multiple_sets (curr_insn)) |
| return false; |
| |
| return ((dclass = get_op_class (dest)) != NO_REGS |
| && (sclass = get_op_class (src)) != NO_REGS |
| /* The backend guarantees that register moves of cost 2 |
| never need reloads. */ |
| && targetm.register_move_cost (GET_MODE (src), sclass, dclass) == 2); |
| } |
| |
| /* Swap operands NOP and NOP + 1. */ |
| static inline void |
| swap_operands (int nop) |
| { |
| std::swap (curr_operand_mode[nop], curr_operand_mode[nop + 1]); |
| std::swap (original_subreg_reg_mode[nop], original_subreg_reg_mode[nop + 1]); |
| std::swap (*curr_id->operand_loc[nop], *curr_id->operand_loc[nop + 1]); |
| std::swap (equiv_substition_p[nop], equiv_substition_p[nop + 1]); |
| /* Swap the duplicates too. */ |
| lra_update_dup (curr_id, nop); |
| lra_update_dup (curr_id, nop + 1); |
| } |
| |
| /* Main entry point of the constraint code: search the body of the |
| current insn to choose the best alternative. It is mimicking insn |
| alternative cost calculation model of former reload pass. That is |
| because machine descriptions were written to use this model. This |
| model can be changed in future. Make commutative operand exchange |
| if it is chosen. |
| |
| if CHECK_ONLY_P is false, do RTL changes to satisfy the |
| constraints. Return true if any change happened during function |
| call. |
| |
| If CHECK_ONLY_P is true then don't do any transformation. Just |
| check that the insn satisfies all constraints. If the insn does |
| not satisfy any constraint, return true. */ |
| static bool |
| curr_insn_transform (bool check_only_p) |
| { |
| int i, j, k; |
| int n_operands; |
| int n_alternatives; |
| int n_outputs; |
| int commutative; |
| signed char goal_alt_matched[MAX_RECOG_OPERANDS][MAX_RECOG_OPERANDS]; |
| signed char match_inputs[MAX_RECOG_OPERANDS + 1]; |
| signed char outputs[MAX_RECOG_OPERANDS + 1]; |
| rtx_insn *before, *after; |
| bool alt_p = false; |
| /* Flag that the insn has been changed through a transformation. */ |
| bool change_p; |
| bool sec_mem_p; |
| bool use_sec_mem_p; |
| int max_regno_before; |
| int reused_alternative_num; |
| |
| curr_insn_set = single_set (curr_insn); |
| if (curr_insn_set != NULL_RTX && simple_move_p ()) |
| { |
| /* We assume that the corresponding insn alternative has no |
| earlier clobbers. If it is not the case, don't define move |
| cost equal to 2 for the corresponding register classes. */ |
| lra_set_used_insn_alternative (curr_insn, LRA_NON_CLOBBERED_ALT); |
| return false; |
| } |
| |
| no_input_reloads_p = no_output_reloads_p = false; |
| goal_alt_number = -1; |
| change_p = sec_mem_p = false; |
| |
| /* CALL_INSNs are not allowed to have any output reloads. */ |
| if (CALL_P (curr_insn)) |
| no_output_reloads_p = true; |
| |
| n_operands = curr_static_id->n_operands; |
| n_alternatives = curr_static_id->n_alternatives; |
| |
| /* Just return "no reloads" if insn has no operands with |
| constraints. */ |
| if (n_operands == 0 || n_alternatives == 0) |
| return false; |
| |
| max_regno_before = max_reg_num (); |
| |
| for (i = 0; i < n_operands; i++) |
| { |
| goal_alt_matched[i][0] = -1; |
| goal_alt_matches[i] = -1; |
| } |
| |
| commutative = curr_static_id->commutative; |
| |
| /* Now see what we need for pseudos that didn't get hard regs or got |
| the wrong kind of hard reg. For this, we must consider all the |
| operands together against the register constraints. */ |
| |
| best_losers = best_overall = INT_MAX; |
| best_reload_sum = 0; |
| |
| curr_swapped = false; |
| goal_alt_swapped = false; |
| |
| if (! check_only_p) |
| /* Make equivalence substitution and memory subreg elimination |
| before address processing because an address legitimacy can |
| depend on memory mode. */ |
| for (i = 0; i < n_operands; i++) |
| { |
| rtx op, subst, old; |
| bool op_change_p = false; |
| |
| if (curr_static_id->operand[i].is_operator) |
| continue; |
| |
| old = op = *curr_id->operand_loc[i]; |
| if (GET_CODE (old) == SUBREG) |
| old = SUBREG_REG (old); |
| subst = get_equiv_with_elimination (old, curr_insn); |
| original_subreg_reg_mode[i] = VOIDmode; |
| equiv_substition_p[i] = false; |
| if (subst != old) |
| { |
| equiv_substition_p[i] = true; |
| subst = copy_rtx (subst); |
| lra_assert (REG_P (old)); |
| if (GET_CODE (op) != SUBREG) |
| *curr_id->operand_loc[i] = subst; |
| else |
| { |
| SUBREG_REG (op) = subst; |
| if (GET_MODE (subst) == VOIDmode) |
| original_subreg_reg_mode[i] = GET_MODE (old); |
| } |
| if (lra_dump_file != NULL) |
| { |
| fprintf (lra_dump_file, |
| "Changing pseudo %d in operand %i of insn %u on equiv ", |
| REGNO (old), i, INSN_UID (curr_insn)); |
| dump_value_slim (lra_dump_file, subst, 1); |
| fprintf (lra_dump_file, "\n"); |
| } |
| op_change_p = change_p = true; |
| } |
| if (simplify_operand_subreg (i, GET_MODE (old)) || op_change_p) |
| { |
| change_p = true; |
| lra_update_dup (curr_id, i); |
| } |
| } |
| |
| /* Reload address registers and displacements. We do it before |
| finding an alternative because of memory constraints. */ |
| before = after = NULL; |
| for (i = 0; i < n_operands; i++) |
| if (! curr_static_id->operand[i].is_operator |
| && process_address (i, check_only_p, &before, &after)) |
| { |
| if (check_only_p) |
| return true; |
| change_p = true; |
| lra_update_dup (curr_id, i); |
| } |
| |
| if (change_p) |
| /* If we've changed the instruction then any alternative that |
| we chose previously may no longer be valid. */ |
| lra_set_used_insn_alternative (curr_insn, LRA_UNKNOWN_ALT); |
| |
| if (! check_only_p && curr_insn_set != NULL_RTX |
| && check_and_process_move (&change_p, &sec_mem_p)) |
| return change_p; |
| |
| try_swapped: |
| |
| reused_alternative_num = check_only_p ? LRA_UNKNOWN_ALT : curr_id->used_insn_alternative; |
| if (lra_dump_file != NULL && reused_alternative_num >= 0) |
| fprintf (lra_dump_file, "Reusing alternative %d for insn #%u\n", |
| reused_alternative_num, INSN_UID (curr_insn)); |
| |
| if (process_alt_operands (reused_alternative_num)) |
| alt_p = true; |
| |
| if (check_only_p) |
| return ! alt_p || best_losers != 0; |
| |
| /* If insn is commutative (it's safe to exchange a certain pair of |
| operands) then we need to try each alternative twice, the second |
| time matching those two operands as if we had exchanged them. To |
| do this, really exchange them in operands. |
| |
| If we have just tried the alternatives the second time, return |
| operands to normal and drop through. */ |
| |
| if (reused_alternative_num < 0 && commutative >= 0) |
| { |
| curr_swapped = !curr_swapped; |
| if (curr_swapped) |
| { |
| swap_operands (commutative); |
| goto try_swapped; |
| } |
| else |
| swap_operands (commutative); |
| } |
| |
| if (! alt_p && ! sec_mem_p) |
| { |
| /* No alternative works with reloads?? */ |
| if (INSN_CODE (curr_insn) >= 0) |
| fatal_insn ("unable to generate reloads for:", curr_insn); |
| error_for_asm (curr_insn, |
| "inconsistent operand constraints in an %<asm%>"); |
| lra_asm_error_p = true; |
| if (! JUMP_P (curr_insn)) |
| { |
| /* Avoid further trouble with this insn. Don't generate use |
| pattern here as we could use the insn SP offset. */ |
| lra_set_insn_deleted (curr_insn); |
| } |
| else |
| { |
| lra_invalidate_insn_data (curr_insn); |
| ira_nullify_asm_goto (curr_insn); |
| lra_update_insn_regno_info (curr_insn); |
| } |
| return true; |
| } |
| |
| /* If the best alternative is with operands 1 and 2 swapped, swap |
| them. Update the operand numbers of any reloads already |
| pushed. */ |
| |
| if (goal_alt_swapped) |
| { |
| if (lra_dump_file != NULL) |
| fprintf (lra_dump_file, " Commutative operand exchange in insn %u\n", |
| INSN_UID (curr_insn)); |
| |
| /* Swap the duplicates too. */ |
| swap_operands (commutative); |
| change_p = true; |
| } |
| |
| /* Some targets' TARGET_SECONDARY_MEMORY_NEEDED (e.g. x86) are defined |
| too conservatively. So we use the secondary memory only if there |
| is no any alternative without reloads. */ |
| use_sec_mem_p = false; |
| if (! alt_p) |
| use_sec_mem_p = true; |
| else if (sec_mem_p) |
| { |
| for (i = 0; i < n_operands; i++) |
| if (! goal_alt_win[i] && ! goal_alt_match_win[i]) |
| break; |
| use_sec_mem_p = i < n_operands; |
| } |
| |
| if (use_sec_mem_p) |
| { |
| int in = -1, out = -1; |
| rtx new_reg, src, dest, rld; |
| machine_mode sec_mode, rld_mode; |
| |
| lra_assert (curr_insn_set != NULL_RTX && sec_mem_p); |
| dest = SET_DEST (curr_insn_set); |
| src = SET_SRC (curr_insn_set); |
| for (i = 0; i < n_operands; i++) |
| if (*curr_id->operand_loc[i] == dest) |
| out = i; |
| else if (*curr_id->operand_loc[i] == src) |
| in = i; |
| for (i = 0; i < curr_static_id->n_dups; i++) |
| if (out < 0 && *curr_id->dup_loc[i] == dest) |
| out = curr_static_id->dup_num[i]; |
| else if (in < 0 && *curr_id->dup_loc[i] == src) |
| in = curr_static_id->dup_num[i]; |
| lra_assert (out >= 0 && in >= 0 |
| && curr_static_id->operand[out].type == OP_OUT |
| && curr_static_id->operand[in].type == OP_IN); |
| rld = partial_subreg_p (GET_MODE (src), GET_MODE (dest)) ? src : dest; |
| rld_mode = GET_MODE (rld); |
| sec_mode = targetm.secondary_memory_needed_mode (rld_mode); |
| new_reg = lra_create_new_reg (sec_mode, NULL_RTX, NO_REGS, NULL, |
| "secondary"); |
| /* If the mode is changed, it should be wider. */ |
| lra_assert (!partial_subreg_p (sec_mode, rld_mode)); |
| if (sec_mode != rld_mode) |
| { |
| /* If the target says specifically to use another mode for |
| secondary memory moves we cannot reuse the original |
| insn. */ |
| after = emit_spill_move (false, new_reg, dest); |
| lra_process_new_insns (curr_insn, NULL, after, |
| "Inserting the sec. move"); |
| /* We may have non null BEFORE here (e.g. after address |
| processing. */ |
| push_to_sequence (before); |
| before = emit_spill_move (true, new_reg, src); |
| emit_insn (before); |
| before = get_insns (); |
| end_sequence (); |
| lra_process_new_insns (curr_insn, before, NULL, "Changing on"); |
| lra_set_insn_deleted (curr_insn); |
| } |
| else if (dest == rld) |
| { |
| *curr_id->operand_loc[out] = new_reg; |
| lra_update_dup (curr_id, out); |
| after = emit_spill_move (false, new_reg, dest); |
| lra_process_new_insns (curr_insn, NULL, after, |
| "Inserting the sec. move"); |
| } |
| else |
| { |
| *curr_id->operand_loc[in] = new_reg; |
| lra_update_dup (curr_id, in); |
| /* See comments above. */ |
| push_to_sequence (before); |
| before = emit_spill_move (true, new_reg, src); |
| emit_insn (before); |
| before = get_insns (); |
| end_sequence (); |
| lra_process_new_insns (curr_insn, before, NULL, |
| "Inserting the sec. move"); |
| } |
| lra_update_insn_regno_info (curr_insn); |
| return true; |
| } |
| |
| lra_assert (goal_alt_number >= 0); |
| lra_set_used_insn_alternative (curr_insn, goal_reuse_alt_p |
| ? goal_alt_number : LRA_UNKNOWN_ALT); |
| |
| if (lra_dump_file != NULL) |
| { |
| const char *p; |
| |
| fprintf (lra_dump_file, " Choosing alt %d in insn %u:", |
| goal_alt_number, INSN_UID (curr_insn)); |
| print_curr_insn_alt (goal_alt_number); |
| if (INSN_CODE (curr_insn) >= 0 |
| && (p = get_insn_name (INSN_CODE (curr_insn))) != NULL) |
| fprintf (lra_dump_file, " {%s}", p); |
| if (maybe_ne (curr_id->sp_offset, 0)) |
| { |
| fprintf (lra_dump_file, " (sp_off="); |
| print_dec (curr_id->sp_offset, lra_dump_file); |
| fprintf (lra_dump_file, ")"); |
| } |
| fprintf (lra_dump_file, "\n"); |
| } |
| |
| /* Right now, for any pair of operands I and J that are required to |
| match, with J < I, goal_alt_matches[I] is J. Add I to |
| goal_alt_matched[J]. */ |
| |
| for (i = 0; i < n_operands; i++) |
| if ((j = goal_alt_matches[i]) >= 0) |
| { |
| for (k = 0; goal_alt_matched[j][k] >= 0; k++) |
| ; |
| /* We allow matching one output operand and several input |
| operands. */ |
| lra_assert (k == 0 |
| || (curr_static_id->operand[j].type == OP_OUT |
| && curr_static_id->operand[i].type == OP_IN |
| && (curr_static_id->operand |
| [goal_alt_matched[j][0]].type == OP_IN))); |
| goal_alt_matched[j][k] = i; |
| goal_alt_matched[j][k + 1] = -1; |
| } |
| |
| for (i = 0; i < n_operands; i++) |
| goal_alt_win[i] |= goal_alt_match_win[i]; |
| |
| /* Any constants that aren't allowed and can't be reloaded into |
| registers are here changed into memory references. */ |
| for (i = 0; i < n_operands; i++) |
| if (goal_alt_win[i]) |
| { |
| int regno; |
| enum reg_class new_class; |
| rtx reg = *curr_id->operand_loc[i]; |
| |
| if (GET_CODE (reg) == SUBREG) |
| reg = SUBREG_REG (reg); |
| |
| if (REG_P (reg) && (regno = REGNO (reg)) >= FIRST_PSEUDO_REGISTER) |
| { |
| bool ok_p = in_class_p (reg, goal_alt[i], &new_class, true); |
| |
| if (new_class != NO_REGS && get_reg_class (regno) != new_class) |
| { |
| lra_assert (ok_p); |
| lra_change_class (regno, new_class, " Change to", true); |
| } |
| } |
| } |
| else |
| { |
| const char *constraint; |
| char c; |
| rtx op = *curr_id->operand_loc[i]; |
| rtx subreg = NULL_RTX; |
| machine_mode mode = curr_operand_mode[i]; |
| |
| if (GET_CODE (op) == SUBREG) |
| { |
| subreg = op; |
| op = SUBREG_REG (op); |
| mode = GET_MODE (op); |
| } |
| |
| if (CONST_POOL_OK_P (mode, op) |
| && ((targetm.preferred_reload_class |
| (op, (enum reg_class) goal_alt[i]) == NO_REGS) |
| || no_input_reloads_p)) |
| { |
| rtx tem = force_const_mem (mode, op); |
| |
| change_p = true; |
| if (subreg != NULL_RTX) |
| tem = gen_rtx_SUBREG (mode, tem, SUBREG_BYTE (subreg)); |
| |
| *curr_id->operand_loc[i] = tem; |
| lra_update_dup (curr_id, i); |
| process_address (i, false, &before, &after); |
| |
| /* If the alternative accepts constant pool refs directly |
| there will be no reload needed at all. */ |
| if (subreg != NULL_RTX) |
| continue; |
| /* Skip alternatives before the one requested. */ |
| constraint = (curr_static_id->operand_alternative |
| [goal_alt_number * n_operands + i].constraint); |
| for (; |
| (c = *constraint) && c != ',' && c != '#'; |
| constraint += CONSTRAINT_LEN (c, constraint)) |
| { |
| enum constraint_num cn = lookup_constraint (constraint); |
| if ((insn_extra_memory_constraint (cn) |
| || insn_extra_special_memory_constraint (cn) |
| || insn_extra_relaxed_memory_constraint (cn)) |
| && satisfies_memory_constraint_p (tem, cn)) |
| break; |
| } |
| if (c == '\0' || c == ',' || c == '#') |
| continue; |
| |
| goal_alt_win[i] = true; |
| } |
| } |
| |
| n_outputs = 0; |
| for (i = 0; i < n_operands; i++) |
| if (curr_static_id->operand[i].type == OP_OUT) |
| outputs[n_outputs++] = i; |
| outputs[n_outputs] = -1; |
| for (i = 0; i < n_operands; i++) |
| { |
| int regno; |
| bool optional_p = false; |
| rtx old, new_reg; |
| rtx op = *curr_id->operand_loc[i]; |
| |
| if (goal_alt_win[i]) |
| { |
| if (goal_alt[i] == NO_REGS |
| && REG_P (op) |
| && (regno = REGNO (op)) >= FIRST_PSEUDO_REGISTER |
| /* We assigned a hard register to the pseudo in the past but now |
| decided to spill it for the insn. If the pseudo is used only |
| in this insn, it is better to spill it here as we free hard |
| registers for other pseudos referenced in the insn. The most |
| common case of this is a scratch register which will be |
| transformed to scratch back at the end of LRA. */ |
| && bitmap_single_bit_set_p (&lra_reg_info[regno].insn_bitmap)) |
| { |
| if (lra_get_allocno_class (regno) != NO_REGS) |
| lra_change_class (regno, NO_REGS, " Change to", true); |
| reg_renumber[regno] = -1; |
| } |
| /* We can do an optional reload. If the pseudo got a hard |
| reg, we might improve the code through inheritance. If |
| it does not get a hard register we coalesce memory/memory |
| moves later. Ignore move insns to avoid cycling. */ |
| if (! lra_simple_p |
| && lra_undo_inheritance_iter < LRA_MAX_INHERITANCE_PASSES |
| && goal_alt[i] != NO_REGS && REG_P (op) |
| && (regno = REGNO (op)) >= FIRST_PSEUDO_REGISTER |
| && regno < new_regno_start |
| && ! ira_former_scratch_p (regno) |
| && reg_renumber[regno] < 0 |
| /* Check that the optional reload pseudo will be able to |
| hold given mode value. */ |
| && ! (prohibited_class_reg_set_mode_p |
| (goal_alt[i], reg_class_contents[goal_alt[i]], |
| PSEUDO_REGNO_MODE (regno))) |
| && (curr_insn_set == NULL_RTX |
| || !((REG_P (SET_SRC (curr_insn_set)) |
| || MEM_P (SET_SRC (curr_insn_set)) |
| || GET_CODE (SET_SRC (curr_insn_set)) == SUBREG) |
| && (REG_P (SET_DEST (curr_insn_set)) |
| || MEM_P (SET_DEST (curr_insn_set)) |
| || GET_CODE (SET_DEST (curr_insn_set)) == SUBREG)))) |
| optional_p = true; |
| else if (goal_alt_matched[i][0] != -1 |
| && curr_static_id->operand[i].type == OP_OUT |
| && (curr_static_id->operand_alternative |
| [goal_alt_number * n_operands + i].earlyclobber) |
| && REG_P (op)) |
| { |
| for (j = 0; goal_alt_matched[i][j] != -1; j++) |
| { |
| rtx op2 = *curr_id->operand_loc[goal_alt_matched[i][j]]; |
| |
| if (REG_P (op2) && REGNO (op) != REGNO (op2)) |
| break; |
| } |
| if (goal_alt_matched[i][j] != -1) |
| { |
| /* Generate reloads for different output and matched |
| input registers. This is the easiest way to avoid |
| creation of non-existing register conflicts in |
| lra-lives.cc. */ |
| match_reload (i, goal_alt_matched[i], outputs, goal_alt[i], |
| &goal_alt_exclude_start_hard_regs[i], &before, |
| &after, true); |
| } |
| continue; |
| } |
| else |
| { |
| enum reg_class rclass, common_class; |
| |
| if (REG_P (op) && goal_alt[i] != NO_REGS |
| && (regno = REGNO (op)) >= new_regno_start |
| && (rclass = get_reg_class (regno)) == ALL_REGS |
| && ((common_class = ira_reg_class_subset[rclass][goal_alt[i]]) |
| != NO_REGS) |
| && common_class != ALL_REGS |
| && enough_allocatable_hard_regs_p (common_class, |
| GET_MODE (op))) |
| /* Refine reload pseudo class from chosen alternative |
| constraint. */ |
| lra_change_class (regno, common_class, " Change to", true); |
| continue; |
| } |
| } |
| |
| /* Operands that match previous ones have already been handled. */ |
| if (goal_alt_matches[i] >= 0) |
| continue; |
| |
| /* We should not have an operand with a non-offsettable address |
| appearing where an offsettable address will do. It also may |
| be a case when the address should be special in other words |
| not a general one (e.g. it needs no index reg). */ |
| if (goal_alt_matched[i][0] == -1 && goal_alt_offmemok[i] && MEM_P (op)) |
| { |
| enum reg_class rclass; |
| rtx *loc = &XEXP (op, 0); |
| enum rtx_code code = GET_CODE (*loc); |
| |
| push_to_sequence (before); |
| rclass = base_reg_class (GET_MODE (op), MEM_ADDR_SPACE (op), |
| MEM, SCRATCH, curr_insn); |
| if (GET_RTX_CLASS (code) == RTX_AUTOINC) |
| new_reg = emit_inc (rclass, *loc, |
| /* This value does not matter for MODIFY. */ |
| GET_MODE_SIZE (GET_MODE (op))); |
| else if (get_reload_reg (OP_IN, Pmode, *loc, rclass, |
| NULL, false, false, |
| "offsetable address", &new_reg)) |
| { |
| rtx addr = *loc; |
| enum rtx_code code = GET_CODE (addr); |
| bool align_p = false; |
| |
| if (code == AND && CONST_INT_P (XEXP (addr, 1))) |
| { |
| /* (and ... (const_int -X)) is used to align to X bytes. */ |
| align_p = true; |
| addr = XEXP (*loc, 0); |
| } |
| else |
| addr = canonicalize_reload_addr (addr); |
| |
| lra_emit_move (new_reg, addr); |
| if (align_p) |
| emit_move_insn (new_reg, gen_rtx_AND (GET_MODE (new_reg), new_reg, XEXP (*loc, 1))); |
| } |
| before = get_insns (); |
| end_sequence (); |
| *loc = new_reg; |
| lra_update_dup (curr_id, i); |
| } |
| else if (goal_alt_matched[i][0] == -1) |
| { |
| machine_mode mode; |
| rtx reg, *loc; |
| int hard_regno; |
| enum op_type type = curr_static_id->operand[i].type; |
| |
| loc = curr_id->operand_loc[i]; |
| mode = curr_operand_mode[i]; |
| if (GET_CODE (*loc) == SUBREG) |
| { |
| reg = SUBREG_REG (*loc); |
| poly_int64 byte = SUBREG_BYTE (*loc); |
| if (REG_P (reg) |
| /* Strict_low_part requires reloading the register and not |
| just the subreg. Likewise for a strict subreg no wider |
| than a word for WORD_REGISTER_OPERATIONS targets. */ |
| && (curr_static_id->operand[i].strict_low |
| || (!paradoxical_subreg_p (mode, GET_MODE (reg)) |
| && (hard_regno |
| = get_try_hard_regno (REGNO (reg))) >= 0 |
| && (simplify_subreg_regno |
| (hard_regno, |
| GET_MODE (reg), byte, mode) < 0) |
| && (goal_alt[i] == NO_REGS |
| || (simplify_subreg_regno |
| (ira_class_hard_regs[goal_alt[i]][0], |
| GET_MODE (reg), byte, mode) >= 0))) |
| || (partial_subreg_p (mode, GET_MODE (reg)) |
| && known_le (GET_MODE_SIZE (GET_MODE (reg)), |
| UNITS_PER_WORD) |
| && WORD_REGISTER_OPERATIONS)) |
| /* Avoid the situation when there are no available hard regs |
| for the pseudo mode but there are ones for the subreg |
| mode: */ |
| && !(goal_alt[i] != NO_REGS |
| && REGNO (reg) >= FIRST_PSEUDO_REGISTER |
| && (prohibited_class_reg_set_mode_p |
| (goal_alt[i], reg_class_contents[goal_alt[i]], |
| GET_MODE (reg))) |
| && !(prohibited_class_reg_set_mode_p |
| (goal_alt[i], reg_class_contents[goal_alt[i]], |
| mode)))) |
| { |
| /* An OP_INOUT is required when reloading a subreg of a |
| mode wider than a word to ensure that data beyond the |
| word being reloaded is preserved. Also automatically |
| ensure that strict_low_part reloads are made into |
| OP_INOUT which should already be true from the backend |
| constraints. */ |
| if (type == OP_OUT |
| && (curr_static_id->operand[i].strict_low |
| || read_modify_subreg_p (*loc))) |
| type = OP_INOUT; |
| loc = &SUBREG_REG (*loc); |
| mode = GET_MODE (*loc); |
| } |
| } |
| old = *loc; |
| if (get_reload_reg (type, mode, old, goal_alt[i], |
| &goal_alt_exclude_start_hard_regs[i], |
| loc != curr_id->operand_loc[i], |
| curr_static_id->operand_alternative |
| [goal_alt_number * n_operands + i].earlyclobber, |
| "", &new_reg) |
| && type != OP_OUT) |
| { |
| push_to_sequence (before); |
| lra_emit_move (new_reg, old); |
| before = get_insns (); |
| end_sequence (); |
| } |
| *loc = new_reg; |
| if (type != OP_IN |
| && find_reg_note (curr_insn, REG_UNUSED, old) == NULL_RTX) |
| { |
| start_sequence (); |
| lra_emit_move (type == OP_INOUT ? copy_rtx (old) : old, new_reg); |
| emit_insn (after); |
| after = get_insns (); |
| end_sequence (); |
| *loc = new_reg; |
| } |
| for (j = 0; j < goal_alt_dont_inherit_ops_num; j++) |
| if (goal_alt_dont_inherit_ops[j] == i) |
| { |
| lra_set_regno_unique_value (REGNO (new_reg)); |
| break; |
| } |
| lra_update_dup (curr_id, i); |
| } |
| else if (curr_static_id->operand[i].type == OP_IN |
| && (curr_static_id->operand[goal_alt_matched[i][0]].type |
| == OP_OUT |
| || (curr_static_id->operand[goal_alt_matched[i][0]].type |
| == OP_INOUT |
| && (operands_match_p |
| (*curr_id->operand_loc[i], |
| *curr_id->operand_loc[goal_alt_matched[i][0]], |
| -1))))) |
| { |
| /* generate reloads for input and matched outputs. */ |
| match_inputs[0] = i; |
| match_inputs[1] = -1; |
| match_reload (goal_alt_matched[i][0], match_inputs, outputs, |
| goal_alt[i], &goal_alt_exclude_start_hard_regs[i], |
| &before, &after, |
| curr_static_id->operand_alternative |
| [goal_alt_number * n_operands + goal_alt_matched[i][0]] |
| .earlyclobber); |
| } |
| else if ((curr_static_id->operand[i].type == OP_OUT |
| || (curr_static_id->operand[i].type == OP_INOUT |
| && (operands_match_p |
| (*curr_id->operand_loc[i], |
| *curr_id->operand_loc[goal_alt_matched[i][0]], |
| -1)))) |
| && (curr_static_id->operand[goal_alt_matched[i][0]].type |
| == OP_IN)) |
| /* Generate reloads for output and matched inputs. */ |
| match_reload (i, goal_alt_matched[i], outputs, goal_alt[i], |
| &goal_alt_exclude_start_hard_regs[i], &before, &after, |
| curr_static_id->operand_alternative |
| [goal_alt_number * n_operands + i].earlyclobber); |
| else if (curr_static_id->operand[i].type == OP_IN |
| && (curr_static_id->operand[goal_alt_matched[i][0]].type |
| == OP_IN)) |
| { |
| /* Generate reloads for matched inputs. */ |
| match_inputs[0] = i; |
| for (j = 0; (k = goal_alt_matched[i][j]) >= 0; j++) |
| match_inputs[j + 1] = k; |
| match_inputs[j + 1] = -1; |
| match_reload (-1, match_inputs, outputs, goal_alt[i], |
| &goal_alt_exclude_start_hard_regs[i], |
| &before, &after, false); |
| } |
| else |
| /* We must generate code in any case when function |
| process_alt_operands decides that it is possible. */ |
| gcc_unreachable (); |
| |
| if (optional_p) |
| { |
| rtx reg = op; |
| |
| lra_assert (REG_P (reg)); |
| regno = REGNO (reg); |
| op = *curr_id->operand_loc[i]; /* Substitution. */ |
| if (GET_CODE (op) == SUBREG) |
| op = SUBREG_REG (op); |
| gcc_assert (REG_P (op) && (int) REGNO (op) >= new_regno_start); |
| bitmap_set_bit (&lra_optional_reload_pseudos, REGNO (op)); |
| lra_reg_info[REGNO (op)].restore_rtx = reg; |
| if (lra_dump_file != NULL) |
| fprintf (lra_dump_file, |
| " Making reload reg %d for reg %d optional\n", |
| REGNO (op), regno); |
| } |
| } |
| if (before != NULL_RTX || after != NULL_RTX |
| || max_regno_before != max_reg_num ()) |
| change_p = true; |
| if (change_p) |
| { |
| lra_update_operator_dups (curr_id); |
| /* Something changes -- process the insn. */ |
| lra_update_insn_regno_info (curr_insn); |
| if (asm_noperands (PATTERN (curr_insn)) >= 0 |
| && ++curr_id->asm_reloads_num >= FIRST_PSEUDO_REGISTER) |
| /* Most probably there are no enough registers to satisfy asm insn: */ |
| lra_asm_insn_error (curr_insn); |
| } |
| if (goal_alt_out_sp_reload_p) |
| { |
| /* We have an output stack pointer reload -- update sp offset: */ |
| rtx set; |
| bool done_p = false; |
| poly_int64 sp_offset = curr_id->sp_offset; |
| for (rtx_insn *insn = after; insn != NULL_RTX; insn = NEXT_INSN (insn)) |
| if ((set = single_set (insn)) != NULL_RTX |
| && SET_DEST (set) == stack_pointer_rtx) |
| { |
| lra_assert (!done_p); |
| done_p = true; |
| curr_id->sp_offset = 0; |
| lra_insn_recog_data_t id = lra_get_insn_recog_data (insn); |
| id->sp_offset = sp_offset; |
| if (lra_dump_file != NULL) |
| fprintf (lra_dump_file, |
| " Moving sp offset from insn %u to %u\n", |
| INSN_UID (curr_insn), INSN_UID (insn)); |
| } |
| lra_assert (done_p); |
| } |
| lra_process_new_insns (curr_insn, before, after, "Inserting insn reload"); |
| return change_p; |
| } |
| |
| /* Return true if INSN satisfies all constraints. In other words, no |
| reload insns are needed. */ |
| bool |
| lra_constrain_insn (rtx_insn *insn) |
| { |
| int saved_new_regno_start = new_regno_start; |
| int saved_new_insn_uid_start = new_insn_uid_start; |
| bool change_p; |
| |
| curr_insn = insn; |
| curr_id = lra_get_insn_recog_data (curr_insn); |
| curr_static_id = curr_id->insn_static_data; |
| new_insn_uid_start = get_max_uid (); |
| new_regno_start = max_reg_num (); |
| change_p = curr_insn_transform (true); |
| new_regno_start = saved_new_regno_start; |
| new_insn_uid_start = saved_new_insn_uid_start; |
| return ! change_p; |
| } |
| |
| /* Return true if X is in LIST. */ |
| static bool |
| in_list_p (rtx x, rtx list) |
| { |
| for (; list != NULL_RTX; list = XEXP (list, 1)) |
| if (XEXP (list, 0) == x) |
| return true; |
| return false; |
| } |
| |
| /* Return true if X contains an allocatable hard register (if |
| HARD_REG_P) or a (spilled if SPILLED_P) pseudo. */ |
| static bool |
| contains_reg_p (rtx x, bool hard_reg_p, bool spilled_p) |
| { |
| int i, j; |
| const char *fmt; |
| enum rtx_code code; |
| |
| code = GET_CODE (x); |
| if (REG_P (x)) |
| { |
| int regno = REGNO (x); |
| HARD_REG_SET alloc_regs; |
| |
| if (hard_reg_p) |
| { |
| if (regno >= FIRST_PSEUDO_REGISTER) |
| regno = lra_get_regno_hard_regno (regno); |
| if (regno < 0) |
| return false; |
| alloc_regs = ~lra_no_alloc_regs; |
| return overlaps_hard_reg_set_p (alloc_regs, GET_MODE (x), regno); |
| } |
| else |
| { |
| if (regno < FIRST_PSEUDO_REGISTER) |
| return false; |
| if (! spilled_p) |
| return true; |
| return lra_get_regno_hard_regno (regno) < 0; |
| } |
| } |
| fmt = GET_RTX_FORMAT (code); |
| for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) |
| { |
| if (fmt[i] == 'e') |
| { |
| if (contains_reg_p (XEXP (x, i), hard_reg_p, spilled_p)) |
| return true; |
| } |
| else if (fmt[i] == 'E') |
| { |
| for (j = XVECLEN (x, i) - 1; j >= 0; j--) |
| if (contains_reg_p (XVECEXP (x, i, j), hard_reg_p, spilled_p)) |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /* Process all regs in location *LOC and change them on equivalent |
| substitution. Return true if any change was done. */ |
| static bool |
| loc_equivalence_change_p (rtx *loc) |
| { |
| rtx subst, reg, x = *loc; |
| bool result = false; |
| enum rtx_code code = GET_CODE (x); |
| const char *fmt; |
| int i, j; |
| |
| if (code == SUBREG) |
| { |
| reg = SUBREG_REG (x); |
| if ((subst = get_equiv_with_elimination (reg, curr_insn)) != reg |
| && GET_MODE (subst) == VOIDmode) |
| { |
| /* We cannot reload debug location. Simplify subreg here |
| while we know the inner mode. */ |
| *loc = simplify_gen_subreg (GET_MODE (x), subst, |
| GET_MODE (reg), SUBREG_BYTE (x)); |
| return true; |
| } |
| } |
| if (code == REG && (subst = get_equiv_with_elimination (x, curr_insn)) != x) |
| { |
| *loc = subst; |
| return true; |
| } |
| |
| /* Scan all the operand sub-expressions. */ |
| fmt = GET_RTX_FORMAT (code); |
| for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) |
| { |
| if (fmt[i] == 'e') |
| result = loc_equivalence_change_p (&XEXP (x, i)) || result; |
| else if (fmt[i] == 'E') |
| for (j = XVECLEN (x, i) - 1; j >= 0; j--) |
| result |
| = loc_equivalence_change_p (&XVECEXP (x, i, j)) || result; |
| } |
| return result; |
| } |
| |
| /* Similar to loc_equivalence_change_p, but for use as |
| simplify_replace_fn_rtx callback. DATA is insn for which the |
| elimination is done. If it null we don't do the elimination. */ |
| static rtx |
| loc_equivalence_callback (rtx loc, const_rtx, void *data) |
| { |
| if (!REG_P (loc)) |
| return NULL_RTX; |
| |
| rtx subst = (data == NULL |
| ? get_equiv (loc) : get_equiv_with_elimination (loc, (rtx_insn *) data)); |
| if (subst != loc) |
| return subst; |
| |
| return NULL_RTX; |
| } |
| |
| /* Maximum number of generated reload insns per an insn. It is for |
| preventing this pass cycling in a bug case. */ |
| #define MAX_RELOAD_INSNS_NUMBER LRA_MAX_INSN_RELOADS |
| |
| /* The current iteration number of this LRA pass. */ |
| int lra_constraint_iter; |
| |
| /* True if we should during assignment sub-pass check assignment |
| correctness for all pseudos and spill some of them to correct |
| conflicts. It can be necessary when we substitute equiv which |
| needs checking register allocation correctness because the |
| equivalent value contains allocatable hard registers, or when we |
| restore multi-register pseudo, or when we change the insn code and |
| its operand became INOUT operand when it was IN one before. */ |
| bool check_and_force_assignment_correctness_p; |
| |
| /* Return true if REGNO is referenced in more than one block. */ |
| static bool |
| multi_block_pseudo_p (int regno) |
| { |
| basic_block bb = NULL; |
| unsigned int uid; |
| bitmap_iterator bi; |
| |
| if (regno < FIRST_PSEUDO_REGISTER) |
| return false; |
| |
| EXECUTE_IF_SET_IN_BITMAP (&lra_reg_info[regno].insn_bitmap, 0, uid, bi) |
| if (bb == NULL) |
| bb = BLOCK_FOR_INSN (lra_insn_recog_data[uid]->insn); |
| else if (BLOCK_FOR_INSN (lra_insn_recog_data[uid]->insn) != bb) |
| return true; |
| return false; |
| } |
| |
| /* Return true if LIST contains a deleted insn. */ |
| static bool |
| contains_deleted_insn_p (rtx_insn_list *list) |
| { |
| for (; list != NULL_RTX; list = list->next ()) |
| if (NOTE_P (list->insn ()) |
| && NOTE_KIND (list->insn ()) == NOTE_INSN_DELETED) |
| return true; |
| return false; |
| } |
| |
| /* Return true if X contains a pseudo dying in INSN. */ |
| static bool |
| dead_pseudo_p (rtx x, rtx_insn *insn) |
| { |
| int i, j; |
| const char *fmt; |
| enum rtx_code code; |
| |
| if (REG_P (x)) |
| return (insn != NULL_RTX |
| && find_regno_note (insn, REG_DEAD, REGNO (x)) != NULL_RTX); |
| code = GET_CODE (x); |
| fmt = GET_RTX_FORMAT (code); |
| for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) |
| { |
| if (fmt[i] == 'e') |
| { |
| if (dead_pseudo_p (XEXP (x, i), insn)) |
| return true; |
| } |
| else if (fmt[i] == 'E') |
| { |
| for (j = XVECLEN (x, i) - 1; j >= 0; j--) |
| if (dead_pseudo_p (XVECEXP (x, i, j), insn)) |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /* Return true if INSN contains a dying pseudo in INSN right hand |
| side. */ |
| static bool |
| insn_rhs_dead_pseudo_p (rtx_insn *insn) |
| { |
| rtx set = single_set (insn); |
| |
| gcc_assert (set != NULL); |
| return dead_pseudo_p (SET_SRC (set), insn); |
| } |
| |
| /* Return true if any init insn of REGNO contains a dying pseudo in |
| insn right hand side. */ |
| static bool |
| init_insn_rhs_dead_pseudo_p (int regno) |
| { |
| rtx_insn_list *insns = ira_reg_equiv[regno].init_insns; |
| |
| if (insns == NULL) |
| return false; |
| for (; insns != NULL_RTX; insns = insns->next ()) |
| if (insn_rhs_dead_pseudo_p (insns->insn ())) |
| return true; |
| return false; |
| } |
| |
| /* Return TRUE if REGNO has a reverse equivalence. The equivalence is |
| reverse only if we have one init insn with given REGNO as a |
| source. */ |
| static bool |
| reverse_equiv_p (int regno) |
| { |
| rtx_insn_list *insns = ira_reg_equiv[regno].init_insns; |
| rtx set; |
| |
| if (insns == NULL) |
| return false; |
| if (! INSN_P (insns->insn ()) |
| || insns->next () != NULL) |
| return false; |
| if ((set = single_set (insns->insn ())) == NULL_RTX) |
| return false; |
| return REG_P (SET_SRC (set)) && (int) REGNO (SET_SRC (set)) == regno; |
| } |
| |
| /* Return TRUE if REGNO was reloaded in an equivalence init insn. We |
| call this function only for non-reverse equivalence. */ |
| static bool |
| contains_reloaded_insn_p (int regno) |
| { |
| rtx set; |
| rtx_insn_list *list = ira_reg_equiv[regno].init_insns; |
| |
| for (; list != NULL; list = list->next ()) |
| if ((set = single_set (list->insn ())) == NULL_RTX |
| || ! REG_P (SET_DEST (set)) |
| || (int) REGNO (SET_DEST (set)) != regno) |
| return true; |
| return false; |
| } |
| |
| /* Try combine secondary memory reload insn FROM for insn TO into TO insn. |
| FROM should be a load insn (usually a secondary memory reload insn). Return |
| TRUE in case of success. */ |
| static bool |
| combine_reload_insn (rtx_insn *from, rtx_insn *to) |
| { |
| bool ok_p; |
| rtx_insn *saved_insn; |
| rtx set, from_reg, to_reg, op; |
| enum reg_class to_class, from_class; |
| int n, nop; |
| signed char changed_nops[MAX_RECOG_OPERANDS + 1]; |
| |
| /* Check conditions for second memory reload and original insn: */ |
| if ((targetm.secondary_memory_needed |
| == hook_bool_mode_reg_class_t_reg_class_t_false) |
| || NEXT_INSN (from) != to |
| || !NONDEBUG_INSN_P (to) |
| || CALL_P (to)) |
| return false; |
| |
| lra_insn_recog_data_t id = lra_get_insn_recog_data (to); |
| struct lra_static_insn_data *static_id = id->insn_static_data; |
| |
| if (id->used_insn_alternative == LRA_UNKNOWN_ALT |
| || (set = single_set (from)) == NULL_RTX) |
| return false; |
| from_reg = SET_DEST (set); |
| to_reg = SET_SRC (set); |
| /* Ignore optional reloads: */ |
| if (! REG_P (from_reg) || ! REG_P (to_reg) |
| || bitmap_bit_p (&lra_optional_reload_pseudos, REGNO (from_reg))) |
| return false; |
| to_class = lra_get_allocno_class (REGNO (to_reg)); |
| from_class = lra_get_allocno_class (REGNO (from_reg)); |
| /* Check that reload insn is a load: */ |
| if (to_class != NO_REGS || from_class == NO_REGS) |
| return false; |
| for (n = nop = 0; nop < static_id->n_operands; nop++) |
| { |
| if (static_id->operand[nop].type != OP_IN) |
| continue; |
| op = *id->operand_loc[nop]; |
| if (!REG_P (op) || REGNO (op) != REGNO (from_reg)) |
| continue; |
| *id->operand_loc[nop] = to_reg; |
| changed_nops[n++] = nop; |
| } |
| changed_nops[n] = -1; |
| lra_update_dups (id, changed_nops); |
| lra_update_insn_regno_info (to); |
| ok_p = recog_memoized (to) >= 0; |
| if (ok_p) |
| { |
| /* Check that combined insn does not need any reloads: */ |
| saved_insn = curr_insn; |
| curr_insn = to; |
| curr_id = lra_get_insn_recog_data (curr_insn); |
| curr_static_id = curr_id->insn_static_data; |
| for (bool swapped_p = false;;) |
| { |
| ok_p = !curr_insn_transform (true); |
| if (ok_p || curr_static_id->commutative < 0) |
| break; |
| swap_operands (curr_static_id->commutative); |
| if (lra_dump_file != NULL) |
| { |
| fprintf (lra_dump_file, |
| " Swapping %scombined insn operands:\n", |
| swapped_p ? "back " : ""); |
| dump_insn_slim (lra_dump_file, to); |
| } |
| if (swapped_p) |
| break; |
| swapped_p = true; |
| } |
| curr_insn = saved_insn; |
| curr_id = lra_get_insn_recog_data (curr_insn); |
| curr_static_id = curr_id->insn_static_data; |
| } |
| if (ok_p) |
| { |
| id->used_insn_alternative = -1; |
| lra_push_insn_and_update_insn_regno_info (to); |
| if (lra_dump_file != NULL) |
| { |
| fprintf (lra_dump_file, " Use combined insn:\n"); |
| dump_insn_slim (lra_dump_file, to); |
| } |
| return true; |
| } |
| if (lra_dump_file != NULL) |
| { |
| fprintf (lra_dump_file, " Failed combined insn:\n"); |
| dump_insn_slim (lra_dump_file, to); |
| } |
| for (int i = 0; i < n; i++) |
| { |
| nop = changed_nops[i]; |
| *id->operand_loc[nop] = from_reg; |
| } |
| lra_update_dups (id, changed_nops); |
| lra_update_insn_regno_info (to); |
| if (lra_dump_file != NULL) |
| { |
| fprintf (lra_dump_file, " Restoring insn after failed combining:\n"); |
| dump_insn_slim (lra_dump_file, to); |
| } |
| return false; |
| } |
| |
| /* Entry function of LRA constraint pass. Return true if the |
| constraint pass did change the code. */ |
| bool |
| lra_constraints (bool first_p) |
| { |
| bool changed_p; |
| int i, hard_regno, new_insns_num; |
| unsigned int min_len, new_min_len, uid; |
| rtx set, x, reg, nosubreg_dest; |
| rtx_insn *original_insn; |
| basic_block last_bb; |
| bitmap_iterator bi; |
| |
| lra_constraint_iter++; |
| if (lra_dump_file != NULL) |
| fprintf (lra_dump_file, "\n********** Local #%d: **********\n\n", |
| lra_constraint_iter); |
| changed_p = false; |
| if (pic_offset_table_rtx |
| && REGNO (pic_offset_table_rtx) >= FIRST_PSEUDO_REGISTER) |
| check_and_force_assignment_correctness_p = true; |
| else if (first_p) |
| /* On the first iteration we should check IRA assignment |
| correctness. In rare cases, the assignments can be wrong as |
| early clobbers operands are ignored in IRA or usages of |
| paradoxical sub-registers are not taken into account by |
| IRA. */ |
| check_and_force_assignment_correctness_p = true; |
| new_insn_uid_start = get_max_uid (); |
| new_regno_start = first_p ? lra_constraint_new_regno_start : max_reg_num (); |
| /* Mark used hard regs for target stack size calulations. */ |
| for (i = FIRST_PSEUDO_REGISTER; i < new_regno_start; i++) |
| if (lra_reg_info[i].nrefs != 0 |
| && (hard_regno = lra_get_regno_hard_regno (i)) >= 0) |
| { |
| int j, nregs; |
| |
| nregs = hard_regno_nregs (hard_regno, lra_reg_info[i].biggest_mode); |
| for (j = 0; j < nregs; j++) |
| df_set_regs_ever_live (hard_regno + j, true); |
| } |
| /* Do elimination before the equivalence processing as we can spill |
| some pseudos during elimination. */ |
| lra_eliminate (false, first_p); |
| auto_bitmap equiv_insn_bitmap (®_obstack); |
| for (i = FIRST_PSEUDO_REGISTER; i < new_regno_start; i++) |
| if (lra_reg_info[i].nrefs != 0) |
| { |
| ira_reg_equiv[i].profitable_p = true; |
| reg = regno_reg_rtx[i]; |
| if (lra_get_regno_hard_regno (i) < 0 && (x = get_equiv (reg)) != reg) |
| { |
| bool pseudo_p = contains_reg_p (x, false, false); |
| |
| /* After RTL transformation, we cannot guarantee that |
| pseudo in the substitution was not reloaded which might |
| make equivalence invalid. For example, in reverse |
| equiv of p0 |
| |
| p0 <- ... |
| ... |
| equiv_mem <- p0 |
| |
| the memory address register was reloaded before the 2nd |
| insn. */ |
| if ((! first_p && pseudo_p) |
| /* We don't use DF for compilation speed sake. So it |
| is problematic to update live info when we use an |
| equivalence containing pseudos in more than one |
| BB. */ |
| || (pseudo_p && multi_block_pseudo_p (i)) |
| /* If an init insn was deleted for some reason, cancel |
| the equiv. We could update the equiv insns after |
| transformations including an equiv insn deletion |
| but it is not worthy as such cases are extremely |
| rare. */ |
| || contains_deleted_insn_p (ira_reg_equiv[i].init_insns) |
| /* If it is not a reverse equivalence, we check that a |
| pseudo in rhs of the init insn is not dying in the |
| insn. Otherwise, the live info at the beginning of |
| the corresponding BB might be wrong after we |
| removed the insn. When the equiv can be a |
| constant, the right hand side of the init insn can |
| be a pseudo. */ |
| || (! reverse_equiv_p (i) |
| && (init_insn_rhs_dead_pseudo_p (i) |
| /* If we reloaded the pseudo in an equivalence |
| init insn, we cannot remove the equiv init |
| insns and the init insns might write into |
| const memory in this case. */ |
| || contains_reloaded_insn_p (i))) |
| /* Prevent access beyond equivalent memory for |
| paradoxical subregs. */ |
| || (MEM_P (x) |
| && maybe_gt (GET_MODE_SIZE (lra_reg_info[i].biggest_mode), |
| GET_MODE_SIZE (GET_MODE (x)))) |
| || (pic_offset_table_rtx |
| && ((CONST_POOL_OK_P (PSEUDO_REGNO_MODE (i), x) |
| && (targetm.preferred_reload_class |
| (x, lra_get_allocno_class (i)) == NO_REGS)) |
| || contains_symbol_ref_p (x)))) |
| ira_reg_equiv[i].defined_p |
| = ira_reg_equiv[i].caller_save_p = false; |
| if (contains_reg_p (x, false, true)) |
| ira_reg_equiv[i].profitable_p = false; |
| if (get_equiv (reg) != reg) |
| bitmap_ior_into (equiv_insn_bitmap, &lra_reg_info[i].insn_bitmap); |
| } |
| } |
| for (i = FIRST_PSEUDO_REGISTER; i < new_regno_start; i++) |
| update_equiv (i); |
| /* We should add all insns containing pseudos which should be |
| substituted by their equivalences. */ |
| EXECUTE_IF_SET_IN_BITMAP (equiv_insn_bitmap, 0, uid, bi) |
| lra_push_insn_by_uid (uid); |
| min_len = lra_insn_stack_length (); |
| new_insns_num = 0; |
| last_bb = NULL; |
| changed_p = false; |
| original_insn = NULL; |
| while ((new_min_len = lra_insn_stack_length ()) != 0) |
| { |
| curr_insn = lra_pop_insn (); |
| --new_min_len; |
| curr_bb = BLOCK_FOR_INSN (curr_insn); |
| if (curr_bb != last_bb) |
| { |
| last_bb = curr_bb; |
| bb_reload_num = lra_curr_reload_num; |
| } |
| if (min_len > new_min_len) |
| { |
| min_len = new_min_len; |
| new_insns_num = 0; |
| original_insn = curr_insn; |
| } |
| else if (combine_reload_insn (curr_insn, original_insn)) |
| { |
| continue; |
| } |
| if (new_insns_num > MAX_RELOAD_INSNS_NUMBER) |
| internal_error |
| ("maximum number of generated reload insns per insn achieved (%d)", |
| MAX_RELOAD_INSNS_NUMBER); |
| new_insns_num++; |
| if (DEBUG_INSN_P (curr_insn)) |
| { |
| /* We need to check equivalence in debug insn and change |
| pseudo to the equivalent value if necessary. */ |
| curr_id = lra_get_insn_recog_data (curr_insn); |
| if (bitmap_bit_p (equiv_insn_bitmap, INSN_UID (curr_insn))) |
| { |
| rtx old = *curr_id->operand_loc[0]; |
| *curr_id->operand_loc[0] |
| = simplify_replace_fn_rtx (old, NULL_RTX, |
| loc_equivalence_callback, curr_insn); |
| if (old != *curr_id->operand_loc[0]) |
| { |
| /* If we substitute pseudo by shared equivalence, we can fail |
| to update LRA reg info and this can result in many |
| unexpected consequences. So keep rtl unshared: */ |
| *curr_id->operand_loc[0] |
| = copy_rtx (*curr_id->operand_loc[0]); |
| lra_update_insn_regno_info (curr_insn); |
| changed_p = true; |
| } |
| } |
| } |
| else if (INSN_P (curr_insn)) |
| { |
| if ((set = single_set (curr_insn)) != NULL_RTX) |
| { |
| nosubreg_dest = SET_DEST (set); |
| /* The equivalence pseudo could be set up as SUBREG in a |
| case when it is a call restore insn in a mode |
| different from the pseudo mode. */ |
| if (GET_CODE (nosubreg_dest) == SUBREG) |
| nosubreg_dest = SUBREG_REG (nosubreg_dest); |
| if ((REG_P (nosubreg_dest) |
| && (x = get_equiv (nosubreg_dest)) != nosubreg_dest |
| /* Remove insns which set up a pseudo whose value |
| cannot be changed. Such insns might be not in |
| init_insns because we don't update equiv data |
| during insn transformations. |
| |
| As an example, let suppose that a pseudo got |
| hard register and on the 1st pass was not |
| changed to equivalent constant. We generate an |
| additional insn setting up the pseudo because of |
| secondary memory movement. Then the pseudo is |
| spilled and we use the equiv constant. In this |
| case we should remove the additional insn and |
| this insn is not init_insns list. */ |
| && (! MEM_P (x) || MEM_READONLY_P (x) |
| /* Check that this is actually an insn setting |
| up the equivalence. */ |
| || in_list_p (curr_insn, |
| ira_reg_equiv |
| [REGNO (nosubreg_dest)].init_insns))) |
| || (((x = get_equiv (SET_SRC (set))) != SET_SRC (set)) |
| && in_list_p (curr_insn, |
| ira_reg_equiv |
| [REGNO (SET_SRC (set))].init_insns) |
| /* This is a reverse equivalence to memory (see ira.cc) |
| in store insn. We can reload all the destination and |
| have an output reload which is a store to memory. If |
| we just remove the insn, we will have the output |
| reload storing an undefined value to the memory. |
| Check that we did not reload the memory to prevent a |
| wrong code generation. We could implement using the |
| equivalence still in such case but doing this is not |
| worth the efforts as such case is very rare. */ |
| && MEM_P (nosubreg_dest))) |
| { |
| /* This is equiv init insn of pseudo which did not get a |
| hard register -- remove the insn. */ |
| if (lra_dump_file != NULL) |
| { |
| fprintf (lra_dump_file, |
| " Removing equiv init insn %i (freq=%d)\n", |
| INSN_UID (curr_insn), |
| REG_FREQ_FROM_BB (BLOCK_FOR_INSN (curr_insn))); |
| dump_insn_slim (lra_dump_file, curr_insn); |
| } |
| if (contains_reg_p (x, true, false)) |
| check_and_force_assignment_correctness_p = true; |
| lra_set_insn_deleted (curr_insn); |
| continue; |
| } |
| } |
| curr_id = lra_get_insn_recog_data (curr_insn); |
| curr_static_id = curr_id->insn_static_data; |
| init_curr_insn_input_reloads (); |
| init_curr_operand_mode (); |
| if (curr_insn_transform (false)) |
| changed_p = true; |
| /* Check non-transformed insns too for equiv change as USE |
| or CLOBBER don't need reloads but can contain pseudos |
| being changed on their equivalences. */ |
| else if (bitmap_bit_p (equiv_insn_bitmap, INSN_UID (curr_insn)) |
| && loc_equivalence_change_p (&PATTERN (curr_insn))) |
| { |
| lra_update_insn_regno_info (curr_insn); |
| changed_p = true; |
| } |
| } |
| } |
| |
| /* If we used a new hard regno, changed_p should be true because the |
| hard reg is assigned to a new pseudo. */ |
| if (flag_checking && !changed_p) |
| { |
| for (i = FIRST_PSEUDO_REGISTER; i < new_regno_start; i++) |
| if (lra_reg_info[i].nrefs != 0 |
| && (hard_regno = lra_get_regno_hard_regno (i)) >= 0) |
| { |
| int j, nregs = hard_regno_nregs (hard_regno, |
| PSEUDO_REGNO_MODE (i)); |
| |
| for (j = 0; j < nregs; j++) |
| lra_assert (df_regs_ever_live_p (hard_regno + j)); |
| } |
| } |
| if (changed_p) |
| lra_dump_insns_if_possible ("changed func after local"); |
| return changed_p; |
| } |
| |
| static void initiate_invariants (void); |
| static void finish_invariants (void); |
| |
| /* Initiate the LRA constraint pass. It is done once per |
| function. */ |
| void |
| lra_constraints_init (void) |
| { |
| initiate_invariants (); |
| } |
| |
| /* Finalize the LRA constraint pass. It is done once per |
| function. */ |
| void |
| lra_constraints_finish (void) |
| { |
| finish_invariants (); |
| } |
| |
| |
| |
| /* Structure describes invariants for ineheritance. */ |
| struct lra_invariant |
| { |
| /* The order number of the invariant. */ |
| int num; |
| /* The invariant RTX. */ |
| rtx invariant_rtx; |
| /* The origin insn of the invariant. */ |
| rtx_insn *insn; |
| }; |
| |
| typedef lra_invariant invariant_t; |
| typedef invariant_t *invariant_ptr_t; |
| typedef const invariant_t *const_invariant_ptr_t; |
| |
| /* Pointer to the inheritance invariants. */ |
| static vec<invariant_ptr_t> invariants; |
| |
| /* Allocation pool for the invariants. */ |
| static object_allocator<lra_invariant> *invariants_pool; |
| |
| /* Hash table for the invariants. */ |
| static htab_t invariant_table; |
| |
| /* Hash function for INVARIANT. */ |
| static hashval_t |
| invariant_hash (const void *invariant) |
| { |
| rtx inv = ((const_invariant_ptr_t) invariant)->invariant_rtx; |
| return lra_rtx_hash (inv); |
| } |
| |
| /* Equal function for invariants INVARIANT1 and INVARIANT2. */ |
| static int |
| invariant_eq_p (const void *invariant1, const void *invariant2) |
| { |
| rtx inv1 = ((const_invariant_ptr_t) invariant1)->invariant_rtx; |
| rtx inv2 = ((const_invariant_ptr_t) invariant2)->invariant_rtx; |
| |
| return rtx_equal_p (inv1, inv2); |
| } |
| |
| /* Insert INVARIANT_RTX into the table if it is not there yet. Return |
| invariant which is in the table. */ |
| static invariant_ptr_t |
| insert_invariant (rtx invariant_rtx) |
| { |
| void **entry_ptr; |
| invariant_t invariant; |
| invariant_ptr_t invariant_ptr; |
| |
| invariant.invariant_rtx = invariant_rtx; |
| entry_ptr = htab_find_slot (invariant_table, &invariant, INSERT); |
| if (*entry_ptr == NULL) |
| { |
| invariant_ptr = invariants_pool->allocate (); |
| invariant_ptr->invariant_rtx = invariant_rtx; |
| invariant_ptr->insn = NULL; |
| invariants.safe_push (invariant_ptr); |
| *entry_ptr = (void *) invariant_ptr; |
| } |
| return (invariant_ptr_t) *entry_ptr; |
| } |
| |
| /* Initiate the invariant table. */ |
| static void |
| initiate_invariants (void) |
| { |
| invariants.create (100); |
| invariants_pool |
| = new object_allocator<lra_invariant> ("Inheritance invariants"); |
| invariant_table = htab_create (100, invariant_hash, invariant_eq_p, NULL); |
| } |
| |
| /* Finish the invariant table. */ |
| static void |
| finish_invariants (void) |
| { |
| htab_delete (invariant_table); |
| delete invariants_pool; |
| invariants.release (); |
| } |
| |
| /* Make the invariant table empty. */ |
| static void |
| clear_invariants (void) |
| { |
| htab_empty (invariant_table); |
| invariants_pool->release (); |
| invariants.truncate (0); |
| } |
| |
| |
| |
| /* This page contains code to do inheritance/split |
| transformations. */ |
| |
| /* Number of reloads passed so far in current EBB. */ |
| static int reloads_num; |
| |
| /* Number of calls passed so far in current EBB. */ |
| static int calls_num; |
| |
| /* Index ID is the CALLS_NUM associated the last call we saw with |
| ABI identifier ID. */ |
| static int last_call_for_abi[NUM_ABI_IDS]; |
| |
| /* Which registers have been fully or partially clobbered by a call |
| since they were last used. */ |
| static HARD_REG_SET full_and_partial_call_clobbers; |
| |
| /* Current reload pseudo check for validity of elements in |
| USAGE_INSNS. */ |
| static int curr_usage_insns_check; |
| |
| /* Info about last usage of registers in EBB to do inheritance/split |
| transformation. Inheritance transformation is done from a spilled |
| pseudo and split transformations from a hard register or a pseudo |
| assigned to a hard register. */ |
| struct usage_insns |
| { |
| /* If the value is equal to CURR_USAGE_INSNS_CHECK, then the member |
| value INSNS is valid. The insns is chain of optional debug insns |
| and a finishing non-debug insn using the corresponding reg. The |
| value is also used to mark the registers which are set up in the |
| current insn. The negated insn uid is used for this. */ |
| int check; |
| /* Value of global reloads_num at the last insn in INSNS. */ |
| int reloads_num; |
| /* Value of global reloads_nums at the last insn in INSNS. */ |
| int calls_num; |
| /* It can be true only for splitting. And it means that the restore |
| insn should be put after insn given by the following member. */ |
| bool after_p; |
| /* Next insns in the current EBB which use the original reg and the |
| original reg value is not changed between the current insn and |
| the next insns. In order words, e.g. for inheritance, if we need |
| to use the original reg value again in the next insns we can try |
| to use the value in a hard register from a reload insn of the |
| current insn. */ |
| rtx insns; |
| }; |
| |
| /* Map: regno -> corresponding pseudo usage insns. */ |
| static struct usage_insns *usage_insns; |
| |
| static void |
| setup_next_usage_insn (int regno, rtx insn, int reloads_num, bool after_p) |
| { |
| usage_insns[regno].check = curr_usage_insns_check; |
| usage_insns[regno].insns = insn; |
| usage_insns[regno].reloads_num = reloads_num; |
| usage_insns[regno].calls_num = calls_num; |
| usage_insns[regno].after_p = after_p; |
| if (regno >= FIRST_PSEUDO_REGISTER && reg_renumber[regno] >= 0) |
| remove_from_hard_reg_set (&full_and_partial_call_clobbers, |
| PSEUDO_REGNO_MODE (regno), |
| reg_renumber[regno]); |
| } |
| |
| /* The function is used to form list REGNO usages which consists of |
| optional debug insns finished by a non-debug insn using REGNO. |
| RELOADS_NUM is current number of reload insns processed so far. */ |
| static void |
| add_next_usage_insn (int regno, rtx_insn *insn, int reloads_num) |
| { |
| rtx next_usage_insns; |
| |
| if (usage_insns[regno].check == curr_usage_insns_check |
| && (next_usage_insns = usage_insns[regno].insns) != NULL_RTX |
| && DEBUG_INSN_P (insn)) |
| { |
| /* Check that we did not add the debug insn yet. */ |
| if (next_usage_insns != insn |
| && (GET_CODE (next_usage_insns) != INSN_LIST |
| || XEXP (next_usage_insns, 0) != insn)) |
| usage_insns[regno].insns = gen_rtx_INSN_LIST (VOIDmode, insn, |
| next_usage_insns); |
| } |
| else if (NONDEBUG_INSN_P (insn)) |
| setup_next_usage_insn (regno, insn, reloads_num, false); |
| else |
| usage_insns[regno].check = 0; |
| } |
| |
| /* Return first non-debug insn in list USAGE_INSNS. */ |
| static rtx_insn * |
| skip_usage_debug_insns (rtx usage_insns) |
| { |
| rtx insn; |
| |
| /* Skip debug insns. */ |
| for (insn = usage_insns; |
| insn != NULL_RTX && GET_CODE (insn) == INSN_LIST; |
| insn = XEXP (insn, 1)) |
| ; |
| return safe_as_a <rtx_insn *> (insn); |
| } |
| |
| /* Return true if we need secondary memory moves for insn in |
| USAGE_INSNS after inserting inherited pseudo of class INHER_CL |
| into the insn. */ |
| static bool |
| check_secondary_memory_needed_p (enum reg_class inher_cl ATTRIBUTE_UNUSED, |
| rtx usage_insns ATTRIBUTE_UNUSED) |
| { |
| rtx_insn *insn; |
| rtx set, dest; |
| enum reg_class cl; |
| |
| if (inher_cl == ALL_REGS |
| || (insn = skip_usage_debug_insns (usage_insns)) == NULL_RTX) |
| return false; |
| lra_assert (INSN_P (insn)); |
| if ((set = single_set (insn)) == NULL_RTX || ! REG_P (SET_DEST (set))) |
| return false; |
| dest = SET_DEST (set); |
| if (! REG_P (dest)) |
| return false; |
| lra_assert (inher_cl != NO_REGS); |
| cl = get_reg_class (REGNO (dest)); |
| return (cl != NO_REGS && cl != ALL_REGS |
| && targetm.secondary_memory_needed (GET_MODE (dest), inher_cl, cl)); |
| } |
| |
| /* Registers involved in inheritance/split in the current EBB |
| (inheritance/split pseudos and original registers). */ |
| static bitmap_head check_only_regs; |
| |
| /* Reload pseudos cannot be involded in invariant inheritance in the |
| current EBB. */ |
| static bitmap_head invalid_invariant_regs; |
| |
| /* Do inheritance transformations for insn INSN, which defines (if |
| DEF_P) or uses ORIGINAL_REGNO. NEXT_USAGE_INSNS specifies which |
| instruction in the EBB next uses ORIGINAL_REGNO; it has the same |
| form as the "insns" field of usage_insns. Return true if we |
| succeed in such transformation. |
| |
| The transformations look like: |
| |
| p <- ... i <- ... |
| ... p <- i (new insn) |
| ... => |
| <- ... p ... <- ... i ... |
| or |
| ... i <- p (new insn) |
| <- ... p ... <- ... i ... |
| ... => |
| <- ... p ... <- ... i ... |
| where p is a spilled original pseudo and i is a new inheritance pseudo. |
| |
| |
| The inheritance pseudo has the smallest class of two classes CL and |
| class of ORIGINAL REGNO. */ |
| static bool |
| inherit_reload_reg (bool def_p, int original_regno, |
| enum reg_class cl, rtx_insn *insn, rtx next_usage_insns) |
| { |
| if (optimize_function_for_size_p (cfun)) |
| return false; |
| |
| enum reg_class rclass = lra_get_allocno_class (original_regno); |
| rtx original_reg = regno_reg_rtx[original_regno]; |
| rtx new_reg, usage_insn; |
| rtx_insn *new_insns; |
| |
| lra_assert (! usage_insns[original_regno].after_p); |
| if (lra_dump_file != NULL) |
| fprintf (lra_dump_file, |
| " <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"); |
| if (! ira_reg_classes_intersect_p[cl][rclass]) |
| { |
| if (lra_dump_file != NULL) |
| { |
| fprintf (lra_dump_file, |
| " Rejecting inheritance for %d " |
| "because of disjoint classes %s and %s\n", |
| original_regno, reg_class_names[cl], |
| reg_class_names[rclass]); |
| fprintf (lra_dump_file, |
| " >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"); |
| } |
| return false; |
| } |
| if ((ira_class_subset_p[cl][rclass] && cl != rclass) |
| /* We don't use a subset of two classes because it can be |
| NO_REGS. This transformation is still profitable in most |
| cases even if the classes are not intersected as register |
| move is probably cheaper than a memory load. */ |
| || ira_class_hard_regs_num[cl] < ira_class_hard_regs_num[rclass]) |
| { |
| if (lra_dump_file != NULL) |
| fprintf (lra_dump_file, " Use smallest class of %s and %s\n", |
| reg_class_names[cl], reg_class_names[rclass]); |
| |
| rclass = cl; |
| } |
| if (check_secondary_memory_needed_p (rclass, next_usage_insns)) |
| { |
| /* Reject inheritance resulting in secondary memory moves. |
| Otherwise, there is a danger in LRA cycling. Also such |
| transformation will be unprofitable. */ |
| if (lra_dump_file != NULL) |
| { |
| rtx_insn *insn = skip_usage_debug_insns (next_usage_insns); |
| rtx set = single_set (insn); |
| |
| lra_assert (set != NULL_RTX); |
| |
| rtx dest = SET_DEST (set); |
| |
| lra_assert (REG_P (dest)); |
| fprintf (lra_dump_file, |
| " Rejecting inheritance for insn %d(%s)<-%d(%s) " |
| "as secondary mem is needed\n", |
| REGNO (dest), reg_class_names[get_reg_class (REGNO (dest))], |
| original_regno, reg_class_names[rclass]); |
| fprintf (lra_dump_file, |
| " >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"); |
| } |
| return false; |
| } |
| new_reg = lra_create_new_reg (GET_MODE (original_reg), original_reg, |
| rclass, NULL, "inheritance"); |
| start_sequence (); |
| if (def_p) |
| lra_emit_move (original_reg, new_reg); |
| else |
| lra_emit_move (new_reg, original_reg); |
| new_insns = get_insns (); |
| end_sequence (); |
| if (NEXT_INSN (new_insns) != NULL_RTX) |
| { |
| if (lra_dump_file != NULL) |
| { |
| fprintf (lra_dump_file, |
| " Rejecting inheritance %d->%d " |
| "as it results in 2 or more insns:\n", |
| original_regno, REGNO (new_reg)); |
| dump_rtl_slim (lra_dump_file, new_insns, NULL, -1, 0); |
| fprintf (lra_dump_file, |
| " >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"); |
| } |
| return false; |
| } |
| lra_substitute_pseudo_within_insn (insn, original_regno, new_reg, false); |
| lra_update_insn_regno_info (insn); |
| if (! def_p) |
| /* We now have a new usage insn for original regno. */ |
| setup_next_usage_insn (original_regno, new_insns, reloads_num, false); |
| if (lra_dump_file != NULL) |
| fprintf (lra_dump_file, " Original reg change %d->%d (bb%d):\n", |
| original_regno, REGNO (new_reg), BLOCK_FOR_INSN (insn)->index); |
| lra_reg_info[REGNO (new_reg)].restore_rtx = regno_reg_rtx[original_regno]; |
| bitmap_set_bit (&check_only_regs, REGNO (new_reg)); |
| bitmap_set_bit (&check_only_regs, original_regno); |
| bitmap_set_bit (&lra_inheritance_pseudos, REGNO (new_reg)); |
| if (def_p) |
| lra_process_new_insns (insn, NULL, new_insns, |
| "Add original<-inheritance"); |
| else |
| lra_process_new_insns (insn, new_insns, NULL, |
| "Add inheritance<-original"); |
| while (next_usage_insns != NULL_RTX) |
| { |
| if (GET_CODE (next_usage_insns) != INSN_LIST) |
| { |
| usage_insn = next_usage_insns; |
| lra_assert (NONDEBUG_INSN_P (usage_insn)); |
| next_usage_insns = NULL; |
| } |
| else |
| { |
| usage_insn = XEXP (next_usage_insns, 0); |
| lra_assert (DEBUG_INSN_P (usage_insn)); |
| next_usage_insns = XEXP (next_usage_insns, 1); |
| } |
| lra_substitute_pseudo (&usage_insn, original_regno, new_reg, false, |
| DEBUG_INSN_P (usage_insn)); |
| lra_update_insn_regno_info (as_a <rtx_insn *> (usage_insn)); |
| if (lra_dump_file != NULL) |
| { |
| basic_block bb = BLOCK_FOR_INSN (usage_insn); |
| fprintf (lra_dump_file, |
| " Inheritance reuse change %d->%d (bb%d):\n", |
| original_regno, REGNO (new_reg), |
| bb ? bb->index : -1); |
| dump_insn_slim (lra_dump_file, as_a <rtx_insn *> (usage_insn)); |
| } |
| } |
| if (lra_dump_file != NULL) |
| fprintf (lra_dump_file, |
| " >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"); |
| return true; |
| } |
| |
| /* Return true if we need a caller save/restore for pseudo REGNO which |
| was assigned to a hard register. */ |
| static inline bool |
| need_for_call_save_p (int regno) |
| { |
| lra_assert (regno >= FIRST_PSEUDO_REGISTER && reg_renumber[regno] >= 0); |
| if (usage_insns[regno].calls_num < calls_num) |
| { |
| unsigned int abis = 0; |
| for (unsigned int i = 0; i < NUM_ABI_IDS; ++i) |
| if (last_call_for_abi[i] > usage_insns[regno].calls_num) |
| abis |= 1 << i; |
| gcc_assert (abis); |
| if (call_clobbered_in_region_p (abis, full_and_partial_call_clobbers, |
| PSEUDO_REGNO_MODE (regno), |
| reg_renumber[regno])) |
| return true; |
| } |
| return false; |
| } |
| |
| /* Global registers occurring in the current EBB. */ |
| static bitmap_head ebb_global_regs; |
| |
| /* Return true if we need a split for hard register REGNO or pseudo |
| REGNO which was assigned to a hard register. |
| POTENTIAL_RELOAD_HARD_REGS contains hard registers which might be |
| used for reloads since the EBB end. It is an approximation of the |
| used hard registers in the split range. The exact value would |
| require expensive calculations. If we were aggressive with |
| splitting because of the approximation, the split pseudo will save |
| the same hard register assignment and will be removed in the undo |
| pass. We still need the approximation because too aggressive |
| splitting would result in too inaccurate cost calculation in the |
| assignment pass because of too many generated moves which will be |
| probably removed in the undo pass. */ |
| static inline bool |
| need_for_split_p (HARD_REG_SET potential_reload_hard_regs, int regno) |
| { |
| int hard_regno = regno < FIRST_PSEUDO_REGISTER ? regno : reg_renumber[regno]; |
| |
| lra_assert (hard_regno >= 0); |
| return ((TEST_HARD_REG_BIT (potential_reload_hard_regs, hard_regno) |
| /* Don't split eliminable hard registers, otherwise we can |
| split hard registers like hard frame pointer, which |
| lives on BB start/end according to DF-infrastructure, |
| when there is a pseudo assigned to the register and |
| living in the same BB. */ |
| && (regno >= FIRST_PSEUDO_REGISTER |
| || ! TEST_HARD_REG_BIT (eliminable_regset, hard_regno)) |
| && ! TEST_HARD_REG_BIT (lra_no_alloc_regs, hard_regno) |
| /* Don't split call clobbered hard regs living through |
| calls, otherwise we might have a check problem in the |
| assign sub-pass as in the most cases (exception is a |
| situation when check_and_force_assignment_correctness_p value is |
| true) the assign pass assumes that all pseudos living |
| through calls are assigned to call saved hard regs. */ |
| && (regno >= FIRST_PSEUDO_REGISTER |
| || !TEST_HARD_REG_BIT (full_and_partial_call_clobbers, regno)) |
| /* We need at least 2 reloads to make pseudo splitting |
| profitable. We should provide hard regno splitting in |
| any case to solve 1st insn scheduling problem when |
| moving hard register definition up might result in |
| impossibility to find hard register for reload pseudo of |
| small register class. */ |
| && (usage_insns[regno].reloads_num |
| + (regno < FIRST_PSEUDO_REGISTER ? 0 : 3) < reloads_num) |
| && (regno < FIRST_PSEUDO_REGISTER |
| /* For short living pseudos, spilling + inheritance can |
| be considered a substitution for splitting. |
| Therefore we do not splitting for local pseudos. It |
| decreases also aggressiveness of splitting. The |
| minimal number of references is chosen taking into |
| account that for 2 references splitting has no sense |
| as we can just spill the pseudo. */ |
| || (regno >= FIRST_PSEUDO_REGISTER |
| && lra_reg_info[regno].nrefs > 3 |
| && bitmap_bit_p (&ebb_global_regs, regno)))) |
| || (regno >= FIRST_PSEUDO_REGISTER && need_for_call_save_p (regno))); |
| } |
| |
| /* Return class for the split pseudo created from original pseudo with |
| ALLOCNO_CLASS and MODE which got a hard register HARD_REGNO. We |
| choose subclass of ALLOCNO_CLASS which contains HARD_REGNO and |
| results in no secondary memory movements. */ |
| static enum reg_class |
| choose_split_class (enum reg_class allocno_class, |
| int hard_regno ATTRIBUTE_UNUSED, |
| machine_mode mode ATTRIBUTE_UNUSED) |
| { |
| int i; |
| enum reg_class cl, best_cl = NO_REGS; |
| enum reg_class hard_reg_class ATTRIBUTE_UNUSED |
| = REGNO_REG_CLASS (hard_regno); |
| |
| if (! targetm.secondary_memory_needed (mode, allocno_class, allocno_class) |
| && TEST_HARD_REG_BIT (reg_class_contents[allocno_class], hard_regno)) |
| return allocno_class; |
| for (i = 0; |
| (cl = reg_class_subclasses[allocno_class][i]) != LIM_REG_CLASSES; |
| i++) |
| if (! targetm.secondary_memory_needed (mode, cl, hard_reg_class) |
| && ! targetm.secondary_memory_needed (mode, hard_reg_class, cl) |
| && TEST_HARD_REG_BIT (reg_class_contents[cl], hard_regno) |
| && (best_cl == NO_REGS |
| || ira_class_hard_regs_num[best_cl] < ira_class_hard_regs_num[cl])) |
| best_cl = cl; |
| return best_cl; |
| } |
| |
| /* Copy any equivalence information from ORIGINAL_REGNO to NEW_REGNO. It only |
| makes sense to call this function if NEW_REGNO is always equal to |
| ORIGINAL_REGNO. Set up defined_p flag when caller_save_p flag is set up and |
| CALL_SAVE_P is true. */ |
| |
| static void |
| lra_copy_reg_equiv (unsigned int new_regno, unsigned int original_regno, |
| bool call_save_p) |
| { |
| if (!ira_reg_equiv[original_regno].defined_p |
| && !(call_save_p && ira_reg_equiv[original_regno].caller_save_p)) |
| return; |
| |
| ira_expand_reg_equiv (); |
| ira_reg_equiv[new_regno].defined_p = true; |
| if (ira_reg_equiv[original_regno].memory) |
| ira_reg_equiv[new_regno].memory |
| = copy_rtx (ira_reg_equiv[original_regno].memory); |
| if (ira_reg_equiv[original_regno].constant) |
| ira_reg_equiv[new_regno].constant |
| = copy_rtx (ira_reg_equiv[original_regno].constant); |
| if (ira_reg_equiv[original_regno].invariant) |
| ira_reg_equiv[new_regno].invariant |
| = copy_rtx (ira_reg_equiv[original_regno].invariant); |
| } |
| |
| /* Do split transformations for insn INSN, which defines or uses |
| ORIGINAL_REGNO. NEXT_USAGE_INSNS specifies which instruction in |
| the EBB next uses ORIGINAL_REGNO; it has the same form as the |
| "insns" field of usage_insns. If TO is not NULL, we don't use |
| usage_insns, we put restore insns after TO insn. It is a case when |
| we call it from lra_split_hard_reg_for, outside the inheritance |
| pass. |
| |
| The transformations look like: |
| |
| p <- ... p <- ... |
| ... s <- p (new insn -- save) |
| ... => |
| ... p <- s (new insn -- restore) |
| <- ... p ... <- ... p ... |
| or |
| <- ... p ... <- ... p ... |
| ... s <- p (new insn -- save) |
| ... => |
| ... p <- s (new insn -- restore) |
| <- ... p ... <- ... p ... |
| |
| where p is an original pseudo got a hard register or a hard |
| register and s is a new split pseudo. The save is put before INSN |
| if BEFORE_P is true. Return true if we succeed in such |
| transformation. */ |
| static bool |
| split_reg (bool before_p, int original_regno, rtx_insn *insn, |
| rtx next_usage_insns, rtx_insn *to) |
| { |
| enum reg_class rclass; |
| rtx original_reg; |
| int hard_regno, nregs; |
| rtx new_reg, usage_insn; |
| rtx_insn *restore, *save; |
| bool after_p; |
| bool call_save_p; |
| machine_mode mode; |
| |
| if (original_regno < FIRST_PSEUDO_REGISTER) |
| { |
| rclass = ira_allocno_class_translate[REGNO_REG_CLASS (original_regno)]; |
| hard_regno = original_regno; |
| call_save_p = false; |
| nregs = 1; |
| mode = lra_reg_info[hard_regno].biggest_mode; |
| machine_mode reg_rtx_mode = GET_MODE (regno_reg_rtx[hard_regno]); |
| /* A reg can have a biggest_mode of VOIDmode if it was only ever seen as |
| part of a multi-word register. In that case, just use the reg_rtx |
| mode. Do the same also if the biggest mode was larger than a register |
| or we can not compare the modes. Otherwise, limit the size to that of |
| the biggest access in the function or to the natural mode at least. */ |
| if (mode == VOIDmode |
| || !ordered_p (GET_MODE_PRECISION (mode), |
| GET_MODE_PRECISION (reg_rtx_mode)) |
| || paradoxical_subreg_p (mode, reg_rtx_mode) |
| || maybe_gt (GET_MODE_PRECISION (reg_rtx_mode), GET_MODE_PRECISION (mode))) |
| { |
| original_reg = regno_reg_rtx[hard_regno]; |
| mode = reg_rtx_mode; |
| } |
| else |
| original_reg = gen_rtx_REG (mode, hard_regno); |
| } |
| else |
| { |
| mode = PSEUDO_REGNO_MODE (original_regno); |
| hard_regno = reg_renumber[original_regno]; |
| nregs = hard_regno_nregs (hard_regno, mode); |
| rclass = lra_get_allocno_class (original_regno); |
| original_reg = regno_reg_rtx[original_regno]; |
| call_save_p = need_for_call_save_p (original_regno); |
| } |
| lra_assert (hard_regno >= 0); |
| if (lra_dump_file != NULL) |
| fprintf (lra_dump_file, |
| " ((((((((((((((((((((((((((((((((((((((((((((((((\n"); |
| |
| if (call_save_p) |
| { |
| mode = HARD_REGNO_CALLER_SAVE_MODE (hard_regno, |
| hard_regno_nregs (hard_regno, mode), |
| mode); |
| new_reg = lra_create_new_reg (mode, NULL_RTX, NO_REGS, NULL, "save"); |
| } |
| else |
| { |
| rclass = choose_split_class (rclass, hard_regno, mode); |
| if (rclass == NO_REGS) |
| { |
| if (lra_dump_file != NULL) |
| { |
| fprintf (lra_dump_file, |
| " Rejecting split of %d(%s): " |
| "no good reg class for %d(%s)\n", |
| original_regno, |
| reg_class_names[lra_get_allocno_class (original_regno)], |
| hard_regno, |
| reg_class_names[REGNO_REG_CLASS (hard_regno)]); |
| fprintf |
| (lra_dump_file, |
| " ))))))))))))))))))))))))))))))))))))))))))))))))\n"); |
| } |
| return false; |
| } |
| /* Split_if_necessary can split hard registers used as part of a |
| multi-register mode but splits each register individually. The |
| mode used for each independent register may not be supported |
| so reject the split. Splitting the wider mode should theoretically |
| be possible but is not implemented. */ |
| if (!targetm.hard_regno_mode_ok (hard_regno, mode)) |
| { |
| if (lra_dump_file != NULL) |
| { |
| fprintf (lra_dump_file, |
| " Rejecting split of %d(%s): unsuitable mode %s\n", |
| original_regno, |
| reg_class_names[lra_get_allocno_class (original_regno)], |
| GET_MODE_NAME (mode)); |
| fprintf |
| (lra_dump_file, |
| " ))))))))))))))))))))))))))))))))))))))))))))))))\n"); |
| } |
| return false; |
| } |
| new_reg = lra_create_new_reg (mode, original_reg, rclass, NULL, "split"); |
| reg_renumber[REGNO (new_reg)] = hard_regno; |
| } |
| int new_regno = REGNO (new_reg); |
| save = emit_spill_move (true, new_reg, original_reg); |
| if (NEXT_INSN (save) != NULL_RTX && !call_save_p) |
| { |
| if (lra_dump_file != NULL) |
| { |
| fprintf |
| (lra_dump_file, |
| " Rejecting split %d->%d resulting in > 2 save insns:\n", |
| original_regno, new_regno); |
| dump_rtl_slim (lra_dump_file, save, NULL, -1, 0); |
| fprintf (lra_dump_file, |
| " ))))))))))))))))))))))))))))))))))))))))))))))))\n"); |
| } |
| return false; |
| } |
| restore = emit_spill_move (false, new_reg, original_reg); |
| if (NEXT_INSN (restore) != NULL_RTX && !call_save_p) |
| { |
| if (lra_dump_file != NULL) |
| { |
| fprintf (lra_dump_file, |
| " Rejecting split %d->%d " |
| "resulting in > 2 restore insns:\n", |
| original_regno, new_regno); |
| dump_rtl_slim (lra_dump_file, restore, NULL, -1, 0); |
| fprintf (lra_dump_file, |
| " ))))))))))))))))))))))))))))))))))))))))))))))))\n"); |
| } |
| return false; |
| } |
| /* Transfer equivalence information to the spill register, so that |
| if we fail to allocate the spill register, we have the option of |
| rematerializing the original value instead of spilling to the stack. */ |
| if (!HARD_REGISTER_NUM_P (original_regno) |
| && mode == PSEUDO_REGNO_MODE (original_regno)) |
| lra_copy_reg_equiv (new_regno, original_regno, call_save_p); |
| lra_reg_info[new_regno].restore_rtx = regno_reg_rtx[original_regno]; |
| bitmap_set_bit (&lra_split_regs, new_regno); |
| if (to != NULL) |
| { |
| lra_assert (next_usage_insns == NULL); |
| usage_insn = to; |
| after_p = true; |
| } |
| else |
| { |
| /* We need check_only_regs only inside the inheritance pass. */ |
| bitmap_set_bit (&check_only_regs, new_regno); |
| bitmap_set_bit (&check_only_regs, original_regno); |
| after_p = usage_insns[original_regno].after_p; |
| for (;;) |
| { |
| if (GET_CODE (next_usage_insns) != INSN_LIST) |
| { |
| usage_insn = next_usage_insns; |
| break; |
| } |
| usage_insn = XEXP (next_usage_insns, 0); |
| lra_assert (DEBUG_INSN_P (usage_insn)); |
| next_usage_insns = XEXP (next_usage_insns, 1); |
| lra_substitute_pseudo (&usage_insn, original_regno, new_reg, false, |
| true); |
| lra_update_insn_regno_info (as_a <rtx_insn *> (usage_insn)); |
| if (lra_dump_file != NULL) |
| { |
| fprintf (lra_dump_file, " Split reuse change %d->%d:\n", |
| original_regno, new_regno); |
| dump_insn_slim (lra_dump_file, as_a <rtx_insn *> (usage_insn)); |
| } |
| } |
| } |
| lra_assert (NOTE_P (usage_insn) || NONDEBUG_INSN_P (usage_insn)); |
| lra_assert (usage_insn != insn || (after_p && before_p)); |
| lra_process_new_insns (as_a <rtx_insn *> (usage_insn), |
| after_p ? NULL : restore, |
| after_p ? restore : NULL, |
| call_save_p |
| ? "Add reg<-save" : "Add reg<-split"); |
| lra_process_new_insns (insn, before_p ? save : NULL, |
| before_p ? NULL : save, |
| call_save_p |
| ? "Add save<-reg" : "Add split<-reg"); |
| if (nregs > 1 || original_regno < FIRST_PSEUDO_REGISTER) |
| /* If we are trying to split multi-register. We should check |
| conflicts on the next assignment sub-pass. IRA can allocate on |
| sub-register levels, LRA do this on pseudos level right now and |
| this discrepancy may create allocation conflicts after |
| splitting. |
| |
| If we are trying to split hard register we should also check conflicts |
| as such splitting can create artificial conflict of the hard register |
| with another pseudo because of simplified conflict calculation in |
| LRA. */ |
| check_and_force_assignment_correctness_p = true; |
| if (lra_dump_file != NULL) |
| fprintf (lra_dump_file, |
| " ))))))))))))))))))))))))))))))))))))))))))))))))\n"); |
| return true; |
| } |
| |
| /* Split a hard reg for reload pseudo REGNO having RCLASS and living |
| in the range [FROM, TO]. Return true if did a split. Otherwise, |
| return false. */ |
| bool |
| spill_hard_reg_in_range (int regno, enum reg_class rclass, rtx_insn *from, rtx_insn *to) |
| { |
| int i, hard_regno; |
| int rclass_size; |
| rtx_insn *insn; |
| unsigned int uid; |
| bitmap_iterator bi; |
| HARD_REG_SET ignore; |
| |
| lra_assert (from != NULL && to != NULL); |
| ignore = lra_no_alloc_regs; |
| EXECUTE_IF_SET_IN_BITMAP (&lra_reg_info[regno].insn_bitmap, 0, uid, bi) |
| { |
| lra_insn_recog_data_t id = lra_insn_recog_data[uid]; |
| struct lra_static_insn_data *static_id = id->insn_static_data; |
| struct lra_insn_reg *reg; |
| |
| for (reg = id->regs; reg != NULL; reg = reg->next) |
| if (reg->regno < FIRST_PSEUDO_REGISTER) |
| SET_HARD_REG_BIT (ignore, reg->regno); |
| for (reg = static_id->hard_regs; reg != NULL; reg = reg->next) |
| SET_HARD_REG_BIT (ignore, reg->regno); |
| } |
| rclass_size = ira_class_hard_regs_num[rclass]; |
| for (i = 0; i < rclass_size; i++) |
| { |
| hard_regno = ira_class_hard_regs[rclass][i]; |
| if (! TEST_HARD_REG_BIT (lra_reg_info[regno].conflict_hard_regs, hard_regno) |
| || TEST_HARD_REG_BIT (ignore, hard_regno)) |
| continue; |
| for (insn = from; insn != NEXT_INSN (to); insn = NEXT_INSN (insn)) |
| { |
| struct lra_static_insn_data *static_id; |
| struct lra_insn_reg *reg; |
| |
| if (!INSN_P (insn)) |
| continue; |
| if (bitmap_bit_p (&lra_reg_info[hard_regno].insn_bitmap, |
| INSN_UID (insn))) |
| break; |
| static_id = lra_get_insn_recog_data (insn)->insn_static_data; |
| for (reg = static_id->hard_regs; reg != NULL; reg = reg->next) |
| if (reg->regno == hard_regno) |
| break; |
| if (reg != NULL) |
| break; |
| } |
| if (insn != NEXT_INSN (to)) |
| continue; |
| if (split_reg (true, hard_regno, from, NULL, to)) |
| return true; |
| } |
| return false; |
| } |
| |
| /* Recognize that we need a split transformation for insn INSN, which |
| defines or uses REGNO in its insn biggest MODE (we use it only if |
| REGNO is a hard register). POTENTIAL_RELOAD_HARD_REGS contains |
| hard registers which might be used for reloads since the EBB end. |
| Put the save before INSN if BEFORE_P is true. MAX_UID is maximla |
| uid before starting INSN processing. Return true if we succeed in |
| such transformation. */ |
| static bool |
| split_if_necessary (int regno, machine_mode mode, |
| HARD_REG_SET potential_reload_hard_regs, |
| bool before_p, rtx_insn *insn, int max_uid) |
| { |
| bool res = false; |
| int i, nregs = 1; |
| rtx next_usage_insns; |
| |
| if (regno < FIRST_PSEUDO_REGISTER) |
| nregs = hard_regno_nregs (regno, mode); |
| for (i = 0; i < nregs; i++) |
| if (usage_insns[regno + i].check == curr_usage_insns_check |
| && (next_usage_insns = usage_insns[regno + i].insns) != NULL_RTX |
| /* To avoid processing the register twice or more. */ |
| && ((GET_CODE (next_usage_insns) != INSN_LIST |
| && INSN_UID (next_usage_insns) < max_uid) |
| || (GET_CODE (next_usage_insns) == INSN_LIST |
| && (INSN_UID (XEXP (next_usage_insns, 0)) < max_uid))) |
| && need_for_split_p (potential_reload_hard_regs, regno + i) |
| && split_reg (before_p, regno + i, insn, next_usage_insns, NULL)) |
| res = true; |
| return res; |
| } |
| |
| /* Return TRUE if rtx X is considered as an invariant for |
| inheritance. */ |
| static bool |
| invariant_p (const_rtx x) |
| { |
| machine_mode mode; |
| const char *fmt; |
| enum rtx_code code; |
| int i, j; |
| |
| if (side_effects_p (x)) |
| return false; |
| |
| code = GET_CODE (x); |
| mode = GET_MODE (x); |
| if (code == SUBREG) |
| { |
| x = SUBREG_REG (x); |
| code = GET_CODE (x); |
| mode = wider_subreg_mode (mode, GET_MODE (x)); |
| } |
| |
| if (MEM_P (x)) |
| return false; |
| |
| if (REG_P (x)) |
| { |
| int i, nregs, regno = REGNO (x); |
| |
| if (regno >= FIRST_PSEUDO_REGISTER || regno == STACK_POINTER_REGNUM |
| || TEST_HARD_REG_BIT (eliminable_regset, regno) |
| || GET_MODE_CLASS (GET_MODE (x)) == MODE_CC) |
| return false; |
| nregs = hard_regno_nregs (regno, mode); |
| for (i = 0; i < nregs; i++) |
| if (! fixed_regs[regno + i] |
| /* A hard register may be clobbered in the current insn |
| but we can ignore this case because if the hard |
| register is used it should be set somewhere after the |
| clobber. */ |
| || bitmap_bit_p (&invalid_invariant_regs, regno + i)) |
| return false; |
| } |
| fmt = GET_RTX_FORMAT (code); |
| for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) |
| { |
| if (fmt[i] == 'e') |
| { |
| if (! invariant_p (XEXP (x, i))) |
| return false; |
| } |
| else if (fmt[i] == 'E') |
| { |
| for (j = XVECLEN (x, i) - 1; j >= 0; j--) |
| if (! invariant_p (XVECEXP (x, i, j))) |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| /* We have 'dest_reg <- invariant'. Let us try to make an invariant |
| inheritance transformation (using dest_reg instead invariant in a |
| subsequent insn). */ |
| static bool |
| process_invariant_for_inheritance (rtx dst_reg, rtx invariant_rtx) |
| { |
| invariant_ptr_t invariant_ptr; |
| rtx_insn *insn, *new_insns; |
| rtx insn_set, insn_reg, new_reg; |
| int insn_regno; |
| bool succ_p = false; |
| int dst_regno = REGNO (dst_reg); |
| machine_mode dst_mode = GET_MODE (dst_reg); |
| enum reg_class cl = lra_get_allocno_class (dst_regno), insn_reg_cl; |
| |
| invariant_ptr = insert_invariant (invariant_rtx); |
| if ((insn = invariant_ptr->insn) != NULL_RTX) |
| { |
| /* We have a subsequent insn using the invariant. */ |
| insn_set = single_set (insn); |
| lra_assert (insn_set != NULL); |
| insn_reg = SET_DEST (insn_set); |
| lra_assert (REG_P (insn_reg)); |
| insn_regno = REGNO (insn_reg); |
| insn_reg_cl = lra_get_allocno_class (insn_regno); |
| |
| if (dst_mode == GET_MODE (insn_reg) |
| /* We should consider only result move reg insns which are |
| cheap. */ |
| && targetm.register_move_cost (dst_mode, cl, insn_reg_cl) == 2 |
| && targetm.register_move_cost (dst_mode, cl, cl) == 2) |
| { |
| if (lra_dump_file != NULL) |
| fprintf (lra_dump_file, |
| " [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[\n"); |
| new_reg = lra_create_new_reg (dst_mode, dst_reg, cl, NULL, |
| "invariant inheritance"); |
| bitmap_set_bit (&lra_inheritance_pseudos, REGNO (new_reg)); |
| bitmap_set_bit (&check_only_regs, REGNO (new_reg)); |
| lra_reg_info[REGNO (new_reg)].restore_rtx = PATTERN (insn); |
| start_sequence (); |
| lra_emit_move (new_reg, dst_reg); |
| new_insns = get_insns (); |
| end_sequence (); |
| lra_process_new_insns (curr_insn, NULL, new_insns, |
| "Add invariant inheritance<-original"); |
| start_sequence (); |
| lra_emit_move (SET_DEST (insn_set), new_reg); |
| new_insns = get_insns (); |
| end_sequence (); |
| lra_process_new_insns (insn, NULL, new_insns, |
| "Changing reload<-inheritance"); |
| lra_set_insn_deleted (insn); |
| succ_p = true; |
| if (lra_dump_file != NULL) |
| { |
| fprintf (lra_dump_file, |
| " Invariant inheritance reuse change %d (bb%d):\n", |
| REGNO (new_reg), BLOCK_FOR_INSN (insn)->index); |
| dump_insn_slim (lra_dump_file, insn); |
| fprintf (lra_dump_file, |
| " ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]\n"); |
| } |
| } |
| } |
| invariant_ptr->insn = curr_insn; |
| return succ_p; |
| } |
| |
| /* Check only registers living at the current program point in the |
| current EBB. */ |
| static bitmap_head live_regs; |
| |
| /* Update live info in EBB given by its HEAD and TAIL insns after |
| inheritance/split transformation. The function removes dead moves |
| too. */ |
| static void |
| update_ebb_live_info (rtx_insn *head, rtx_insn *tail) |
| { |
| unsigned int j; |
| int i, regno; |
| bool live_p; |
| rtx_insn *prev_insn; |
| rtx set; |
| bool remove_p; |
| basic_block last_bb, prev_bb, curr_bb; |
| bitmap_iterator bi; |
| struct lra_insn_reg *reg; |
| edge e; |
| edge_iterator ei; |
| |
| last_bb = BLOCK_FOR_INSN (tail); |
| prev_bb = NULL; |
| for (curr_insn = tail; |
| curr_insn != PREV_INSN (head); |
| curr_insn = prev_insn) |
| { |
| prev_insn = PREV_INSN (curr_insn); |
| /* We need to process empty blocks too. They contain |
| NOTE_INSN_BASIC_BLOCK referring for the basic block. */ |
| if (NOTE_P (curr_insn) && NOTE_KIND (curr_insn) != NOTE_INSN_BASIC_BLOCK) |
| continue; |
| curr_bb = BLOCK_FOR_INSN (curr_insn); |
| if (curr_bb != prev_bb) |
| { |
| if (prev_bb != NULL) |
| { |
| /* Update df_get_live_in (prev_bb): */ |
| EXECUTE_IF_SET_IN_BITMAP (&check_only_regs, 0, j, bi) |
| if (bitmap_bit_p (&live_regs, j)) |
| bitmap_set_bit (df_get_live_in (prev_bb), j); |
| else |
| bitmap_clear_bit (df_get_live_in (prev_bb), j); |
| } |
| if (curr_bb != last_bb) |
| { |
| /* Update df_get_live_out (curr_bb): */ |
| EXECUTE_IF_SET_IN_BITMAP (&check_only_regs, 0, j, bi) |
| { |
| live_p = bitmap_bit_p (&live_regs, j); |
| if (! live_p) |
| FOR_EACH_EDGE (e, ei, curr_bb->succs) |
| if (bitmap_bit_p (df_get_live_in (e->dest), j)) |
| { |
| live_p = true; |
| break; |
| } |
| if (live_p) |
| bitmap_set_bit (df_get_live_out (curr_bb), j); |
| else |
| bitmap_clear_bit (df_get_live_out (curr_bb), j); |
| } |
| } |
| prev_bb = curr_bb; |
| bitmap_and (&live_regs, &check_only_regs, df_get_live_out (curr_bb)); |
| } |
| if (! NONDEBUG_INSN_P (curr_insn)) |
| continue; |
| curr_id = lra_get_insn_recog_data (curr_insn); |
| curr_static_id = curr_id->insn_static_data; |
| remove_p = false; |
| if ((set = single_set (curr_insn)) != NULL_RTX |
| && REG_P (SET_DEST (set)) |
| && (regno = REGNO (SET_DEST (set))) >= FIRST_PSEUDO_REGISTER |
| && SET_DEST (set) != pic_offset_table_rtx |
| && bitmap_bit_p (&check_only_regs, regno) |
| && ! bitmap_bit_p (&live_regs, regno)) |
| remove_p = true; |
| /* See which defined values die here. */ |
| for (reg = curr_id->regs; reg != NULL; reg = reg->next) |
| if (reg->type == OP_OUT && ! reg->subreg_p) |
| bitmap_clear_bit (&live_regs, reg->regno); |
| for (reg = curr_static_id->hard_regs; reg != NULL; reg = reg->next) |
| if (reg->type == OP_OUT && ! reg->subreg_p) |
| bitmap_clear_bit (&live_regs, reg->regno); |
| if (curr_id->arg_hard_regs != NULL) |
| /* Make clobbered argument hard registers die. */ |
| for (i = 0; (regno = curr_id->arg_hard_regs[i]) >= 0; i++) |
| if (regno >= FIRST_PSEUDO_REGISTER) |
| bitmap_clear_bit (&live_regs, regno - FIRST_PSEUDO_REGISTER); |
| /* Mark each used value as live. */ |
| for (reg = curr_id->regs; reg != NULL; reg = reg->next) |
| if (reg->type != OP_OUT |
| && bitmap_bit_p (&check_only_regs, reg->regno)) |
| bitmap_set_bit (&live_regs, reg->regno); |
| for (reg = curr_static_id->hard_regs; reg != NULL; reg = reg->next) |
| if (reg->type != OP_OUT |
| && bitmap_bit_p (&check_only_regs, reg->regno)) |
| bitmap_set_bit (&live_regs, reg->regno); |
| if (curr_id->arg_hard_regs != NULL) |
| /* Make used argument hard registers live. */ |
| for (i = 0; (regno = curr_id->arg_hard_regs[i]) >= 0; i++) |
| if (regno < FIRST_PSEUDO_REGISTER |
| && bitmap_bit_p (&check_only_regs, regno)) |
| bitmap_set_bit (&live_regs, regno); |
| /* It is quite important to remove dead move insns because it |
| means removing dead store. We don't need to process them for |
| constraints. */ |
| if (remove_p) |
| { |
| if (lra_dump_file != NULL) |
| { |
| fprintf (lra_dump_file, " Removing dead insn:\n "); |
| dump_insn_slim (lra_dump_file, curr_insn); |
| } |
| lra_set_insn_deleted (curr_insn); |
| } |
| } |
| } |
| |
| /* The structure describes info to do an inheritance for the current |
| insn. We need to collect such info first before doing the |
| transformations because the transformations change the insn |
| internal representation. */ |
| struct to_inherit |
| { |
| /* Original regno. */ |
| int regno; |
| /* Subsequent insns which can inherit original reg value. */ |
| rtx insns; |
| }; |
| |
| /* Array containing all info for doing inheritance from the current |
| insn. */ |
| static struct to_inherit to_inherit[LRA_MAX_INSN_RELOADS]; |
| |
| /* Number elements in the previous array. */ |
| static int to_inherit_num; |
| |
| /* Add inheritance info REGNO and INSNS. Their meaning is described in |
| structure to_inherit. */ |
| static void |
| add_to_inherit (int regno, rtx insns) |
| { |
| int i; |
| |
| for (i = 0; i < to_inherit_num; i++) |
| if (to_inherit[i].regno == regno) |
| return; |
| lra_assert (to_inherit_num < LRA_MAX_INSN_RELOADS); |
| to_inherit[to_inherit_num].regno = regno; |
| to_inherit[to_inherit_num++].insns = insns; |
| } |
| |
| /* Return the last non-debug insn in basic block BB, or the block begin |
| note if none. */ |
| static rtx_insn * |
| get_last_insertion_point (basic_block bb) |
| { |
| rtx_insn *insn; |
| |
| FOR_BB_INSNS_REVERSE (bb, insn) |
| if (NONDEBUG_INSN_P (insn) || NOTE_INSN_BASIC_BLOCK_P (insn)) |
| return insn; |
| gcc_unreachable (); |
| } |
| |
| /* Set up RES by registers living on edges FROM except the edge (FROM, |
| TO) or by registers set up in a jump insn in BB FROM. */ |
| static void |
| get_live_on_other_edges (basic_block from, basic_block to, bitmap res) |
| { |
| rtx_insn *last; |
| struct lra_insn_reg *reg; |
| edge e; |
| edge_iterator ei; |
| |
| lra_assert (to != NULL); |
| bitmap_clear (res); |
| FOR_EACH_EDGE (e, ei, from->succs) |
| if (e->dest != to) |
| bitmap_ior_into (res, df_get_live_in (e->dest)); |
| last = get_last_insertion_point (from); |
| if (! JUMP_P (last)) |
| return; |
| curr_id = lra_get_insn_recog_data (last); |
| for (reg = curr_id->regs; reg != NULL; reg = reg->next) |
| if (reg->type != OP_IN) |
| bitmap_set_bit (res, reg->regno); |
| } |
| |
| /* Used as a temporary results of some bitmap calculations. */ |
| static bitmap_head temp_bitmap; |
| |
| /* We split for reloads of small class of hard regs. The following |
| defines how many hard regs the class should have to be qualified as |
| small. The code is mostly oriented to x86/x86-64 architecture |
| where some insns need to use only specific register or pair of |
| registers and these register can live in RTL explicitly, e.g. for |
| parameter passing. */ |
| static const int max_small_class_regs_num = 2; |
| |
| /* Do inheritance/split transformations in EBB starting with HEAD and |
| finishing on TAIL. We process EBB insns in the reverse order. |
| Return true if we did any inheritance/split transformation in the |
| EBB. |
| |
| We should avoid excessive splitting which results in worse code |
| because of inaccurate cost calculations for spilling new split |
| pseudos in such case. To achieve this we do splitting only if |
| register pressure is high in given basic block and there are reload |
| pseudos requiring hard registers. We could do more register |
| pressure calculations at any given program point to avoid necessary |
| splitting even more but it is to expensive and the current approach |
| works well enough. */ |
| static bool |
| inherit_in_ebb (rtx_insn *head, rtx_insn *tail) |
| { |
| int i, src_regno, dst_regno, nregs; |
| bool change_p, succ_p, update_reloads_num_p; |
| rtx_insn *prev_insn, *last_insn; |
| rtx next_usage_insns, curr_set; |
| enum reg_class cl; |
| struct lra_insn_reg *reg; |
| basic_block last_processed_bb, curr_bb = NULL; |
| HARD_REG_SET potential_reload_hard_regs, live_hard_regs; |
| bitmap to_process; |
| unsigned int j; |
| bitmap_iterator bi; |
| bool head_p, after_p; |
| |
| change_p = false; |
| curr_usage_insns_check++; |
| clear_invariants (); |
| reloads_num = calls_num = 0; |
| for (unsigned int i = 0; i < NUM_ABI_IDS; ++i) |
| last_call_for_abi[i] = 0; |
| CLEAR_HARD_REG_SET (full_and_partial_call_clobbers); |
| bitmap_clear (&check_only_regs); |
| bitmap_clear (&invalid_invariant_regs); |
| last_processed_bb = NULL; |
| CLEAR_HARD_REG_SET (potential_reload_hard_regs); |
| live_hard_regs = eliminable_regset | lra_no_alloc_regs; |
| /* We don't process new insns generated in the loop. */ |
| for (curr_insn = tail; curr_insn != PREV_INSN (head); curr_insn = prev_insn) |
| { |
| prev_insn = PREV_INSN (curr_insn); |
| if (BLOCK_FOR_INSN (curr_insn) != NULL) |
| curr_bb = BLOCK_FOR_INSN (curr_insn); |
| if (last_processed_bb != curr_bb) |
| { |
| /* We are at the end of BB. Add qualified living |
| pseudos for potential splitting. */ |
| to_process = df_get_live_out (curr_bb); |
| if (last_processed_bb != NULL) |
| { |
| /* We are somewhere in the middle of EBB. */ |
| get_live_on_other_edges (curr_bb, last_processed_bb, |
| &temp_bitmap); |
| to_process = &temp_bitmap; |
| } |
| last_processed_bb = curr_bb; |
| last_insn = get_last_insertion_point (curr_bb); |
| after_p = (! JUMP_P (last_insn) |
| && (! CALL_P (last_insn) |
| || (find_reg_note (last_insn, |
| REG_NORETURN, NULL_RTX) == NULL_RTX |
| && ! SIBLING_CALL_P (last_insn)))); |
| CLEAR_HARD_REG_SET (potential_reload_hard_regs); |
| EXECUTE_IF_SET_IN_BITMAP (to_process, 0, j, bi) |
| { |
| if ((int) j >= lra_constraint_new_regno_start) |
| break; |
| if (j < FIRST_PSEUDO_REGISTER || reg_renumber[j] >= 0) |
| { |
| if (j < FIRST_PSEUDO_REGISTER) |
| SET_HARD_REG_BIT (live_hard_regs, j); |
| else |
| add_to_hard_reg_set (&live_hard_regs, |
| PSEUDO_REGNO_MODE (j), |
| reg_renumber[j]); |
| setup_next_usage_insn (j, last_insn, reloads_num, after_p); |
| } |
| } |
| } |
| src_regno = dst_regno = -1; |
| curr_set = single_set (curr_insn); |
| if (curr_set != NULL_RTX && REG_P (SET_DEST (curr_set))) |
| dst_regno = REGNO (SET_DEST (curr_set)); |
| if (curr_set != NULL_RTX && REG_P (SET_SRC (curr_set))) |
| src_regno = REGNO (SET_SRC (curr_set)); |
| update_reloads_num_p = true; |
| if (src_regno < lra_constraint_new_regno_start |
| && src_regno >= FIRST_PSEUDO_REGISTER |
| && reg_renumber[src_regno] < 0 |
| && dst_regno >= lra_constraint_new_regno_start |
| && (cl = lra_get_allocno_class (dst_regno)) != NO_REGS) |
| { |
| /* 'reload_pseudo <- original_pseudo'. */ |
| if (ira_class_hard_regs_num[cl] <= max_small_class_regs_num) |
| reloads_num++; |
| update_reloads_num_p = false; |
| succ_p = false; |
| if (usage_insns[src_regno].check == curr_usage_insns_check |
| && (next_usage_insns = usage_insns[src_regno].insns) != NULL_RTX) |
| succ_p = inherit_reload_reg (false, src_regno, cl, |
| curr_insn, next_usage_insns); |
| if (succ_p) |
| change_p = true; |
| else |
| setup_next_usage_insn (src_regno, curr_insn, reloads_num, false); |
| if (hard_reg_set_subset_p (reg_class_contents[cl], live_hard_regs)) |
| potential_reload_hard_regs |= reg_class_contents[cl]; |
| } |
| else if (src_regno < 0 |
| && dst_regno >= lra_constraint_new_regno_start |
| && invariant_p (SET_SRC (curr_set)) |
| && (cl = lra_get_allocno_class (dst_regno)) != NO_REGS |
| && ! bitmap_bit_p (&invalid_invariant_regs, dst_regno) |
| && ! bitmap_bit_p (&invalid_invariant_regs, |
| ORIGINAL_REGNO(regno_reg_rtx[dst_regno]))) |
| { |
| /* 'reload_pseudo <- invariant'. */ |
| if (ira_class_hard_regs_num[cl] <= max_small_class_regs_num) |
| reloads_num++; |
| update_reloads_num_p = false; |
| if (process_invariant_for_inheritance (SET_DEST (curr_set), SET_SRC (curr_set))) |
| change_p = true; |
| if (hard_reg_set_subset_p (reg_class_contents[cl], live_hard_regs)) |
| potential_reload_hard_regs |= reg_class_contents[cl]; |
| } |
| else if (src_regno >= lra_constraint_new_regno_start |
| && dst_regno < lra_constraint_new_regno_start |
| && dst_regno >= FIRST_PSEUDO_REGISTER |
| && reg_renumber[dst_regno] < 0 |
| && (cl = lra_get_allocno_class (src_regno)) != NO_REGS |
| && usage_insns[dst_regno].check == curr_usage_insns_check |
| && (next_usage_insns |
| = usage_insns[dst_regno].insns) != NULL_RTX) |
| { |
| if (ira_class_hard_regs_num[cl] <= max_small_class_regs_num) |
| reloads_num++; |
| update_reloads_num_p = false; |
| /* 'original_pseudo <- reload_pseudo'. */ |
| if (! JUMP_P (curr_insn) |
| && inherit_reload_reg (true, dst_regno, cl, |
| curr_insn, next_usage_insns)) |
| change_p = true; |
| /* Invalidate. */ |
| usage_insns[dst_regno].check = 0; |
| if (hard_reg_set_subset_p (reg_class_contents[cl], live_hard_regs)) |
| potential_reload_hard_regs |= reg_class_contents[cl]; |
| } |
| else if (INSN_P (curr_insn)) |
| { |
| int iter; |
| int max_uid = get_max_uid (); |
| |
| curr_id = lra_get_insn_recog_data (curr_insn); |
| curr_static_id = curr_id->insn_static_data; |
| to_inherit_num = 0; |
| /* Process insn definitions. */ |
| for (iter = 0; iter < 2; iter++) |
| for (reg = iter == 0 ? curr_id->regs : curr_static_id->hard_regs; |
| reg != NULL; |
| reg = reg->next) |
| if (reg->type != OP_IN |
| && (dst_regno = reg->regno) < lra_constraint_new_regno_start) |
| { |
| if (dst_regno >= FIRST_PSEUDO_REGISTER && reg->type == OP_OUT |
| && reg_renumber[dst_regno] < 0 && ! reg->subreg_p |
| && usage_insns[dst_regno].check == curr_usage_insns_check |
| && (next_usage_insns |
| = usage_insns[dst_regno].insns) != NULL_RTX) |
| { |
| struct lra_insn_reg *r; |
| |
| for (r = curr_id->regs; r != NULL; r = r->next) |
| if (r->type != OP_OUT && r->regno == dst_regno) |
| break; |
| /* Don't do inheritance if the pseudo is also |
| used in the insn. */ |
| if (r == NULL) |
| /* We cannot do inheritance right now |
| because the current insn reg info (chain |
| regs) can change after that. */ |
| add_to_inherit (dst_regno, next_usage_insns); |
| } |
| /* We cannot process one reg twice here because of |
| usage_insns invalidation. */ |
| if ((dst_regno < FIRST_PSEUDO_REGISTER |
| || reg_renumber[dst_regno] >= 0) |
| && ! reg->subreg_p && reg->type != OP_IN) |
| { |
| HARD_REG_SET s; |
| |
| if (split_if_necessary (dst_regno, reg->biggest_mode, |
| potential_reload_hard_regs, |
| false, curr_insn, max_uid)) |
| change_p = true; |
| CLEAR_HARD_REG_SET (s); |
| if (dst_regno < FIRST_PSEUDO_REGISTER) |
| add_to_hard_reg_set (&s, reg->biggest_mode, dst_regno); |
| else |
| add_to_hard_reg_set (&s, PSEUDO_REGNO_MODE (dst_regno), |
| reg_renumber[dst_regno]); |
| live_hard_regs &= ~s; |
| potential_reload_hard_regs &= ~s; |
| } |
| /* We should invalidate potential inheritance or |
| splitting for the current insn usages to the next |
| usage insns (see code below) as the output pseudo |
| prevents this. */ |
| if ((dst_regno >= FIRST_PSEUDO_REGISTER |
| && reg_renumber[dst_regno] < 0) |
| || (reg->type == OP_OUT && ! reg->subreg_p |
| && (dst_regno < FIRST_PSEUDO_REGISTER |
| || reg_renumber[dst_regno] >= 0))) |
| { |
| /* Invalidate and mark definitions. */ |
| if (dst_regno >= FIRST_PSEUDO_REGISTER) |
| usage_insns[dst_regno].check = -(int) INSN_UID (curr_insn); |
| else |
| { |
| nregs = hard_regno_nregs (dst_regno, |
| reg->biggest_mode); |
| for (i = 0; i < nregs; i++) |
| usage_insns[dst_regno + i].check |
| = -(int) INSN_UID (curr_insn); |
| } |
| } |
| } |
| /* Process clobbered call regs. */ |
| if (curr_id->arg_hard_regs != NULL) |
| for (i = 0; (dst_regno = curr_id->arg_hard_regs[i]) >= 0; i++) |
| if (dst_regno >= FIRST_PSEUDO_REGISTER) |
| usage_insns[dst_regno - FIRST_PSEUDO_REGISTER].check |
| = -(int) INSN_UID (curr_insn); |
| if (! JUMP_P (curr_insn)) |
| for (i = 0; i < to_inherit_num; i++) |
| if (inherit_reload_reg (true, to_inherit[i].regno, |
| ALL_REGS, curr_insn, |
| to_inherit[i].insns)) |
| change_p = true; |
| if (CALL_P (curr_insn)) |
| { |
| rtx cheap, pat, dest; |
| rtx_insn *restore; |
| int regno, hard_regno; |
| |
| calls_num++; |
| function_abi callee_abi = insn_callee_abi (curr_insn); |
| last_call_for_abi[callee_abi.id ()] = calls_num; |
| full_and_partial_call_clobbers |
| |= callee_abi.full_and_partial_reg_clobbers (); |
| if ((cheap = find_reg_note (curr_insn, |
| REG_RETURNED, NULL_RTX)) != NULL_RTX |
| && ((cheap = XEXP (cheap, 0)), true) |
| && (regno = REGNO (cheap)) >= FIRST_PSEUDO_REGISTER |
| && (hard_regno = reg_renumber[regno]) >= 0 |
| && usage_insns[regno].check == curr_usage_insns_check |
| /* If there are pending saves/restores, the |
| optimization is not worth. */ |
| && usage_insns[regno].calls_num == calls_num - 1 |
| && callee_abi.clobbers_reg_p (GET_MODE (cheap), hard_regno)) |
| { |
| /* Restore the pseudo from the call result as |
| REG_RETURNED note says that the pseudo value is |
| in the call result and the pseudo is an argument |
| of the call. */ |
| pat = PATTERN (curr_insn); |
| if (GET_CODE (pat) == PARALLEL) |
| pat = XVECEXP (pat, 0, 0); |
| dest = SET_DEST (pat); |
| /* For multiple return values dest is PARALLEL. |
| Currently we handle only single return value case. */ |
| if (REG_P (dest)) |
| { |
| start_sequence (); |
| emit_move_insn (cheap, copy_rtx (dest)); |
| restore = get_insns (); |
| end_sequence (); |
| lra_process_new_insns (curr_insn, NULL, restore, |
| "Inserting call parameter restore"); |
| /* We don't need to save/restore of the pseudo from |
| this call. */ |
| usage_insns[regno].calls_num = calls_num; |
| remove_from_hard_reg_set |
| (&full_and_partial_call_clobbers, |
| GET_MODE (cheap), hard_regno); |
| bitmap_set_bit (&check_only_regs, regno); |
| } |
| } |
| } |
| to_inherit_num = 0; |
| /* Process insn usages. */ |
| for (iter = 0; iter < 2; iter++) |
| for (reg = iter == 0 ? curr_id->regs : curr_static_id->hard_regs; |
| reg != NULL; |
| reg = reg->next) |
| if ((reg->type != OP_OUT |
| || (reg->type == OP_OUT && reg->subreg_p)) |
| && (src_regno = reg->regno) < lra_constraint_new_regno_start) |
| { |
| if (src_regno >= FIRST_PSEUDO_REGISTER |
| && reg_renumber[src_regno] < 0 && reg->type == OP_IN) |
| { |
| if (usage_insns[src_regno].check == curr_usage_insns_check |
| && (next_usage_insns |
| = usage_insns[src_regno].insns) != NULL_RTX |
| && NONDEBUG_INSN_P (curr_insn)) |
| add_to_inherit (src_regno, next_usage_insns); |
| else if (usage_insns[src_regno].check |
| != -(int) INSN_UID (curr_insn)) |
| /* Add usages but only if the reg is not set up |
| in the same insn. */ |
| add_next_usage_insn (src_regno, curr_insn, reloads_num); |
| } |
| else if (src_regno < FIRST_PSEUDO_REGISTER |
| || reg_renumber[src_regno] >= 0) |
| { |
| bool before_p; |
| rtx_insn *use_insn = curr_insn; |
| |
| before_p = (JUMP_P (curr_insn) |
| || (CALL_P (curr_insn) && reg->type == OP_IN)); |
| if (NONDEBUG_INSN_P (curr_insn) |
| && (! JUMP_P (curr_insn) || reg->type == OP_IN) |
| && split_if_necessary (src_regno, reg->biggest_mode, |
| potential_reload_hard_regs, |
| before_p, curr_insn, max_uid)) |
| { |
| if (reg->subreg_p) |
| check_and_force_assignment_correctness_p = true; |
| change_p = true; |
| /* Invalidate. */ |
| usage_insns[src_regno].check = 0; |
| if (before_p) |
| use_insn = PREV_INSN (curr_insn); |
| } |
| if (NONDEBUG_INSN_P (curr_insn)) |
| { |
| if (src_regno < FIRST_PSEUDO_REGISTER) |
| add_to_hard_reg_set (&live_hard_regs, |
| reg->biggest_mode, src_regno); |
| else |
| add_to_hard_reg_set (&live_hard_regs, |
| PSEUDO_REGNO_MODE (src_regno), |
| reg_renumber[src_regno]); |
| } |
| if (src_regno >= FIRST_PSEUDO_REGISTER) |
| add_next_usage_insn (src_regno, use_insn, reloads_num); |
| else |
| { |
| for (i = 0; i < hard_regno_nregs (src_regno, reg->biggest_mode); i++) |
| add_next_usage_insn (src_regno + i, use_insn, reloads_num); |
| } |
| } |
| } |
| /* Process used call regs. */ |
| if (curr_id->arg_hard_regs != NULL) |
| for (i = 0; (src_regno = curr_id->arg_hard_regs[i]) >= 0; i++) |
| if (src_regno < FIRST_PSEUDO_REGISTER) |
| { |
| SET_HARD_REG_BIT (live_hard_regs, src_regno); |
| add_next_usage_insn (src_regno, curr_insn, reloads_num); |
| } |
| for (i = 0; i < to_inherit_num; i++) |
| { |
| src_regno = to_inherit[i].regno; |
| if (inherit_reload_reg (false, src_regno, ALL_REGS, |
| curr_insn, to_inherit[i].insns)) |
| change_p = true; |
| else |
| setup_next_usage_insn (src_regno, curr_insn, reloads_num, false); |
| } |
| } |
| if (update_reloads_num_p |
| && NONDEBUG_INSN_P (curr_insn) && curr_set != NULL_RTX) |
| { |
| int regno = -1; |
| if ((REG_P (SET_DEST (curr_set)) |
| && (regno = REGNO (SET_DEST (curr_set))) >= lra_constraint_new_regno_start |
| && reg_renumber[regno] < 0 |
| && (cl = lra_get_allocno_class (regno)) != NO_REGS) |
| || (REG_P (SET_SRC (curr_set)) |
| && (regno = REGNO (SET_SRC (curr_set))) >= lra_constraint_new_regno_start |
| && reg_renumber[regno] < 0 |
| && (cl = lra_get_allocno_class (regno)) != NO_REGS)) |
| { |
| if (ira_class_hard_regs_num[cl] <= max_small_class_regs_num) |
| reloads_num++; |
| if (hard_reg_set_subset_p (reg_class_contents[cl], live_hard_regs)) |
| potential_reload_hard_regs |= reg_class_contents[cl]; |
| } |
| } |
| if (NONDEBUG_INSN_P (curr_insn)) |
| { |
| int regno; |
| |
| /* Invalidate invariants with changed regs. */ |
| curr_id = lra_get_insn_recog_data (curr_insn); |
| for (reg = curr_id->regs; reg != NULL; reg = reg->next) |
| if (reg->type != OP_IN) |
| { |
| bitmap_set_bit (&invalid_invariant_regs, reg->regno); |
| bitmap_set_bit (&invalid_invariant_regs, |
| ORIGINAL_REGNO (regno_reg_rtx[reg->regno])); |
| } |
| curr_static_id = curr_id->insn_static_data; |
| for (reg = curr_static_id->hard_regs; reg != NULL; reg = reg->next) |
| if (reg->type != OP_IN) |
| bitmap_set_bit (&invalid_invariant_regs, reg->regno); |
| if (curr_id->arg_hard_regs != NULL) |
| for (i = 0; (regno = curr_id->arg_hard_regs[i]) >= 0; i++) |
| if (regno >= FIRST_PSEUDO_REGISTER) |
| bitmap_set_bit (&invalid_invariant_regs, |
| regno - FIRST_PSEUDO_REGISTER); |
| } |
| /* We reached the start of the current basic block. */ |
| if (prev_insn == NULL_RTX || prev_insn == PREV_INSN (head) |
| || BLOCK_FOR_INSN (prev_insn) != curr_bb) |
| { |
| /* We reached the beginning of the current block -- do |
| rest of spliting in the current BB. */ |
| to_process = df_get_live_in (curr_bb); |
| if (BLOCK_FOR_INSN (head) != curr_bb) |
| { |
| /* We are somewhere in the middle of EBB. */ |
| get_live_on_other_edges (EDGE_PRED (curr_bb, 0)->src, |
| curr_bb, &temp_bitmap); |
| to_process = &temp_bitmap; |
| } |
| head_p = true; |
| EXECUTE_IF_SET_IN_BITMAP (to_process, 0, j, bi) |
| { |
| if ((int) j >= lra_constraint_new_regno_start) |
| break; |
| if (((int) j < FIRST_PSEUDO_REGISTER || reg_renumber[j] >= 0) |
| && usage_insns[j].check == curr_usage_insns_check |
| && (next_usage_insns = usage_insns[j].insns) != NULL_RTX) |
| { |
| if (need_for_split_p (potential_reload_hard_regs, j)) |
| { |
| if (lra_dump_file != NULL && head_p) |
| { |
| fprintf (lra_dump_file, |
| " ----------------------------------\n"); |
| head_p = false; |
| } |
| if (split_reg (false, j, bb_note (curr_bb), |
| next_usage_insns, NULL)) |
| change_p = true; |
| } |
| usage_insns[j].check = 0; |
| } |
| } |
| } |
| } |
| return change_p; |
| } |
| |
| /* This value affects EBB forming. If probability of edge from EBB to |
| a BB is not greater than the following value, we don't add the BB |
| to EBB. */ |
| #define EBB_PROBABILITY_CUTOFF \ |
| ((REG_BR_PROB_BASE * param_lra_inheritance_ebb_probability_cutoff) / 100) |
| |
| /* Current number of inheritance/split iteration. */ |
| int lra_inheritance_iter; |
| |
| /* Entry function for inheritance/split pass. */ |
| void |
| lra_inheritance (void) |
| { |
| int i; |
| basic_block bb, start_bb; |
| edge e; |
| |
| lra_inheritance_iter++; |
| if (lra_inheritance_iter > LRA_MAX_INHERITANCE_PASSES) |
| return; |
| timevar_push (TV_LRA_INHERITANCE); |
| if (lra_dump_file != NULL) |
| fprintf (lra_dump_file, "\n********** Inheritance #%d: **********\n\n", |
| lra_inheritance_iter); |
| curr_usage_insns_check = 0; |
| usage_insns = XNEWVEC (struct usage_insns, lra_constraint_new_regno_start); |
| for (i = 0; i < lra_constraint_new_regno_start; i++) |
| usage_insns[i].check = 0; |
| bitmap_initialize (&check_only_regs, ®_obstack); |
| bitmap_initialize (&invalid_invariant_regs, ®_obstack); |
| bitmap_initialize (&live_regs, ®_obstack); |
| bitmap_initialize (&temp_bitmap, ®_obstack); |
| bitmap_initialize (&ebb_global_regs, ®_obstack); |
| FOR_EACH_BB_FN (bb, cfun) |
| { |
| start_bb = bb; |
| if (lra_dump_file != NULL) |
| fprintf (lra_dump_file, "EBB"); |
| /* Form a EBB starting with BB. */ |
| bitmap_clear (&ebb_global_regs); |
| bitmap_ior_into (&ebb_global_regs, df_get_live_in (bb)); |
| for (;;) |
| { |
| if (lra_dump_file != NULL) |
| fprintf (lra_dump_file, " %d", bb->index); |
| if (bb->next_bb == EXIT_BLOCK_PTR_FOR_FN (cfun) |
| || LABEL_P (BB_HEAD (bb->next_bb))) |
| break; |
| e = find_fallthru_edge (bb->succs); |
| if (! e) |
| break; |
| if (e->probability.initialized_p () |
| && e->probability.to_reg_br_prob_base () < EBB_PROBABILITY_CUTOFF) |
| break; |
| bb = bb->next_bb; |
| } |
| bitmap_ior_into (&ebb_global_regs, df_get_live_out (bb)); |
| if (lra_dump_file != NULL) |
| fprintf (lra_dump_file, "\n"); |
| if (inherit_in_ebb (BB_HEAD (start_bb), BB_END (bb))) |
| /* Remember that the EBB head and tail can change in |
| inherit_in_ebb. */ |
| update_ebb_live_info (BB_HEAD (start_bb), BB_END (bb)); |
| } |
| bitmap_release (&ebb_global_regs); |
| bitmap_release (&temp_bitmap); |
| bitmap_release (&live_regs); |
| bitmap_release (&invalid_invariant_regs); |
| bitmap_release (&check_only_regs); |
| free (usage_insns); |
| lra_dump_insns_if_possible ("func after inheritance"); |
| timevar_pop (TV_LRA_INHERITANCE); |
| } |
| |
| |
| |
| /* This page contains code to undo failed inheritance/split |
| transformations. */ |
| |
| /* Current number of iteration undoing inheritance/split. */ |
| int lra_undo_inheritance_iter; |
| |
| /* Fix BB live info LIVE after removing pseudos created on pass doing |
| inheritance/split which are REMOVED_PSEUDOS. */ |
| static void |
| fix_bb_live_info (bitmap live, bitmap removed_pseudos) |
| { |
| unsigned int regno; |
| bitmap_iterator bi; |
| |
| EXECUTE_IF_SET_IN_BITMAP (removed_pseudos, 0, regno, bi) |
| if (bitmap_clear_bit (live, regno) |
| && REG_P (lra_reg_info[regno].restore_rtx)) |
| bitmap_set_bit (live, REGNO (lra_reg_info[regno].restore_rtx)); |
| } |
| |
| /* Return regno of the (subreg of) REG. Otherwise, return a negative |
| number. */ |
| static int |
| get_regno (rtx reg) |
| { |
| if (GET_CODE (reg) == SUBREG) |
| reg = SUBREG_REG (reg); |
| if (REG_P (reg)) |
| return REGNO (reg); |
| return -1; |
| } |
| |
| /* Delete a move INSN with destination reg DREGNO and a previous |
| clobber insn with the same regno. The inheritance/split code can |
| generate moves with preceding clobber and when we delete such moves |
| we should delete the clobber insn too to keep the correct life |
| info. */ |
| static void |
| delete_move_and_clobber (rtx_insn *insn, int dregno) |
| { |
| rtx_insn *prev_insn = PREV_INSN (insn); |
| |
| lra_set_insn_deleted (insn); |
| lra_assert (dregno >= 0); |
| if (prev_insn != NULL && NONDEBUG_INSN_P (prev_insn) |
| && GET_CODE (PATTERN (prev_insn)) == CLOBBER |
| && dregno == get_regno (XEXP (PATTERN (prev_insn), 0))) |
| lra_set_insn_deleted (prev_insn); |
| } |
| |
| /* Remove inheritance/split pseudos which are in REMOVE_PSEUDOS and |
| return true if we did any change. The undo transformations for |
| inheritance looks like |
| i <- i2 |
| p <- i => p <- i2 |
| or removing |
| p <- i, i <- p, and i <- i3 |
| where p is original pseudo from which inheritance pseudo i was |
| created, i and i3 are removed inheritance pseudos, i2 is another |
| not removed inheritance pseudo. All split pseudos or other |
| occurrences of removed inheritance pseudos are changed on the |
| corresponding original pseudos. |
| |
| The function also schedules insns changed and created during |
| inheritance/split pass for processing by the subsequent constraint |
| pass. */ |
| static bool |
| remove_inheritance_pseudos (bitmap remove_pseudos) |
| { |
| basic_block bb; |
| int regno, sregno, prev_sregno, dregno; |
| rtx restore_rtx; |
| rtx set, prev_set; |
| rtx_insn *prev_insn; |
| bool change_p, done_p; |
| |
| change_p = ! bitmap_empty_p (remove_pseudos); |
| /* We cannot finish the function right away if CHANGE_P is true |
| because we need to marks insns affected by previous |
| inheritance/split pass for processing by the subsequent |
| constraint pass. */ |
| FOR_EACH_BB_FN (bb, cfun) |
| { |
| fix_bb_live_info (df_get_live_in (bb), remove_pseudos); |
| fix_bb_live_info (df_get_live_out (bb), remove_pseudos); |
| FOR_BB_INSNS_REVERSE (bb, curr_insn) |
| { |
| if (! INSN_P (curr_insn)) |
| continue; |
| done_p = false; |
| sregno = dregno = -1; |
| if (change_p && NONDEBUG_INSN_P (curr_insn) |
| && (set = single_set (curr_insn)) != NULL_RTX) |
| { |
| dregno = get_regno (SET_DEST (set)); |
| sregno = get_regno (SET_SRC (set)); |
| } |
| |
| if (sregno >= 0 && dregno >= 0) |
| { |
| if (bitmap_bit_p (remove_pseudos, dregno) |
| && ! REG_P (lra_reg_info[dregno].restore_rtx)) |
| { |
| /* invariant inheritance pseudo <- original pseudo */ |
| if (lra_dump_file != NULL) |
| { |
| fprintf (lra_dump_file, " Removing invariant inheritance:\n"); |
| dump_insn_slim (lra_dump_file, curr_insn); |
| fprintf (lra_dump_file, "\n"); |
| } |
| delete_move_and_clobber (curr_insn, dregno); |
| done_p = true; |
| } |
| else if (bitmap_bit_p (remove_pseudos, sregno) |
| && ! REG_P (lra_reg_info[sregno].restore_rtx)) |
| { |
| /* reload pseudo <- invariant inheritance pseudo */ |
| start_sequence (); |
| /* We cannot just change the source. It might be |
| an insn different from the move. */ |
| emit_insn (lra_reg_info[sregno].restore_rtx); |
| rtx_insn *new_insns = get_insns (); |
| end_sequence (); |
| lra_assert (single_set (new_insns) != NULL |
| && SET_DEST (set) == SET_DEST (single_set (new_insns))); |
| lra_process_new_insns (curr_insn, NULL, new_insns, |
| "Changing reload<-invariant inheritance"); |
| delete_move_and_clobber (curr_insn, dregno); |
| done_p = true; |
| } |
| else if ((bitmap_bit_p (remove_pseudos, sregno) |
| && (get_regno (lra_reg_info[sregno].restore_rtx) == dregno |
| || (bitmap_bit_p (remove_pseudos, dregno) |
| && get_regno (lra_reg_info[sregno].restore_rtx) >= 0 |
| && (get_regno (lra_reg_info[sregno].restore_rtx) |
| == get_regno (lra_reg_info[dregno].restore_rtx))))) |
| || (bitmap_bit_p (remove_pseudos, dregno) |
| && get_regno (lra_reg_info[dregno].restore_rtx) == sregno)) |
| /* One of the following cases: |
| original <- removed inheritance pseudo |
| removed inherit pseudo <- another removed inherit pseudo |
| removed inherit pseudo <- original pseudo |
| Or |
| removed_split_pseudo <- original_reg |
| original_reg <- removed_split_pseudo */ |
| { |
| if (lra_dump_file != NULL) |
| { |
| fprintf (lra_dump_file, " Removing %s:\n", |
| bitmap_bit_p (&lra_split_regs, sregno) |
| || bitmap_bit_p (&lra_split_regs, dregno) |
| ? "split" : "inheritance"); |
| dump_insn_slim (lra_dump_file, curr_insn); |
| } |
| delete_move_and_clobber (curr_insn, dregno); |
| done_p = true; |
| } |
| else if (bitmap_bit_p (remove_pseudos, sregno) |
| && bitmap_bit_p (&lra_inheritance_pseudos, sregno)) |
| { |
| /* Search the following pattern: |
| inherit_or_split_pseudo1 <- inherit_or_split_pseudo2 |
| original_pseudo <- inherit_or_split_pseudo1 |
| where the 2nd insn is the current insn and |
| inherit_or_split_pseudo2 is not removed. If it is found, |
| change the current insn onto: |
| original_pseudo <- inherit_or_split_pseudo2. */ |
| for (prev_insn = PREV_INSN (curr_insn); |
| prev_insn != NULL_RTX && ! NONDEBUG_INSN_P (prev_insn); |
| prev_insn = PREV_INSN (prev_insn)) |
| ; |
| if (prev_insn != NULL_RTX && BLOCK_FOR_INSN (prev_insn) == bb |
| && (prev_set = single_set (prev_insn)) != NULL_RTX |
| /* There should be no subregs in insn we are |
| searching because only the original reg might |
| be in subreg when we changed the mode of |
| load/store for splitting. */ |
| && REG_P (SET_DEST (prev_set)) |
| && REG_P (SET_SRC (prev_set)) |
| && (int) REGNO (SET_DEST (prev_set)) == sregno |
| && ((prev_sregno = REGNO (SET_SRC (prev_set))) |
| >= FIRST_PSEUDO_REGISTER) |
| && (lra_reg_info[prev_sregno].restore_rtx == NULL_RTX |
| || |
| /* As we consider chain of inheritance or |
| splitting described in above comment we should |
| check that sregno and prev_sregno were |
| inheritance/split pseudos created from the |
| same original regno. */ |
| (get_regno (lra_reg_info[sregno].restore_rtx) >= 0 |
| && (get_regno (lra_reg_info[sregno].restore_rtx) |
| == get_regno (lra_reg_info[prev_sregno].restore_rtx)))) |
| && ! bitmap_bit_p (remove_pseudos, prev_sregno)) |
| { |
| int restore_regno = get_regno (lra_reg_info[sregno].restore_rtx); |
| if (restore_regno < 0) |
| restore_regno = prev_sregno; |
| lra_assert (GET_MODE (SET_SRC (prev_set)) |
| == GET_MODE (regno_reg_rtx[restore_regno])); |
| /* Although we have a single set, the insn can |
| contain more one sregno register occurrence |
| as a source. Change all occurrences. */ |
| lra_substitute_pseudo_within_insn (curr_insn, sregno, |
| regno_reg_rtx[restore_regno], |
| false); |
| /* As we are finishing with processing the insn |
| here, check the destination too as it might |
| inheritance pseudo for another pseudo. */ |
| if (bitmap_bit_p (remove_pseudos, dregno) |
| && bitmap_bit_p (&lra_inheritance_pseudos, dregno) |
| && (restore_rtx |
| = lra_reg_info[dregno].restore_rtx) != NULL_RTX) |
| { |
| if (GET_CODE (SET_DEST (set)) == SUBREG) |
| SUBREG_REG (SET_DEST (set)) = restore_rtx; |
| else |
| SET_DEST (set) = restore_rtx; |
| } |
| lra_push_insn_and_update_insn_regno_info (curr_insn); |
| lra_set_used_insn_alternative_by_uid |
| (INSN_UID (curr_insn), LRA_UNKNOWN_ALT); |
| done_p = true; |
| if (lra_dump_file != NULL) |
| { |
| fprintf (lra_dump_file, " Change reload insn:\n"); |
| dump_insn_slim (lra_dump_file, curr_insn); |
| } |
| } |
| } |
| } |
| if (! done_p) |
| { |
| struct lra_insn_reg *reg; |
| bool restored_regs_p = false; |
| bool kept_regs_p = false; |
| |
| curr_id = lra_get_insn_recog_data (curr_insn); |
| for (reg = curr_id->regs; reg != NULL; reg = reg->next) |
| { |
| regno = reg->regno; |
| restore_rtx = lra_reg_info[regno].restore_rtx; |
| if (restore_rtx != NULL_RTX) |
| { |
| if (change_p && bitmap_bit_p (remove_pseudos, regno)) |
| { |
| lra_substitute_pseudo_within_insn |
| (curr_insn, regno, restore_rtx, false); |
| restored_regs_p = true; |
| } |
| else |
| kept_regs_p = true; |
| } |
| } |
| if (NONDEBUG_INSN_P (curr_insn) && kept_regs_p) |
| { |
| /* The instruction has changed since the previous |
| constraints pass. */ |
| lra_push_insn_and_update_insn_regno_info (curr_insn); |
| lra_set_used_insn_alternative_by_uid |
| (INSN_UID (curr_insn), LRA_UNKNOWN_ALT); |
| } |
| else if (restored_regs_p) |
| /* The instruction has been restored to the form that |
| it had during the previous constraints pass. */ |
| lra_update_insn_regno_info (curr_insn); |
| if (restored_regs_p && lra_dump_file != NULL) |
| { |
| fprintf (lra_dump_file, " Insn after restoring regs:\n"); |
| dump_insn_slim (lra_dump_file, curr_insn); |
| } |
| } |
| } |
| } |
| return change_p; |
| } |
| |
| /* If optional reload pseudos failed to get a hard register or was not |
| inherited, it is better to remove optional reloads. We do this |
| transformation after undoing inheritance to figure out necessity to |
| remove optional reloads easier. Return true if we do any |
| change. */ |
| static bool |
| undo_optional_reloads (void) |
| { |
| bool change_p, keep_p; |
| unsigned int regno, uid; |
| bitmap_iterator bi, bi2; |
| rtx_insn *insn; |
| rtx set, src, dest; |
| auto_bitmap removed_optional_reload_pseudos (®_obstack); |
| |
| bitmap_copy (removed_optional_reload_pseudos, &lra_optional_reload_pseudos); |
| EXECUTE_IF_SET_IN_BITMAP (&lra_optional_reload_pseudos, 0, regno, bi) |
| { |
| keep_p = false; |
| /* Keep optional reloads from previous subpasses. */ |
| if (lra_reg_info[regno].restore_rtx == NULL_RTX |
| /* If the original pseudo changed its allocation, just |
| removing the optional pseudo is dangerous as the original |
| pseudo will have longer live range. */ |
| || reg_renumber[REGNO (lra_reg_info[regno].restore_rtx)] >= 0) |
| keep_p = true; |
| else if (reg_renumber[regno] >= 0) |
| EXECUTE_IF_SET_IN_BITMAP (&lra_reg_info[regno].insn_bitmap, 0, uid, bi2) |
| { |
| insn = lra_insn_recog_data[uid]->insn; |
| if ((set = single_set (insn)) == NULL_RTX) |
| continue; |
| src = SET_SRC (set); |
| dest = SET_DEST (set); |
| if ((! REG_P (src) && ! SUBREG_P (src)) |
| || (! REG_P (dest) && ! SUBREG_P (dest))) |
| continue; |
| if (get_regno (dest) == (int) regno |
| /* Ignore insn for optional reloads itself. */ |
| && (get_regno (lra_reg_info[regno].restore_rtx) |
| != get_regno (src)) |
| /* Check only inheritance on last inheritance pass. */ |
| && get_regno (src) >= new_regno_start |
| /* Check that the optional reload was inherited. */ |
| && bitmap_bit_p (&lra_inheritance_pseudos, get_regno (src))) |
| { |
| keep_p = true; |
| break; |
| } |
| } |
| if (keep_p) |
| { |
| bitmap_clear_bit (removed_optional_reload_pseudos, regno); |
| if (lra_dump_file != NULL) |
| fprintf (lra_dump_file, "Keep optional reload reg %d\n", regno); |
| } |
| } |
| change_p = ! bitmap_empty_p (removed_optional_reload_pseudos); |
| auto_bitmap insn_bitmap (®_obstack); |
| EXECUTE_IF_SET_IN_BITMAP (removed_optional_reload_pseudos, 0, regno, bi) |
| { |
| if (lra_dump_file != NULL) |
| fprintf (lra_dump_file, "Remove optional reload reg %d\n", regno); |
| bitmap_copy (insn_bitmap, &lra_reg_info[regno].insn_bitmap); |
| EXECUTE_IF_SET_IN_BITMAP (insn_bitmap, 0, uid, bi2) |
| { |
| /* We may have already removed a clobber. */ |
| if (!lra_insn_recog_data[uid]) |
| continue; |
| insn = lra_insn_recog_data[uid]->insn; |
| if ((set = single_set (insn)) != NULL_RTX) |
| { |
| src = SET_SRC (set); |
| dest = SET_DEST (set); |
| if ((REG_P (src) || SUBREG_P (src)) |
| && (REG_P (dest) || SUBREG_P (dest)) |
| && ((get_regno (src) == (int) regno |
| && (get_regno (lra_reg_info[regno].restore_rtx) |
| == get_regno (dest))) |
| || (get_regno (dest) == (int) regno |
| && (get_regno (lra_reg_info[regno].restore_rtx) |
| == get_regno (src))))) |
| { |
| if (lra_dump_file != NULL) |
| { |
| fprintf (lra_dump_file, " Deleting move %u\n", |
| INSN_UID (insn)); |
| dump_insn_slim (lra_dump_file, insn); |
| } |
| delete_move_and_clobber (insn, get_regno (dest)); |
| continue; |
| } |
| /* We should not worry about generation memory-memory |
| moves here as if the corresponding inheritance did |
| not work (inheritance pseudo did not get a hard reg), |
| we remove the inheritance pseudo and the optional |
| reload. */ |
| } |
| if (GET_CODE (PATTERN (insn)) == CLOBBER |
| && REG_P (SET_DEST (insn)) |
| && get_regno (SET_DEST (insn)) == (int) regno) |
| /* Refuse to remap clobbers to preexisting pseudos. */ |
| gcc_unreachable (); |
| lra_substitute_pseudo_within_insn |
| (insn, regno, lra_reg_info[regno].restore_rtx, false); |
| lra_update_insn_regno_info (insn); |
| if (lra_dump_file != NULL) |
| { |
| fprintf (lra_dump_file, |
| " Restoring original insn:\n"); |
| dump_insn_slim (lra_dump_file, insn); |
| } |
| } |
| } |
| /* Clear restore_regnos. */ |
| EXECUTE_IF_SET_IN_BITMAP (&lra_optional_reload_pseudos, 0, regno, bi) |
| lra_reg_info[regno].restore_rtx = NULL_RTX; |
| return change_p; |
| } |
| |
| /* Entry function for undoing inheritance/split transformation. Return true |
| if we did any RTL change in this pass. */ |
| bool |
| lra_undo_inheritance (void) |
| { |
| unsigned int regno; |
| int hard_regno; |
| int n_all_inherit, n_inherit, n_all_split, n_split; |
| rtx restore_rtx; |
| bitmap_iterator bi; |
| bool change_p; |
| |
| lra_undo_inheritance_iter++; |
| if (lra_undo_inheritance_iter > LRA_MAX_INHERITANCE_PASSES) |
| return false; |
| if (lra_dump_file != NULL) |
| fprintf (lra_dump_file, |
| "\n********** Undoing inheritance #%d: **********\n\n", |
| lra_undo_inheritance_iter); |
| auto_bitmap remove_pseudos (®_obstack); |
| n_inherit = n_all_inherit = 0; |
| EXECUTE_IF_SET_IN_BITMAP (&lra_inheritance_pseudos, 0, regno, bi) |
| if (lra_reg_info[regno].restore_rtx != NULL_RTX) |
| { |
| n_all_inherit++; |
| if (reg_renumber[regno] < 0 |
| /* If the original pseudo changed its allocation, just |
| removing inheritance is dangerous as for changing |
| allocation we used shorter live-ranges. */ |
| && (! REG_P (lra_reg_info[regno].restore_rtx) |
| || reg_renumber[REGNO (lra_reg_info[regno].restore_rtx)] < 0)) |
| bitmap_set_bit (remove_pseudos, regno); |
| else |
| n_inherit++; |
| } |
| if (lra_dump_file != NULL && n_all_inherit != 0) |
| fprintf (lra_dump_file, "Inherit %d out of %d (%.2f%%)\n", |
| n_inherit, n_all_inherit, |
| (double) n_inherit / n_all_inherit * 100); |
| n_split = n_all_split = 0; |
| EXECUTE_IF_SET_IN_BITMAP (&lra_split_regs, 0, regno, bi) |
| if ((restore_rtx = lra_reg_info[regno].restore_rtx) != NULL_RTX) |
| { |
| int restore_regno = REGNO (restore_rtx); |
| |
| n_all_split++; |
| hard_regno = (restore_regno >= FIRST_PSEUDO_REGISTER |
| ? reg_renumber[restore_regno] : restore_regno); |
| if (hard_regno < 0 || reg_renumber[regno] == hard_regno) |
| bitmap_set_bit (remove_pseudos, regno); |
| else |
| { |
| n_split++; |
| if (lra_dump_file != NULL) |
| fprintf (lra_dump_file, " Keep split r%d (orig=r%d)\n", |
| regno, restore_regno); |
| } |
| } |
| if (lra_dump_file != NULL && n_all_split != 0) |
| fprintf (lra_dump_file, "Split %d out of %d (%.2f%%)\n", |
| n_split, n_all_split, |
| (double) n_split / n_all_split * 100); |
| change_p = remove_inheritance_pseudos (remove_pseudos); |
| /* Clear restore_regnos. */ |
| EXECUTE_IF_SET_IN_BITMAP (&lra_inheritance_pseudos, 0, regno, bi) |
| lra_reg_info[regno].restore_rtx = NULL_RTX; |
| EXECUTE_IF_SET_IN_BITMAP (&lra_split_regs, 0, regno, bi) |
| lra_reg_info[regno].restore_rtx = NULL_RTX; |
| change_p = undo_optional_reloads () || change_p; |
| if (change_p) |
| lra_dump_insns_if_possible ("changed func after undoing inheritance"); |
| return change_p; |
| } |