return block_clear_fn;
}
\f
-/* Generate code to copy Y into X.
- Both Y and X must have the same mode, except that
- Y can be a constant with VOIDmode.
- This mode cannot be BLKmode; use emit_block_move for that.
+/* Write to one of the components of the complex value CPLX. Write VAL to
+ the real part if IMAG_P is false, and the imaginary part if its true. */
- Return the last instruction emitted. */
+static void
+write_complex_part (rtx cplx, rtx val, bool imag_p)
+{
+ if (GET_CODE (cplx) == CONCAT)
+ emit_move_insn (XEXP (cplx, imag_p), val);
+ else
+ {
+ enum machine_mode cmode = GET_MODE (cplx);
+ enum machine_mode imode = GET_MODE_INNER (cmode);
+ unsigned ibitsize = GET_MODE_BITSIZE (imode);
-rtx
-emit_move_insn (rtx x, rtx y)
+ store_bit_field (cplx, ibitsize, imag_p ? ibitsize : 0, imode, val);
+ }
+}
+
+/* Extract one of the components of the complex value CPLX. Extract the
+ real part if IMAG_P is false, and the imaginary part if it's true. */
+
+static rtx
+read_complex_part (rtx cplx, bool imag_p)
{
- enum machine_mode mode = GET_MODE (x);
- rtx y_cst = NULL_RTX;
- rtx last_insn, set;
+ enum machine_mode cmode, imode;
+ unsigned ibitsize;
- gcc_assert (mode != BLKmode
- && (GET_MODE (y) == mode || GET_MODE (y) == VOIDmode));
+ if (GET_CODE (cplx) == CONCAT)
+ return XEXP (cplx, imag_p);
- if (CONSTANT_P (y))
+ cmode = GET_MODE (cplx);
+ imode = GET_MODE_INNER (cmode);
+ ibitsize = GET_MODE_BITSIZE (imode);
+
+ /* Special case reads from complex constants that got spilled to memory. */
+ if (MEM_P (cplx) && GET_CODE (XEXP (cplx, 0)) == SYMBOL_REF)
{
- if (optimize
- && SCALAR_FLOAT_MODE_P (GET_MODE (x))
- && (last_insn = compress_float_constant (x, y)))
- return last_insn;
+ tree decl = SYMBOL_REF_DECL (XEXP (cplx, 0));
+ if (decl && TREE_CODE (decl) == COMPLEX_CST)
+ {
+ tree part = imag_p ? TREE_IMAGPART (decl) : TREE_REALPART (decl);
+ if (CONSTANT_CLASS_P (part))
+ return expand_expr (part, NULL_RTX, imode, EXPAND_NORMAL);
+ }
+ }
- y_cst = y;
+ return extract_bit_field (cplx, ibitsize, imag_p ? ibitsize : 0,
+ true, NULL_RTX, imode, imode);
+}
+\f
+/* A subroutine of emit_move_insn_1. Generate a move from Y into X using
+ ALT_MODE instead of the operand's natural mode, MODE. CODE is the insn
+ code for the move in ALT_MODE, and is known to be valid. Returns the
+ instruction emitted. */
- if (!LEGITIMATE_CONSTANT_P (y))
+static rtx
+emit_move_via_alt_mode (enum machine_mode alt_mode, enum machine_mode mode,
+ enum insn_code code, rtx x, rtx y)
+{
+ /* Get X and Y in ALT_MODE. We can't use gen_lowpart here because it
+ may call change_address which is not appropriate if we were
+ called when a reload was in progress. We don't have to worry
+ about changing the address since the size in bytes is supposed to
+ be the same. Copy the MEM to change the mode and move any
+ substitutions from the old MEM to the new one. */
+
+ if (reload_in_progress)
+ {
+ rtx x1 = x, y1 = y;
+
+ x = gen_lowpart_common (alt_mode, x1);
+ if (x == 0 && MEM_P (x1))
{
- y = force_const_mem (mode, y);
+ x = adjust_address_nv (x1, alt_mode, 0);
+ copy_replacements (x1, x);
+ }
- /* If the target's cannot_force_const_mem prevented the spill,
- assume that the target's move expanders will also take care
- of the non-legitimate constant. */
- if (!y)
- y = y_cst;
+ y = gen_lowpart_common (alt_mode, y1);
+ if (y == 0 && MEM_P (y1))
+ {
+ y = adjust_address_nv (y1, alt_mode, 0);
+ copy_replacements (y1, y);
}
}
+ else
+ {
+ x = simplify_gen_subreg (alt_mode, x, mode, 0);
+ y = simplify_gen_subreg (alt_mode, y, mode, 0);
+ }
- /* If X or Y are memory references, verify that their addresses are valid
- for the machine. */
- if (MEM_P (x)
- && ((! memory_address_p (GET_MODE (x), XEXP (x, 0))
- && ! push_operand (x, GET_MODE (x)))
- || (flag_force_addr
- && CONSTANT_ADDRESS_P (XEXP (x, 0)))))
- x = validize_mem (x);
+ return emit_insn (GEN_FCN (code) (x, y));
+}
- if (MEM_P (y)
- && (! memory_address_p (GET_MODE (y), XEXP (y, 0))
- || (flag_force_addr
- && CONSTANT_ADDRESS_P (XEXP (y, 0)))))
- y = validize_mem (y);
+/* A subroutine of emit_move_insn_1. Generate a move from Y into X using
+ an integer mode of the same size as MODE. Returns the instruction
+ emitted, or NULL if such a move could not be generated. */
- gcc_assert (mode != BLKmode);
+static rtx
+emit_move_via_integer (enum machine_mode mode, rtx x, rtx y)
+{
+ enum machine_mode imode;
+ enum insn_code code;
- last_insn = emit_move_insn_1 (x, y);
+ /* There must exist a mode of the exact size we require. */
+ imode = int_mode_for_mode (mode);
+ if (imode == BLKmode)
+ return NULL_RTX;
- if (y_cst && REG_P (x)
- && (set = single_set (last_insn)) != NULL_RTX
- && SET_DEST (set) == x
- && ! rtx_equal_p (y_cst, SET_SRC (set)))
- set_unique_reg_note (last_insn, REG_EQUAL, y_cst);
+ /* The target must support moves in this mode. */
+ code = mov_optab->handlers[imode].insn_code;
+ if (code == CODE_FOR_nothing)
+ return NULL_RTX;
- return last_insn;
+ return emit_move_via_alt_mode (imode, mode, code, x, y);
}
-/* Low level part of emit_move_insn.
- Called just like emit_move_insn, but assumes X and Y
- are basically valid. */
+/* A subroutine of emit_move_insn_1. X is a push_operand in MODE.
+ Return an equivalent MEM that does not use an auto-increment. */
-rtx
-emit_move_insn_1 (rtx x, rtx y)
+static rtx
+emit_move_resolve_push (enum machine_mode mode, rtx x)
{
- enum machine_mode mode = GET_MODE (x);
- enum machine_mode submode;
+ enum rtx_code code = GET_CODE (XEXP (x, 0));
+ HOST_WIDE_INT adjust;
+ rtx temp;
- gcc_assert ((unsigned int) mode < (unsigned int) MAX_MACHINE_MODE);
+ adjust = GET_MODE_SIZE (mode);
+#ifdef PUSH_ROUNDING
+ adjust = PUSH_ROUNDING (adjust);
+#endif
+ if (code == PRE_DEC || code == POST_DEC)
+ adjust = -adjust;
- if (mov_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
- return
- emit_insn (GEN_FCN (mov_optab->handlers[(int) mode].insn_code) (x, y));
+ /* Do not use anti_adjust_stack, since we don't want to update
+ stack_pointer_delta. */
+ temp = expand_simple_binop (Pmode, PLUS, stack_pointer_rtx,
+ GEN_INT (adjust), stack_pointer_rtx,
+ 0, OPTAB_LIB_WIDEN);
+ if (temp != stack_pointer_rtx)
+ emit_move_insn (stack_pointer_rtx, temp);
- /* Expand complex moves by moving real part and imag part, if possible. */
- else if (COMPLEX_MODE_P (mode)
- && BLKmode != (submode = GET_MODE_INNER (mode))
- && (mov_optab->handlers[(int) submode].insn_code
- != CODE_FOR_nothing))
+ switch (code)
{
- unsigned int modesize = GET_MODE_SIZE (mode);
- unsigned int submodesize = GET_MODE_SIZE (submode);
-
- /* Don't split destination if it is a stack push. */
- int stack = push_operand (x, mode);
+ case PRE_INC:
+ case PRE_DEC:
+ temp = stack_pointer_rtx;
+ break;
+ case POST_INC:
+ temp = plus_constant (stack_pointer_rtx, -GET_MODE_SIZE (mode));
+ break;
+ case POST_DEC:
+ temp = plus_constant (stack_pointer_rtx, GET_MODE_SIZE (mode));
+ break;
+ default:
+ gcc_unreachable ();
+ }
-#ifdef PUSH_ROUNDING
- /* In case we output to the stack, but the size is smaller than the
- machine can push exactly, we need to use move instructions. */
- if (stack && PUSH_ROUNDING (submodesize) != submodesize)
- {
- rtx temp;
- HOST_WIDE_INT offset1, offset2;
+ return replace_equiv_address (x, temp);
+}
- /* Do not use anti_adjust_stack, since we don't want to update
- stack_pointer_delta. */
- temp = expand_binop (Pmode,
-#ifdef STACK_GROWS_DOWNWARD
- sub_optab,
-#else
- add_optab,
-#endif
- stack_pointer_rtx,
- GEN_INT (PUSH_ROUNDING (modesize)),
- stack_pointer_rtx, 0, OPTAB_LIB_WIDEN);
+/* A subroutine of emit_move_complex. Generate a move from Y into X.
+ X is known to satisfy push_operand, and MODE is known to be complex.
+ Returns the last instruction emitted. */
- if (temp != stack_pointer_rtx)
- emit_move_insn (stack_pointer_rtx, temp);
+static rtx
+emit_move_complex_push (enum machine_mode mode, rtx x, rtx y)
+{
+ enum machine_mode submode = GET_MODE_INNER (mode);
+ bool imag_first;
-#ifdef STACK_GROWS_DOWNWARD
- offset1 = 0;
- offset2 = submodesize;
-#else
- offset1 = -PUSH_ROUNDING (modesize);
- offset2 = -PUSH_ROUNDING (modesize) + submodesize;
-#endif
+#ifdef PUSH_ROUNDING
+ unsigned int submodesize = GET_MODE_SIZE (submode);
- emit_move_insn (change_address (x, submode,
- gen_rtx_PLUS (Pmode,
- stack_pointer_rtx,
- GEN_INT (offset1))),
- gen_realpart (submode, y));
- emit_move_insn (change_address (x, submode,
- gen_rtx_PLUS (Pmode,
- stack_pointer_rtx,
- GEN_INT (offset2))),
- gen_imagpart (submode, y));
- }
- else
+ /* In case we output to the stack, but the size is smaller than the
+ machine can push exactly, we need to use move instructions. */
+ if (PUSH_ROUNDING (submodesize) != submodesize)
+ {
+ x = emit_move_resolve_push (mode, x);
+ return emit_move_insn (x, y);
+ }
#endif
- /* If this is a stack, push the highpart first, so it
- will be in the argument order.
- In that case, change_address is used only to convert
- the mode, not to change the address. */
- if (stack)
- {
- /* Note that the real part always precedes the imag part in memory
- regardless of machine's endianness. */
-#ifdef STACK_GROWS_DOWNWARD
- emit_move_insn (gen_rtx_MEM (submode, XEXP (x, 0)),
- gen_imagpart (submode, y));
- emit_move_insn (gen_rtx_MEM (submode, XEXP (x, 0)),
- gen_realpart (submode, y));
-#else
- emit_move_insn (gen_rtx_MEM (submode, XEXP (x, 0)),
- gen_realpart (submode, y));
- emit_move_insn (gen_rtx_MEM (submode, XEXP (x, 0)),
- gen_imagpart (submode, y));
-#endif
- }
- else
- {
- rtx realpart_x, realpart_y;
- rtx imagpart_x, imagpart_y;
-
- /* If this is a complex value with each part being smaller than a
- word, the usual calling sequence will likely pack the pieces into
- a single register. Unfortunately, SUBREG of hard registers only
- deals in terms of words, so we have a problem converting input
- arguments to the CONCAT of two registers that is used elsewhere
- for complex values. If this is before reload, we can copy it into
- memory and reload. FIXME, we should see about using extract and
- insert on integer registers, but complex short and complex char
- variables should be rarely used. */
- if ((reload_in_progress | reload_completed) == 0
- && (!validate_subreg (submode, mode, NULL, submodesize)
- || !validate_subreg (submode, mode, NULL, 0)))
- {
- if (REG_P (x) || REG_P (y))
- {
- rtx mem, cmem;
- enum machine_mode reg_mode
- = mode_for_size (GET_MODE_BITSIZE (mode), MODE_INT, 1);
+ /* Note that the real part always precedes the imag part in memory
+ regardless of machine's endianness. */
+ switch (GET_CODE (XEXP (x, 0)))
+ {
+ case PRE_DEC:
+ case POST_DEC:
+ imag_first = true;
+ break;
+ case PRE_INC:
+ case POST_INC:
+ imag_first = false;
+ break;
+ default:
+ gcc_unreachable ();
+ }
- gcc_assert (reg_mode != BLKmode);
+ emit_move_insn (gen_rtx_MEM (submode, XEXP (x, 0)),
+ read_complex_part (y, imag_first));
+ return emit_move_insn (gen_rtx_MEM (submode, XEXP (x, 0)),
+ read_complex_part (y, !imag_first));
+}
- mem = assign_stack_temp (reg_mode, modesize, 0);
- cmem = adjust_address (mem, mode, 0);
+/* A subroutine of emit_move_insn_1. Generate a move from Y into X.
+ MODE is known to be complex. Returns the last instruction emitted. */
- if (REG_P (x))
- {
- rtx sreg = gen_rtx_SUBREG (reg_mode, x, 0);
- emit_move_insn_1 (cmem, y);
- return emit_move_insn_1 (sreg, mem);
- }
- else
- {
- rtx sreg = gen_rtx_SUBREG (reg_mode, y, 0);
- emit_move_insn_1 (mem, sreg);
- return emit_move_insn_1 (x, cmem);
- }
- }
- }
+static rtx
+emit_move_complex (enum machine_mode mode, rtx x, rtx y)
+{
+ bool try_int;
- realpart_x = gen_realpart (submode, x);
- realpart_y = gen_realpart (submode, y);
- imagpart_x = gen_imagpart (submode, x);
- imagpart_y = gen_imagpart (submode, y);
-
- /* Show the output dies here. This is necessary for SUBREGs
- of pseudos since we cannot track their lifetimes correctly;
- hard regs shouldn't appear here except as return values.
- We never want to emit such a clobber after reload. */
- if (x != y
- && ! (reload_in_progress || reload_completed)
- && (GET_CODE (realpart_x) == SUBREG
- || GET_CODE (imagpart_x) == SUBREG))
- emit_insn (gen_rtx_CLOBBER (VOIDmode, x));
-
- emit_move_insn (realpart_x, realpart_y);
- emit_move_insn (imagpart_x, imagpart_y);
- }
+ /* Need to take special care for pushes, to maintain proper ordering
+ of the data, and possibly extra padding. */
+ if (push_operand (x, mode))
+ return emit_move_complex_push (mode, x, y);
+ /* For memory to memory moves, optimial behaviour can be had with the
+ existing block move logic. */
+ if (MEM_P (x) && MEM_P (y))
+ {
+ emit_block_move (x, y, GEN_INT (GET_MODE_SIZE (mode)),
+ BLOCK_OP_NO_LIBCALL);
return get_last_insn ();
}
- /* Handle MODE_CC modes: If we don't have a special move insn for this mode,
- find a mode to do it in. If we have a movcc, use it. Otherwise,
- find the MODE_INT mode of the same width. */
- else if (GET_MODE_CLASS (mode) == MODE_CC
- && mov_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing)
+ /* See if we can coerce the target into moving both values at once. */
+
+ /* Not possible if the values are inherently not adjacent. */
+ if (GET_CODE (x) == CONCAT || GET_CODE (y) == CONCAT)
+ try_int = false;
+ /* Is possible if both are registers (or subregs of registers). */
+ else if (register_operand (x, mode) && register_operand (y, mode))
+ try_int = true;
+ /* If one of the operands is a memory, and alignment constraints
+ are friendly enough, we may be able to do combined memory operations.
+ We do not attempt this if Y is a constant because that combination is
+ usually better with the by-parts thing below. */
+ else if ((MEM_P (x) ? !CONSTANT_P (y) : MEM_P (y))
+ && (!STRICT_ALIGNMENT
+ || get_mode_alignment (mode) == BIGGEST_ALIGNMENT))
+ try_int = true;
+ else
+ try_int = false;
+
+ if (try_int)
{
- enum insn_code insn_code;
- enum machine_mode tmode = VOIDmode;
- rtx x1 = x, y1 = y;
+ rtx ret = emit_move_via_integer (mode, x, y);
+ if (ret)
+ return ret;
+ }
- if (mode != CCmode
- && mov_optab->handlers[(int) CCmode].insn_code != CODE_FOR_nothing)
- tmode = CCmode;
- else
- for (tmode = QImode; tmode != VOIDmode;
- tmode = GET_MODE_WIDER_MODE (tmode))
- if (GET_MODE_SIZE (tmode) == GET_MODE_SIZE (mode))
- break;
+ /* Show the output dies here. This is necessary for SUBREGs
+ of pseudos since we cannot track their lifetimes correctly;
+ hard regs shouldn't appear here except as return values. */
+ if (!reload_completed && !reload_in_progress
+ && REG_P (x) && !reg_overlap_mentioned_p (x, y))
+ emit_insn (gen_rtx_CLOBBER (VOIDmode, x));
- gcc_assert (tmode != VOIDmode);
+ write_complex_part (x, read_complex_part (y, false), false);
+ write_complex_part (x, read_complex_part (y, true), true);
+ return get_last_insn ();
+}
- /* Get X and Y in TMODE. We can't use gen_lowpart here because it
- may call change_address which is not appropriate if we were
- called when a reload was in progress. We don't have to worry
- about changing the address since the size in bytes is supposed to
- be the same. Copy the MEM to change the mode and move any
- substitutions from the old MEM to the new one. */
+/* A subroutine of emit_move_insn_1. Generate a move from Y into X.
+ MODE is known to be MODE_CC. Returns the last instruction emitted. */
- if (reload_in_progress)
- {
- x = gen_lowpart_common (tmode, x1);
- if (x == 0 && MEM_P (x1))
- {
- x = adjust_address_nv (x1, tmode, 0);
- copy_replacements (x1, x);
- }
+static rtx
+emit_move_ccmode (enum machine_mode mode, rtx x, rtx y)
+{
+ rtx ret;
- y = gen_lowpart_common (tmode, y1);
- if (y == 0 && MEM_P (y1))
- {
- y = adjust_address_nv (y1, tmode, 0);
- copy_replacements (y1, y);
- }
- }
- else
+ /* Assume all MODE_CC modes are equivalent; if we have movcc, use it. */
+ if (mode != CCmode)
+ {
+ enum insn_code code = mov_optab->handlers[CCmode].insn_code;
+ if (code != CODE_FOR_nothing)
+ return emit_move_via_alt_mode (CCmode, mode, code, x, y);
+ }
+
+ /* Otherwise, find the MODE_INT mode of the same width. */
+ ret = emit_move_via_integer (mode, x, y);
+ gcc_assert (ret != NULL);
+ return ret;
+}
+
+/* A subroutine of emit_move_insn_1. Generate a move from Y into X.
+ MODE is any multi-word or full-word mode that lacks a move_insn
+ pattern. Note that you will get better code if you define such
+ patterns, even if they must turn into multiple assembler instructions. */
+
+static rtx
+emit_move_multi_word (enum machine_mode mode, rtx x, rtx y)
+{
+ rtx last_insn = 0;
+ rtx seq, inner;
+ bool need_clobber;
+ int i;
+
+ gcc_assert (GET_MODE_SIZE (mode) >= UNITS_PER_WORD);
+
+ /* If X is a push on the stack, do the push now and replace
+ X with a reference to the stack pointer. */
+ if (push_operand (x, mode))
+ x = emit_move_resolve_push (mode, x);
+
+ /* If we are in reload, see if either operand is a MEM whose address
+ is scheduled for replacement. */
+ if (reload_in_progress && MEM_P (x)
+ && (inner = find_replacement (&XEXP (x, 0))) != XEXP (x, 0))
+ x = replace_equiv_address_nv (x, inner);
+ if (reload_in_progress && MEM_P (y)
+ && (inner = find_replacement (&XEXP (y, 0))) != XEXP (y, 0))
+ y = replace_equiv_address_nv (y, inner);
+
+ start_sequence ();
+
+ need_clobber = false;
+ for (i = 0;
+ i < (GET_MODE_SIZE (mode) + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD;
+ i++)
+ {
+ rtx xpart = operand_subword (x, i, 1, mode);
+ rtx ypart = operand_subword (y, i, 1, mode);
+
+ /* If we can't get a part of Y, put Y into memory if it is a
+ constant. Otherwise, force it into a register. If we still
+ can't get a part of Y, abort. */
+ if (ypart == 0 && CONSTANT_P (y))
{
- x = gen_lowpart (tmode, x);
- y = gen_lowpart (tmode, y);
+ y = force_const_mem (mode, y);
+ ypart = operand_subword (y, i, 1, mode);
}
+ else if (ypart == 0)
+ ypart = operand_subword_force (y, i, mode);
+
+ gcc_assert (xpart && ypart);
+
+ need_clobber |= (GET_CODE (xpart) == SUBREG);
- insn_code = mov_optab->handlers[(int) tmode].insn_code;
- return emit_insn (GEN_FCN (insn_code) (x, y));
+ last_insn = emit_move_insn (xpart, ypart);
}
+ seq = get_insns ();
+ end_sequence ();
+
+ /* Show the output dies here. This is necessary for SUBREGs
+ of pseudos since we cannot track their lifetimes correctly;
+ hard regs shouldn't appear here except as return values.
+ We never want to emit such a clobber after reload. */
+ if (x != y
+ && ! (reload_in_progress || reload_completed)
+ && need_clobber != 0)
+ emit_insn (gen_rtx_CLOBBER (VOIDmode, x));
+
+ emit_insn (seq);
+
+ return last_insn;
+}
+
+/* Low level part of emit_move_insn.
+ Called just like emit_move_insn, but assumes X and Y
+ are basically valid. */
+
+rtx
+emit_move_insn_1 (rtx x, rtx y)
+{
+ enum machine_mode mode = GET_MODE (x);
+ enum insn_code code;
+
+ gcc_assert ((unsigned int) mode < (unsigned int) MAX_MACHINE_MODE);
+
+ code = mov_optab->handlers[mode].insn_code;
+ if (code != CODE_FOR_nothing)
+ return emit_insn (GEN_FCN (code) (x, y));
+
+ /* Expand complex moves by moving real part and imag part. */
+ if (COMPLEX_MODE_P (mode))
+ return emit_move_complex (mode, x, y);
+
+ if (GET_MODE_CLASS (mode) == MODE_CC)
+ return emit_move_ccmode (mode, x, y);
+
/* Try using a move pattern for the corresponding integer mode. This is
only safe when simplify_subreg can convert MODE constants into integer
constants. At present, it can only do this reliably if the value
fits within a HOST_WIDE_INT. */
- else if (GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
- && (submode = int_mode_for_mode (mode)) != BLKmode
- && mov_optab->handlers[submode].insn_code != CODE_FOR_nothing)
- return emit_insn (GEN_FCN (mov_optab->handlers[submode].insn_code)
- (simplify_gen_subreg (submode, x, mode, 0),
- simplify_gen_subreg (submode, y, mode, 0)));
-
- /* This will handle any multi-word or full-word mode that lacks a move_insn
- pattern. However, you will get better code if you define such patterns,
- even if they must turn into multiple assembler instructions. */
- else
+ if (!CONSTANT_P (y) || GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT)
{
- rtx last_insn = 0;
- rtx seq, inner;
- int need_clobber;
- int i;
-
- gcc_assert (GET_MODE_SIZE (mode) >= UNITS_PER_WORD);
-
-#ifdef PUSH_ROUNDING
-
- /* If X is a push on the stack, do the push now and replace
- X with a reference to the stack pointer. */
- if (push_operand (x, GET_MODE (x)))
- {
- rtx temp;
- enum rtx_code code;
+ rtx ret = emit_move_via_integer (mode, x, y);
+ if (ret)
+ return ret;
+ }
- /* Do not use anti_adjust_stack, since we don't want to update
- stack_pointer_delta. */
- temp = expand_binop (Pmode,
-#ifdef STACK_GROWS_DOWNWARD
- sub_optab,
-#else
- add_optab,
-#endif
- stack_pointer_rtx,
- GEN_INT
- (PUSH_ROUNDING
- (GET_MODE_SIZE (GET_MODE (x)))),
- stack_pointer_rtx, 0, OPTAB_LIB_WIDEN);
-
- if (temp != stack_pointer_rtx)
- emit_move_insn (stack_pointer_rtx, temp);
-
- code = GET_CODE (XEXP (x, 0));
-
- /* Just hope that small offsets off SP are OK. */
- if (code == POST_INC)
- temp = gen_rtx_PLUS (Pmode, stack_pointer_rtx,
- GEN_INT (-((HOST_WIDE_INT)
- GET_MODE_SIZE (GET_MODE (x)))));
- else if (code == POST_DEC)
- temp = gen_rtx_PLUS (Pmode, stack_pointer_rtx,
- GEN_INT (GET_MODE_SIZE (GET_MODE (x))));
- else
- temp = stack_pointer_rtx;
+ return emit_move_multi_word (mode, x, y);
+}
- x = change_address (x, VOIDmode, temp);
- }
-#endif
+/* Generate code to copy Y into X.
+ Both Y and X must have the same mode, except that
+ Y can be a constant with VOIDmode.
+ This mode cannot be BLKmode; use emit_block_move for that.
- /* If we are in reload, see if either operand is a MEM whose address
- is scheduled for replacement. */
- if (reload_in_progress && MEM_P (x)
- && (inner = find_replacement (&XEXP (x, 0))) != XEXP (x, 0))
- x = replace_equiv_address_nv (x, inner);
- if (reload_in_progress && MEM_P (y)
- && (inner = find_replacement (&XEXP (y, 0))) != XEXP (y, 0))
- y = replace_equiv_address_nv (y, inner);
+ Return the last instruction emitted. */
- start_sequence ();
+rtx
+emit_move_insn (rtx x, rtx y)
+{
+ enum machine_mode mode = GET_MODE (x);
+ rtx y_cst = NULL_RTX;
+ rtx last_insn, set;
- need_clobber = 0;
- for (i = 0;
- i < (GET_MODE_SIZE (mode) + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD;
- i++)
- {
- rtx xpart = operand_subword (x, i, 1, mode);
- rtx ypart = operand_subword (y, i, 1, mode);
+ gcc_assert (mode != BLKmode
+ && (GET_MODE (y) == mode || GET_MODE (y) == VOIDmode));
- /* If we can't get a part of Y, put Y into memory if it is a
- constant. Otherwise, force it into a register. If we still
- can't get a part of Y, abort. */
- if (ypart == 0 && CONSTANT_P (y))
- {
- y = force_const_mem (mode, y);
- ypart = operand_subword (y, i, 1, mode);
- }
- else if (ypart == 0)
- ypart = operand_subword_force (y, i, mode);
+ if (CONSTANT_P (y))
+ {
+ if (optimize
+ && SCALAR_FLOAT_MODE_P (GET_MODE (x))
+ && (last_insn = compress_float_constant (x, y)))
+ return last_insn;
- gcc_assert (xpart && ypart);
+ y_cst = y;
- need_clobber |= (GET_CODE (xpart) == SUBREG);
+ if (!LEGITIMATE_CONSTANT_P (y))
+ {
+ y = force_const_mem (mode, y);
- last_insn = emit_move_insn (xpart, ypart);
+ /* If the target's cannot_force_const_mem prevented the spill,
+ assume that the target's move expanders will also take care
+ of the non-legitimate constant. */
+ if (!y)
+ y = y_cst;
}
+ }
- seq = get_insns ();
- end_sequence ();
+ /* If X or Y are memory references, verify that their addresses are valid
+ for the machine. */
+ if (MEM_P (x)
+ && ((! memory_address_p (GET_MODE (x), XEXP (x, 0))
+ && ! push_operand (x, GET_MODE (x)))
+ || (flag_force_addr
+ && CONSTANT_ADDRESS_P (XEXP (x, 0)))))
+ x = validize_mem (x);
- /* Show the output dies here. This is necessary for SUBREGs
- of pseudos since we cannot track their lifetimes correctly;
- hard regs shouldn't appear here except as return values.
- We never want to emit such a clobber after reload. */
- if (x != y
- && ! (reload_in_progress || reload_completed)
- && need_clobber != 0)
- emit_insn (gen_rtx_CLOBBER (VOIDmode, x));
+ if (MEM_P (y)
+ && (! memory_address_p (GET_MODE (y), XEXP (y, 0))
+ || (flag_force_addr
+ && CONSTANT_ADDRESS_P (XEXP (y, 0)))))
+ y = validize_mem (y);
- emit_insn (seq);
+ gcc_assert (mode != BLKmode);
- return last_insn;
- }
+ last_insn = emit_move_insn_1 (x, y);
+
+ if (y_cst && REG_P (x)
+ && (set = single_set (last_insn)) != NULL_RTX
+ && SET_DEST (set) == x
+ && ! rtx_equal_p (y_cst, SET_SRC (set)))
+ set_unique_reg_note (last_insn, REG_EQUAL, y_cst);
+
+ return last_insn;
}
/* If Y is representable exactly in a narrower mode, and the target can
case ADDR_EXPR:
return expand_expr_addr_expr (exp, target, tmode, modifier);
- /* COMPLEX type for Extended Pascal & Fortran */
case COMPLEX_EXPR:
- {
- enum machine_mode mode = TYPE_MODE (TREE_TYPE (TREE_TYPE (exp)));
- rtx insns;
-
- /* Get the rtx code of the operands. */
- op0 = expand_expr (TREE_OPERAND (exp, 0), 0, VOIDmode, 0);
- op1 = expand_expr (TREE_OPERAND (exp, 1), 0, VOIDmode, 0);
-
- if (! target)
- target = gen_reg_rtx (TYPE_MODE (TREE_TYPE (exp)));
-
- start_sequence ();
-
- /* Move the real (op0) and imaginary (op1) parts to their location. */
- emit_move_insn (gen_realpart (mode, target), op0);
- emit_move_insn (gen_imagpart (mode, target), op1);
+ /* Get the rtx code of the operands. */
+ op0 = expand_expr (TREE_OPERAND (exp, 0), 0, VOIDmode, 0);
+ op1 = expand_expr (TREE_OPERAND (exp, 1), 0, VOIDmode, 0);
- insns = get_insns ();
- end_sequence ();
+ if (!target)
+ target = gen_reg_rtx (TYPE_MODE (TREE_TYPE (exp)));
- /* Complex construction should appear as a single unit. */
- /* If TARGET is a CONCAT, we got insns like RD = RS, ID = IS,
- each with a separate pseudo as destination.
- It's not correct for flow to treat them as a unit. */
- if (GET_CODE (target) != CONCAT)
- emit_no_conflict_block (insns, target, op0, op1, NULL_RTX);
- else
- emit_insn (insns);
+ /* Move the real (op0) and imaginary (op1) parts to their location. */
+ write_complex_part (target, op0, false);
+ write_complex_part (target, op1, true);
- return target;
- }
+ return target;
case REALPART_EXPR:
op0 = expand_expr (TREE_OPERAND (exp, 0), 0, VOIDmode, 0);
- return gen_realpart (mode, op0);
+ return read_complex_part (op0, false);
case IMAGPART_EXPR:
op0 = expand_expr (TREE_OPERAND (exp, 0), 0, VOIDmode, 0);
- return gen_imagpart (mode, op0);
+ return read_complex_part (op0, true);
case RESX_EXPR:
expand_resx_expr (exp);