| /* Compute different info about registers. |
| Copyright (C) 1987-2018 Free Software Foundation, Inc. |
| |
| 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 regscan pass of the compiler and passes for |
| dealing with info about modes of pseudo-registers inside |
| subregisters. It also defines some tables of information about the |
| hardware registers, function init_reg_sets to initialize the |
| tables, and other auxiliary functions to deal with info about |
| registers and their classes. */ |
| |
| #include "config.h" |
| #include "system.h" |
| #include "coretypes.h" |
| #include "backend.h" |
| #include "target.h" |
| #include "rtl.h" |
| #include "tree.h" |
| #include "df.h" |
| #include "memmodel.h" |
| #include "tm_p.h" |
| #include "insn-config.h" |
| #include "regs.h" |
| #include "ira.h" |
| #include "recog.h" |
| #include "diagnostic-core.h" |
| #include "reload.h" |
| #include "output.h" |
| #include "tree-pass.h" |
| |
| /* Maximum register number used in this function, plus one. */ |
| |
| int max_regno; |
| |
| /* Used to cache the results of simplifiable_subregs. SHAPE is the input |
| parameter and SIMPLIFIABLE_REGS is the result. */ |
| struct simplifiable_subreg |
| { |
| simplifiable_subreg (const subreg_shape &); |
| |
| subreg_shape shape; |
| HARD_REG_SET simplifiable_regs; |
| }; |
| |
| struct target_hard_regs default_target_hard_regs; |
| struct target_regs default_target_regs; |
| #if SWITCHABLE_TARGET |
| struct target_hard_regs *this_target_hard_regs = &default_target_hard_regs; |
| struct target_regs *this_target_regs = &default_target_regs; |
| #endif |
| |
| /* Data for initializing fixed_regs. */ |
| static const char initial_fixed_regs[] = FIXED_REGISTERS; |
| |
| /* Data for initializing call_used_regs. */ |
| static const char initial_call_used_regs[] = CALL_USED_REGISTERS; |
| |
| #ifdef CALL_REALLY_USED_REGISTERS |
| /* Data for initializing call_really_used_regs. */ |
| static const char initial_call_really_used_regs[] = CALL_REALLY_USED_REGISTERS; |
| #endif |
| |
| #ifdef CALL_REALLY_USED_REGISTERS |
| #define CALL_REALLY_USED_REGNO_P(X) call_really_used_regs[X] |
| #else |
| #define CALL_REALLY_USED_REGNO_P(X) call_used_regs[X] |
| #endif |
| |
| /* Indexed by hard register number, contains 1 for registers |
| that are being used for global register decls. |
| These must be exempt from ordinary flow analysis |
| and are also considered fixed. */ |
| char global_regs[FIRST_PSEUDO_REGISTER]; |
| |
| /* Declaration for the global register. */ |
| tree global_regs_decl[FIRST_PSEUDO_REGISTER]; |
| |
| /* Same information as REGS_INVALIDATED_BY_CALL but in regset form to be used |
| in dataflow more conveniently. */ |
| regset regs_invalidated_by_call_regset; |
| |
| /* Same information as FIXED_REG_SET but in regset form. */ |
| regset fixed_reg_set_regset; |
| |
| /* The bitmap_obstack is used to hold some static variables that |
| should not be reset after each function is compiled. */ |
| static bitmap_obstack persistent_obstack; |
| |
| /* Used to initialize reg_alloc_order. */ |
| #ifdef REG_ALLOC_ORDER |
| static int initial_reg_alloc_order[FIRST_PSEUDO_REGISTER] = REG_ALLOC_ORDER; |
| #endif |
| |
| /* The same information, but as an array of unsigned ints. We copy from |
| these unsigned ints to the table above. We do this so the tm.h files |
| do not have to be aware of the wordsize for machines with <= 64 regs. |
| Note that we hard-code 32 here, not HOST_BITS_PER_INT. */ |
| #define N_REG_INTS \ |
| ((FIRST_PSEUDO_REGISTER + (32 - 1)) / 32) |
| |
| static const unsigned int_reg_class_contents[N_REG_CLASSES][N_REG_INTS] |
| = REG_CLASS_CONTENTS; |
| |
| /* Array containing all of the register names. */ |
| static const char *const initial_reg_names[] = REGISTER_NAMES; |
| |
| /* Array containing all of the register class names. */ |
| const char * reg_class_names[] = REG_CLASS_NAMES; |
| |
| /* No more global register variables may be declared; true once |
| reginfo has been initialized. */ |
| static int no_global_reg_vars = 0; |
| |
| /* Given a register bitmap, turn on the bits in a HARD_REG_SET that |
| correspond to the hard registers, if any, set in that map. This |
| could be done far more efficiently by having all sorts of special-cases |
| with moving single words, but probably isn't worth the trouble. */ |
| void |
| reg_set_to_hard_reg_set (HARD_REG_SET *to, const_bitmap from) |
| { |
| unsigned i; |
| bitmap_iterator bi; |
| |
| EXECUTE_IF_SET_IN_BITMAP (from, 0, i, bi) |
| { |
| if (i >= FIRST_PSEUDO_REGISTER) |
| return; |
| SET_HARD_REG_BIT (*to, i); |
| } |
| } |
| |
| /* Function called only once per target_globals to initialize the |
| target_hard_regs structure. Once this is done, various switches |
| may override. */ |
| void |
| init_reg_sets (void) |
| { |
| int i, j; |
| |
| /* First copy the register information from the initial int form into |
| the regsets. */ |
| |
| for (i = 0; i < N_REG_CLASSES; i++) |
| { |
| CLEAR_HARD_REG_SET (reg_class_contents[i]); |
| |
| /* Note that we hard-code 32 here, not HOST_BITS_PER_INT. */ |
| for (j = 0; j < FIRST_PSEUDO_REGISTER; j++) |
| if (int_reg_class_contents[i][j / 32] |
| & ((unsigned) 1 << (j % 32))) |
| SET_HARD_REG_BIT (reg_class_contents[i], j); |
| } |
| |
| /* Sanity check: make sure the target macros FIXED_REGISTERS and |
| CALL_USED_REGISTERS had the right number of initializers. */ |
| gcc_assert (sizeof fixed_regs == sizeof initial_fixed_regs); |
| gcc_assert (sizeof call_used_regs == sizeof initial_call_used_regs); |
| #ifdef CALL_REALLY_USED_REGISTERS |
| gcc_assert (sizeof call_really_used_regs |
| == sizeof initial_call_really_used_regs); |
| #endif |
| #ifdef REG_ALLOC_ORDER |
| gcc_assert (sizeof reg_alloc_order == sizeof initial_reg_alloc_order); |
| #endif |
| gcc_assert (sizeof reg_names == sizeof initial_reg_names); |
| |
| memcpy (fixed_regs, initial_fixed_regs, sizeof fixed_regs); |
| memcpy (call_used_regs, initial_call_used_regs, sizeof call_used_regs); |
| #ifdef CALL_REALLY_USED_REGISTERS |
| memcpy (call_really_used_regs, initial_call_really_used_regs, |
| sizeof call_really_used_regs); |
| #endif |
| #ifdef REG_ALLOC_ORDER |
| memcpy (reg_alloc_order, initial_reg_alloc_order, sizeof reg_alloc_order); |
| #endif |
| memcpy (reg_names, initial_reg_names, sizeof reg_names); |
| |
| SET_HARD_REG_SET (accessible_reg_set); |
| SET_HARD_REG_SET (operand_reg_set); |
| } |
| |
| /* We need to save copies of some of the register information which |
| can be munged by command-line switches so we can restore it during |
| subsequent back-end reinitialization. */ |
| static char saved_fixed_regs[FIRST_PSEUDO_REGISTER]; |
| static char saved_call_used_regs[FIRST_PSEUDO_REGISTER]; |
| #ifdef CALL_REALLY_USED_REGISTERS |
| static char saved_call_really_used_regs[FIRST_PSEUDO_REGISTER]; |
| #endif |
| static const char *saved_reg_names[FIRST_PSEUDO_REGISTER]; |
| static HARD_REG_SET saved_accessible_reg_set; |
| static HARD_REG_SET saved_operand_reg_set; |
| |
| /* Save the register information. */ |
| void |
| save_register_info (void) |
| { |
| /* Sanity check: make sure the target macros FIXED_REGISTERS and |
| CALL_USED_REGISTERS had the right number of initializers. */ |
| gcc_assert (sizeof fixed_regs == sizeof saved_fixed_regs); |
| gcc_assert (sizeof call_used_regs == sizeof saved_call_used_regs); |
| memcpy (saved_fixed_regs, fixed_regs, sizeof fixed_regs); |
| memcpy (saved_call_used_regs, call_used_regs, sizeof call_used_regs); |
| |
| /* Likewise for call_really_used_regs. */ |
| #ifdef CALL_REALLY_USED_REGISTERS |
| gcc_assert (sizeof call_really_used_regs |
| == sizeof saved_call_really_used_regs); |
| memcpy (saved_call_really_used_regs, call_really_used_regs, |
| sizeof call_really_used_regs); |
| #endif |
| |
| /* And similarly for reg_names. */ |
| gcc_assert (sizeof reg_names == sizeof saved_reg_names); |
| memcpy (saved_reg_names, reg_names, sizeof reg_names); |
| COPY_HARD_REG_SET (saved_accessible_reg_set, accessible_reg_set); |
| COPY_HARD_REG_SET (saved_operand_reg_set, operand_reg_set); |
| } |
| |
| /* Restore the register information. */ |
| static void |
| restore_register_info (void) |
| { |
| memcpy (fixed_regs, saved_fixed_regs, sizeof fixed_regs); |
| memcpy (call_used_regs, saved_call_used_regs, sizeof call_used_regs); |
| |
| #ifdef CALL_REALLY_USED_REGISTERS |
| memcpy (call_really_used_regs, saved_call_really_used_regs, |
| sizeof call_really_used_regs); |
| #endif |
| |
| memcpy (reg_names, saved_reg_names, sizeof reg_names); |
| COPY_HARD_REG_SET (accessible_reg_set, saved_accessible_reg_set); |
| COPY_HARD_REG_SET (operand_reg_set, saved_operand_reg_set); |
| } |
| |
| /* After switches have been processed, which perhaps alter |
| `fixed_regs' and `call_used_regs', convert them to HARD_REG_SETs. */ |
| static void |
| init_reg_sets_1 (void) |
| { |
| unsigned int i, j; |
| unsigned int /* machine_mode */ m; |
| |
| restore_register_info (); |
| |
| #ifdef REG_ALLOC_ORDER |
| for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) |
| inv_reg_alloc_order[reg_alloc_order[i]] = i; |
| #endif |
| |
| /* Let the target tweak things if necessary. */ |
| |
| targetm.conditional_register_usage (); |
| |
| /* Compute number of hard regs in each class. */ |
| |
| memset (reg_class_size, 0, sizeof reg_class_size); |
| for (i = 0; i < N_REG_CLASSES; i++) |
| { |
| bool any_nonfixed = false; |
| for (j = 0; j < FIRST_PSEUDO_REGISTER; j++) |
| if (TEST_HARD_REG_BIT (reg_class_contents[i], j)) |
| { |
| reg_class_size[i]++; |
| if (!fixed_regs[j]) |
| any_nonfixed = true; |
| } |
| class_only_fixed_regs[i] = !any_nonfixed; |
| } |
| |
| /* Initialize the table of subunions. |
| reg_class_subunion[I][J] gets the largest-numbered reg-class |
| that is contained in the union of classes I and J. */ |
| |
| memset (reg_class_subunion, 0, sizeof reg_class_subunion); |
| for (i = 0; i < N_REG_CLASSES; i++) |
| { |
| for (j = 0; j < N_REG_CLASSES; j++) |
| { |
| HARD_REG_SET c; |
| int k; |
| |
| COPY_HARD_REG_SET (c, reg_class_contents[i]); |
| IOR_HARD_REG_SET (c, reg_class_contents[j]); |
| for (k = 0; k < N_REG_CLASSES; k++) |
| if (hard_reg_set_subset_p (reg_class_contents[k], c) |
| && !hard_reg_set_subset_p (reg_class_contents[k], |
| reg_class_contents |
| [(int) reg_class_subunion[i][j]])) |
| reg_class_subunion[i][j] = (enum reg_class) k; |
| } |
| } |
| |
| /* Initialize the table of superunions. |
| reg_class_superunion[I][J] gets the smallest-numbered reg-class |
| containing the union of classes I and J. */ |
| |
| memset (reg_class_superunion, 0, sizeof reg_class_superunion); |
| for (i = 0; i < N_REG_CLASSES; i++) |
| { |
| for (j = 0; j < N_REG_CLASSES; j++) |
| { |
| HARD_REG_SET c; |
| int k; |
| |
| COPY_HARD_REG_SET (c, reg_class_contents[i]); |
| IOR_HARD_REG_SET (c, reg_class_contents[j]); |
| for (k = 0; k < N_REG_CLASSES; k++) |
| if (hard_reg_set_subset_p (c, reg_class_contents[k])) |
| break; |
| |
| reg_class_superunion[i][j] = (enum reg_class) k; |
| } |
| } |
| |
| /* Initialize the tables of subclasses and superclasses of each reg class. |
| First clear the whole table, then add the elements as they are found. */ |
| |
| for (i = 0; i < N_REG_CLASSES; i++) |
| { |
| for (j = 0; j < N_REG_CLASSES; j++) |
| reg_class_subclasses[i][j] = LIM_REG_CLASSES; |
| } |
| |
| for (i = 0; i < N_REG_CLASSES; i++) |
| { |
| if (i == (int) NO_REGS) |
| continue; |
| |
| for (j = i + 1; j < N_REG_CLASSES; j++) |
| if (hard_reg_set_subset_p (reg_class_contents[i], |
| reg_class_contents[j])) |
| { |
| /* Reg class I is a subclass of J. |
| Add J to the table of superclasses of I. */ |
| enum reg_class *p; |
| |
| /* Add I to the table of superclasses of J. */ |
| p = ®_class_subclasses[j][0]; |
| while (*p != LIM_REG_CLASSES) p++; |
| *p = (enum reg_class) i; |
| } |
| } |
| |
| /* Initialize "constant" tables. */ |
| |
| CLEAR_HARD_REG_SET (fixed_reg_set); |
| CLEAR_HARD_REG_SET (call_used_reg_set); |
| CLEAR_HARD_REG_SET (call_fixed_reg_set); |
| CLEAR_HARD_REG_SET (regs_invalidated_by_call); |
| if (!regs_invalidated_by_call_regset) |
| { |
| bitmap_obstack_initialize (&persistent_obstack); |
| regs_invalidated_by_call_regset = ALLOC_REG_SET (&persistent_obstack); |
| } |
| else |
| CLEAR_REG_SET (regs_invalidated_by_call_regset); |
| if (!fixed_reg_set_regset) |
| fixed_reg_set_regset = ALLOC_REG_SET (&persistent_obstack); |
| else |
| CLEAR_REG_SET (fixed_reg_set_regset); |
| |
| AND_HARD_REG_SET (operand_reg_set, accessible_reg_set); |
| for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) |
| { |
| /* As a special exception, registers whose class is NO_REGS are |
| not accepted by `register_operand'. The reason for this change |
| is to allow the representation of special architecture artifacts |
| (such as a condition code register) without extending the rtl |
| definitions. Since registers of class NO_REGS cannot be used |
| as registers in any case where register classes are examined, |
| it is better to apply this exception in a target-independent way. */ |
| if (REGNO_REG_CLASS (i) == NO_REGS) |
| CLEAR_HARD_REG_BIT (operand_reg_set, i); |
| |
| /* If a register is too limited to be treated as a register operand, |
| then it should never be allocated to a pseudo. */ |
| if (!TEST_HARD_REG_BIT (operand_reg_set, i)) |
| { |
| fixed_regs[i] = 1; |
| call_used_regs[i] = 1; |
| } |
| |
| /* call_used_regs must include fixed_regs. */ |
| gcc_assert (!fixed_regs[i] || call_used_regs[i]); |
| #ifdef CALL_REALLY_USED_REGISTERS |
| /* call_used_regs must include call_really_used_regs. */ |
| gcc_assert (!call_really_used_regs[i] || call_used_regs[i]); |
| #endif |
| |
| if (fixed_regs[i]) |
| { |
| SET_HARD_REG_BIT (fixed_reg_set, i); |
| SET_REGNO_REG_SET (fixed_reg_set_regset, i); |
| } |
| |
| if (call_used_regs[i]) |
| SET_HARD_REG_BIT (call_used_reg_set, i); |
| |
| /* There are a couple of fixed registers that we know are safe to |
| exclude from being clobbered by calls: |
| |
| The frame pointer is always preserved across calls. The arg |
| pointer is if it is fixed. The stack pointer usually is, |
| unless TARGET_RETURN_POPS_ARGS, in which case an explicit |
| CLOBBER will be present. If we are generating PIC code, the |
| PIC offset table register is preserved across calls, though the |
| target can override that. */ |
| |
| if (i == STACK_POINTER_REGNUM) |
| ; |
| else if (global_regs[i]) |
| { |
| SET_HARD_REG_BIT (regs_invalidated_by_call, i); |
| SET_REGNO_REG_SET (regs_invalidated_by_call_regset, i); |
| } |
| else if (i == FRAME_POINTER_REGNUM) |
| ; |
| else if (!HARD_FRAME_POINTER_IS_FRAME_POINTER |
| && i == HARD_FRAME_POINTER_REGNUM) |
| ; |
| else if (FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM |
| && i == ARG_POINTER_REGNUM && fixed_regs[i]) |
| ; |
| else if (!PIC_OFFSET_TABLE_REG_CALL_CLOBBERED |
| && i == (unsigned) PIC_OFFSET_TABLE_REGNUM && fixed_regs[i]) |
| ; |
| else if (CALL_REALLY_USED_REGNO_P (i)) |
| { |
| SET_HARD_REG_BIT (regs_invalidated_by_call, i); |
| SET_REGNO_REG_SET (regs_invalidated_by_call_regset, i); |
| } |
| } |
| |
| COPY_HARD_REG_SET (call_fixed_reg_set, fixed_reg_set); |
| COPY_HARD_REG_SET (fixed_nonglobal_reg_set, fixed_reg_set); |
| |
| /* Preserve global registers if called more than once. */ |
| for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) |
| { |
| if (global_regs[i]) |
| { |
| fixed_regs[i] = call_used_regs[i] = 1; |
| SET_HARD_REG_BIT (fixed_reg_set, i); |
| SET_HARD_REG_BIT (call_used_reg_set, i); |
| SET_HARD_REG_BIT (call_fixed_reg_set, i); |
| } |
| } |
| |
| memset (have_regs_of_mode, 0, sizeof (have_regs_of_mode)); |
| memset (contains_reg_of_mode, 0, sizeof (contains_reg_of_mode)); |
| for (m = 0; m < (unsigned int) MAX_MACHINE_MODE; m++) |
| { |
| HARD_REG_SET ok_regs, ok_regs2; |
| CLEAR_HARD_REG_SET (ok_regs); |
| CLEAR_HARD_REG_SET (ok_regs2); |
| for (j = 0; j < FIRST_PSEUDO_REGISTER; j++) |
| if (!TEST_HARD_REG_BIT (fixed_nonglobal_reg_set, j) |
| && targetm.hard_regno_mode_ok (j, (machine_mode) m)) |
| { |
| SET_HARD_REG_BIT (ok_regs, j); |
| if (!fixed_regs[j]) |
| SET_HARD_REG_BIT (ok_regs2, j); |
| } |
| |
| for (i = 0; i < N_REG_CLASSES; i++) |
| if ((targetm.class_max_nregs ((reg_class_t) i, (machine_mode) m) |
| <= reg_class_size[i]) |
| && hard_reg_set_intersect_p (ok_regs, reg_class_contents[i])) |
| { |
| contains_reg_of_mode[i][m] = 1; |
| if (hard_reg_set_intersect_p (ok_regs2, reg_class_contents[i])) |
| { |
| have_regs_of_mode[m] = 1; |
| contains_allocatable_reg_of_mode[i][m] = 1; |
| } |
| } |
| } |
| } |
| |
| /* Compute the table of register modes. |
| These values are used to record death information for individual registers |
| (as opposed to a multi-register mode). |
| This function might be invoked more than once, if the target has support |
| for changing register usage conventions on a per-function basis. |
| */ |
| void |
| init_reg_modes_target (void) |
| { |
| int i, j; |
| |
| for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) |
| for (j = 0; j < MAX_MACHINE_MODE; j++) |
| this_target_regs->x_hard_regno_nregs[i][j] |
| = targetm.hard_regno_nregs (i, (machine_mode) j); |
| |
| for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) |
| { |
| reg_raw_mode[i] = choose_hard_reg_mode (i, 1, false); |
| |
| /* If we couldn't find a valid mode, just use the previous mode |
| if it is suitable, otherwise fall back on word_mode. */ |
| if (reg_raw_mode[i] == VOIDmode) |
| { |
| if (i > 0 && hard_regno_nregs (i, reg_raw_mode[i - 1]) == 1) |
| reg_raw_mode[i] = reg_raw_mode[i - 1]; |
| else |
| reg_raw_mode[i] = word_mode; |
| } |
| } |
| } |
| |
| /* Finish initializing the register sets and initialize the register modes. |
| This function might be invoked more than once, if the target has support |
| for changing register usage conventions on a per-function basis. |
| */ |
| void |
| init_regs (void) |
| { |
| /* This finishes what was started by init_reg_sets, but couldn't be done |
| until after register usage was specified. */ |
| init_reg_sets_1 (); |
| } |
| |
| /* The same as previous function plus initializing IRA. */ |
| void |
| reinit_regs (void) |
| { |
| init_regs (); |
| /* caller_save needs to be re-initialized. */ |
| caller_save_initialized_p = false; |
| if (this_target_rtl->target_specific_initialized) |
| { |
| ira_init (); |
| recog_init (); |
| } |
| } |
| |
| /* Initialize some fake stack-frame MEM references for use in |
| memory_move_secondary_cost. */ |
| void |
| init_fake_stack_mems (void) |
| { |
| int i; |
| |
| for (i = 0; i < MAX_MACHINE_MODE; i++) |
| top_of_stack[i] = gen_rtx_MEM ((machine_mode) i, stack_pointer_rtx); |
| } |
| |
| |
| /* Compute cost of moving data from a register of class FROM to one of |
| TO, using MODE. */ |
| |
| int |
| register_move_cost (machine_mode mode, reg_class_t from, reg_class_t to) |
| { |
| return targetm.register_move_cost (mode, from, to); |
| } |
| |
| /* Compute cost of moving registers to/from memory. */ |
| |
| int |
| memory_move_cost (machine_mode mode, reg_class_t rclass, bool in) |
| { |
| return targetm.memory_move_cost (mode, rclass, in); |
| } |
| |
| /* Compute extra cost of moving registers to/from memory due to reloads. |
| Only needed if secondary reloads are required for memory moves. */ |
| int |
| memory_move_secondary_cost (machine_mode mode, reg_class_t rclass, |
| bool in) |
| { |
| reg_class_t altclass; |
| int partial_cost = 0; |
| /* We need a memory reference to feed to SECONDARY... macros. */ |
| /* mem may be unused even if the SECONDARY_ macros are defined. */ |
| rtx mem ATTRIBUTE_UNUSED = top_of_stack[(int) mode]; |
| |
| altclass = secondary_reload_class (in ? 1 : 0, rclass, mode, mem); |
| |
| if (altclass == NO_REGS) |
| return 0; |
| |
| if (in) |
| partial_cost = register_move_cost (mode, altclass, rclass); |
| else |
| partial_cost = register_move_cost (mode, rclass, altclass); |
| |
| if (rclass == altclass) |
| /* This isn't simply a copy-to-temporary situation. Can't guess |
| what it is, so TARGET_MEMORY_MOVE_COST really ought not to be |
| calling here in that case. |
| |
| I'm tempted to put in an assert here, but returning this will |
| probably only give poor estimates, which is what we would've |
| had before this code anyways. */ |
| return partial_cost; |
| |
| /* Check if the secondary reload register will also need a |
| secondary reload. */ |
| return memory_move_secondary_cost (mode, altclass, in) + partial_cost; |
| } |
| |
| /* Return a machine mode that is legitimate for hard reg REGNO and large |
| enough to save nregs. If we can't find one, return VOIDmode. |
| If CALL_SAVED is true, only consider modes that are call saved. */ |
| machine_mode |
| choose_hard_reg_mode (unsigned int regno ATTRIBUTE_UNUSED, |
| unsigned int nregs, bool call_saved) |
| { |
| unsigned int /* machine_mode */ m; |
| machine_mode found_mode = VOIDmode, mode; |
| |
| /* We first look for the largest integer mode that can be validly |
| held in REGNO. If none, we look for the largest floating-point mode. |
| If we still didn't find a valid mode, try CCmode. |
| |
| The tests use maybe_gt rather than known_gt because we want (for example) |
| N V4SFs to win over plain V4SF even though N might be 1. */ |
| FOR_EACH_MODE_IN_CLASS (mode, MODE_INT) |
| if (hard_regno_nregs (regno, mode) == nregs |
| && targetm.hard_regno_mode_ok (regno, mode) |
| && (!call_saved |
| || !targetm.hard_regno_call_part_clobbered (regno, mode)) |
| && maybe_gt (GET_MODE_SIZE (mode), GET_MODE_SIZE (found_mode))) |
| found_mode = mode; |
| |
| FOR_EACH_MODE_IN_CLASS (mode, MODE_FLOAT) |
| if (hard_regno_nregs (regno, mode) == nregs |
| && targetm.hard_regno_mode_ok (regno, mode) |
| && (!call_saved |
| || !targetm.hard_regno_call_part_clobbered (regno, mode)) |
| && maybe_gt (GET_MODE_SIZE (mode), GET_MODE_SIZE (found_mode))) |
| found_mode = mode; |
| |
| FOR_EACH_MODE_IN_CLASS (mode, MODE_VECTOR_FLOAT) |
| if (hard_regno_nregs (regno, mode) == nregs |
| && targetm.hard_regno_mode_ok (regno, mode) |
| && (!call_saved |
| || !targetm.hard_regno_call_part_clobbered (regno, mode)) |
| && maybe_gt (GET_MODE_SIZE (mode), GET_MODE_SIZE (found_mode))) |
| found_mode = mode; |
| |
| FOR_EACH_MODE_IN_CLASS (mode, MODE_VECTOR_INT) |
| if (hard_regno_nregs (regno, mode) == nregs |
| && targetm.hard_regno_mode_ok (regno, mode) |
| && (!call_saved |
| || !targetm.hard_regno_call_part_clobbered (regno, mode)) |
| && maybe_gt (GET_MODE_SIZE (mode), GET_MODE_SIZE (found_mode))) |
| found_mode = mode; |
| |
| if (found_mode != VOIDmode) |
| return found_mode; |
| |
| /* Iterate over all of the CCmodes. */ |
| for (m = (unsigned int) CCmode; m < (unsigned int) NUM_MACHINE_MODES; ++m) |
| { |
| mode = (machine_mode) m; |
| if (hard_regno_nregs (regno, mode) == nregs |
| && targetm.hard_regno_mode_ok (regno, mode) |
| && (!call_saved |
| || !targetm.hard_regno_call_part_clobbered (regno, mode))) |
| return mode; |
| } |
| |
| /* We can't find a mode valid for this register. */ |
| return VOIDmode; |
| } |
| |
| /* Specify the usage characteristics of the register named NAME. |
| It should be a fixed register if FIXED and a |
| call-used register if CALL_USED. */ |
| void |
| fix_register (const char *name, int fixed, int call_used) |
| { |
| int i; |
| int reg, nregs; |
| |
| /* Decode the name and update the primary form of |
| the register info. */ |
| |
| if ((reg = decode_reg_name_and_count (name, &nregs)) >= 0) |
| { |
| gcc_assert (nregs >= 1); |
| for (i = reg; i < reg + nregs; i++) |
| { |
| if ((i == STACK_POINTER_REGNUM |
| #ifdef HARD_FRAME_POINTER_REGNUM |
| || i == HARD_FRAME_POINTER_REGNUM |
| #else |
| || i == FRAME_POINTER_REGNUM |
| #endif |
| ) |
| && (fixed == 0 || call_used == 0)) |
| { |
| switch (fixed) |
| { |
| case 0: |
| switch (call_used) |
| { |
| case 0: |
| error ("can%'t use %qs as a call-saved register", name); |
| break; |
| |
| case 1: |
| error ("can%'t use %qs as a call-used register", name); |
| break; |
| |
| default: |
| gcc_unreachable (); |
| } |
| break; |
| |
| case 1: |
| switch (call_used) |
| { |
| case 1: |
| error ("can%'t use %qs as a fixed register", name); |
| break; |
| |
| case 0: |
| default: |
| gcc_unreachable (); |
| } |
| break; |
| |
| default: |
| gcc_unreachable (); |
| } |
| } |
| else |
| { |
| fixed_regs[i] = fixed; |
| call_used_regs[i] = call_used; |
| #ifdef CALL_REALLY_USED_REGISTERS |
| if (fixed == 0) |
| call_really_used_regs[i] = call_used; |
| #endif |
| } |
| } |
| } |
| else |
| { |
| warning (0, "unknown register name: %s", name); |
| } |
| } |
| |
| /* Mark register number I as global. */ |
| void |
| globalize_reg (tree decl, int i) |
| { |
| location_t loc = DECL_SOURCE_LOCATION (decl); |
| |
| #ifdef STACK_REGS |
| if (IN_RANGE (i, FIRST_STACK_REG, LAST_STACK_REG)) |
| { |
| error ("stack register used for global register variable"); |
| return; |
| } |
| #endif |
| |
| if (fixed_regs[i] == 0 && no_global_reg_vars) |
| error_at (loc, "global register variable follows a function definition"); |
| |
| if (global_regs[i]) |
| { |
| warning_at (loc, 0, |
| "register of %qD used for multiple global register variables", |
| decl); |
| inform (DECL_SOURCE_LOCATION (global_regs_decl[i]), |
| "conflicts with %qD", global_regs_decl[i]); |
| return; |
| } |
| |
| if (call_used_regs[i] && ! fixed_regs[i]) |
| warning_at (loc, 0, "call-clobbered register used for global register variable"); |
| |
| global_regs[i] = 1; |
| global_regs_decl[i] = decl; |
| |
| /* If we're globalizing the frame pointer, we need to set the |
| appropriate regs_invalidated_by_call bit, even if it's already |
| set in fixed_regs. */ |
| if (i != STACK_POINTER_REGNUM) |
| { |
| SET_HARD_REG_BIT (regs_invalidated_by_call, i); |
| SET_REGNO_REG_SET (regs_invalidated_by_call_regset, i); |
| } |
| |
| /* If already fixed, nothing else to do. */ |
| if (fixed_regs[i]) |
| return; |
| |
| fixed_regs[i] = call_used_regs[i] = 1; |
| #ifdef CALL_REALLY_USED_REGISTERS |
| call_really_used_regs[i] = 1; |
| #endif |
| |
| SET_HARD_REG_BIT (fixed_reg_set, i); |
| SET_HARD_REG_BIT (call_used_reg_set, i); |
| SET_HARD_REG_BIT (call_fixed_reg_set, i); |
| |
| reinit_regs (); |
| } |
| |
| |
| /* Structure used to record preferences of given pseudo. */ |
| struct reg_pref |
| { |
| /* (enum reg_class) prefclass is the preferred class. May be |
| NO_REGS if no class is better than memory. */ |
| char prefclass; |
| |
| /* altclass is a register class that we should use for allocating |
| pseudo if no register in the preferred class is available. |
| If no register in this class is available, memory is preferred. |
| |
| It might appear to be more general to have a bitmask of classes here, |
| but since it is recommended that there be a class corresponding to the |
| union of most major pair of classes, that generality is not required. */ |
| char altclass; |
| |
| /* allocnoclass is a register class that IRA uses for allocating |
| the pseudo. */ |
| char allocnoclass; |
| }; |
| |
| /* Record preferences of each pseudo. This is available after RA is |
| run. */ |
| static struct reg_pref *reg_pref; |
| |
| /* Current size of reg_info. */ |
| static int reg_info_size; |
| /* Max_reg_num still last resize_reg_info call. */ |
| static int max_regno_since_last_resize; |
| |
| /* Return the reg_class in which pseudo reg number REGNO is best allocated. |
| This function is sometimes called before the info has been computed. |
| When that happens, just return GENERAL_REGS, which is innocuous. */ |
| enum reg_class |
| reg_preferred_class (int regno) |
| { |
| if (reg_pref == 0) |
| return GENERAL_REGS; |
| |
| gcc_assert (regno < reg_info_size); |
| return (enum reg_class) reg_pref[regno].prefclass; |
| } |
| |
| enum reg_class |
| reg_alternate_class (int regno) |
| { |
| if (reg_pref == 0) |
| return ALL_REGS; |
| |
| gcc_assert (regno < reg_info_size); |
| return (enum reg_class) reg_pref[regno].altclass; |
| } |
| |
| /* Return the reg_class which is used by IRA for its allocation. */ |
| enum reg_class |
| reg_allocno_class (int regno) |
| { |
| if (reg_pref == 0) |
| return NO_REGS; |
| |
| gcc_assert (regno < reg_info_size); |
| return (enum reg_class) reg_pref[regno].allocnoclass; |
| } |
| |
| |
| |
| /* Allocate space for reg info and initilize it. */ |
| static void |
| allocate_reg_info (void) |
| { |
| int i; |
| |
| max_regno_since_last_resize = max_reg_num (); |
| reg_info_size = max_regno_since_last_resize * 3 / 2 + 1; |
| gcc_assert (! reg_pref && ! reg_renumber); |
| reg_renumber = XNEWVEC (short, reg_info_size); |
| reg_pref = XCNEWVEC (struct reg_pref, reg_info_size); |
| memset (reg_renumber, -1, reg_info_size * sizeof (short)); |
| for (i = 0; i < reg_info_size; i++) |
| { |
| reg_pref[i].prefclass = GENERAL_REGS; |
| reg_pref[i].altclass = ALL_REGS; |
| reg_pref[i].allocnoclass = GENERAL_REGS; |
| } |
| } |
| |
| |
| /* Resize reg info. The new elements will be initialized. Return TRUE |
| if new pseudos were added since the last call. */ |
| bool |
| resize_reg_info (void) |
| { |
| int old, i; |
| bool change_p; |
| |
| if (reg_pref == NULL) |
| { |
| allocate_reg_info (); |
| return true; |
| } |
| change_p = max_regno_since_last_resize != max_reg_num (); |
| max_regno_since_last_resize = max_reg_num (); |
| if (reg_info_size >= max_reg_num ()) |
| return change_p; |
| old = reg_info_size; |
| reg_info_size = max_reg_num () * 3 / 2 + 1; |
| gcc_assert (reg_pref && reg_renumber); |
| reg_renumber = XRESIZEVEC (short, reg_renumber, reg_info_size); |
| reg_pref = XRESIZEVEC (struct reg_pref, reg_pref, reg_info_size); |
| memset (reg_pref + old, -1, |
| (reg_info_size - old) * sizeof (struct reg_pref)); |
| memset (reg_renumber + old, -1, (reg_info_size - old) * sizeof (short)); |
| for (i = old; i < reg_info_size; i++) |
| { |
| reg_pref[i].prefclass = GENERAL_REGS; |
| reg_pref[i].altclass = ALL_REGS; |
| reg_pref[i].allocnoclass = GENERAL_REGS; |
| } |
| return true; |
| } |
| |
| |
| /* Free up the space allocated by allocate_reg_info. */ |
| void |
| free_reg_info (void) |
| { |
| if (reg_pref) |
| { |
| free (reg_pref); |
| reg_pref = NULL; |
| } |
| |
| if (reg_renumber) |
| { |
| free (reg_renumber); |
| reg_renumber = NULL; |
| } |
| } |
| |
| /* Initialize some global data for this pass. */ |
| static unsigned int |
| reginfo_init (void) |
| { |
| if (df) |
| df_compute_regs_ever_live (true); |
| |
| /* This prevents dump_reg_info from losing if called |
| before reginfo is run. */ |
| reg_pref = NULL; |
| reg_info_size = max_regno_since_last_resize = 0; |
| /* No more global register variables may be declared. */ |
| no_global_reg_vars = 1; |
| return 1; |
| } |
| |
| namespace { |
| |
| const pass_data pass_data_reginfo_init = |
| { |
| RTL_PASS, /* type */ |
| "reginfo", /* name */ |
| OPTGROUP_NONE, /* optinfo_flags */ |
| TV_NONE, /* tv_id */ |
| 0, /* properties_required */ |
| 0, /* properties_provided */ |
| 0, /* properties_destroyed */ |
| 0, /* todo_flags_start */ |
| 0, /* todo_flags_finish */ |
| }; |
| |
| class pass_reginfo_init : public rtl_opt_pass |
| { |
| public: |
| pass_reginfo_init (gcc::context *ctxt) |
| : rtl_opt_pass (pass_data_reginfo_init, ctxt) |
| {} |
| |
| /* opt_pass methods: */ |
| virtual unsigned int execute (function *) { return reginfo_init (); } |
| |
| }; // class pass_reginfo_init |
| |
| } // anon namespace |
| |
| rtl_opt_pass * |
| make_pass_reginfo_init (gcc::context *ctxt) |
| { |
| return new pass_reginfo_init (ctxt); |
| } |
| |
| |
| |
| /* Set up preferred, alternate, and allocno classes for REGNO as |
| PREFCLASS, ALTCLASS, and ALLOCNOCLASS. */ |
| void |
| setup_reg_classes (int regno, |
| enum reg_class prefclass, enum reg_class altclass, |
| enum reg_class allocnoclass) |
| { |
| if (reg_pref == NULL) |
| return; |
| gcc_assert (reg_info_size >= max_reg_num ()); |
| reg_pref[regno].prefclass = prefclass; |
| reg_pref[regno].altclass = altclass; |
| reg_pref[regno].allocnoclass = allocnoclass; |
| } |
| |
| |
| /* This is the `regscan' pass of the compiler, run just before cse and |
| again just before loop. It finds the first and last use of each |
| pseudo-register. */ |
| |
| static void reg_scan_mark_refs (rtx, rtx_insn *); |
| |
| void |
| reg_scan (rtx_insn *f, unsigned int nregs ATTRIBUTE_UNUSED) |
| { |
| rtx_insn *insn; |
| |
| timevar_push (TV_REG_SCAN); |
| |
| for (insn = f; insn; insn = NEXT_INSN (insn)) |
| if (INSN_P (insn)) |
| { |
| reg_scan_mark_refs (PATTERN (insn), insn); |
| if (REG_NOTES (insn)) |
| reg_scan_mark_refs (REG_NOTES (insn), insn); |
| } |
| |
| timevar_pop (TV_REG_SCAN); |
| } |
| |
| |
| /* X is the expression to scan. INSN is the insn it appears in. |
| NOTE_FLAG is nonzero if X is from INSN's notes rather than its body. |
| We should only record information for REGs with numbers |
| greater than or equal to MIN_REGNO. */ |
| static void |
| reg_scan_mark_refs (rtx x, rtx_insn *insn) |
| { |
| enum rtx_code code; |
| rtx dest; |
| rtx note; |
| |
| if (!x) |
| return; |
| code = GET_CODE (x); |
| switch (code) |
| { |
| case CONST: |
| CASE_CONST_ANY: |
| case CC0: |
| case PC: |
| case SYMBOL_REF: |
| case LABEL_REF: |
| case ADDR_VEC: |
| case ADDR_DIFF_VEC: |
| case REG: |
| return; |
| |
| case EXPR_LIST: |
| if (XEXP (x, 0)) |
| reg_scan_mark_refs (XEXP (x, 0), insn); |
| if (XEXP (x, 1)) |
| reg_scan_mark_refs (XEXP (x, 1), insn); |
| break; |
| |
| case INSN_LIST: |
| case INT_LIST: |
| if (XEXP (x, 1)) |
| reg_scan_mark_refs (XEXP (x, 1), insn); |
| break; |
| |
| case CLOBBER: |
| if (MEM_P (XEXP (x, 0))) |
| reg_scan_mark_refs (XEXP (XEXP (x, 0), 0), insn); |
| break; |
| |
| case SET: |
| /* Count a set of the destination if it is a register. */ |
| for (dest = SET_DEST (x); |
| GET_CODE (dest) == SUBREG || GET_CODE (dest) == STRICT_LOW_PART |
| || GET_CODE (dest) == ZERO_EXTRACT; |
| dest = XEXP (dest, 0)) |
| ; |
| |
| /* If this is setting a pseudo from another pseudo or the sum of a |
| pseudo and a constant integer and the other pseudo is known to be |
| a pointer, set the destination to be a pointer as well. |
| |
| Likewise if it is setting the destination from an address or from a |
| value equivalent to an address or to the sum of an address and |
| something else. |
| |
| But don't do any of this if the pseudo corresponds to a user |
| variable since it should have already been set as a pointer based |
| on the type. */ |
| |
| if (REG_P (SET_DEST (x)) |
| && REGNO (SET_DEST (x)) >= FIRST_PSEUDO_REGISTER |
| /* If the destination pseudo is set more than once, then other |
| sets might not be to a pointer value (consider access to a |
| union in two threads of control in the presence of global |
| optimizations). So only set REG_POINTER on the destination |
| pseudo if this is the only set of that pseudo. */ |
| && DF_REG_DEF_COUNT (REGNO (SET_DEST (x))) == 1 |
| && ! REG_USERVAR_P (SET_DEST (x)) |
| && ! REG_POINTER (SET_DEST (x)) |
| && ((REG_P (SET_SRC (x)) |
| && REG_POINTER (SET_SRC (x))) |
| || ((GET_CODE (SET_SRC (x)) == PLUS |
| || GET_CODE (SET_SRC (x)) == LO_SUM) |
| && CONST_INT_P (XEXP (SET_SRC (x), 1)) |
| && REG_P (XEXP (SET_SRC (x), 0)) |
| && REG_POINTER (XEXP (SET_SRC (x), 0))) |
| || GET_CODE (SET_SRC (x)) == CONST |
| || GET_CODE (SET_SRC (x)) == SYMBOL_REF |
| || GET_CODE (SET_SRC (x)) == LABEL_REF |
| || (GET_CODE (SET_SRC (x)) == HIGH |
| && (GET_CODE (XEXP (SET_SRC (x), 0)) == CONST |
| || GET_CODE (XEXP (SET_SRC (x), 0)) == SYMBOL_REF |
| || GET_CODE (XEXP (SET_SRC (x), 0)) == LABEL_REF)) |
| || ((GET_CODE (SET_SRC (x)) == PLUS |
| || GET_CODE (SET_SRC (x)) == LO_SUM) |
| && (GET_CODE (XEXP (SET_SRC (x), 1)) == CONST |
| || GET_CODE (XEXP (SET_SRC (x), 1)) == SYMBOL_REF |
| || GET_CODE (XEXP (SET_SRC (x), 1)) == LABEL_REF)) |
| || ((note = find_reg_note (insn, REG_EQUAL, 0)) != 0 |
| && (GET_CODE (XEXP (note, 0)) == CONST |
| || GET_CODE (XEXP (note, 0)) == SYMBOL_REF |
| || GET_CODE (XEXP (note, 0)) == LABEL_REF)))) |
| REG_POINTER (SET_DEST (x)) = 1; |
| |
| /* If this is setting a register from a register or from a simple |
| conversion of a register, propagate REG_EXPR. */ |
| if (REG_P (dest) && !REG_ATTRS (dest)) |
| set_reg_attrs_from_value (dest, SET_SRC (x)); |
| |
| /* fall through */ |
| |
| default: |
| { |
| const char *fmt = GET_RTX_FORMAT (code); |
| int i; |
| for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) |
| { |
| if (fmt[i] == 'e') |
| reg_scan_mark_refs (XEXP (x, i), insn); |
| else if (fmt[i] == 'E' && XVEC (x, i) != 0) |
| { |
| int j; |
| for (j = XVECLEN (x, i) - 1; j >= 0; j--) |
| reg_scan_mark_refs (XVECEXP (x, i, j), insn); |
| } |
| } |
| } |
| } |
| } |
| |
| |
| /* Return nonzero if C1 is a subset of C2, i.e., if every register in C1 |
| is also in C2. */ |
| int |
| reg_class_subset_p (reg_class_t c1, reg_class_t c2) |
| { |
| return (c1 == c2 |
| || c2 == ALL_REGS |
| || hard_reg_set_subset_p (reg_class_contents[(int) c1], |
| reg_class_contents[(int) c2])); |
| } |
| |
| /* Return nonzero if there is a register that is in both C1 and C2. */ |
| int |
| reg_classes_intersect_p (reg_class_t c1, reg_class_t c2) |
| { |
| return (c1 == c2 |
| || c1 == ALL_REGS |
| || c2 == ALL_REGS |
| || hard_reg_set_intersect_p (reg_class_contents[(int) c1], |
| reg_class_contents[(int) c2])); |
| } |
| |
| |
| inline hashval_t |
| simplifiable_subregs_hasher::hash (const simplifiable_subreg *value) |
| { |
| inchash::hash h; |
| h.add_hwi (value->shape.unique_id ()); |
| return h.end (); |
| } |
| |
| inline bool |
| simplifiable_subregs_hasher::equal (const simplifiable_subreg *value, |
| const subreg_shape *compare) |
| { |
| return value->shape == *compare; |
| } |
| |
| inline simplifiable_subreg::simplifiable_subreg (const subreg_shape &shape_in) |
| : shape (shape_in) |
| { |
| CLEAR_HARD_REG_SET (simplifiable_regs); |
| } |
| |
| /* Return the set of hard registers that are able to form the subreg |
| described by SHAPE. */ |
| |
| const HARD_REG_SET & |
| simplifiable_subregs (const subreg_shape &shape) |
| { |
| if (!this_target_hard_regs->x_simplifiable_subregs) |
| this_target_hard_regs->x_simplifiable_subregs |
| = new hash_table <simplifiable_subregs_hasher> (30); |
| inchash::hash h; |
| h.add_hwi (shape.unique_id ()); |
| simplifiable_subreg **slot |
| = (this_target_hard_regs->x_simplifiable_subregs |
| ->find_slot_with_hash (&shape, h.end (), INSERT)); |
| |
| if (!*slot) |
| { |
| simplifiable_subreg *info = new simplifiable_subreg (shape); |
| for (unsigned int i = 0; i < FIRST_PSEUDO_REGISTER; ++i) |
| if (targetm.hard_regno_mode_ok (i, shape.inner_mode) |
| && simplify_subreg_regno (i, shape.inner_mode, shape.offset, |
| shape.outer_mode) >= 0) |
| SET_HARD_REG_BIT (info->simplifiable_regs, i); |
| *slot = info; |
| } |
| return (*slot)->simplifiable_regs; |
| } |
| |
| /* Passes for keeping and updating info about modes of registers |
| inside subregisters. */ |
| |
| static HARD_REG_SET **valid_mode_changes; |
| static obstack valid_mode_changes_obstack; |
| |
| /* Restrict the choice of register for SUBREG_REG (SUBREG) based |
| on information about SUBREG. |
| |
| If PARTIAL_DEF, SUBREG is a partial definition of a multipart inner |
| register and we want to ensure that the other parts of the inner |
| register are correctly preserved. If !PARTIAL_DEF we need to |
| ensure that SUBREG itself can be formed. */ |
| |
| static void |
| record_subregs_of_mode (rtx subreg, bool partial_def) |
| { |
| unsigned int regno; |
| |
| if (!REG_P (SUBREG_REG (subreg))) |
| return; |
| |
| regno = REGNO (SUBREG_REG (subreg)); |
| if (regno < FIRST_PSEUDO_REGISTER) |
| return; |
| |
| subreg_shape shape (shape_of_subreg (subreg)); |
| if (partial_def) |
| { |
| /* The number of independently-accessible SHAPE.outer_mode values |
| in SHAPE.inner_mode is GET_MODE_SIZE (SHAPE.inner_mode) / SIZE. |
| We need to check that the assignment will preserve all the other |
| SIZE-byte chunks in the inner register besides the one that |
| includes SUBREG. |
| |
| In practice it is enough to check whether an equivalent |
| SHAPE.inner_mode value in an adjacent SIZE-byte chunk can be formed. |
| If the underlying registers are small enough, both subregs will |
| be valid. If the underlying registers are too large, one of the |
| subregs will be invalid. |
| |
| This relies on the fact that we've already been passed |
| SUBREG with PARTIAL_DEF set to false. |
| |
| The size of the outer mode must ordered wrt the size of the |
| inner mode's registers, since otherwise we wouldn't know at |
| compile time how many registers the outer mode occupies. */ |
| poly_uint64 size = ordered_max (REGMODE_NATURAL_SIZE (shape.inner_mode), |
| GET_MODE_SIZE (shape.outer_mode)); |
| gcc_checking_assert (known_lt (size, GET_MODE_SIZE (shape.inner_mode))); |
| if (known_ge (shape.offset, size)) |
| shape.offset -= size; |
| else |
| shape.offset += size; |
| } |
| |
| if (valid_mode_changes[regno]) |
| AND_HARD_REG_SET (*valid_mode_changes[regno], |
| simplifiable_subregs (shape)); |
| else |
| { |
| valid_mode_changes[regno] |
| = XOBNEW (&valid_mode_changes_obstack, HARD_REG_SET); |
| COPY_HARD_REG_SET (*valid_mode_changes[regno], |
| simplifiable_subregs (shape)); |
| } |
| } |
| |
| /* Call record_subregs_of_mode for all the subregs in X. */ |
| static void |
| find_subregs_of_mode (rtx x) |
| { |
| enum rtx_code code = GET_CODE (x); |
| const char * const fmt = GET_RTX_FORMAT (code); |
| int i; |
| |
| if (code == SUBREG) |
| record_subregs_of_mode (x, false); |
| |
| /* Time for some deep diving. */ |
| for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) |
| { |
| if (fmt[i] == 'e') |
| find_subregs_of_mode (XEXP (x, i)); |
| else if (fmt[i] == 'E') |
| { |
| int j; |
| for (j = XVECLEN (x, i) - 1; j >= 0; j--) |
| find_subregs_of_mode (XVECEXP (x, i, j)); |
| } |
| } |
| } |
| |
| void |
| init_subregs_of_mode (void) |
| { |
| basic_block bb; |
| rtx_insn *insn; |
| |
| gcc_obstack_init (&valid_mode_changes_obstack); |
| valid_mode_changes = XCNEWVEC (HARD_REG_SET *, max_reg_num ()); |
| |
| FOR_EACH_BB_FN (bb, cfun) |
| FOR_BB_INSNS (bb, insn) |
| if (NONDEBUG_INSN_P (insn)) |
| { |
| find_subregs_of_mode (PATTERN (insn)); |
| df_ref def; |
| FOR_EACH_INSN_DEF (def, insn) |
| if (DF_REF_FLAGS_IS_SET (def, DF_REF_PARTIAL) |
| && read_modify_subreg_p (DF_REF_REG (def))) |
| record_subregs_of_mode (DF_REF_REG (def), true); |
| } |
| } |
| |
| const HARD_REG_SET * |
| valid_mode_changes_for_regno (unsigned int regno) |
| { |
| return valid_mode_changes[regno]; |
| } |
| |
| void |
| finish_subregs_of_mode (void) |
| { |
| XDELETEVEC (valid_mode_changes); |
| obstack_free (&valid_mode_changes_obstack, NULL); |
| } |
| |
| /* Free all data attached to the structure. This isn't a destructor because |
| we don't want to run on exit. */ |
| |
| void |
| target_hard_regs::finalize () |
| { |
| delete x_simplifiable_subregs; |
| } |