X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=gcc%2Flower-subreg.c;h=4f68a7381b750fe160bb5436b2601d4ec572cc7c;hb=1fcf995f9521c6a958944b5f8bd94d18131d8a0a;hp=a1331c0050795185c790324bfbe3800c6f8257cb;hpb=3aea1f79290f0b8428a0af7d9d18dc1d3f1d799a;p=thirdparty%2Fgcc.git diff --git a/gcc/lower-subreg.c b/gcc/lower-subreg.c index a1331c005079..4f68a7381b75 100644 --- a/gcc/lower-subreg.c +++ b/gcc/lower-subreg.c @@ -1,5 +1,5 @@ /* Decompose multiword subregs. - Copyright (C) 2007-2014 Free Software Foundation, Inc. + Copyright (C) 2007-2019 Free Software Foundation, Inc. Contributed by Richard Henderson Ian Lance Taylor @@ -22,31 +22,25 @@ along with GCC; see the file COPYING3. If not see #include "config.h" #include "system.h" #include "coretypes.h" -#include "machmode.h" -#include "tm.h" -#include "tree.h" +#include "backend.h" #include "rtl.h" +#include "tree.h" +#include "cfghooks.h" +#include "df.h" +#include "memmodel.h" #include "tm_p.h" -#include "flags.h" +#include "expmed.h" #include "insn-config.h" -#include "obstack.h" -#include "basic-block.h" +#include "emit-rtl.h" #include "recog.h" -#include "bitmap.h" +#include "cfgrtl.h" +#include "cfgbuild.h" #include "dce.h" #include "expr.h" -#include "except.h" -#include "regs.h" #include "tree-pass.h" -#include "df.h" #include "lower-subreg.h" - -#ifdef STACK_GROWS_DOWNWARD -# undef STACK_GROWS_DOWNWARD -# define STACK_GROWS_DOWNWARD 1 -#else -# define STACK_GROWS_DOWNWARD 0 -#endif +#include "rtl-iter.h" +#include "target.h" /* Decompose multi-word pseudo-registers into individual @@ -85,7 +79,7 @@ along with GCC; see the file COPYING3. If not see static bitmap decomposable_context; /* Bit N in this bitmap is set if regno N is used in a context in - which it can not be decomposed. */ + which it cannot be decomposed. */ static bitmap non_decomposable_context; /* Bit N in this bitmap is set if regno N is used in a subreg @@ -109,6 +103,19 @@ struct target_lower_subreg *this_target_lower_subreg #define choices \ this_target_lower_subreg->x_choices +/* Return true if MODE is a mode we know how to lower. When returning true, + store its byte size in *BYTES and its word size in *WORDS. */ + +static inline bool +interesting_mode_p (machine_mode mode, unsigned int *bytes, + unsigned int *words) +{ + if (!GET_MODE_SIZE (mode).is_constant (bytes)) + return false; + *words = CEIL (*bytes, UNITS_PER_WORD); + return true; +} + /* RTXes used while computing costs. */ struct cost_rtxes { /* Source and target registers. */ @@ -130,13 +137,13 @@ struct cost_rtxes { static int shift_cost (bool speed_p, struct cost_rtxes *rtxes, enum rtx_code code, - enum machine_mode mode, int op1) + machine_mode mode, int op1) { PUT_CODE (rtxes->shift, code); PUT_MODE (rtxes->shift, mode); PUT_MODE (rtxes->source, mode); - XEXP (rtxes->shift, 1) = GEN_INT (op1); - return set_src_cost (rtxes->shift, speed_p); + XEXP (rtxes->shift, 1) = gen_int_shift_amount (mode, op1); + return set_src_cost (rtxes->shift, mode, speed_p); } /* For each X in the range [0, BITS_PER_WORD), set SPLITTING[X] @@ -204,11 +211,11 @@ compute_costs (bool speed_p, struct cost_rtxes *rtxes) for (i = 0; i < MAX_MACHINE_MODE; i++) { - enum machine_mode mode = (enum machine_mode) i; - int factor = GET_MODE_SIZE (mode) / UNITS_PER_WORD; - if (factor > 1) + machine_mode mode = (machine_mode) i; + unsigned int size, factor; + if (interesting_mode_p (mode, &size, &factor) && factor > 1) { - int mode_move_cost; + unsigned int mode_move_cost; PUT_MODE (rtxes->target, mode); PUT_MODE (rtxes->source, mode); @@ -240,7 +247,7 @@ compute_costs (bool speed_p, struct cost_rtxes *rtxes) /* The only case here to check to see if moving the upper part with a zero is cheaper than doing the zext itself. */ PUT_MODE (rtxes->source, word_mode); - zext_cost = set_src_cost (rtxes->zext, speed_p); + zext_cost = set_src_cost (rtxes->zext, twice_word_mode, speed_p); if (LOG_COSTS) fprintf (stderr, "%s %s: original cost %d, split cost %d + %d\n", @@ -273,11 +280,11 @@ init_lower_subreg (void) memset (this_target_lower_subreg, 0, sizeof (*this_target_lower_subreg)); - twice_word_mode = GET_MODE_2XWIDER_MODE (word_mode); + twice_word_mode = GET_MODE_2XWIDER_MODE (word_mode).require (); - rtxes.target = gen_rtx_REG (word_mode, FIRST_PSEUDO_REGISTER); - rtxes.source = gen_rtx_REG (word_mode, FIRST_PSEUDO_REGISTER + 1); - rtxes.set = gen_rtx_SET (VOIDmode, rtxes.target, rtxes.source); + rtxes.target = gen_rtx_REG (word_mode, LAST_VIRTUAL_REGISTER + 1); + rtxes.source = gen_rtx_REG (word_mode, LAST_VIRTUAL_REGISTER + 2); + rtxes.set = gen_rtx_SET (rtxes.target, rtxes.source); rtxes.zext = gen_rtx_ZERO_EXTEND (twice_word_mode, rtxes.source); rtxes.shift = gen_rtx_ASHIFT (twice_word_mode, rtxes.source, const0_rtx); @@ -313,6 +320,24 @@ simple_move_operand (rtx x) return true; } +/* If X is an operator that can be treated as a simple move that we + can split, then return the operand that is operated on. */ + +static rtx +operand_for_swap_move_operator (rtx x) +{ + /* A word sized rotate of a register pair is equivalent to swapping + the registers in the register pair. */ + if (GET_CODE (x) == ROTATE + && GET_MODE (x) == twice_word_mode + && simple_move_operand (XEXP (x, 0)) + && CONST_INT_P (XEXP (x, 1)) + && INTVAL (XEXP (x, 1)) == BITS_PER_WORD) + return XEXP (x, 0); + + return NULL_RTX; +} + /* If INSN is a single set between two objects that we want to split, return the single set. SPEED_P says whether we are optimizing INSN for speed or size. @@ -321,11 +346,11 @@ simple_move_operand (rtx x) is called. */ static rtx -simple_move (rtx insn, bool speed_p) +simple_move (rtx_insn *insn, bool speed_p) { - rtx x; + rtx x, op; rtx set; - enum machine_mode mode; + machine_mode mode; if (recog_data.n_operands != 2) return NULL_RTX; @@ -341,6 +366,9 @@ simple_move (rtx insn, bool speed_p) return NULL_RTX; x = SET_SRC (set); + if ((op = operand_for_swap_move_operator (x)) != NULL_RTX) + x = op; + if (x != recog_data.operand[0] && x != recog_data.operand[1]) return NULL_RTX; /* For the src we can handle ASM_OPERANDS, and it is beneficial for @@ -356,8 +384,7 @@ simple_move (rtx insn, bool speed_p) size. */ mode = GET_MODE (SET_DEST (set)); if (!SCALAR_INT_MODE_P (mode) - && (mode_for_size (GET_MODE_SIZE (mode) * BITS_PER_UNIT, MODE_INT, 0) - == BLKmode)) + && !int_mode_for_size (GET_MODE_BITSIZE (mode), 0).exists ()) return NULL_RTX; /* Reject PARTIAL_INT modes. They are used for processor specific @@ -380,9 +407,13 @@ find_pseudo_copy (rtx set) { rtx dest = SET_DEST (set); rtx src = SET_SRC (set); + rtx op; unsigned int rd, rs; bitmap b; + if ((op = operand_for_swap_move_operator (src)) != NULL_RTX) + src = op; + if (!REG_P (dest) || !REG_P (src)) return false; @@ -412,10 +443,7 @@ find_pseudo_copy (rtx set) static void propagate_pseudo_copies (void) { - bitmap queue, propagate; - - queue = BITMAP_ALLOC (NULL); - propagate = BITMAP_ALLOC (NULL); + auto_bitmap queue, propagate; bitmap_copy (queue, decomposable_context); do @@ -436,13 +464,10 @@ propagate_pseudo_copies (void) bitmap_ior_into (decomposable_context, propagate); } while (!bitmap_empty_p (queue)); - - BITMAP_FREE (queue); - BITMAP_FREE (propagate); } /* A pointer to one of these values is passed to - find_decomposable_subregs via for_each_rtx. */ + find_decomposable_subregs. */ enum classify_move_insn { @@ -454,120 +479,131 @@ enum classify_move_insn SIMPLE_MOVE }; -/* This is called via for_each_rtx. If we find a SUBREG which we - could use to decompose a pseudo-register, set a bit in - DECOMPOSABLE_CONTEXT. If we find an unadorned register which is - not a simple pseudo-register copy, DATA will point at the type of - move, and we set a bit in DECOMPOSABLE_CONTEXT or - NON_DECOMPOSABLE_CONTEXT as appropriate. */ +/* If we find a SUBREG in *LOC which we could use to decompose a + pseudo-register, set a bit in DECOMPOSABLE_CONTEXT. If we find an + unadorned register which is not a simple pseudo-register copy, + DATA will point at the type of move, and we set a bit in + DECOMPOSABLE_CONTEXT or NON_DECOMPOSABLE_CONTEXT as appropriate. */ -static int -find_decomposable_subregs (rtx *px, void *data) +static void +find_decomposable_subregs (rtx *loc, enum classify_move_insn *pcmi) { - enum classify_move_insn *pcmi = (enum classify_move_insn *) data; - rtx x = *px; - - if (x == NULL_RTX) - return 0; - - if (GET_CODE (x) == SUBREG) + subrtx_var_iterator::array_type array; + FOR_EACH_SUBRTX_VAR (iter, array, *loc, NONCONST) { - rtx inner = SUBREG_REG (x); - unsigned int regno, outer_size, inner_size, outer_words, inner_words; - - if (!REG_P (inner)) - return 0; - - regno = REGNO (inner); - if (HARD_REGISTER_NUM_P (regno)) - return -1; + rtx x = *iter; + if (GET_CODE (x) == SUBREG) + { + rtx inner = SUBREG_REG (x); + unsigned int regno, outer_size, inner_size, outer_words, inner_words; - outer_size = GET_MODE_SIZE (GET_MODE (x)); - inner_size = GET_MODE_SIZE (GET_MODE (inner)); - outer_words = (outer_size + UNITS_PER_WORD - 1) / UNITS_PER_WORD; - inner_words = (inner_size + UNITS_PER_WORD - 1) / UNITS_PER_WORD; + if (!REG_P (inner)) + continue; - /* We only try to decompose single word subregs of multi-word - registers. When we find one, we return -1 to avoid iterating - over the inner register. + regno = REGNO (inner); + if (HARD_REGISTER_NUM_P (regno)) + { + iter.skip_subrtxes (); + continue; + } - ??? This doesn't allow, e.g., DImode subregs of TImode values - on 32-bit targets. We would need to record the way the - pseudo-register was used, and only decompose if all the uses - were the same number and size of pieces. Hopefully this - doesn't happen much. */ + if (!interesting_mode_p (GET_MODE (x), &outer_size, &outer_words) + || !interesting_mode_p (GET_MODE (inner), &inner_size, + &inner_words)) + continue; - if (outer_words == 1 && inner_words > 1) - { - bitmap_set_bit (decomposable_context, regno); - return -1; - } + /* We only try to decompose single word subregs of multi-word + registers. When we find one, we return -1 to avoid iterating + over the inner register. + + ??? This doesn't allow, e.g., DImode subregs of TImode values + on 32-bit targets. We would need to record the way the + pseudo-register was used, and only decompose if all the uses + were the same number and size of pieces. Hopefully this + doesn't happen much. */ + + if (outer_words == 1 + && inner_words > 1 + /* Don't allow to decompose floating point subregs of + multi-word pseudos if the floating point mode does + not have word size, because otherwise we'd generate + a subreg with that floating mode from a different + sized integral pseudo which is not allowed by + validate_subreg. */ + && (!FLOAT_MODE_P (GET_MODE (x)) + || outer_size == UNITS_PER_WORD)) + { + bitmap_set_bit (decomposable_context, regno); + iter.skip_subrtxes (); + continue; + } - /* If this is a cast from one mode to another, where the modes - have the same size, and they are not tieable, then mark this - register as non-decomposable. If we decompose it we are - likely to mess up whatever the backend is trying to do. */ - if (outer_words > 1 - && outer_size == inner_size - && !MODES_TIEABLE_P (GET_MODE (x), GET_MODE (inner))) - { - bitmap_set_bit (non_decomposable_context, regno); - bitmap_set_bit (subreg_context, regno); - return -1; + /* If this is a cast from one mode to another, where the modes + have the same size, and they are not tieable, then mark this + register as non-decomposable. If we decompose it we are + likely to mess up whatever the backend is trying to do. */ + if (outer_words > 1 + && outer_size == inner_size + && !targetm.modes_tieable_p (GET_MODE (x), GET_MODE (inner))) + { + bitmap_set_bit (non_decomposable_context, regno); + bitmap_set_bit (subreg_context, regno); + iter.skip_subrtxes (); + continue; + } } - } - else if (REG_P (x)) - { - unsigned int regno; - - /* We will see an outer SUBREG before we see the inner REG, so - when we see a plain REG here it means a direct reference to - the register. - - If this is not a simple copy from one location to another, - then we can not decompose this register. If this is a simple - copy we want to decompose, and the mode is right, - then we mark the register as decomposable. - Otherwise we don't say anything about this register -- - it could be decomposed, but whether that would be - profitable depends upon how it is used elsewhere. - - We only set bits in the bitmap for multi-word - pseudo-registers, since those are the only ones we care about - and it keeps the size of the bitmaps down. */ - - regno = REGNO (x); - if (!HARD_REGISTER_NUM_P (regno) - && GET_MODE_SIZE (GET_MODE (x)) > UNITS_PER_WORD) + else if (REG_P (x)) { - switch (*pcmi) + unsigned int regno, size, words; + + /* We will see an outer SUBREG before we see the inner REG, so + when we see a plain REG here it means a direct reference to + the register. + + If this is not a simple copy from one location to another, + then we cannot decompose this register. If this is a simple + copy we want to decompose, and the mode is right, + then we mark the register as decomposable. + Otherwise we don't say anything about this register -- + it could be decomposed, but whether that would be + profitable depends upon how it is used elsewhere. + + We only set bits in the bitmap for multi-word + pseudo-registers, since those are the only ones we care about + and it keeps the size of the bitmaps down. */ + + regno = REGNO (x); + if (!HARD_REGISTER_NUM_P (regno) + && interesting_mode_p (GET_MODE (x), &size, &words) + && words > 1) { - case NOT_SIMPLE_MOVE: - bitmap_set_bit (non_decomposable_context, regno); - break; - case DECOMPOSABLE_SIMPLE_MOVE: - if (MODES_TIEABLE_P (GET_MODE (x), word_mode)) - bitmap_set_bit (decomposable_context, regno); - break; - case SIMPLE_MOVE: - break; - default: - gcc_unreachable (); + switch (*pcmi) + { + case NOT_SIMPLE_MOVE: + bitmap_set_bit (non_decomposable_context, regno); + break; + case DECOMPOSABLE_SIMPLE_MOVE: + if (targetm.modes_tieable_p (GET_MODE (x), word_mode)) + bitmap_set_bit (decomposable_context, regno); + break; + case SIMPLE_MOVE: + break; + default: + gcc_unreachable (); + } } } - } - else if (MEM_P (x)) - { - enum classify_move_insn cmi_mem = NOT_SIMPLE_MOVE; + else if (MEM_P (x)) + { + enum classify_move_insn cmi_mem = NOT_SIMPLE_MOVE; - /* Any registers used in a MEM do not participate in a - SIMPLE_MOVE or DECOMPOSABLE_SIMPLE_MOVE. Do our own recursion - here, and return -1 to block the parent's recursion. */ - for_each_rtx (&XEXP (x, 0), find_decomposable_subregs, &cmi_mem); - return -1; + /* Any registers used in a MEM do not participate in a + SIMPLE_MOVE or DECOMPOSABLE_SIMPLE_MOVE. Do our own recursion + here, and return -1 to block the parent's recursion. */ + find_decomposable_subregs (&XEXP (x, 0), &cmi_mem); + iter.skip_subrtxes (); + } } - - return 0; } /* Decompose REGNO into word-sized components. We smash the REG node @@ -579,15 +615,15 @@ static void decompose_register (unsigned int regno) { rtx reg; - unsigned int words, i; + unsigned int size, words, i; rtvec v; reg = regno_reg_rtx[regno]; regno_reg_rtx[regno] = NULL_RTX; - words = GET_MODE_SIZE (GET_MODE (reg)); - words = (words + UNITS_PER_WORD - 1) / UNITS_PER_WORD; + if (!interesting_mode_p (GET_MODE (reg), &size, &words)) + gcc_unreachable (); v = rtvec_alloc (words); for (i = 0; i < words; ++i) @@ -608,39 +644,44 @@ decompose_register (unsigned int regno) /* Get a SUBREG of a CONCATN. */ static rtx -simplify_subreg_concatn (enum machine_mode outermode, rtx op, - unsigned int byte) +simplify_subreg_concatn (machine_mode outermode, rtx op, poly_uint64 orig_byte) { - unsigned int inner_size; - enum machine_mode innermode, partmode; + unsigned int outer_size, outer_words, inner_size, inner_words; + machine_mode innermode, partmode; rtx part; unsigned int final_offset; + unsigned int byte; + + innermode = GET_MODE (op); + if (!interesting_mode_p (outermode, &outer_size, &outer_words) + || !interesting_mode_p (innermode, &inner_size, &inner_words)) + gcc_unreachable (); + /* Must be constant if interesting_mode_p passes. */ + byte = orig_byte.to_constant (); gcc_assert (GET_CODE (op) == CONCATN); - gcc_assert (byte % GET_MODE_SIZE (outermode) == 0); + gcc_assert (byte % outer_size == 0); - innermode = GET_MODE (op); - gcc_assert (byte < GET_MODE_SIZE (innermode)); - gcc_assert (GET_MODE_SIZE (outermode) <= GET_MODE_SIZE (innermode)); + gcc_assert (byte < inner_size); + if (outer_size > inner_size) + return NULL_RTX; - inner_size = GET_MODE_SIZE (innermode) / XVECLEN (op, 0); + inner_size /= XVECLEN (op, 0); part = XVECEXP (op, 0, byte / inner_size); partmode = GET_MODE (part); + final_offset = byte % inner_size; + if (final_offset + outer_size > inner_size) + return NULL_RTX; + /* VECTOR_CSTs in debug expressions are expanded into CONCATN instead of regular CONST_VECTORs. They have vector or integer modes, depending on the capabilities of the target. Cope with them. */ if (partmode == VOIDmode && VECTOR_MODE_P (innermode)) partmode = GET_MODE_INNER (innermode); else if (partmode == VOIDmode) - { - enum mode_class mclass = GET_MODE_CLASS (innermode); - partmode = mode_for_size (inner_size * BITS_PER_UNIT, mclass, 0); - } - - final_offset = byte % inner_size; - if (final_offset + GET_MODE_SIZE (outermode) > inner_size) - return NULL_RTX; + partmode = mode_for_size (inner_size * BITS_PER_UNIT, + GET_MODE_CLASS (innermode), 0).require (); return simplify_gen_subreg (outermode, part, partmode, final_offset); } @@ -648,8 +689,8 @@ simplify_subreg_concatn (enum machine_mode outermode, rtx op, /* Wrapper around simplify_gen_subreg which handles CONCATN. */ static rtx -simplify_gen_subreg_concatn (enum machine_mode outermode, rtx op, - enum machine_mode innermode, unsigned int byte) +simplify_gen_subreg_concatn (machine_mode outermode, rtx op, + machine_mode innermode, unsigned int byte) { rtx ret; @@ -661,9 +702,9 @@ simplify_gen_subreg_concatn (enum machine_mode outermode, rtx op, { rtx op2; - if ((GET_MODE_SIZE (GET_MODE (op)) - == GET_MODE_SIZE (GET_MODE (SUBREG_REG (op)))) - && SUBREG_BYTE (op) == 0) + if (known_eq (GET_MODE_SIZE (GET_MODE (op)), + GET_MODE_SIZE (GET_MODE (SUBREG_REG (op)))) + && known_eq (SUBREG_BYTE (op), 0)) return simplify_gen_subreg_concatn (outermode, SUBREG_REG (op), GET_MODE (SUBREG_REG (op)), byte); @@ -672,10 +713,8 @@ simplify_gen_subreg_concatn (enum machine_mode outermode, rtx op, if (op2 == NULL_RTX) { /* We don't handle paradoxical subregs here. */ - gcc_assert (GET_MODE_SIZE (outermode) - <= GET_MODE_SIZE (GET_MODE (op))); - gcc_assert (GET_MODE_SIZE (GET_MODE (op)) - <= GET_MODE_SIZE (GET_MODE (SUBREG_REG (op)))); + gcc_assert (!paradoxical_subreg_p (outermode, GET_MODE (op))); + gcc_assert (!paradoxical_subreg_p (op)); op2 = simplify_subreg_concatn (outermode, SUBREG_REG (op), byte + SUBREG_BYTE (op)); gcc_assert (op2 != NULL_RTX); @@ -696,10 +735,7 @@ simplify_gen_subreg_concatn (enum machine_mode outermode, rtx op, resolve_simple_move will ask for the high part of the paradoxical subreg, which does not have a value. Just return a zero. */ if (ret == NULL_RTX - && GET_CODE (op) == SUBREG - && SUBREG_BYTE (op) == 0 - && (GET_MODE_SIZE (innermode) - > GET_MODE_SIZE (GET_MODE (SUBREG_REG (op))))) + && paradoxical_subreg_p (op)) return CONST0_RTX (outermode); gcc_assert (ret != NULL_RTX); @@ -726,81 +762,49 @@ resolve_subreg_p (rtx x) return resolve_reg_p (SUBREG_REG (x)); } -/* This is called via for_each_rtx. Look for SUBREGs which need to be - decomposed. */ +/* Look for SUBREGs in *LOC which need to be decomposed. */ -static int -resolve_subreg_use (rtx *px, void *data) +static bool +resolve_subreg_use (rtx *loc, rtx insn) { - rtx insn = (rtx) data; - rtx x = *px; - - if (x == NULL_RTX) - return 0; - - if (resolve_subreg_p (x)) + subrtx_ptr_iterator::array_type array; + FOR_EACH_SUBRTX_PTR (iter, array, loc, NONCONST) { - x = simplify_subreg_concatn (GET_MODE (x), SUBREG_REG (x), - SUBREG_BYTE (x)); - - /* It is possible for a note to contain a reference which we can - decompose. In this case, return 1 to the caller to indicate - that the note must be removed. */ - if (!x) + rtx *loc = *iter; + rtx x = *loc; + if (resolve_subreg_p (x)) { - gcc_assert (!insn); - return 1; - } + x = simplify_subreg_concatn (GET_MODE (x), SUBREG_REG (x), + SUBREG_BYTE (x)); - validate_change (insn, px, x, 1); - return -1; - } - - if (resolve_reg_p (x)) - { - /* Return 1 to the caller to indicate that we found a direct - reference to a register which is being decomposed. This can - happen inside notes, multiword shift or zero-extend - instructions. */ - return 1; - } - - return 0; -} - -/* This is called via for_each_rtx. Look for SUBREGs which can be - decomposed and decomposed REGs that need copying. */ - -static int -adjust_decomposed_uses (rtx *px, void *data ATTRIBUTE_UNUSED) -{ - rtx x = *px; - - if (x == NULL_RTX) - return 0; - - if (resolve_subreg_p (x)) - { - x = simplify_subreg_concatn (GET_MODE (x), SUBREG_REG (x), - SUBREG_BYTE (x)); + /* It is possible for a note to contain a reference which we can + decompose. In this case, return 1 to the caller to indicate + that the note must be removed. */ + if (!x) + { + gcc_assert (!insn); + return true; + } - if (x) - *px = x; - else - x = copy_rtx (*px); + validate_change (insn, loc, x, 1); + iter.skip_subrtxes (); + } + else if (resolve_reg_p (x)) + /* Return 1 to the caller to indicate that we found a direct + reference to a register which is being decomposed. This can + happen inside notes, multiword shift or zero-extend + instructions. */ + return true; } - if (resolve_reg_p (x)) - *px = copy_rtx (x); - - return 0; + return false; } /* Resolve any decomposed registers which appear in register notes on INSN. */ static void -resolve_reg_notes (rtx insn) +resolve_reg_notes (rtx_insn *insn) { rtx *pnote, note; @@ -808,7 +812,7 @@ resolve_reg_notes (rtx insn) if (note) { int old_count = num_validated_changes (); - if (for_each_rtx (&XEXP (note, 0), resolve_subreg_use, NULL)) + if (resolve_subreg_use (&XEXP (note, 0), NULL_RTX)) remove_note (insn, note); else if (old_count != num_validated_changes ()) @@ -851,9 +855,10 @@ can_decompose_p (rtx x) if (HARD_REGISTER_NUM_P (regno)) { - unsigned int byte, num_bytes; + unsigned int byte, num_bytes, num_words; - num_bytes = GET_MODE_SIZE (GET_MODE (x)); + if (!interesting_mode_p (GET_MODE (x), &num_bytes, &num_words)) + return false; for (byte = 0; byte < num_bytes; byte += UNITS_PER_WORD) if (simplify_subreg_regno (regno, GET_MODE (x), byte, word_mode) < 0) return false; @@ -866,23 +871,40 @@ can_decompose_p (rtx x) return true; } +/* OPND is a concatn operand this is used with a simple move operator. + Return a new rtx with the concatn's operands swapped. */ + +static rtx +resolve_operand_for_swap_move_operator (rtx opnd) +{ + gcc_assert (GET_CODE (opnd) == CONCATN); + rtx concatn = copy_rtx (opnd); + rtx op0 = XVECEXP (concatn, 0, 0); + rtx op1 = XVECEXP (concatn, 0, 1); + XVECEXP (concatn, 0, 0) = op1; + XVECEXP (concatn, 0, 1) = op0; + return concatn; +} + /* Decompose the registers used in a simple move SET within INSN. If we don't change anything, return INSN, otherwise return the start of the sequence of moves. */ -static rtx -resolve_simple_move (rtx set, rtx insn) +static rtx_insn * +resolve_simple_move (rtx set, rtx_insn *insn) { - rtx src, dest, real_dest, insns; - enum machine_mode orig_mode, dest_mode; - unsigned int words; + rtx src, dest, real_dest, src_op; + rtx_insn *insns; + machine_mode orig_mode, dest_mode; + unsigned int orig_size, words; bool pushing; src = SET_SRC (set); dest = SET_DEST (set); orig_mode = GET_MODE (dest); - words = (GET_MODE_SIZE (orig_mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD; + if (!interesting_mode_p (orig_mode, &orig_size, &words)) + gcc_unreachable (); gcc_assert (words > 1); start_sequence (); @@ -894,11 +916,27 @@ resolve_simple_move (rtx set, rtx insn) real_dest = NULL_RTX; + if ((src_op = operand_for_swap_move_operator (src)) != NULL_RTX) + { + if (resolve_reg_p (dest)) + { + /* DEST is a CONCATN, so swap its operands and strip + SRC's operator. */ + dest = resolve_operand_for_swap_move_operator (dest); + src = src_op; + } + else if (resolve_reg_p (src_op)) + { + /* SRC is an operation on a CONCATN, so strip the operator and + swap the CONCATN's operands. */ + src = resolve_operand_for_swap_move_operator (src_op); + } + } + if (GET_CODE (src) == SUBREG && resolve_reg_p (SUBREG_REG (src)) - && (SUBREG_BYTE (src) != 0 - || (GET_MODE_SIZE (orig_mode) - != GET_MODE_SIZE (GET_MODE (SUBREG_REG (src)))))) + && (maybe_ne (SUBREG_BYTE (src), 0) + || maybe_ne (orig_size, GET_MODE_SIZE (GET_MODE (SUBREG_REG (src)))))) { real_dest = dest; dest = gen_reg_rtx (orig_mode); @@ -911,11 +949,12 @@ resolve_simple_move (rtx set, rtx insn) if (GET_CODE (dest) == SUBREG && resolve_reg_p (SUBREG_REG (dest)) - && (SUBREG_BYTE (dest) != 0 - || (GET_MODE_SIZE (orig_mode) - != GET_MODE_SIZE (GET_MODE (SUBREG_REG (dest)))))) + && (maybe_ne (SUBREG_BYTE (dest), 0) + || maybe_ne (orig_size, + GET_MODE_SIZE (GET_MODE (SUBREG_REG (dest)))))) { - rtx reg, minsn, smove; + rtx reg, smove; + rtx_insn *minsn; reg = gen_reg_rtx (orig_mode); minsn = emit_move_insn (reg, src); @@ -943,16 +982,16 @@ resolve_simple_move (rtx set, rtx insn) /* It's possible for the code to use a subreg of a decomposed register while forming an address. We need to handle that before passing the address to emit_move_insn. We pass NULL_RTX as the - insn parameter to resolve_subreg_use because we can not validate + insn parameter to resolve_subreg_use because we cannot validate the insn yet. */ if (MEM_P (src) || MEM_P (dest)) { int acg; if (MEM_P (src)) - for_each_rtx (&XEXP (src, 0), resolve_subreg_use, NULL_RTX); + resolve_subreg_use (&XEXP (src, 0), NULL_RTX); if (MEM_P (dest)) - for_each_rtx (&XEXP (dest, 0), resolve_subreg_use, NULL_RTX); + resolve_subreg_use (&XEXP (dest, 0), NULL_RTX); acg = apply_change_group (); gcc_assert (acg); } @@ -968,19 +1007,19 @@ resolve_simple_move (rtx set, rtx insn) reg = gen_reg_rtx (orig_mode); -#ifdef AUTO_INC_DEC - { - rtx move = emit_move_insn (reg, src); - if (MEM_P (src)) - { - rtx note = find_reg_note (insn, REG_INC, NULL_RTX); - if (note) - add_reg_note (move, REG_INC, XEXP (note, 0)); - } - } -#else - emit_move_insn (reg, src); -#endif + if (AUTO_INC_DEC) + { + rtx_insn *move = emit_move_insn (reg, src); + if (MEM_P (src)) + { + rtx note = find_reg_note (insn, REG_INC, NULL_RTX); + if (note) + add_reg_note (move, REG_INC, XEXP (note, 0)); + } + } + else + emit_move_insn (reg, src); + src = reg; } @@ -1002,11 +1041,7 @@ resolve_simple_move (rtx set, rtx insn) if (real_dest == NULL_RTX) real_dest = dest; if (!SCALAR_INT_MODE_P (dest_mode)) - { - dest_mode = mode_for_size (GET_MODE_SIZE (dest_mode) * BITS_PER_UNIT, - MODE_INT, 0); - gcc_assert (dest_mode != BLKmode); - } + dest_mode = int_mode_for_mode (dest_mode).require (); dest = gen_reg_rtx (dest_mode); if (REG_P (real_dest)) REG_ATTRS (dest) = REG_ATTRS (real_dest); @@ -1016,7 +1051,7 @@ resolve_simple_move (rtx set, rtx insn) { unsigned int i, j, jinc; - gcc_assert (GET_MODE_SIZE (orig_mode) % UNITS_PER_WORD == 0); + gcc_assert (orig_size % UNITS_PER_WORD == 0); gcc_assert (GET_CODE (XEXP (dest, 0)) != PRE_MODIFY); gcc_assert (GET_CODE (XEXP (dest, 0)) != POST_MODIFY); @@ -1062,7 +1097,8 @@ resolve_simple_move (rtx set, rtx insn) if (real_dest != NULL_RTX) { - rtx mdest, minsn, smove; + rtx mdest, smove; + rtx_insn *minsn; if (dest_mode == orig_mode) mdest = dest; @@ -1070,15 +1106,13 @@ resolve_simple_move (rtx set, rtx insn) mdest = simplify_gen_subreg (orig_mode, dest, GET_MODE (dest), 0); minsn = emit_move_insn (real_dest, mdest); -#ifdef AUTO_INC_DEC - if (MEM_P (real_dest) + if (AUTO_INC_DEC && MEM_P (real_dest) && !(resolve_reg_p (real_dest) || resolve_subreg_p (real_dest))) { rtx note = find_reg_note (insn, REG_INC, NULL_RTX); if (note) add_reg_note (minsn, REG_INC, XEXP (note, 0)); } -#endif smove = single_set (minsn); gcc_assert (smove != NULL_RTX); @@ -1108,11 +1142,11 @@ resolve_simple_move (rtx set, rtx insn) component registers. Return whether we changed something. */ static bool -resolve_clobber (rtx pat, rtx insn) +resolve_clobber (rtx pat, rtx_insn *insn) { rtx reg; - enum machine_mode orig_mode; - unsigned int words, i; + machine_mode orig_mode; + unsigned int orig_size, words, i; int ret; reg = XEXP (pat, 0); @@ -1120,8 +1154,8 @@ resolve_clobber (rtx pat, rtx insn) return false; orig_mode = GET_MODE (reg); - words = GET_MODE_SIZE (orig_mode); - words = (words + UNITS_PER_WORD - 1) / UNITS_PER_WORD; + if (!interesting_mode_p (orig_mode, &orig_size, &words)) + gcc_unreachable (); ret = validate_change (NULL_RTX, &XEXP (pat, 0), simplify_gen_subreg_concatn (word_mode, reg, @@ -1149,7 +1183,7 @@ resolve_clobber (rtx pat, rtx insn) whether we changed something. */ static bool -resolve_use (rtx pat, rtx insn) +resolve_use (rtx pat, rtx_insn *insn) { if (resolve_reg_p (XEXP (pat, 0)) || resolve_subreg_p (XEXP (pat, 0))) { @@ -1165,9 +1199,26 @@ resolve_use (rtx pat, rtx insn) /* A VAR_LOCATION can be simplified. */ static void -resolve_debug (rtx insn) +resolve_debug (rtx_insn *insn) { - for_each_rtx (&PATTERN (insn), adjust_decomposed_uses, NULL_RTX); + subrtx_ptr_iterator::array_type array; + FOR_EACH_SUBRTX_PTR (iter, array, &PATTERN (insn), NONCONST) + { + rtx *loc = *iter; + rtx x = *loc; + if (resolve_subreg_p (x)) + { + x = simplify_subreg_concatn (GET_MODE (x), SUBREG_REG (x), + SUBREG_BYTE (x)); + + if (x) + *loc = x; + else + x = copy_rtx (*loc); + } + if (resolve_reg_p (x)) + *loc = copy_rtx (x); + } df_insn_rescan (insn); @@ -1180,7 +1231,7 @@ resolve_debug (rtx insn) if INSN is decomposable. */ static bool -find_decomposable_shift_zext (rtx insn, bool speed_p) +find_decomposable_shift_zext (rtx_insn *insn, bool speed_p) { rtx set; rtx op; @@ -1236,33 +1287,36 @@ find_decomposable_shift_zext (rtx insn, bool speed_p) and 'set to zero' insn. Return a pointer to the new insn when a replacement was done. */ -static rtx -resolve_shift_zext (rtx insn) +static rtx_insn * +resolve_shift_zext (rtx_insn *insn) { rtx set; rtx op; rtx op_operand; - rtx insns; + rtx_insn *insns; rtx src_reg, dest_reg, dest_upper, upper_src = NULL_RTX; int src_reg_num, dest_reg_num, offset1, offset2, src_offset; + scalar_int_mode inner_mode; set = single_set (insn); if (!set) - return NULL_RTX; + return NULL; op = SET_SRC (set); if (GET_CODE (op) != ASHIFT && GET_CODE (op) != LSHIFTRT && GET_CODE (op) != ASHIFTRT && GET_CODE (op) != ZERO_EXTEND) - return NULL_RTX; + return NULL; op_operand = XEXP (op, 0); + if (!is_a (GET_MODE (op_operand), &inner_mode)) + return NULL; /* We can tear this operation apart only if the regs were already torn apart. */ if (!resolve_reg_p (SET_DEST (set)) && !resolve_reg_p (op_operand)) - return NULL_RTX; + return NULL; /* src_reg_num is the number of the word mode register which we are operating on. For a left shift and a zero_extend on little @@ -1270,8 +1324,7 @@ resolve_shift_zext (rtx insn) src_reg_num = (GET_CODE (op) == LSHIFTRT || GET_CODE (op) == ASHIFTRT) ? 1 : 0; - if (WORDS_BIG_ENDIAN - && GET_MODE_SIZE (GET_MODE (op_operand)) > UNITS_PER_WORD) + if (WORDS_BIG_ENDIAN && GET_MODE_SIZE (inner_mode) > UNITS_PER_WORD) src_reg_num = 1 - src_reg_num; if (GET_CODE (op) == ZERO_EXTEND) @@ -1326,7 +1379,7 @@ resolve_shift_zext (rtx insn) if (dump_file) { - rtx in; + rtx_insn *in; fprintf (dump_file, "; Replacing insn: %d with insns: ", INSN_UID (insn)); for (in = insns; in != insn; in = NEXT_INSN (in)) fprintf (dump_file, "%d ", INSN_UID (in)); @@ -1366,17 +1419,18 @@ dump_shift_choices (enum rtx_code code, bool *splitting) static void dump_choices (bool speed_p, const char *description) { - unsigned int i; + unsigned int size, factor, i; fprintf (dump_file, "Choices when optimizing for %s:\n", description); for (i = 0; i < MAX_MACHINE_MODE; i++) - if (GET_MODE_SIZE (i) > UNITS_PER_WORD) + if (interesting_mode_p ((machine_mode) i, &size, &factor) + && factor > 1) fprintf (dump_file, " %s mode %s for copy lowering.\n", choices[speed_p].move_modes_to_split[i] ? "Splitting" : "Skipping", - GET_MODE_NAME ((enum machine_mode) i)); + GET_MODE_NAME ((machine_mode) i)); fprintf (dump_file, " %s mode %s for zero_extend lowering.\n", choices[speed_p].splitting_zext ? "Splitting" : "Skipping", @@ -1426,7 +1480,7 @@ decompose_multiword_subregs (bool decompose_copies) for (i = FIRST_PSEUDO_REGISTER; i < max; ++i) if (regno_reg_rtx[i] != NULL) { - enum machine_mode mode = GET_MODE (regno_reg_rtx[i]); + machine_mode mode = GET_MODE (regno_reg_rtx[i]); if (choices[false].move_modes_to_split[(int) mode] || choices[true].move_modes_to_split[(int) mode]) { @@ -1465,7 +1519,7 @@ decompose_multiword_subregs (bool decompose_copies) speed_p = optimize_function_for_speed_p (cfun); FOR_EACH_BB_FN (bb, cfun) { - rtx insn; + rtx_insn *insn; FOR_BB_INSNS (bb, insn) { @@ -1507,9 +1561,7 @@ decompose_multiword_subregs (bool decompose_copies) n = recog_data.n_operands; for (i = 0; i < n; ++i) { - for_each_rtx (&recog_data.operand[i], - find_decomposable_subregs, - &cmi); + find_decomposable_subregs (&recog_data.operand[i], &cmi); /* We handle ASM_OPERANDS as a special case to support things like x86 rdtsc which returns a DImode value. @@ -1529,7 +1581,6 @@ decompose_multiword_subregs (bool decompose_copies) bitmap_and_compl_into (decomposable_context, non_decomposable_context); if (!bitmap_empty_p (decomposable_context)) { - sbitmap sub_blocks; unsigned int i; sbitmap_iterator sbi; bitmap_iterator iter; @@ -1537,7 +1588,7 @@ decompose_multiword_subregs (bool decompose_copies) propagate_pseudo_copies (); - sub_blocks = sbitmap_alloc (last_basic_block_for_fn (cfun)); + auto_sbitmap sub_blocks (last_basic_block_for_fn (cfun)); bitmap_clear (sub_blocks); EXECUTE_IF_SET_IN_BITMAP (decomposable_context, 0, regno, iter) @@ -1545,7 +1596,7 @@ decompose_multiword_subregs (bool decompose_copies) FOR_EACH_BB_FN (bb, cfun) { - rtx insn; + rtx_insn *insn; FOR_BB_INSNS (bb, insn) { @@ -1572,7 +1623,7 @@ decompose_multiword_subregs (bool decompose_copies) set = simple_move (insn, speed_p); if (set) { - rtx orig_insn = insn; + rtx_insn *orig_insn = insn; bool cfi = control_flow_insn_p (insn); /* We can end up splitting loads to multi-word pseudos @@ -1602,7 +1653,7 @@ decompose_multiword_subregs (bool decompose_copies) } else { - rtx decomposed_shift; + rtx_insn *decomposed_shift; decomposed_shift = resolve_shift_zext (insn); if (decomposed_shift != NULL_RTX) @@ -1614,9 +1665,7 @@ decompose_multiword_subregs (bool decompose_copies) } for (i = recog_data.n_operands - 1; i >= 0; --i) - for_each_rtx (recog_data.operand_loc[i], - resolve_subreg_use, - insn); + resolve_subreg_use (recog_data.operand_loc[i], insn); resolve_reg_notes (insn); @@ -1644,7 +1693,7 @@ decompose_multiword_subregs (bool decompose_copies) loads to appear. */ EXECUTE_IF_SET_IN_BITMAP (sub_blocks, 0, i, sbi) { - rtx insn, end; + rtx_insn *insn, *end; edge fallthru; bb = BASIC_BLOCK_FOR_FN (cfun, i); @@ -1667,8 +1716,6 @@ decompose_multiword_subregs (bool decompose_copies) insn = NEXT_INSN (insn); } } - - sbitmap_free (sub_blocks); } { @@ -1687,32 +1734,8 @@ decompose_multiword_subregs (bool decompose_copies) BITMAP_FREE (subreg_context); } -/* Gate function for lower subreg pass. */ - -static bool -gate_handle_lower_subreg (void) -{ - return flag_split_wide_types != 0; -} - /* Implement first lower subreg pass. */ -static unsigned int -rest_of_handle_lower_subreg (void) -{ - decompose_multiword_subregs (false); - return 0; -} - -/* Implement second lower subreg pass. */ - -static unsigned int -rest_of_handle_lower_subreg2 (void) -{ - decompose_multiword_subregs (true); - return 0; -} - namespace { const pass_data pass_data_lower_subreg = @@ -1720,14 +1743,12 @@ const pass_data pass_data_lower_subreg = RTL_PASS, /* type */ "subreg1", /* name */ OPTGROUP_NONE, /* optinfo_flags */ - true, /* has_gate */ - true, /* has_execute */ TV_LOWER_SUBREG, /* tv_id */ 0, /* properties_required */ 0, /* properties_provided */ 0, /* properties_destroyed */ 0, /* todo_flags_start */ - TODO_verify_flow, /* todo_flags_finish */ + 0, /* todo_flags_finish */ }; class pass_lower_subreg : public rtl_opt_pass @@ -1738,8 +1759,12 @@ public: {} /* opt_pass methods: */ - bool gate () { return gate_handle_lower_subreg (); } - unsigned int execute () { return rest_of_handle_lower_subreg (); } + virtual bool gate (function *) { return flag_split_wide_types != 0; } + virtual unsigned int execute (function *) + { + decompose_multiword_subregs (false); + return 0; + } }; // class pass_lower_subreg @@ -1751,6 +1776,8 @@ make_pass_lower_subreg (gcc::context *ctxt) return new pass_lower_subreg (ctxt); } +/* Implement second lower subreg pass. */ + namespace { const pass_data pass_data_lower_subreg2 = @@ -1758,15 +1785,12 @@ const pass_data pass_data_lower_subreg2 = RTL_PASS, /* type */ "subreg2", /* name */ OPTGROUP_NONE, /* optinfo_flags */ - true, /* has_gate */ - true, /* has_execute */ TV_LOWER_SUBREG, /* tv_id */ 0, /* properties_required */ 0, /* properties_provided */ 0, /* properties_destroyed */ 0, /* todo_flags_start */ - ( TODO_df_finish | TODO_verify_rtl_sharing - | TODO_verify_flow ), /* todo_flags_finish */ + TODO_df_finish, /* todo_flags_finish */ }; class pass_lower_subreg2 : public rtl_opt_pass @@ -1777,8 +1801,12 @@ public: {} /* opt_pass methods: */ - bool gate () { return gate_handle_lower_subreg (); } - unsigned int execute () { return rest_of_handle_lower_subreg2 (); } + virtual bool gate (function *) { return flag_split_wide_types != 0; } + virtual unsigned int execute (function *) + { + decompose_multiword_subregs (true); + return 0; + } }; // class pass_lower_subreg2