+/* If the modes are different and the value's source and target do not
+ line up, we need to extract the value from lower part of the rhs of
+ the store, shift it, and then put it into a form that can be shoved
+ into the read_insn. This function generates a right SHIFT of a
+ value that is at least ACCESS_SIZE bytes wide of READ_MODE. The
+ shift sequence is returned or NULL if we failed to find a
+ shift. */
+
+static rtx
+find_shift_sequence (int access_size,
+ store_info_t store_info,
+ enum machine_mode read_mode,
+ int shift, bool speed, bool require_cst)
+{
+ enum machine_mode store_mode = GET_MODE (store_info->mem);
+ enum machine_mode new_mode;
+ rtx read_reg = NULL;
+
+ /* Some machines like the x86 have shift insns for each size of
+ operand. Other machines like the ppc or the ia-64 may only have
+ shift insns that shift values within 32 or 64 bit registers.
+ This loop tries to find the smallest shift insn that will right
+ justify the value we want to read but is available in one insn on
+ the machine. */
+
+ for (new_mode = smallest_mode_for_size (access_size * BITS_PER_UNIT,
+ MODE_INT);
+ GET_MODE_BITSIZE (new_mode) <= BITS_PER_WORD;
+ new_mode = GET_MODE_WIDER_MODE (new_mode))
+ {
+ rtx target, new_reg, shift_seq, insn, new_lhs;
+ int cost;
+
+ /* If a constant was stored into memory, try to simplify it here,
+ otherwise the cost of the shift might preclude this optimization
+ e.g. at -Os, even when no actual shift will be needed. */
+ if (store_info->const_rhs)
+ {
+ unsigned int byte = subreg_lowpart_offset (new_mode, store_mode);
+ rtx ret = simplify_subreg (new_mode, store_info->const_rhs,
+ store_mode, byte);
+ if (ret && CONSTANT_P (ret))
+ {
+ ret = simplify_const_binary_operation (LSHIFTRT, new_mode,
+ ret, GEN_INT (shift));
+ if (ret && CONSTANT_P (ret))
+ {
+ byte = subreg_lowpart_offset (read_mode, new_mode);
+ ret = simplify_subreg (read_mode, ret, new_mode, byte);
+ if (ret && CONSTANT_P (ret)
+ && rtx_cost (ret, SET, speed) <= COSTS_N_INSNS (1))
+ return ret;
+ }
+ }
+ }
+
+ if (require_cst)
+ return NULL_RTX;
+
+ /* Try a wider mode if truncating the store mode to NEW_MODE
+ requires a real instruction. */
+ if (GET_MODE_BITSIZE (new_mode) < GET_MODE_BITSIZE (store_mode)
+ && !TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (new_mode),
+ GET_MODE_BITSIZE (store_mode)))
+ continue;
+
+ /* Also try a wider mode if the necessary punning is either not
+ desirable or not possible. */
+ if (!CONSTANT_P (store_info->rhs)
+ && !MODES_TIEABLE_P (new_mode, store_mode))
+ continue;
+
+ new_reg = gen_reg_rtx (new_mode);
+
+ start_sequence ();
+
+ /* In theory we could also check for an ashr. Ian Taylor knows
+ of one dsp where the cost of these two was not the same. But
+ this really is a rare case anyway. */
+ target = expand_binop (new_mode, lshr_optab, new_reg,
+ GEN_INT (shift), new_reg, 1, OPTAB_DIRECT);
+
+ shift_seq = get_insns ();
+ end_sequence ();
+
+ if (target != new_reg || shift_seq == NULL)
+ continue;
+
+ cost = 0;
+ for (insn = shift_seq; insn != NULL_RTX; insn = NEXT_INSN (insn))
+ if (INSN_P (insn))
+ cost += insn_rtx_cost (PATTERN (insn), speed);
+
+ /* The computation up to here is essentially independent
+ of the arguments and could be precomputed. It may
+ not be worth doing so. We could precompute if
+ worthwhile or at least cache the results. The result
+ technically depends on both SHIFT and ACCESS_SIZE,
+ but in practice the answer will depend only on ACCESS_SIZE. */
+
+ if (cost > COSTS_N_INSNS (1))
+ continue;
+
+ new_lhs = extract_low_bits (new_mode, store_mode,
+ copy_rtx (store_info->rhs));
+ if (new_lhs == NULL_RTX)
+ continue;
+
+ /* We found an acceptable shift. Generate a move to
+ take the value from the store and put it into the
+ shift pseudo, then shift it, then generate another
+ move to put in into the target of the read. */
+ emit_move_insn (new_reg, new_lhs);
+ emit_insn (shift_seq);
+ read_reg = extract_low_bits (read_mode, new_mode, new_reg);
+ break;
+ }
+
+ return read_reg;
+}
+
+
+/* Call back for note_stores to find the hard regs set or clobbered by
+ insn. Data is a bitmap of the hardregs set so far. */
+
+static void
+look_for_hardregs (rtx x, const_rtx pat ATTRIBUTE_UNUSED, void *data)
+{
+ bitmap regs_set = (bitmap) data;
+
+ if (REG_P (x)
+ && REGNO (x) < FIRST_PSEUDO_REGISTER)
+ {
+ int regno = REGNO (x);
+ int n = hard_regno_nregs[regno][GET_MODE (x)];
+ while (--n >= 0)
+ bitmap_set_bit (regs_set, regno + n);
+ }
+}
+
+/* Helper function for replace_read and record_store.
+ Attempt to return a value stored in STORE_INFO, from READ_BEGIN
+ to one before READ_END bytes read in READ_MODE. Return NULL
+ if not successful. If REQUIRE_CST is true, return always constant. */
+
+static rtx
+get_stored_val (store_info_t store_info, enum machine_mode read_mode,
+ HOST_WIDE_INT read_begin, HOST_WIDE_INT read_end,
+ basic_block bb, bool require_cst)
+{
+ enum machine_mode store_mode = GET_MODE (store_info->mem);
+ int shift;
+ int access_size; /* In bytes. */
+ rtx read_reg;
+
+ /* To get here the read is within the boundaries of the write so
+ shift will never be negative. Start out with the shift being in
+ bytes. */
+ if (store_mode == BLKmode)
+ shift = 0;
+ else if (BYTES_BIG_ENDIAN)
+ shift = store_info->end - read_end;
+ else
+ shift = read_begin - store_info->begin;
+
+ access_size = shift + GET_MODE_SIZE (read_mode);
+
+ /* From now on it is bits. */
+ shift *= BITS_PER_UNIT;
+
+ if (shift)
+ read_reg = find_shift_sequence (access_size, store_info, read_mode, shift,
+ optimize_bb_for_speed_p (bb),
+ require_cst);
+ else if (store_mode == BLKmode)
+ {
+ /* The store is a memset (addr, const_val, const_size). */
+ gcc_assert (CONST_INT_P (store_info->rhs));
+ store_mode = int_mode_for_mode (read_mode);
+ if (store_mode == BLKmode)
+ read_reg = NULL_RTX;
+ else if (store_info->rhs == const0_rtx)
+ read_reg = extract_low_bits (read_mode, store_mode, const0_rtx);
+ else if (GET_MODE_BITSIZE (store_mode) > HOST_BITS_PER_WIDE_INT
+ || BITS_PER_UNIT >= HOST_BITS_PER_WIDE_INT)
+ read_reg = NULL_RTX;
+ else
+ {
+ unsigned HOST_WIDE_INT c
+ = INTVAL (store_info->rhs)
+ & (((HOST_WIDE_INT) 1 << BITS_PER_UNIT) - 1);
+ int shift = BITS_PER_UNIT;
+ while (shift < HOST_BITS_PER_WIDE_INT)
+ {
+ c |= (c << shift);
+ shift <<= 1;
+ }
+ read_reg = GEN_INT (trunc_int_for_mode (c, store_mode));
+ read_reg = extract_low_bits (read_mode, store_mode, read_reg);
+ }
+ }
+ else if (store_info->const_rhs
+ && (require_cst
+ || GET_MODE_CLASS (read_mode) != GET_MODE_CLASS (store_mode)))
+ read_reg = extract_low_bits (read_mode, store_mode,
+ copy_rtx (store_info->const_rhs));
+ else
+ read_reg = extract_low_bits (read_mode, store_mode,
+ copy_rtx (store_info->rhs));
+ if (require_cst && read_reg && !CONSTANT_P (read_reg))
+ read_reg = NULL_RTX;
+ return read_reg;
+}
+