+/* 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. */
+
+static void
+write_complex_part (rtx cplx, rtx val, bool imag_p)
+{
+ enum machine_mode cmode;
+ enum machine_mode imode;
+ unsigned ibitsize;
+
+ if (GET_CODE (cplx) == CONCAT)
+ {
+ emit_move_insn (XEXP (cplx, imag_p), val);
+ return;
+ }
+
+ cmode = GET_MODE (cplx);
+ imode = GET_MODE_INNER (cmode);
+ ibitsize = GET_MODE_BITSIZE (imode);
+
+ /* If the sub-object is at least word sized, then we know that subregging
+ will work. This special case is important, since store_bit_field
+ wants to operate on integer modes, and there's rarely an OImode to
+ correspond to TCmode. */
+ if (ibitsize >= BITS_PER_WORD
+ /* For hard regs we have exact predicates. Assume we can split
+ the original object if it spans an even number of hard regs.
+ This special case is important for SCmode on 64-bit platforms
+ where the natural size of floating-point regs is 32-bit. */
+ || (GET_CODE (cplx) == REG
+ && REGNO (cplx) < FIRST_PSEUDO_REGISTER
+ && hard_regno_nregs[REGNO (cplx)][cmode] % 2 == 0)
+ /* For MEMs we always try to make a "subreg", that is to adjust
+ the MEM, because store_bit_field may generate overly
+ convoluted RTL for sub-word fields. */
+ || MEM_P (cplx))
+ {
+ rtx part = simplify_gen_subreg (imode, cplx, cmode,
+ imag_p ? GET_MODE_SIZE (imode) : 0);
+ if (part)
+ {
+ emit_move_insn (part, val);
+ return;
+ }
+ else
+ /* simplify_gen_subreg may fail for sub-word MEMs. */
+ gcc_assert (MEM_P (cplx) && ibitsize < BITS_PER_WORD);
+ }
+
+ 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 cmode, imode;
+ unsigned ibitsize;
+
+ if (GET_CODE (cplx) == CONCAT)
+ return XEXP (cplx, imag_p);
+
+ 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)
+ {
+ 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);
+ }
+ }
+
+ /* If the sub-object is at least word sized, then we know that subregging
+ will work. This special case is important, since extract_bit_field
+ wants to operate on integer modes, and there's rarely an OImode to
+ correspond to TCmode. */
+ if (ibitsize >= BITS_PER_WORD
+ /* For hard regs we have exact predicates. Assume we can split
+ the original object if it spans an even number of hard regs.
+ This special case is important for SCmode on 64-bit platforms
+ where the natural size of floating-point regs is 32-bit. */
+ || (GET_CODE (cplx) == REG
+ && REGNO (cplx) < FIRST_PSEUDO_REGISTER
+ && hard_regno_nregs[REGNO (cplx)][cmode] % 2 == 0)
+ /* For MEMs we always try to make a "subreg", that is to adjust
+ the MEM, because extract_bit_field may generate overly
+ convoluted RTL for sub-word fields. */
+ || MEM_P (cplx))
+ {
+ rtx ret = simplify_gen_subreg (imode, cplx, cmode,
+ imag_p ? GET_MODE_SIZE (imode) : 0);
+ if (ret)
+ return ret;
+ else
+ /* simplify_gen_subreg may fail for sub-word MEMs. */
+ gcc_assert (MEM_P (cplx) && ibitsize < BITS_PER_WORD);
+ }
+
+ return extract_bit_field (cplx, ibitsize, imag_p ? ibitsize : 0,
+ true, NULL_RTX, imode, imode);
+}
+\f
+/* A subroutine of emit_move_insn_1. Yet another lowpart generator.
+ NEW_MODE and OLD_MODE are the same size. Return NULL if X cannot be
+ represented in NEW_MODE. If FORCE is true, this will never happen, as
+ we'll force-create a SUBREG if needed. */
+
+static rtx
+emit_move_change_mode (enum machine_mode new_mode,
+ enum machine_mode old_mode, rtx x, bool force)
+{
+ rtx ret;
+
+ if (reload_in_progress && MEM_P (x))
+ {
+ /* 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. */
+
+ ret = adjust_address_nv (x, new_mode, 0);
+ copy_replacements (x, ret);
+ }
+ else
+ {
+ /* Note that we do want simplify_subreg's behavior of validating
+ that the new mode is ok for a hard register. If we were to use
+ simplify_gen_subreg, we would create the subreg, but would
+ probably run into the target not being able to implement it. */
+ /* Except, of course, when FORCE is true, when this is exactly what
+ we want. Which is needed for CCmodes on some targets. */
+ if (force)
+ ret = simplify_gen_subreg (new_mode, x, old_mode, 0);
+ else
+ ret = simplify_subreg (new_mode, x, old_mode, 0);
+ }
+
+ return ret;
+}
+
+/* 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. */
+
+static rtx
+emit_move_via_integer (enum machine_mode mode, rtx x, rtx y)
+{
+ enum machine_mode imode;
+ enum insn_code code;
+
+ /* There must exist a mode of the exact size we require. */
+ imode = int_mode_for_mode (mode);
+ if (imode == BLKmode)
+ return NULL_RTX;
+
+ /* The target must support moves in this mode. */
+ code = mov_optab->handlers[imode].insn_code;
+ if (code == CODE_FOR_nothing)
+ return NULL_RTX;
+
+ x = emit_move_change_mode (imode, mode, x, false);
+ if (x == NULL_RTX)
+ return NULL_RTX;
+ y = emit_move_change_mode (imode, mode, y, false);
+ if (y == NULL_RTX)
+ return NULL_RTX;
+ return emit_insn (GEN_FCN (code) (x, y));
+}
+
+/* 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. */
+
+static rtx
+emit_move_resolve_push (enum machine_mode mode, rtx x)
+{
+ enum rtx_code code = GET_CODE (XEXP (x, 0));
+ HOST_WIDE_INT adjust;
+ rtx temp;
+
+ adjust = GET_MODE_SIZE (mode);
+#ifdef PUSH_ROUNDING
+ adjust = PUSH_ROUNDING (adjust);
+#endif
+ if (code == PRE_DEC || code == POST_DEC)
+ adjust = -adjust;
+
+ /* 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);
+
+ switch (code)
+ {
+ 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 ();
+ }
+
+ return replace_equiv_address (x, temp);
+}
+
+/* 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. */
+
+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 PUSH_ROUNDING
+ unsigned int submodesize = GET_MODE_SIZE (submode);
+
+ /* 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
+
+ /* 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 ();
+ }
+
+ 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));
+}
+
+/* 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. */
+
+static rtx
+emit_move_complex (enum machine_mode mode, rtx x, rtx y)
+{
+ bool try_int;
+
+ /* 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);
+
+ /* See if we can coerce the target into moving both values at once. */
+
+ /* Move floating point as parts. */
+ if (GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT
+ && mov_optab->handlers[GET_MODE_INNER (mode)].insn_code != CODE_FOR_nothing)
+ try_int = false;
+ /* Not possible if the values are inherently not adjacent. */
+ else 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)
+ {
+ rtx ret;
+
+ /* For memory to memory moves, optimal behavior 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 ();
+ }
+
+ ret = emit_move_via_integer (mode, x, y);
+ if (ret)
+ return ret;
+ }
+
+ /* 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));
+
+ write_complex_part (x, read_complex_part (y, false), false);
+ write_complex_part (x, read_complex_part (y, true), true);
+ return get_last_insn ();
+}
+
+/* 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. */
+
+static rtx
+emit_move_ccmode (enum machine_mode mode, rtx x, rtx y)
+{
+ rtx ret;
+
+ /* 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)
+ {
+ x = emit_move_change_mode (CCmode, mode, x, true);
+ y = emit_move_change_mode (CCmode, mode, y, true);
+ return emit_insn (GEN_FCN (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. Then we must
+ be able to get a part of Y. */
+ 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);
+
+ gcc_assert (xpart && ypart);
+
+ need_clobber |= (GET_CODE (xpart) == SUBREG);
+
+ 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. */
+ if (!CONSTANT_P (y) || GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT)
+ {
+ rtx ret = emit_move_via_integer (mode, x, y);
+ if (ret)
+ return ret;
+ }
+
+ return emit_move_multi_word (mode, x, y);
+}
+