diff options
author | Bernd Schmidt <bernds@redhat.com> | 2016-06-03 14:20:53 +0000 |
---|---|---|
committer | Bernd Schmidt <bernds@gcc.gnu.org> | 2016-06-03 14:20:53 +0000 |
commit | 36b85e432865a312aa8edc8978e38266e8a0f14c (patch) | |
tree | a37f7aeb86dcb59106ae4cd545a9e74dfaf5f8d7 /gcc/expr.c | |
parent | bfeee8acaabebf2eb6d4587731b2a639f530293e (diff) | |
download | gcc-36b85e432865a312aa8edc8978e38266e8a0f14c.zip gcc-36b85e432865a312aa8edc8978e38266e8a0f14c.tar.gz gcc-36b85e432865a312aa8edc8978e38266e8a0f14c.tar.bz2 |
re PR tree-optimization/52171 (memcmp/strcmp/strncmp can be optimized when the result is tested for [in]equality with 0)
PR tree-optimization/52171
* builtins.c (expand_cmpstrn_or_cmpmem): Delete, moved elsewhere.
(expand_builtin_memcmp): New arg RESULT_EQ. All callers changed.
Look for constant strings. Move some code to emit_block_cmp_hints
and use it.
* builtins.def (BUILT_IN_MEMCMP_EQ): New.
* defaults.h (COMPARE_MAX_PIECES): New macro.
* expr.c (move_by_pieces_d, store_by_pieces_d): Remove old structs.
(move_by_pieces_1, store_by_pieces_1, store_by_pieces_2): Remvoe.
(clear_by_pieces_1): Don't declare. Move definition before use.
(can_do_by_pieces): New static function.
(can_move_by_pieces): Use it. Return bool.
(by_pieces_ninsns): Renamed from move_by_pieces_ninsns. New arg
OP. All callers changed. Handle COMPARE_BY_PIECES.
(class pieces_addr); New.
(pieces_addr::pieces_addr, pieces_addr::decide_autoinc,
pieces_addr::adjust, pieces_addr::increment_address,
pieces_addr::maybe_predec, pieces_addr::maybe_postinc): New member
functions for it.
(class op_by_pieces_d): New.
(op_by_pieces_d::op_by_pieces_d, op_by_pieces_d::run): New member
functions for it.
(class move_by_pieces_d, class compare_by_pieces_d,
class store_by_pieces_d): New subclasses of op_by_pieces_d.
(move_by_pieces_d::prepare_mode, move_by_pieces_d::generate,
move_by_pieces_d::finish_endp, store_by_pieces_d::prepare_mode,
store_by_pieces_d::generate, store_by_pieces_d::finish_endp,
compare_by_pieces_d::generate, compare_by_pieces_d::prepare_mode,
compare_by_pieces_d::finish_mode): New member functions.
(compare_by_pieces, emit_block_cmp_via_cmpmem): New static
functions.
(expand_cmpstrn_or_cmpmem): Moved here from builtins.c.
(emit_block_cmp_hints): New function.
(move_by_pieces, store_by_pieces, clear_by_pieces): Rewrite to just
use the newly defined classes.
* expr.h (by_pieces_constfn): New typedef.
(can_store_by_pieces, store_by_pieces): Use it in arg declarations.
(emit_block_cmp_hints, expand_cmpstrn_or_cmpmem): Declare.
(move_by_pieces_ninsns): Don't declare.
(can_move_by_pieces): Change return value to bool.
* target.def (TARGET_USE_BY_PIECES_INFRASTRUCTURE_P): Update docs.
(compare_by_pieces_branch_ratio): New hook.
* target.h (enum by_pieces_operation): Add COMPARE_BY_PIECES.
(by_pieces_ninsns): Declare.
* targethooks.c (default_use_by_pieces_infrastructure_p): Handle
COMPARE_BY_PIECES.
(default_compare_by_pieces_branch_ratio): New function.
* targhooks.h (default_compare_by_pieces_branch_ratio): Declare.
* doc/tm.texi.in (STORE_MAX_PIECES, COMPARE_MAX_PIECES): Document.
* doc/tm.texi: Regenerate.
* tree-ssa-strlen.c: Include "builtins.h".
(handle_builtin_memcmp): New static function.
(strlen_optimize_stmt): Call it for BUILT_IN_MEMCMP.
* tree.c (build_common_builtin_nodes): Create __builtin_memcmp_eq.
testsuite/
PR tree-optimization/52171
* gcc.dg/pr52171.c: New test.
* gcc.target/i386/pr52171.c: New test.
From-SVN: r237069
Diffstat (limited to 'gcc/expr.c')
-rw-r--r-- | gcc/expr.c | 1356 |
1 files changed, 815 insertions, 541 deletions
@@ -70,51 +70,12 @@ along with GCC; see the file COPYING3. If not see the same indirect address eventually. */ int cse_not_expected; -/* This structure is used by move_by_pieces to describe the move to - be performed. */ -struct move_by_pieces_d -{ - rtx to; - rtx to_addr; - int autinc_to; - int explicit_inc_to; - rtx from; - rtx from_addr; - int autinc_from; - int explicit_inc_from; - unsigned HOST_WIDE_INT len; - HOST_WIDE_INT offset; - int reverse; -}; - -/* This structure is used by store_by_pieces to describe the clear to - be performed. */ - -struct store_by_pieces_d -{ - rtx to; - rtx to_addr; - int autinc_to; - int explicit_inc_to; - unsigned HOST_WIDE_INT len; - HOST_WIDE_INT offset; - rtx (*constfun) (void *, HOST_WIDE_INT, machine_mode); - void *constfundata; - int reverse; -}; - -static void move_by_pieces_1 (insn_gen_fn, machine_mode, - struct move_by_pieces_d *); static bool block_move_libcall_safe_for_call_parm (void); static bool emit_block_move_via_movmem (rtx, rtx, rtx, unsigned, unsigned, HOST_WIDE_INT, unsigned HOST_WIDE_INT, unsigned HOST_WIDE_INT, unsigned HOST_WIDE_INT); static void emit_block_move_via_loop (rtx, rtx, rtx, unsigned); -static rtx clear_by_pieces_1 (void *, HOST_WIDE_INT, machine_mode); static void clear_by_pieces (rtx, unsigned HOST_WIDE_INT, unsigned int); -static void store_by_pieces_1 (struct store_by_pieces_d *, unsigned int); -static void store_by_pieces_2 (insn_gen_fn, machine_mode, - struct store_by_pieces_d *); static rtx_insn *compress_float_constant (rtx, rtx); static rtx get_subtarget (rtx); static void store_constructor_field (rtx, unsigned HOST_WIDE_INT, @@ -767,276 +728,799 @@ widest_int_mode_for_size (unsigned int size) return mode; } +/* Determine whether an operation OP on LEN bytes with alignment ALIGN can + and should be performed piecewise. */ + +static bool +can_do_by_pieces (unsigned HOST_WIDE_INT len, unsigned int align, + enum by_pieces_operation op) +{ + return targetm.use_by_pieces_infrastructure_p (len, align, op, + optimize_insn_for_speed_p ()); +} + /* Determine whether the LEN bytes can be moved by using several move instructions. Return nonzero if a call to move_by_pieces should succeed. */ -int -can_move_by_pieces (unsigned HOST_WIDE_INT len, - unsigned int align) +bool +can_move_by_pieces (unsigned HOST_WIDE_INT len, unsigned int align) { - return targetm.use_by_pieces_infrastructure_p (len, align, MOVE_BY_PIECES, - optimize_insn_for_speed_p ()); + return can_do_by_pieces (len, align, MOVE_BY_PIECES); } -/* Generate several move instructions to copy LEN bytes from block FROM to - block TO. (These are MEM rtx's with BLKmode). +/* Return number of insns required to perform operation OP by pieces + for L bytes. ALIGN (in bits) is maximum alignment we can assume. */ - If PUSH_ROUNDING is defined and TO is NULL, emit_single_push_insn is - used to push FROM to the stack. +unsigned HOST_WIDE_INT +by_pieces_ninsns (unsigned HOST_WIDE_INT l, unsigned int align, + unsigned int max_size, by_pieces_operation op) +{ + unsigned HOST_WIDE_INT n_insns = 0; - ALIGN is maximum stack alignment we can assume. + align = alignment_for_piecewise_move (MOVE_MAX_PIECES, align); - If ENDP is 0 return to, if ENDP is 1 return memory at the end ala - mempcpy, and if ENDP is 2 return memory the end minus one byte ala - stpcpy. */ + while (max_size > 1 && l > 0) + { + machine_mode mode; + enum insn_code icode; -rtx -move_by_pieces (rtx to, rtx from, unsigned HOST_WIDE_INT len, - unsigned int align, int endp) + mode = widest_int_mode_for_size (max_size); + + if (mode == VOIDmode) + break; + unsigned int modesize = GET_MODE_SIZE (mode); + + icode = optab_handler (mov_optab, mode); + if (icode != CODE_FOR_nothing && align >= GET_MODE_ALIGNMENT (mode)) + { + unsigned HOST_WIDE_INT n_pieces = l / modesize; + l %= modesize; + switch (op) + { + default: + n_insns += n_pieces; + break; + + case COMPARE_BY_PIECES: + int batch = targetm.compare_by_pieces_branch_ratio (mode); + int batch_ops = 4 * batch - 1; + int full = n_pieces / batch; + n_insns += full * batch_ops; + if (n_pieces % batch != 0) + n_insns++; + break; + + } + } + max_size = modesize; + } + + gcc_assert (!l); + return n_insns; +} + +/* Used when performing piecewise block operations, holds information + about one of the memory objects involved. The member functions + can be used to generate code for loading from the object and + updating the address when iterating. */ + +class pieces_addr { - struct move_by_pieces_d data; - machine_mode to_addr_mode; - machine_mode from_addr_mode = get_address_mode (from); - rtx to_addr, from_addr = XEXP (from, 0); - unsigned int max_size = MOVE_MAX_PIECES + 1; - enum insn_code icode; + /* The object being referenced, a MEM. Can be NULL_RTX to indicate + stack pushes. */ + rtx m_obj; + /* The address of the object. Can differ from that seen in the + MEM rtx if we copied the address to a register. */ + rtx m_addr; + /* Nonzero if the address on the object has an autoincrement already, + signifies whether that was an increment or decrement. */ + signed char m_addr_inc; + /* Nonzero if we intend to use autoinc without the address already + having autoinc form. We will insert add insns around each memory + reference, expecting later passes to form autoinc addressing modes. + The only supported options are predecrement and postincrement. */ + signed char m_explicit_inc; + /* True if we have either of the two possible cases of using + autoincrement. */ + bool m_auto; + /* True if this is an address to be used for load operations rather + than stores. */ + bool m_is_load; + + /* Optionally, a function to obtain constants for any given offset into + the objects, and data associated with it. */ + by_pieces_constfn m_constfn; + void *m_cfndata; +public: + pieces_addr (rtx, bool, by_pieces_constfn, void *); + rtx adjust (machine_mode, HOST_WIDE_INT); + void increment_address (HOST_WIDE_INT); + void maybe_predec (HOST_WIDE_INT); + void maybe_postinc (HOST_WIDE_INT); + void decide_autoinc (machine_mode, bool, HOST_WIDE_INT); + int get_addr_inc () + { + return m_addr_inc; + } +}; - align = MIN (to ? MEM_ALIGN (to) : align, MEM_ALIGN (from)); +/* Initialize a pieces_addr structure from an object OBJ. IS_LOAD is + true if the operation to be performed on this object is a load + rather than a store. For stores, OBJ can be NULL, in which case we + assume the operation is a stack push. For loads, the optional + CONSTFN and its associated CFNDATA can be used in place of the + memory load. */ - data.offset = 0; - data.from_addr = from_addr; - if (to) - { - to_addr_mode = get_address_mode (to); - to_addr = XEXP (to, 0); - data.to = to; - data.autinc_to - = (GET_CODE (to_addr) == PRE_INC || GET_CODE (to_addr) == PRE_DEC - || GET_CODE (to_addr) == POST_INC || GET_CODE (to_addr) == POST_DEC); - data.reverse - = (GET_CODE (to_addr) == PRE_DEC || GET_CODE (to_addr) == POST_DEC); +pieces_addr::pieces_addr (rtx obj, bool is_load, by_pieces_constfn constfn, + void *cfndata) + : m_obj (obj), m_is_load (is_load), m_constfn (constfn), m_cfndata (cfndata) +{ + m_addr_inc = 0; + m_auto = false; + if (obj) + { + rtx addr = XEXP (obj, 0); + rtx_code code = GET_CODE (addr); + m_addr = addr; + bool dec = code == PRE_DEC || code == POST_DEC; + bool inc = code == PRE_INC || code == POST_INC; + m_auto = inc || dec; + if (m_auto) + m_addr_inc = dec ? -1 : 1; + + /* While we have always looked for these codes here, the code + implementing the memory operation has never handled them. + Support could be added later if necessary or beneficial. */ + gcc_assert (code != PRE_INC && code != POST_DEC); } else { - to_addr_mode = VOIDmode; - to_addr = NULL_RTX; - data.to = NULL_RTX; - data.autinc_to = 1; - if (STACK_GROWS_DOWNWARD) - data.reverse = 1; + m_addr = NULL_RTX; + if (!is_load) + { + m_auto = true; + if (STACK_GROWS_DOWNWARD) + m_addr_inc = -1; + else + m_addr_inc = 1; + } else - data.reverse = 0; + gcc_assert (constfn != NULL); } - data.to_addr = to_addr; - data.from = from; - data.autinc_from - = (GET_CODE (from_addr) == PRE_INC || GET_CODE (from_addr) == PRE_DEC - || GET_CODE (from_addr) == POST_INC - || GET_CODE (from_addr) == POST_DEC); + m_explicit_inc = 0; + if (constfn) + gcc_assert (is_load); +} + +/* Decide whether to use autoinc for an address involved in a memory op. + MODE is the mode of the accesses, REVERSE is true if we've decided to + perform the operation starting from the end, and LEN is the length of + the operation. Don't override an earlier decision to set m_auto. */ + +void +pieces_addr::decide_autoinc (machine_mode ARG_UNUSED (mode), bool reverse, + HOST_WIDE_INT len) +{ + if (m_auto || m_obj == NULL_RTX) + return; - data.explicit_inc_from = 0; - data.explicit_inc_to = 0; - if (data.reverse) data.offset = len; - data.len = len; + bool use_predec = (m_is_load + ? USE_LOAD_PRE_DECREMENT (mode) + : USE_STORE_PRE_DECREMENT (mode)); + bool use_postinc = (m_is_load + ? USE_LOAD_POST_INCREMENT (mode) + : USE_STORE_POST_INCREMENT (mode)); + machine_mode addr_mode = get_address_mode (m_obj); + + if (use_predec && reverse) + { + m_addr = copy_to_mode_reg (addr_mode, + plus_constant (addr_mode, + m_addr, len)); + m_auto = true; + m_explicit_inc = -1; + } + else if (use_postinc && !reverse) + { + m_addr = copy_to_mode_reg (addr_mode, m_addr); + m_auto = true; + m_explicit_inc = 1; + } + else if (CONSTANT_P (m_addr)) + m_addr = copy_to_mode_reg (addr_mode, m_addr); +} + +/* Adjust the address to refer to the data at OFFSET in MODE. If we + are using autoincrement for this address, we don't add the offset, + but we still modify the MEM's properties. */ + +rtx +pieces_addr::adjust (machine_mode mode, HOST_WIDE_INT offset) +{ + if (m_constfn) + return m_constfn (m_cfndata, offset, mode); + if (m_obj == NULL_RTX) + return NULL_RTX; + if (m_auto) + return adjust_automodify_address (m_obj, mode, m_addr, offset); + else + return adjust_address (m_obj, mode, offset); +} + +/* Emit an add instruction to increment the address by SIZE. */ + +void +pieces_addr::increment_address (HOST_WIDE_INT size) +{ + rtx amount = gen_int_mode (size, GET_MODE (m_addr)); + emit_insn (gen_add2_insn (m_addr, amount)); +} + +/* If we are supposed to decrement the address after each access, emit code + to do so now. Increment by SIZE (which has should have the correct sign + already). */ + +void +pieces_addr::maybe_predec (HOST_WIDE_INT size) +{ + if (m_explicit_inc >= 0) + return; + gcc_assert (HAVE_PRE_DECREMENT); + increment_address (size); +} + +/* If we are supposed to decrement the address after each access, emit code + to do so now. Increment by SIZE. */ + +void +pieces_addr::maybe_postinc (HOST_WIDE_INT size) +{ + if (m_explicit_inc <= 0) + return; + gcc_assert (HAVE_POST_INCREMENT); + increment_address (size); +} + +/* This structure is used by do_op_by_pieces to describe the operation + to be performed. */ + +class op_by_pieces_d +{ + protected: + pieces_addr m_to, m_from; + unsigned HOST_WIDE_INT m_len; + HOST_WIDE_INT m_offset; + unsigned int m_align; + unsigned int m_max_size; + bool m_reverse; + + /* Virtual functions, overriden by derived classes for the specific + operation. */ + virtual void generate (rtx, rtx, machine_mode) = 0; + virtual bool prepare_mode (machine_mode, unsigned int) = 0; + virtual void finish_mode (machine_mode) + { + } + + public: + op_by_pieces_d (rtx, bool, rtx, bool, by_pieces_constfn, void *, + unsigned HOST_WIDE_INT, unsigned int); + void run (); +}; + +/* The constructor for an op_by_pieces_d structure. We require two + objects named TO and FROM, which are identified as loads or stores + by TO_LOAD and FROM_LOAD. If FROM is a load, the optional FROM_CFN + and its associated FROM_CFN_DATA can be used to replace loads with + constant values. LEN describes the length of the operation. */ + +op_by_pieces_d::op_by_pieces_d (rtx to, bool to_load, + rtx from, bool from_load, + by_pieces_constfn from_cfn, + void *from_cfn_data, + unsigned HOST_WIDE_INT len, + unsigned int align) + : m_to (to, to_load, NULL, NULL), + m_from (from, from_load, from_cfn, from_cfn_data), + m_len (len), m_max_size (MOVE_MAX_PIECES + 1) +{ + int toi = m_to.get_addr_inc (); + int fromi = m_from.get_addr_inc (); + if (toi >= 0 && fromi >= 0) + m_reverse = false; + else if (toi <= 0 && fromi <= 0) + m_reverse = true; + else + gcc_unreachable (); + + m_offset = m_reverse ? len : 0; + align = MIN (to ? MEM_ALIGN (to) : align, + from ? MEM_ALIGN (from) : align); /* If copying requires more than two move insns, copy addresses to registers (to make displacements shorter) and use post-increment if available. */ - if (!(data.autinc_from && data.autinc_to) - && move_by_pieces_ninsns (len, align, max_size) > 2) + if (by_pieces_ninsns (len, align, m_max_size, MOVE_BY_PIECES) > 2) { - /* Find the mode of the largest move... - MODE might not be used depending on the definitions of the - USE_* macros below. */ - machine_mode mode ATTRIBUTE_UNUSED - = widest_int_mode_for_size (max_size); + /* Find the mode of the largest comparison. */ + machine_mode mode = widest_int_mode_for_size (m_max_size); - if (USE_LOAD_PRE_DECREMENT (mode) && data.reverse && ! data.autinc_from) - { - data.from_addr = copy_to_mode_reg (from_addr_mode, - plus_constant (from_addr_mode, - from_addr, len)); - data.autinc_from = 1; - data.explicit_inc_from = -1; - } - if (USE_LOAD_POST_INCREMENT (mode) && ! data.autinc_from) - { - data.from_addr = copy_to_mode_reg (from_addr_mode, from_addr); - data.autinc_from = 1; - data.explicit_inc_from = 1; - } - if (!data.autinc_from && CONSTANT_P (from_addr)) - data.from_addr = copy_to_mode_reg (from_addr_mode, from_addr); - if (USE_STORE_PRE_DECREMENT (mode) && data.reverse && ! data.autinc_to) - { - data.to_addr = copy_to_mode_reg (to_addr_mode, - plus_constant (to_addr_mode, - to_addr, len)); - data.autinc_to = 1; - data.explicit_inc_to = -1; - } - if (USE_STORE_POST_INCREMENT (mode) && ! data.reverse && ! data.autinc_to) - { - data.to_addr = copy_to_mode_reg (to_addr_mode, to_addr); - data.autinc_to = 1; - data.explicit_inc_to = 1; - } - if (!data.autinc_to && CONSTANT_P (to_addr)) - data.to_addr = copy_to_mode_reg (to_addr_mode, to_addr); + m_from.decide_autoinc (mode, m_reverse, len); + m_to.decide_autoinc (mode, m_reverse, len); } align = alignment_for_piecewise_move (MOVE_MAX_PIECES, align); + m_align = align; +} - /* First move what we can in the largest integer mode, then go to - successively smaller modes. */ +/* This function contains the main loop used for expanding a block + operation. First move what we can in the largest integer mode, + then go to successively smaller modes. For every access, call + GENFUN with the two operands and the EXTRA_DATA. */ - while (max_size > 1 && data.len > 0) +void +op_by_pieces_d::run () +{ + while (m_max_size > 1 && m_len > 0) { - machine_mode mode = widest_int_mode_for_size (max_size); + machine_mode mode = widest_int_mode_for_size (m_max_size); if (mode == VOIDmode) break; - icode = optab_handler (mov_optab, mode); - if (icode != CODE_FOR_nothing && align >= GET_MODE_ALIGNMENT (mode)) - move_by_pieces_1 (GEN_FCN (icode), mode, &data); + if (prepare_mode (mode, m_align)) + { + unsigned int size = GET_MODE_SIZE (mode); + rtx to1 = NULL_RTX, from1; + + while (m_len >= size) + { + if (m_reverse) + m_offset -= size; + + to1 = m_to.adjust (mode, m_offset); + from1 = m_from.adjust (mode, m_offset); + + m_to.maybe_predec (-(HOST_WIDE_INT)size); + m_from.maybe_predec (-(HOST_WIDE_INT)size); + + generate (to1, from1, mode); + + m_to.maybe_postinc (size); + m_from.maybe_postinc (size); + + if (!m_reverse) + m_offset += size; - max_size = GET_MODE_SIZE (mode); + m_len -= size; + } + + finish_mode (mode); + } + + m_max_size = GET_MODE_SIZE (mode); } /* The code above should have handled everything. */ - gcc_assert (!data.len); + gcc_assert (!m_len); +} + +/* Derived class from op_by_pieces_d, providing support for block move + operations. */ + +class move_by_pieces_d : public op_by_pieces_d +{ + insn_gen_fn m_gen_fun; + void generate (rtx, rtx, machine_mode); + bool prepare_mode (machine_mode, unsigned int); + + public: + move_by_pieces_d (rtx to, rtx from, unsigned HOST_WIDE_INT len, + unsigned int align) + : op_by_pieces_d (to, false, from, true, NULL, NULL, len, align) + { + } + rtx finish_endp (int); +}; + +/* Return true if MODE can be used for a set of copies, given an + alignment ALIGN. Prepare whatever data is necessary for later + calls to generate. */ + +bool +move_by_pieces_d::prepare_mode (machine_mode mode, unsigned int align) +{ + insn_code icode = optab_handler (mov_optab, mode); + m_gen_fun = GEN_FCN (icode); + return icode != CODE_FOR_nothing && align >= GET_MODE_ALIGNMENT (mode); +} + +/* A callback used when iterating for a compare_by_pieces_operation. + OP0 and OP1 are the values that have been loaded and should be + compared in MODE. If OP0 is NULL, this means we should generate a + push; otherwise EXTRA_DATA holds a pointer to a pointer to the insn + gen function that should be used to generate the mode. */ + +void +move_by_pieces_d::generate (rtx op0, rtx op1, machine_mode mode) +{ +#ifdef PUSH_ROUNDING + if (op0 == NULL_RTX) + { + emit_single_push_insn (mode, op1, NULL); + return; + } +#endif + emit_insn (m_gen_fun (op0, op1)); +} + +/* Perform the final adjustment at the end of a string to obtain the + correct return value for the block operation. If ENDP is 1 return + memory at the end ala mempcpy, and if ENDP is 2 return memory the + end minus one byte ala stpcpy. */ + +rtx +move_by_pieces_d::finish_endp (int endp) +{ + gcc_assert (!m_reverse); + if (endp == 2) + { + m_to.maybe_postinc (-1); + --m_offset; + } + return m_to.adjust (QImode, m_offset); +} + +/* Generate several move instructions to copy LEN bytes from block FROM to + block TO. (These are MEM rtx's with BLKmode). + + If PUSH_ROUNDING is defined and TO is NULL, emit_single_push_insn is + used to push FROM to the stack. + + ALIGN is maximum stack alignment we can assume. + + If ENDP is 0 return to, if ENDP is 1 return memory at the end ala + mempcpy, and if ENDP is 2 return memory the end minus one byte ala + stpcpy. */ + +rtx +move_by_pieces (rtx to, rtx from, unsigned HOST_WIDE_INT len, + unsigned int align, int endp) +{ +#ifndef PUSH_ROUNDING + if (to == NULL) + gcc_unreachable (); +#endif + + move_by_pieces_d data (to, from, len, align); + + data.run (); if (endp) + return data.finish_endp (endp); + else + return to; +} + +/* Derived class from op_by_pieces_d, providing support for block move + operations. */ + +class store_by_pieces_d : public op_by_pieces_d +{ + insn_gen_fn m_gen_fun; + void generate (rtx, rtx, machine_mode); + bool prepare_mode (machine_mode, unsigned int); + + public: + store_by_pieces_d (rtx to, by_pieces_constfn cfn, void *cfn_data, + unsigned HOST_WIDE_INT len, unsigned int align) + : op_by_pieces_d (to, false, NULL_RTX, true, cfn, cfn_data, len, align) + { + } + rtx finish_endp (int); +}; + +/* Return true if MODE can be used for a set of stores, given an + alignment ALIGN. Prepare whatever data is necessary for later + calls to generate. */ + +bool +store_by_pieces_d::prepare_mode (machine_mode mode, unsigned int align) +{ + insn_code icode = optab_handler (mov_optab, mode); + m_gen_fun = GEN_FCN (icode); + return icode != CODE_FOR_nothing && align >= GET_MODE_ALIGNMENT (mode); +} + +/* A callback used when iterating for a store_by_pieces_operation. + OP0 and OP1 are the values that have been loaded and should be + compared in MODE. If OP0 is NULL, this means we should generate a + push; otherwise EXTRA_DATA holds a pointer to a pointer to the insn + gen function that should be used to generate the mode. */ + +void +store_by_pieces_d::generate (rtx op0, rtx op1, machine_mode) +{ + emit_insn (m_gen_fun (op0, op1)); +} + +/* Perform the final adjustment at the end of a string to obtain the + correct return value for the block operation. If ENDP is 1 return + memory at the end ala mempcpy, and if ENDP is 2 return memory the + end minus one byte ala stpcpy. */ + +rtx +store_by_pieces_d::finish_endp (int endp) +{ + gcc_assert (!m_reverse); + if (endp == 2) { - rtx to1; + m_to.maybe_postinc (-1); + --m_offset; + } + return m_to.adjust (QImode, m_offset); +} + +/* Determine whether the LEN bytes generated by CONSTFUN can be + stored to memory using several move instructions. CONSTFUNDATA is + a pointer which will be passed as argument in every CONSTFUN call. + ALIGN is maximum alignment we can assume. MEMSETP is true if this is + a memset operation and false if it's a copy of a constant string. + Return nonzero if a call to store_by_pieces should succeed. */ + +int +can_store_by_pieces (unsigned HOST_WIDE_INT len, + rtx (*constfun) (void *, HOST_WIDE_INT, machine_mode), + void *constfundata, unsigned int align, bool memsetp) +{ + unsigned HOST_WIDE_INT l; + unsigned int max_size; + HOST_WIDE_INT offset = 0; + machine_mode mode; + enum insn_code icode; + int reverse; + /* cst is set but not used if LEGITIMATE_CONSTANT doesn't use it. */ + rtx cst ATTRIBUTE_UNUSED; + + if (len == 0) + return 1; + + if (!targetm.use_by_pieces_infrastructure_p (len, align, + memsetp + ? SET_BY_PIECES + : STORE_BY_PIECES, + optimize_insn_for_speed_p ())) + return 0; + + align = alignment_for_piecewise_move (STORE_MAX_PIECES, align); + + /* We would first store what we can in the largest integer mode, then go to + successively smaller modes. */ - gcc_assert (!data.reverse); - if (data.autinc_to) + for (reverse = 0; + reverse <= (HAVE_PRE_DECREMENT || HAVE_POST_DECREMENT); + reverse++) + { + l = len; + max_size = STORE_MAX_PIECES + 1; + while (max_size > 1 && l > 0) { - if (endp == 2) + mode = widest_int_mode_for_size (max_size); + + if (mode == VOIDmode) + break; + + icode = optab_handler (mov_optab, mode); + if (icode != CODE_FOR_nothing + && align >= GET_MODE_ALIGNMENT (mode)) { - if (HAVE_POST_INCREMENT && data.explicit_inc_to > 0) - emit_insn (gen_add2_insn (data.to_addr, constm1_rtx)); - else - data.to_addr = copy_to_mode_reg (to_addr_mode, - plus_constant (to_addr_mode, - data.to_addr, - -1)); + unsigned int size = GET_MODE_SIZE (mode); + + while (l >= size) + { + if (reverse) + offset -= size; + + cst = (*constfun) (constfundata, offset, mode); + if (!targetm.legitimate_constant_p (mode, cst)) + return 0; + + if (!reverse) + offset += size; + + l -= size; + } } - to1 = adjust_automodify_address (data.to, QImode, data.to_addr, - data.offset); - } - else - { - if (endp == 2) - --data.offset; - to1 = adjust_address (data.to, QImode, data.offset); + + max_size = GET_MODE_SIZE (mode); } - return to1; + + /* The code above should have handled everything. */ + gcc_assert (!l); } + + return 1; +} + +/* Generate several move instructions to store LEN bytes generated by + CONSTFUN to block TO. (A MEM rtx with BLKmode). CONSTFUNDATA is a + pointer which will be passed as argument in every CONSTFUN call. + ALIGN is maximum alignment we can assume. MEMSETP is true if this is + a memset operation and false if it's a copy of a constant string. + If ENDP is 0 return to, if ENDP is 1 return memory at the end ala + mempcpy, and if ENDP is 2 return memory the end minus one byte ala + stpcpy. */ + +rtx +store_by_pieces (rtx to, unsigned HOST_WIDE_INT len, + rtx (*constfun) (void *, HOST_WIDE_INT, machine_mode), + void *constfundata, unsigned int align, bool memsetp, int endp) +{ + if (len == 0) + { + gcc_assert (endp != 2); + return to; + } + + gcc_assert (targetm.use_by_pieces_infrastructure_p + (len, align, + memsetp ? SET_BY_PIECES : STORE_BY_PIECES, + optimize_insn_for_speed_p ())); + + store_by_pieces_d data (to, constfun, constfundata, len, align); + data.run (); + + if (endp) + return data.finish_endp (endp); else - return data.to; + return to; } -/* Return number of insns required to move L bytes by pieces. - ALIGN (in bits) is maximum alignment we can assume. */ +/* Callback routine for clear_by_pieces. + Return const0_rtx unconditionally. */ -unsigned HOST_WIDE_INT -move_by_pieces_ninsns (unsigned HOST_WIDE_INT l, unsigned int align, - unsigned int max_size) +static rtx +clear_by_pieces_1 (void *, HOST_WIDE_INT, machine_mode) { - unsigned HOST_WIDE_INT n_insns = 0; + return const0_rtx; +} - align = alignment_for_piecewise_move (MOVE_MAX_PIECES, align); +/* Generate several move instructions to clear LEN bytes of block TO. (A MEM + rtx with BLKmode). ALIGN is maximum alignment we can assume. */ - while (max_size > 1 && l > 0) - { - machine_mode mode; - enum insn_code icode; +static void +clear_by_pieces (rtx to, unsigned HOST_WIDE_INT len, unsigned int align) +{ + if (len == 0) + return; - mode = widest_int_mode_for_size (max_size); + store_by_pieces_d data (to, clear_by_pieces_1, NULL, len, align); + data.run (); +} - if (mode == VOIDmode) - break; +/* Context used by compare_by_pieces_genfn. It stores the fail label + to jump to in case of miscomparison, and for branch ratios greater than 1, + it stores an accumulator and the current and maximum counts before + emitting another branch. */ - icode = optab_handler (mov_optab, mode); - if (icode != CODE_FOR_nothing && align >= GET_MODE_ALIGNMENT (mode)) - n_insns += l / GET_MODE_SIZE (mode), l %= GET_MODE_SIZE (mode); +class compare_by_pieces_d : public op_by_pieces_d +{ + rtx_code_label *m_fail_label; + rtx m_accumulator; + int m_count, m_batch; + + void generate (rtx, rtx, machine_mode); + bool prepare_mode (machine_mode, unsigned int); + void finish_mode (machine_mode); + public: + compare_by_pieces_d (rtx op0, rtx op1, by_pieces_constfn op1_cfn, + void *op1_cfn_data, HOST_WIDE_INT len, int align, + rtx_code_label *fail_label) + : op_by_pieces_d (op0, true, op1, true, op1_cfn, op1_cfn_data, len, align) + { + m_fail_label = fail_label; + } +}; - max_size = GET_MODE_SIZE (mode); +/* A callback used when iterating for a compare_by_pieces_operation. + OP0 and OP1 are the values that have been loaded and should be + compared in MODE. DATA holds a pointer to the compare_by_pieces_data + context structure. */ + +void +compare_by_pieces_d::generate (rtx op0, rtx op1, machine_mode mode) +{ + if (m_batch > 1) + { + rtx temp = expand_binop (mode, sub_optab, op0, op1, NULL_RTX, + true, OPTAB_LIB_WIDEN); + if (m_count != 0) + temp = expand_binop (mode, ior_optab, m_accumulator, temp, temp, + true, OPTAB_LIB_WIDEN); + m_accumulator = temp; + + if (++m_count < m_batch) + return; + + m_count = 0; + op0 = m_accumulator; + op1 = const0_rtx; + m_accumulator = NULL_RTX; } + do_compare_rtx_and_jump (op0, op1, NE, true, mode, NULL_RTX, NULL, + m_fail_label, -1); +} - gcc_assert (!l); - return n_insns; +/* Return true if MODE can be used for a set of moves and comparisons, + given an alignment ALIGN. Prepare whatever data is necessary for + later calls to generate. */ + +bool +compare_by_pieces_d::prepare_mode (machine_mode mode, unsigned int align) +{ + insn_code icode = optab_handler (mov_optab, mode); + if (icode == CODE_FOR_nothing + || align < GET_MODE_ALIGNMENT (mode) + || !can_compare_p (EQ, mode, ccp_jump)) + return false; + m_batch = targetm.compare_by_pieces_branch_ratio (mode); + if (m_batch < 0) + return false; + m_accumulator = NULL_RTX; + m_count = 0; + return true; } -/* Subroutine of move_by_pieces. Move as many bytes as appropriate - with move instructions for mode MODE. GENFUN is the gen_... function - to make a move insn for that mode. DATA has all the other info. */ +/* Called after expanding a series of comparisons in MODE. If we have + accumulated results for which we haven't emitted a branch yet, do + so now. */ -static void -move_by_pieces_1 (insn_gen_fn genfun, machine_mode mode, - struct move_by_pieces_d *data) +void +compare_by_pieces_d::finish_mode (machine_mode mode) { - unsigned int size = GET_MODE_SIZE (mode); - rtx to1 = NULL_RTX, from1; + if (m_accumulator != NULL_RTX) + do_compare_rtx_and_jump (m_accumulator, const0_rtx, NE, true, mode, + NULL_RTX, NULL, m_fail_label, -1); +} - while (data->len >= size) - { - if (data->reverse) - data->offset -= size; +/* Generate several move instructions to compare LEN bytes from blocks + ARG0 and ARG1. (These are MEM rtx's with BLKmode). - if (data->to) - { - if (data->autinc_to) - to1 = adjust_automodify_address (data->to, mode, data->to_addr, - data->offset); - else - to1 = adjust_address (data->to, mode, data->offset); - } + If PUSH_ROUNDING is defined and TO is NULL, emit_single_push_insn is + used to push FROM to the stack. - if (data->autinc_from) - from1 = adjust_automodify_address (data->from, mode, data->from_addr, - data->offset); - else - from1 = adjust_address (data->from, mode, data->offset); - - if (HAVE_PRE_DECREMENT && data->explicit_inc_to < 0) - emit_insn (gen_add2_insn (data->to_addr, - gen_int_mode (-(HOST_WIDE_INT) size, - GET_MODE (data->to_addr)))); - if (HAVE_PRE_DECREMENT && data->explicit_inc_from < 0) - emit_insn (gen_add2_insn (data->from_addr, - gen_int_mode (-(HOST_WIDE_INT) size, - GET_MODE (data->from_addr)))); - - if (data->to) - emit_insn ((*genfun) (to1, from1)); - else - { -#ifdef PUSH_ROUNDING - emit_single_push_insn (mode, from1, NULL); -#else - gcc_unreachable (); -#endif - } + ALIGN is maximum stack alignment we can assume. - if (HAVE_POST_INCREMENT && data->explicit_inc_to > 0) - emit_insn (gen_add2_insn (data->to_addr, - gen_int_mode (size, - GET_MODE (data->to_addr)))); - if (HAVE_POST_INCREMENT && data->explicit_inc_from > 0) - emit_insn (gen_add2_insn (data->from_addr, - gen_int_mode (size, - GET_MODE (data->from_addr)))); + Optionally, the caller can pass a constfn and associated data in A1_CFN + and A1_CFN_DATA. describing that the second operand being compared is a + known constant and how to obtain its data. */ - if (! data->reverse) - data->offset += size; +static rtx +compare_by_pieces (rtx arg0, rtx arg1, unsigned HOST_WIDE_INT len, + rtx target, unsigned int align, + by_pieces_constfn a1_cfn, void *a1_cfn_data) +{ + rtx_code_label *fail_label = gen_label_rtx (); + rtx_code_label *end_label = gen_label_rtx (); - data->len -= size; - } + if (target == NULL_RTX + || !REG_P (target) || REGNO (target) < FIRST_PSEUDO_REGISTER) + target = gen_reg_rtx (TYPE_MODE (integer_type_node)); + + compare_by_pieces_d data (arg0, arg1, a1_cfn, a1_cfn_data, len, align, + fail_label); + + data.run (); + + emit_move_insn (target, const0_rtx); + emit_jump (end_label); + emit_barrier (); + emit_label (fail_label); + emit_move_insn (target, const1_rtx); + emit_label (end_label); + + return target; } /* Emit code to move a block Y to a block X. This may be done with @@ -1066,8 +1550,7 @@ emit_block_move_hints (rtx x, rtx y, rtx size, enum block_op_methods method, unsigned int align; gcc_assert (size); - if (CONST_INT_P (size) - && INTVAL (size) == 0) + if (CONST_INT_P (size) && INTVAL (size) == 0) return 0; switch (method) @@ -1394,6 +1877,99 @@ emit_block_op_via_libcall (enum built_in_function fncode, rtx dst, rtx src, return expand_call (call_expr, NULL_RTX, false); } + +/* Try to expand cmpstrn or cmpmem operation ICODE with the given operands. + ARG3_TYPE is the type of ARG3_RTX. Return the result rtx on success, + otherwise return null. */ + +rtx +expand_cmpstrn_or_cmpmem (insn_code icode, rtx target, rtx arg1_rtx, + rtx arg2_rtx, tree arg3_type, rtx arg3_rtx, + HOST_WIDE_INT align) +{ + machine_mode insn_mode = insn_data[icode].operand[0].mode; + + if (target && (!REG_P (target) || HARD_REGISTER_P (target))) + target = NULL_RTX; + + struct expand_operand ops[5]; + create_output_operand (&ops[0], target, insn_mode); + create_fixed_operand (&ops[1], arg1_rtx); + create_fixed_operand (&ops[2], arg2_rtx); + create_convert_operand_from (&ops[3], arg3_rtx, TYPE_MODE (arg3_type), + TYPE_UNSIGNED (arg3_type)); + create_integer_operand (&ops[4], align); + if (maybe_expand_insn (icode, 5, ops)) + return ops[0].value; + return NULL_RTX; +} + +/* Expand a block compare between X and Y with length LEN using the + cmpmem optab, placing the result in TARGET. LEN_TYPE is the type + of the expression that was used to calculate the length. ALIGN + gives the known minimum common alignment. */ + +static rtx +emit_block_cmp_via_cmpmem (rtx x, rtx y, rtx len, tree len_type, rtx target, + unsigned align) +{ + /* Note: The cmpstrnsi pattern, if it exists, is not suitable for + implementing memcmp because it will stop if it encounters two + zero bytes. */ + insn_code icode = direct_optab_handler (cmpmem_optab, SImode); + + if (icode == CODE_FOR_nothing) + return NULL_RTX; + + return expand_cmpstrn_or_cmpmem (icode, target, x, y, len_type, len, align); +} + +/* Emit code to compare a block Y to a block X. This may be done with + string-compare instructions, with multiple scalar instructions, + or with a library call. + + Both X and Y must be MEM rtx's. LEN is an rtx that says how long + they are. LEN_TYPE is the type of the expression that was used to + calculate it. + + If EQUALITY_ONLY is true, it means we don't have to return the tri-state + value of a normal memcmp call, instead we can just compare for equality. + If FORCE_LIBCALL is true, we should emit a call to memcmp rather than + returning NULL_RTX. + + Optionally, the caller can pass a constfn and associated data in Y_CFN + and Y_CFN_DATA. describing that the second operand being compared is a + known constant and how to obtain its data. + Return the result of the comparison, or NULL_RTX if we failed to + perform the operation. */ + +rtx +emit_block_cmp_hints (rtx x, rtx y, rtx len, tree len_type, rtx target, + bool equality_only, by_pieces_constfn y_cfn, + void *y_cfndata) +{ + rtx result = 0; + + if (CONST_INT_P (len) && INTVAL (len) == 0) + return const0_rtx; + + gcc_assert (MEM_P (x) && MEM_P (y)); + unsigned int align = MIN (MEM_ALIGN (x), MEM_ALIGN (y)); + gcc_assert (align >= BITS_PER_UNIT); + + x = adjust_address (x, BLKmode, 0); + y = adjust_address (y, BLKmode, 0); + + if (equality_only + && CONST_INT_P (len) + && can_do_by_pieces (INTVAL (len), align, COMPARE_BY_PIECES)) + result = compare_by_pieces (x, y, INTVAL (len), target, align, + y_cfn, y_cfndata); + else + result = emit_block_cmp_via_cmpmem (x, y, len, len_type, target, align); + + return result; +} /* Copy all or part of a value X into registers starting at REGNO. The number of registers to be filled is NREGS. */ @@ -2330,308 +2906,6 @@ get_def_for_expr_class (tree name, enum tree_code_class tclass) return def_stmt; } - -/* Determine whether the LEN bytes generated by CONSTFUN can be - stored to memory using several move instructions. CONSTFUNDATA is - a pointer which will be passed as argument in every CONSTFUN call. - ALIGN is maximum alignment we can assume. MEMSETP is true if this is - a memset operation and false if it's a copy of a constant string. - Return nonzero if a call to store_by_pieces should succeed. */ - -int -can_store_by_pieces (unsigned HOST_WIDE_INT len, - rtx (*constfun) (void *, HOST_WIDE_INT, machine_mode), - void *constfundata, unsigned int align, bool memsetp) -{ - unsigned HOST_WIDE_INT l; - unsigned int max_size; - HOST_WIDE_INT offset = 0; - machine_mode mode; - enum insn_code icode; - int reverse; - /* cst is set but not used if LEGITIMATE_CONSTANT doesn't use it. */ - rtx cst ATTRIBUTE_UNUSED; - - if (len == 0) - return 1; - - if (!targetm.use_by_pieces_infrastructure_p (len, align, - memsetp - ? SET_BY_PIECES - : STORE_BY_PIECES, - optimize_insn_for_speed_p ())) - return 0; - - align = alignment_for_piecewise_move (STORE_MAX_PIECES, align); - - /* We would first store what we can in the largest integer mode, then go to - successively smaller modes. */ - - for (reverse = 0; - reverse <= (HAVE_PRE_DECREMENT || HAVE_POST_DECREMENT); - reverse++) - { - l = len; - max_size = STORE_MAX_PIECES + 1; - while (max_size > 1 && l > 0) - { - mode = widest_int_mode_for_size (max_size); - - if (mode == VOIDmode) - break; - - icode = optab_handler (mov_optab, mode); - if (icode != CODE_FOR_nothing - && align >= GET_MODE_ALIGNMENT (mode)) - { - unsigned int size = GET_MODE_SIZE (mode); - - while (l >= size) - { - if (reverse) - offset -= size; - - cst = (*constfun) (constfundata, offset, mode); - if (!targetm.legitimate_constant_p (mode, cst)) - return 0; - - if (!reverse) - offset += size; - - l -= size; - } - } - - max_size = GET_MODE_SIZE (mode); - } - - /* The code above should have handled everything. */ - gcc_assert (!l); - } - - return 1; -} - -/* Generate several move instructions to store LEN bytes generated by - CONSTFUN to block TO. (A MEM rtx with BLKmode). CONSTFUNDATA is a - pointer which will be passed as argument in every CONSTFUN call. - ALIGN is maximum alignment we can assume. MEMSETP is true if this is - a memset operation and false if it's a copy of a constant string. - If ENDP is 0 return to, if ENDP is 1 return memory at the end ala - mempcpy, and if ENDP is 2 return memory the end minus one byte ala - stpcpy. */ - -rtx -store_by_pieces (rtx to, unsigned HOST_WIDE_INT len, - rtx (*constfun) (void *, HOST_WIDE_INT, machine_mode), - void *constfundata, unsigned int align, bool memsetp, int endp) -{ - machine_mode to_addr_mode = get_address_mode (to); - struct store_by_pieces_d data; - - if (len == 0) - { - gcc_assert (endp != 2); - return to; - } - - gcc_assert (targetm.use_by_pieces_infrastructure_p - (len, align, - memsetp - ? SET_BY_PIECES - : STORE_BY_PIECES, - optimize_insn_for_speed_p ())); - - data.constfun = constfun; - data.constfundata = constfundata; - data.len = len; - data.to = to; - store_by_pieces_1 (&data, align); - if (endp) - { - rtx to1; - - gcc_assert (!data.reverse); - if (data.autinc_to) - { - if (endp == 2) - { - if (HAVE_POST_INCREMENT && data.explicit_inc_to > 0) - emit_insn (gen_add2_insn (data.to_addr, constm1_rtx)); - else - data.to_addr = copy_to_mode_reg (to_addr_mode, - plus_constant (to_addr_mode, - data.to_addr, - -1)); - } - to1 = adjust_automodify_address (data.to, QImode, data.to_addr, - data.offset); - } - else - { - if (endp == 2) - --data.offset; - to1 = adjust_address (data.to, QImode, data.offset); - } - return to1; - } - else - return data.to; -} - -/* Generate several move instructions to clear LEN bytes of block TO. (A MEM - rtx with BLKmode). ALIGN is maximum alignment we can assume. */ - -static void -clear_by_pieces (rtx to, unsigned HOST_WIDE_INT len, unsigned int align) -{ - struct store_by_pieces_d data; - - if (len == 0) - return; - - data.constfun = clear_by_pieces_1; - data.constfundata = NULL; - data.len = len; - data.to = to; - store_by_pieces_1 (&data, align); -} - -/* Callback routine for clear_by_pieces. - Return const0_rtx unconditionally. */ - -static rtx -clear_by_pieces_1 (void *data ATTRIBUTE_UNUSED, - HOST_WIDE_INT offset ATTRIBUTE_UNUSED, - machine_mode mode ATTRIBUTE_UNUSED) -{ - return const0_rtx; -} - -/* Subroutine of clear_by_pieces and store_by_pieces. - Generate several move instructions to store LEN bytes of block TO. (A MEM - rtx with BLKmode). ALIGN is maximum alignment we can assume. */ - -static void -store_by_pieces_1 (struct store_by_pieces_d *data ATTRIBUTE_UNUSED, - unsigned int align ATTRIBUTE_UNUSED) -{ - machine_mode to_addr_mode = get_address_mode (data->to); - rtx to_addr = XEXP (data->to, 0); - unsigned int max_size = STORE_MAX_PIECES + 1; - enum insn_code icode; - - data->offset = 0; - data->to_addr = to_addr; - data->autinc_to - = (GET_CODE (to_addr) == PRE_INC || GET_CODE (to_addr) == PRE_DEC - || GET_CODE (to_addr) == POST_INC || GET_CODE (to_addr) == POST_DEC); - - data->explicit_inc_to = 0; - data->reverse - = (GET_CODE (to_addr) == PRE_DEC || GET_CODE (to_addr) == POST_DEC); - if (data->reverse) - data->offset = data->len; - - /* If storing requires more than two move insns, - copy addresses to registers (to make displacements shorter) - and use post-increment if available. */ - if (!data->autinc_to - && move_by_pieces_ninsns (data->len, align, max_size) > 2) - { - /* Determine the main mode we'll be using. - MODE might not be used depending on the definitions of the - USE_* macros below. */ - machine_mode mode ATTRIBUTE_UNUSED - = widest_int_mode_for_size (max_size); - - if (USE_STORE_PRE_DECREMENT (mode) && data->reverse && ! data->autinc_to) - { - data->to_addr = copy_to_mode_reg (to_addr_mode, - plus_constant (to_addr_mode, - to_addr, - data->len)); - data->autinc_to = 1; - data->explicit_inc_to = -1; - } - - if (USE_STORE_POST_INCREMENT (mode) && ! data->reverse - && ! data->autinc_to) - { - data->to_addr = copy_to_mode_reg (to_addr_mode, to_addr); - data->autinc_to = 1; - data->explicit_inc_to = 1; - } - - if ( !data->autinc_to && CONSTANT_P (to_addr)) - data->to_addr = copy_to_mode_reg (to_addr_mode, to_addr); - } - - align = alignment_for_piecewise_move (STORE_MAX_PIECES, align); - - /* First store what we can in the largest integer mode, then go to - successively smaller modes. */ - - while (max_size > 1 && data->len > 0) - { - machine_mode mode = widest_int_mode_for_size (max_size); - - if (mode == VOIDmode) - break; - - icode = optab_handler (mov_optab, mode); - if (icode != CODE_FOR_nothing && align >= GET_MODE_ALIGNMENT (mode)) - store_by_pieces_2 (GEN_FCN (icode), mode, data); - - max_size = GET_MODE_SIZE (mode); - } - - /* The code above should have handled everything. */ - gcc_assert (!data->len); -} - -/* Subroutine of store_by_pieces_1. Store as many bytes as appropriate - with move instructions for mode MODE. GENFUN is the gen_... function - to make a move insn for that mode. DATA has all the other info. */ - -static void -store_by_pieces_2 (insn_gen_fn genfun, machine_mode mode, - struct store_by_pieces_d *data) -{ - unsigned int size = GET_MODE_SIZE (mode); - rtx to1, cst; - - while (data->len >= size) - { - if (data->reverse) - data->offset -= size; - - if (data->autinc_to) - to1 = adjust_automodify_address (data->to, mode, data->to_addr, - data->offset); - else - to1 = adjust_address (data->to, mode, data->offset); - - if (HAVE_PRE_DECREMENT && data->explicit_inc_to < 0) - emit_insn (gen_add2_insn (data->to_addr, - gen_int_mode (-(HOST_WIDE_INT) size, - GET_MODE (data->to_addr)))); - - cst = (*data->constfun) (data->constfundata, data->offset, mode); - emit_insn ((*genfun) (to1, cst)); - - if (HAVE_POST_INCREMENT && data->explicit_inc_to > 0) - emit_insn (gen_add2_insn (data->to_addr, - gen_int_mode (size, - GET_MODE (data->to_addr)))); - - if (! data->reverse) - data->offset += size; - - data->len -= size; - } -} - /* Write zeros through the storage of OBJECT. If OBJECT has BLKmode, SIZE is its length in bytes. */ |