]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
re PR rtl-optimization/15289 (reload error with non-lowpart subregs)
authorRichard Henderson <rth@redhat.com>
Wed, 1 Dec 2004 18:13:31 +0000 (10:13 -0800)
committerRichard Henderson <rth@gcc.gnu.org>
Wed, 1 Dec 2004 18:13:31 +0000 (10:13 -0800)
        PR rtl-opt/15289
        * emit-rtl.c (gen_complex_constant_part): Remove.
        (gen_realpart, gen_imagpart): Remove.
        * rtl.h (gen_realpart, gen_imagpart): Remove.
        * expmed.c (extract_bit_field): Remove CONCAT hack catering to
        gen_realpart/gen_imagpart.
        * expr.c (write_complex_part, read_complex_part): New.
        (emit_move_via_alt_mode, emit_move_via_integer, emit_move_resolve_push,
        emit_move_complex_push, emit_move_complex, emit_move_ccmode,
        emit_move_multi_word): Split out from ...
        (emit_move_insn_1): ... here.
        (expand_expr_real_1) <COMPLEX_EXPR>: Use write_complex_part.
        <REALPART_EXPR, IMAGPART_EXPR>: Use read_complex_part.
        * function.c (assign_parm_setup_reg): Hard-code transformations
        instead of using gen_realpart/gen_imagpart.

From-SVN: r91571

gcc/ChangeLog
gcc/emit-rtl.c
gcc/expmed.c
gcc/expr.c
gcc/function.c
gcc/rtl.h

index 7048e87cf3ccb98662e1dfb5a524f8fc1d524409..7a2aa56a0e4c99571e7d975b6669fc9422550c93 100644 (file)
@@ -1,5 +1,21 @@
 2004-12-01  Richard Henderson  <rth@redhat.com>
 
+       PR rtl-opt/15289
+       * emit-rtl.c (gen_complex_constant_part): Remove.
+       (gen_realpart, gen_imagpart): Remove.
+       * rtl.h (gen_realpart, gen_imagpart): Remove.
+       * expmed.c (extract_bit_field): Remove CONCAT hack catering to
+       gen_realpart/gen_imagpart.
+       * expr.c (write_complex_part, read_complex_part): New.
+       (emit_move_via_alt_mode, emit_move_via_integer, emit_move_resolve_push,
+       emit_move_complex_push, emit_move_complex, emit_move_ccmode, 
+       emit_move_multi_word): Split out from ...
+       (emit_move_insn_1): ... here.
+       (expand_expr_real_1) <COMPLEX_EXPR>: Use write_complex_part.
+       <REALPART_EXPR, IMAGPART_EXPR>: Use read_complex_part.
+       * function.c (assign_parm_setup_reg): Hard-code transformations
+       instead of using gen_realpart/gen_imagpart.
+
        * expr.c (optimize_bitfield_assignment_op): Split out from ... 
        (expand_assignment): ... here.  Use handled_component_p to gate
        get_inner_reference code.  Simplify MEM handling.  Special case
index fba241266386506feb2384285194139fe0abb43b..d722f90ad697ffbf8ba0560d4a1b3aed52edf0be 100644 (file)
@@ -184,7 +184,6 @@ static int reg_attrs_htab_eq (const void *, const void *);
 static reg_attrs *get_reg_attrs (tree, int);
 static tree component_ref_for_mem_expr (tree);
 static rtx gen_const_vector (enum machine_mode, int);
-static rtx gen_complex_constant_part (enum machine_mode, rtx, int);
 static void copy_rtx_if_shared_1 (rtx *orig);
 
 /* Probability of the conditional branch currently proceeded by try_split.
@@ -1169,81 +1168,6 @@ gen_lowpart_common (enum machine_mode mode, rtx x)
   return 0;
 }
 \f
-/* Return the constant real or imaginary part (which has mode MODE)
-   of a complex value X.  The IMAGPART_P argument determines whether
-   the real or complex component should be returned.  This function
-   returns NULL_RTX if the component isn't a constant.  */
-
-static rtx
-gen_complex_constant_part (enum machine_mode mode, rtx x, int imagpart_p)
-{
-  tree decl, part;
-
-  if (MEM_P (x)
-      && GET_CODE (XEXP (x, 0)) == SYMBOL_REF)
-    {
-      decl = SYMBOL_REF_DECL (XEXP (x, 0));
-      if (decl != NULL_TREE && TREE_CODE (decl) == COMPLEX_CST)
-       {
-         part = imagpart_p ? TREE_IMAGPART (decl) : TREE_REALPART (decl);
-         if (TREE_CODE (part) == REAL_CST
-             || TREE_CODE (part) == INTEGER_CST)
-           return expand_expr (part, NULL_RTX, mode, 0);
-       }
-    }
-  return NULL_RTX;
-}
-
-/* Return the real part (which has mode MODE) of a complex value X.
-   This always comes at the low address in memory.  */
-
-rtx
-gen_realpart (enum machine_mode mode, rtx x)
-{
-  rtx part;
-
-  /* Handle complex constants.  */
-  part = gen_complex_constant_part (mode, x, 0);
-  if (part != NULL_RTX)
-    return part;
-
-  if (WORDS_BIG_ENDIAN
-      && GET_MODE_BITSIZE (mode) < BITS_PER_WORD
-      && REG_P (x)
-      && REGNO (x) < FIRST_PSEUDO_REGISTER)
-    internal_error
-      ("can't access real part of complex value in hard register");
-  else if (WORDS_BIG_ENDIAN)
-    return gen_highpart (mode, x);
-  else
-    return gen_lowpart (mode, x);
-}
-
-/* Return the imaginary part (which has mode MODE) of a complex value X.
-   This always comes at the high address in memory.  */
-
-rtx
-gen_imagpart (enum machine_mode mode, rtx x)
-{
-  rtx part;
-
-  /* Handle complex constants.  */
-  part = gen_complex_constant_part (mode, x, 1);
-  if (part != NULL_RTX)
-    return part;
-
-  if (WORDS_BIG_ENDIAN)
-    return gen_lowpart (mode, x);
-  else if (! WORDS_BIG_ENDIAN
-          && GET_MODE_BITSIZE (mode) < BITS_PER_WORD
-          && REG_P (x)
-          && REGNO (x) < FIRST_PSEUDO_REGISTER)
-    internal_error
-      ("can't access imaginary part of complex value in hard register");
-  else
-    return gen_highpart (mode, x);
-}
-\f
 rtx
 gen_highpart (enum machine_mode mode, rtx x)
 {
index fe0db062b5628a848fb877469562f79795365b86..c6c8058cc9ea755e7a9596cc10c4ad8cd46b753c 100644 (file)
@@ -1611,28 +1611,6 @@ extract_bit_field (rtx str_rtx, unsigned HOST_WIDE_INT bitsize,
     return spec_target;
   if (GET_MODE (target) != tmode && GET_MODE (target) != mode)
     {
-      /* If the target mode is complex, then extract the two scalar elements
-        from the value now.  Creating (subreg:SC (reg:DI) 0), as we would do
-        with the clause below, will cause gen_realpart or gen_imagpart to
-        fail, since those functions must return lvalues.  */
-      if (COMPLEX_MODE_P (tmode))
-       {
-         rtx realpart, imagpart;
-         enum machine_mode itmode = GET_MODE_INNER (tmode);
-
-         target = convert_to_mode (mode_for_size (GET_MODE_BITSIZE (tmode),
-                                                  MODE_INT, 0),
-                                   target, unsignedp);
-
-         realpart = extract_bit_field (target, GET_MODE_BITSIZE (itmode), 0,
-                                       unsignedp, NULL, itmode, itmode);
-         imagpart = extract_bit_field (target, GET_MODE_BITSIZE (itmode),
-                                       GET_MODE_BITSIZE (itmode), unsignedp,
-                                       NULL, itmode, itmode);
-
-         return gen_rtx_CONCAT (tmode, realpart, imagpart);
-       }
-
       /* If the target mode is not a scalar integral, first convert to the
         integer mode of that size and then access it as a floating-point
         value via a SUBREG.  */
index a0ccf77af1a8cc32699478724737b6406f415cca..f02da24cd612516c02f9c0fbe560d9136a86ba08 100644 (file)
@@ -2574,404 +2574,467 @@ clear_storage_libcall_fn (int for_call)
   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
@@ -8083,47 +8146,27 @@ expand_expr_real_1 (tree exp, rtx target, enum machine_mode tmode,
     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);
index 1021fc2ecf39e9389d41d474bfadf2eb6bf4308e..046a4adda6d4fed71a3a7facc49162fc753a3f22 100644 (file)
@@ -2869,10 +2869,11 @@ assign_parm_setup_reg (struct assign_parm_data_all *all, tree parm,
        {
          enum machine_mode submode
            = GET_MODE_INNER (GET_MODE (parmreg));
-         int regnor = REGNO (gen_realpart (submode, parmreg));
-         int regnoi = REGNO (gen_imagpart (submode, parmreg));
-         rtx stackr = gen_realpart (submode, data->stack_parm);
-         rtx stacki = gen_imagpart (submode, data->stack_parm);
+         int regnor = REGNO (XEXP (parmreg, 0));
+         int regnoi = REGNO (XEXP (parmreg, 1));
+         rtx stackr = adjust_address_nv (data->stack_parm, submode, 0);
+         rtx stacki = adjust_address_nv (data->stack_parm, submode,
+                                         GET_MODE_SIZE (submode));
 
          /* Scan backwards for the set of the real and
             imaginary parts.  */
index edb2913ac85704c5b66ab8a0ef58f864eb2cd792..364c0a2bafba37b71be2b615205c6ad8a98760a1 100644 (file)
--- a/gcc/rtl.h
+++ b/gcc/rtl.h
@@ -1383,8 +1383,6 @@ extern rtx gen_lowpart_if_possible (enum machine_mode, rtx);
 /* In emit-rtl.c */
 extern rtx gen_highpart (enum machine_mode, rtx);
 extern rtx gen_highpart_mode (enum machine_mode, enum machine_mode, rtx);
-extern rtx gen_realpart (enum machine_mode, rtx);
-extern rtx gen_imagpart (enum machine_mode, rtx);
 extern rtx operand_subword (rtx, unsigned int, int, enum machine_mode);
 
 /* In emit-rtl.c */