OSDN Git Service

* i386.md (ashlsi patterns): Call output_ashl instead of output_ashlsi3.
[pf3gnuchains/gcc-fork.git] / gcc / config / i386 / i386.c
index e9da36b..50acbb1 100644 (file)
@@ -123,7 +123,7 @@ const int x86_use_leave = m_386 | m_K6;
 const int x86_push_memory = m_386 | m_K6;
 const int x86_zero_extend_with_and = m_486 | m_PENT;
 const int x86_movx = m_386 | m_PPRO | m_K6;
-const int x86_double_with_add = ~m_386;
+const int x86_double_with_add = ~(m_386 | m_PENT | m_PPRO);
 const int x86_use_bit_test = m_386;
 const int x86_unroll_strlen = m_486 | m_PENT | m_PPRO;
 const int x86_use_q_reg = m_PENT | m_PPRO | m_K6;
@@ -854,54 +854,6 @@ function_arg_partial_nregs (cum, mode, type, named)
   return 0;
 }
 \f
-/* Output an insn whose source is a 386 integer register.  SRC is the
-   rtx for the register, and TEMPLATE is the op-code template.  SRC may
-   be either SImode or DImode.
-
-   The template will be output with operands[0] as SRC, and operands[1]
-   as a pointer to the top of the 386 stack.  So a call from floatsidf2
-   would look like this:
-
-      output_op_from_reg (operands[1], AS1 (fild%z0,%1));
-
-   where %z0 corresponds to the caller's operands[1], and is used to
-   emit the proper size suffix.
-
-   ??? Extend this to handle HImode - a 387 can load and store HImode
-   values directly. */
-
-void
-output_op_from_reg (src, template)
-     rtx src;
-     char *template;
-{
-  rtx xops[4];
-  int size = GET_MODE_SIZE (GET_MODE (src));
-
-  xops[0] = src;
-  xops[1] = AT_SP (Pmode);
-  xops[2] = GEN_INT (size);
-  xops[3] = stack_pointer_rtx;
-
-  if (size > UNITS_PER_WORD)
-    {
-      rtx high;
-
-      if (size > 2 * UNITS_PER_WORD)
-       {
-         high = gen_rtx_REG (SImode, REGNO (src) + 2);
-         output_asm_insn (AS1 (push%L0,%0), &high);
-       }
-
-      high = gen_rtx_REG (SImode, REGNO (src) + 1);
-      output_asm_insn (AS1 (push%L0,%0), &high);
-    }
-
-  output_asm_insn (AS1 (push%L0,%0), &src);
-  output_asm_insn (template, xops);
-  output_asm_insn (AS2 (add%L3,%2,%3), xops);
-}
-\f
 /* Output an insn to pop an value from the 387 top-of-stack to 386
    register DEST. The 387 register stack is popped if DIES is true.  If
    the mode of DEST is an integer mode, a `fist' integer store is done,
@@ -1480,125 +1432,6 @@ output_move_pushmem (operands, insn, length, tmp_start, n_operands)
   return "";
 }
 \f
-/* Output the appropriate code to move data between two memory locations */
-
-char *
-output_move_memory (operands, insn, length, tmp_start, n_operands)
-     rtx operands[];
-     rtx insn;
-     int length;
-     int tmp_start;
-     int n_operands;
-{
-  struct
-    {
-      char *load;
-      char *store;
-      rtx   xops[3];
-    } tmp_info[MAX_TMPS];
-
-  rtx dest = operands[0];
-  rtx src  = operands[1];
-  rtx qi_tmp = NULL_RTX;
-  int max_tmps = 0;
-  int offset = 0;
-  int i, num_tmps;
-  rtx xops[3];
-
-  if (GET_CODE (dest) == MEM
-      && GET_CODE (XEXP (dest, 0)) == PRE_INC
-      && XEXP (XEXP (dest, 0), 0) == stack_pointer_rtx)
-    return output_move_pushmem (operands, insn, length, tmp_start, n_operands);
-
-  if (! offsettable_memref_p (src))
-    fatal_insn ("Source is not offsettable", insn);
-
-  if (! offsettable_memref_p (dest))
-    fatal_insn ("Destination is not offsettable", insn);
-
-  /* Figure out which temporary registers we have available */
-  for (i = tmp_start; i < n_operands; i++)
-    {
-      if (GET_CODE (operands[i]) == REG)
-       {
-         if ((length & 1) != 0 && qi_tmp == 0 && QI_REG_P (operands[i]))
-           qi_tmp = operands[i];
-
-         if (reg_overlap_mentioned_p (operands[i], dest))
-           fatal_insn ("Temporary register overlaps the destination", insn);
-
-         if (reg_overlap_mentioned_p (operands[i], src))
-           fatal_insn ("Temporary register overlaps the source", insn);
-
-         tmp_info[max_tmps++].xops[2] = operands[i];
-         if (max_tmps == MAX_TMPS)
-           break;
-       }
-    }
-
-  if (max_tmps == 0)
-    fatal_insn ("No scratch registers were found to do memory->memory moves",
-               insn);
-
-  if ((length & 1) != 0)
-    {
-      if (qi_tmp == 0)
-       fatal_insn ("No byte register found when moving odd # of bytes.",
-                   insn);
-    }
-
-  while (length > 1)
-    {
-      for (num_tmps = 0; num_tmps < max_tmps; num_tmps++)
-       {
-         if (length >= 4)
-           {
-             tmp_info[num_tmps].load    = AS2(mov%L0,%1,%2);
-             tmp_info[num_tmps].store   = AS2(mov%L0,%2,%0);
-             tmp_info[num_tmps].xops[0]
-               = adj_offsettable_operand (dest, offset);
-             tmp_info[num_tmps].xops[1]
-               = adj_offsettable_operand (src, offset);
-
-             offset += 4;
-             length -= 4;
-           }
-
-         else if (length >= 2)
-           {
-             tmp_info[num_tmps].load    = AS2(mov%W0,%1,%2);
-             tmp_info[num_tmps].store   = AS2(mov%W0,%2,%0);
-             tmp_info[num_tmps].xops[0]
-               = adj_offsettable_operand (dest, offset);
-             tmp_info[num_tmps].xops[1]
-               = adj_offsettable_operand (src, offset);
-
-             offset += 2;
-             length -= 2;
-           }
-         else
-           break;
-       }
-
-      for (i = 0; i < num_tmps; i++)
-       output_asm_insn (tmp_info[i].load, tmp_info[i].xops);
-
-      for (i = 0; i < num_tmps; i++)
-       output_asm_insn (tmp_info[i].store, tmp_info[i].xops);
-    }
-
-  if (length == 1)
-    {
-      xops[0] = adj_offsettable_operand (dest, offset);
-      xops[1] = adj_offsettable_operand (src, offset);
-      xops[2] = qi_tmp;
-      output_asm_insn (AS2(mov%B0,%1,%2), xops);
-      output_asm_insn (AS2(mov%B0,%2,%0), xops);
-    }
-
-  return "";
-}
-\f
 int
 standard_80387_constant_p (x)
      rtx x;
@@ -1687,6 +1520,17 @@ symbolic_operand (op, mode)
     }
 }
 
+/* Return nonzero if OP is a constant shift count small enough to
+   encode into an lea instruction.  */
+
+int
+small_shift_operand (op, mode)
+     rtx op;
+     enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+  return (GET_CODE (op) == CONST_INT && INTVAL (op) > 0 && INTVAL (op) < 4);
+}
+
 /* Test for a valid operand for a call instruction.
    Don't allow the arg pointer register or virtual regs
    since they may change into reg + const, which the patterns
@@ -3772,6 +3616,11 @@ print_operand_address (file, addr)
   switch (GET_CODE (addr))
     {
     case REG:
+      /* ESI addressing makes instruction vector decoded on the K6.  We can
+        avoid this by ESI+0 addressing.  */
+      if (REGNO_REG_CLASS (REGNO (addr)) == SIREG
+         && ix86_cpu == PROCESSOR_K6 && !optimize_size)
+       output_addr_const (file, const0_rtx);
       ADDR_BEG (file);
       fprintf (file, "%se", RP);
       fputs (hi_reg_name[REGNO (addr)], file);
@@ -3887,8 +3736,16 @@ print_operand_address (file, addr)
            ireg = XEXP (addr, 0);
          }
 
-       output_addr_const (file, const0_rtx);
-       PRINT_B_I_S (NULL_RTX, ireg, scale, file);
+       /* (reg,reg,) is shorter than (,reg,2).  */
+       if (scale == 2)
+         {
+           PRINT_B_I_S (ireg, ireg, 1, file);
+         } 
+       else 
+         {
+           output_addr_const (file, const0_rtx);
+           PRINT_B_I_S (NULL_RTX, ireg, scale, file);
+         }
       }
       break;
 
@@ -3937,8 +3794,7 @@ notice_update_cc (exp)
       if (REG_P (SET_DEST (exp))
          && (REG_P (SET_SRC (exp)) || GET_CODE (SET_SRC (exp)) == MEM
              || GET_RTX_CLASS (GET_CODE (SET_SRC (exp))) == '<'
-             || (GET_CODE (SET_SRC (exp)) == IF_THEN_ELSE
-                 && GET_MODE_CLASS (GET_MODE (SET_DEST (exp))) == MODE_INT)))
+             || GET_CODE (SET_SRC (exp)) == IF_THEN_ELSE))
        {
          if (cc_status.value1
              && reg_overlap_mentioned_p (SET_DEST (exp), cc_status.value1))
@@ -4175,35 +4031,19 @@ output_387_binary_op (insn, operands)
   switch (GET_CODE (operands[3]))
     {
     case PLUS:
-      if (GET_MODE_CLASS (GET_MODE (operands[1])) == MODE_INT
-         || GET_MODE_CLASS (GET_MODE (operands[2])) == MODE_INT)
-       base_op = "fiadd";
-      else
-       base_op = "fadd";
+      base_op = "fadd";
       break;
 
     case MINUS:
-      if (GET_MODE_CLASS (GET_MODE (operands[1])) == MODE_INT
-         || GET_MODE_CLASS (GET_MODE (operands[2])) == MODE_INT)
-       base_op = "fisub";
-      else
-       base_op = "fsub";
+      base_op = "fsub";
       break;
 
     case MULT:
-      if (GET_MODE_CLASS (GET_MODE (operands[1])) == MODE_INT
-         || GET_MODE_CLASS (GET_MODE (operands[2])) == MODE_INT)
-       base_op = "fimul";
-      else
-       base_op = "fmul";
+      base_op = "fmul";
       break;
 
     case DIV:
-      if (GET_MODE_CLASS (GET_MODE (operands[1])) == MODE_INT
-         || GET_MODE_CLASS (GET_MODE (operands[2])) == MODE_INT)
-       base_op = "fidiv";
-      else
-       base_op = "fdiv";
+      base_op = "fdiv";
       break;
 
     default:
@@ -4226,17 +4066,8 @@ output_387_binary_op (insn, operands)
       if (GET_CODE (operands[2]) == MEM)
        return strcat (buf, AS1 (%z2,%2));
 
-      if (NON_STACK_REG_P (operands[1]))
-       {
-         output_op_from_reg (operands[1], strcat (buf, AS1 (%z0,%1)));
-         return "";
-       }
-
-      else if (NON_STACK_REG_P (operands[2]))
-       {
-         output_op_from_reg (operands[2], strcat (buf, AS1 (%z0,%1)));
-         return "";
-       }
+      if (! STACK_REG_P (operands[1]) || ! STACK_REG_P (operands[2]))
+       abort ();
 
       if (find_regno_note (insn, REG_DEAD, REGNO (operands[2])))
        {
@@ -4259,18 +4090,6 @@ output_387_binary_op (insn, operands)
       if (GET_CODE (operands[2]) == MEM)
        return strcat (buf, AS1 (%z2,%2));
 
-      if (NON_STACK_REG_P (operands[1]))
-       {
-         output_op_from_reg (operands[1], strcat (buf, AS1 (r%z0,%1)));
-         return "";
-       }
-
-      else if (NON_STACK_REG_P (operands[2]))
-       {
-         output_op_from_reg (operands[2], strcat (buf, AS1 (%z0,%1)));
-         return "";
-       }
-
       if (! STACK_REG_P (operands[1]) || ! STACK_REG_P (operands[2]))
        abort ();
 
@@ -4360,6 +4179,66 @@ output_fix_trunc (insn, operands)
   return AS1 (fldc%W2,%2);
 }
 \f
+/* Output code for INSN to extend a float.  OPERANDS are the insn
+   operands.  The output may be DFmode or XFmode and the input operand
+   may be SFmode or DFmode.  Operands 2 and 3 are scratch memory and
+   are only necessary if operands 0 or 1 are non-stack registers.  */
+
+void
+output_float_extend (insn, operands)
+     rtx insn;
+     rtx *operands;
+{
+  int stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0;
+  rtx xops[2];
+
+  if (! STACK_TOP_P (operands[0]) && ! STACK_TOP_P (operands[1]))
+    abort ();
+
+  if (STACK_TOP_P (operands[0]) && STACK_TOP_P (operands[1]) && stack_top_dies)
+    return;
+
+  if (STACK_TOP_P (operands[0]) )
+    {
+      if (NON_STACK_REG_P (operands[1]))
+       {
+         if (GET_MODE (operands[1]) == SFmode)
+           output_asm_insn (AS2 (mov%L0,%1,%2), operands);
+         else
+           {
+             xops[0] = operands[2];
+             xops[1] = operands[1];
+             output_asm_insn (output_move_double (xops), xops);
+           }
+       }
+
+      xops[0] = NON_STACK_REG_P (operands[1]) ? operands[2] : operands[1];
+
+      output_asm_insn (AS1 (fld%z0,%y0), xops);
+    }
+  else
+    {
+      xops[0] = NON_STACK_REG_P (operands[0]) ? operands[3] : operands[0];
+
+      if (stack_top_dies
+         || (GET_CODE (xops[0]) == MEM && GET_MODE (xops[0]) == XFmode))
+       {
+         output_asm_insn (AS1 (fstp%z0,%y0), xops);
+         if (! stack_top_dies)
+           output_asm_insn (AS1 (fld%z0,%y0), xops);
+       }
+      else
+       output_asm_insn (AS1 (fst%z0,%y0), xops);
+
+      if (NON_STACK_REG_P (operands[0]))
+       {
+         xops[0] = operands[0];
+         xops[1] = operands[3];
+         output_asm_insn (output_move_double (xops), xops);
+       }
+    }
+}
+\f
 /* Output code for INSN to compare OPERANDS.  The two operands might
    not have the same mode: one might be within a FLOAT or FLOAT_EXTEND
    expression.  If the compare is in mode CCFPEQmode, use an opcode that
@@ -4430,24 +4309,19 @@ output_float_compare (insn, operands)
     {
       static char buf[100];
 
-      /* Decide if this is the integer or float compare opcode, or the
-        unordered float compare. */
+      /* Decide if this is a float compare or an unordered float compare. */
 
       if (unordered_compare)
        strcpy (buf, (cc_status.flags & CC_FCOMI) ? "fucomi" : "fucom");
-      else if (GET_MODE_CLASS (GET_MODE (operands[1])) == MODE_FLOAT)
-       strcpy (buf, (cc_status.flags & CC_FCOMI) ? "fcomi" : "fcom");
       else
-       strcpy (buf, "ficom");
+       strcpy (buf, (cc_status.flags & CC_FCOMI) ? "fcomi" : "fcom");
 
       /* Modify the opcode if the 387 stack is to be popped. */
 
       if (stack_top_dies)
        strcat (buf, "p");
 
-      if (NON_STACK_REG_P (operands[1]))
-       output_op_from_reg (operands[1], strcat (buf, AS1 (%z0,%1)));
-      else if (cc_status.flags & CC_FCOMI)
+      if (cc_status.flags & CC_FCOMI)
        {
          output_asm_insn (strcat (buf, AS2 (%z1,%y1,%0)), operands);
          return "";
@@ -5223,16 +5097,36 @@ int
 agi_dependent (insn, dep_insn)
      rtx insn, dep_insn;
 {
+  int push = 0, push_dep = 0;
   if (GET_CODE (dep_insn) == INSN
       && GET_CODE (PATTERN (dep_insn)) == SET
-      && GET_CODE (SET_DEST (PATTERN (dep_insn))) == REG)
-    return reg_mentioned_in_mem (SET_DEST (PATTERN (dep_insn)), insn);
+      && GET_CODE (SET_DEST (PATTERN (dep_insn))) == REG
+      && reg_mentioned_in_mem (SET_DEST (PATTERN (dep_insn)), insn))
+    return 1;
+
+  if (GET_CODE (insn) == INSN && GET_CODE (PATTERN (insn)) == SET
+      && GET_CODE (SET_DEST (PATTERN (insn))) == MEM
+      && push_operand (SET_DEST (PATTERN (insn)),
+                       GET_MODE (SET_DEST (PATTERN (insn)))))
+    push = 1;
 
   if (GET_CODE (dep_insn) == INSN && GET_CODE (PATTERN (dep_insn)) == SET
       && GET_CODE (SET_DEST (PATTERN (dep_insn))) == MEM
       && push_operand (SET_DEST (PATTERN (dep_insn)),
                        GET_MODE (SET_DEST (PATTERN (dep_insn)))))
-    return reg_mentioned_in_mem (stack_pointer_rtx, insn);
+    push_dep = 1;
+
+  /* CPUs contain special hardware to allow two pushes.  */
+  if (push && push_dep) 
+    return 0;
+
+  /* Push operation implicitly change stack pointer causing AGI stalls.  */
+  if (push_dep && reg_mentioned_in_mem (stack_pointer_rtx, insn))
+    return 1;
+
+  /* Push also implicitly read stack pointer.  */
+  if (push && modified_in_p (stack_pointer_rtx, dep_insn))
+    return 1;
 
   return 0;
 }
@@ -5492,6 +5386,13 @@ output_fp_conditional_move (which_alternative, operands)
      int which_alternative;
      rtx operands[];
 {
+  enum rtx_code code = GET_CODE (operands[1]);
+
+  /* This should never happen.  */
+  if (!(cc_prev_status.flags & CC_IN_80387)
+      && (code == GT || code == LE || code == GE || code == LT))
+    abort ();
+
   switch (which_alternative)
     {
     case 0:
@@ -5516,7 +5417,7 @@ output_int_conditional_move (which_alternative, operands)
      int which_alternative;
      rtx operands[];
 {
-  int code = GET_CODE (operands[1]);
+  enum rtx_code code = GET_CODE (operands[1]);
 
   /* This is very tricky. We have to do it right. For a code segement
      like:
@@ -5583,7 +5484,7 @@ x86_adjust_cost (insn, link, dep_insn, cost)
        return 0;
 
       if (agi_dependent (insn, dep_insn))
-       return 3;
+       return cost ? cost + 1 : 2;
 
       if (GET_CODE (insn) == INSN
          && GET_CODE (PATTERN (insn)) == SET
@@ -5594,6 +5495,10 @@ x86_adjust_cost (insn, link, dep_insn, cost)
        return 0;
       break;
 
+      /* Stores stalls one cycle longer than other insns.  */
+      if (is_fp_insn (insn) && cost && is_fp_store (dep_insn))
+       cost++;
+
     case PROCESSOR_K6:
     default:
       if (!is_fp_dest (dep_insn))
@@ -5618,3 +5523,278 @@ x86_adjust_cost (insn, link, dep_insn, cost)
 
   return cost;
 }
+
+/* Output assembly code for a left shift.
+
+   Always use "sal" when shifting a memory operand or for a non constant
+   shift count.
+
+   When optimizing for size, we know that src == dest, and we should always
+   use "sal".  If src != dest, then copy src to dest and use "sal".
+   
+   Pentium and PPro (speed):
+
+     When src == dest, use "add" for a shift counts of one, else use
+     "sal".  If we modeled Pentium AGI stalls and U/V pipelining better we
+     would want to generate lea for some shifts on the Pentium.
+
+     When src != dest, use "lea" for small shift counts.  Otherwise,
+     copy src to dest and use the normal shifting code.  Exception for
+     TARGET_DOUBLE_WITH_ADD.  */
+
+char *
+output_ashl (insn, operands)
+     rtx insn, *operands;
+{
+  /* Handle case where srcreg != dstreg.  */
+  if (REG_P (operands[0]) && REGNO (operands[0]) != REGNO (operands[1]))
+    {
+      if (TARGET_DOUBLE_WITH_ADD && INTVAL (operands[2]) == 1)
+       switch (GET_MODE (operands[0]))
+         {
+         case SImode:
+           output_asm_insn (AS2 (mov%L0,%1,%0), operands);
+           return AS2 (add%L0,%1,%0);
+         case HImode:
+           output_asm_insn (AS2 (mov%L0,%k1,%k0), operands);
+           if (i386_cc_probably_useless_p (insn))
+             {
+               CC_STATUS_INIT;
+               return AS2 (add%L0,%k1,%k0);
+             }
+           return AS2 (add%W0,%k1,%k0);
+         case QImode:
+           output_asm_insn (AS2 (mov%B0,%1,%0), operands);
+           return AS2 (add%B0,%1,%0);
+         default:
+           abort ();
+         }
+      else
+       {
+         CC_STATUS_INIT;
+
+         /* This should be extremely rare (impossible?).  We can not encode a
+            shift of the stack pointer using an lea instruction.  So copy the
+            stack pointer into the destination register and use an lea.  */
+         if (operands[1] == stack_pointer_rtx)
+           {
+             output_asm_insn (AS2 (mov%L0,%k1,%k0), operands);
+             operands[1] = operands[0];
+           }
+
+         /* For shifts up to and including 3 bits, use lea.  */
+         operands[1] = gen_rtx_MULT (SImode,
+                                     gen_rtx_REG (SImode, REGNO (operands[1])),
+                                     GEN_INT (1 << INTVAL (operands[2])));
+         return AS2 (lea%L0,%a1,%k0);
+       }
+    }
+
+  /* Source and destination match.  */
+
+  /* Handle variable shift.  */
+  if (REG_P (operands[2]))
+    switch (GET_MODE (operands[0]))
+      {
+      case SImode:
+       return AS2 (sal%L0,%b2,%0);
+      case HImode:
+       if (REG_P (operands[0]) && i386_cc_probably_useless_p (insn))
+         {
+           CC_STATUS_INIT;
+           return AS2 (sal%L0,%b2,%k0);
+         }
+       else
+         return AS2 (sal%W0,%b2,%0);
+      case QImode:
+       return AS2 (sal%B0,%b2,%0);
+      default:
+       abort ();
+      }
+
+  /* Always perform shift by 1 using an add instruction.  */
+  if (REG_P (operands[0]) && operands[2] == const1_rtx)
+    switch (GET_MODE (operands[0]))
+      {
+      case SImode:
+       return AS2 (add%L0,%0,%0);
+      case HImode:
+       if (REG_P (operands[0]) && i386_cc_probably_useless_p (insn))
+         {
+           CC_STATUS_INIT;
+           return AS2 (add%L0,%k0,%k0);
+         }
+       else
+         return AS2 (add%W0,%0,%0);
+      case QImode:
+         return AS2 (add%B0,%0,%0);
+      default:
+         abort ();
+      }
+
+#if 0
+  /* ??? Currently disabled.  Because our model of Pentium is far from being
+     exact, this change will need some benchmarking.  */
+  /* Shift reg by 2 or 3 use an lea instruction for Pentium if this is
+     insn is expected to issue into the V pipe (the insn's mode will be
+     TImode for a U pipe, and !TImode for a V pipe instruction).  */
+  if (! optimize_size
+      && REG_P (operands[0])
+      && GET_CODE (operands[2]) == CONST_INT
+      && INTVAL (operands[2]) <= 3
+      && (int)ix86_cpu == (int)PROCESSOR_PENTIUM
+      && GET_MODE (insn) != TImode)
+    {
+      CC_STATUS_INIT;
+      operands[1] = gen_rtx_MULT (SImode, gen_rtx_REG (SImode, REGNO (operands[1])),
+                                 GEN_INT (1 << INTVAL (operands[2])));
+      return AS2 (lea%L0,%a1,%0);
+    }
+#endif
+
+  /* Otherwise use a shift instruction.  */
+  switch (GET_MODE (operands[0]))
+    {
+    case SImode:
+      return AS2 (sal%L0,%2,%0);
+    case HImode:
+      if (REG_P (operands[0]) && i386_cc_probably_useless_p (insn))
+       {
+         CC_STATUS_INIT;
+         return AS2 (sal%L0,%2,%k0);
+       }
+      else
+       return AS2 (sal%W0,%2,%0);
+    case QImode:
+      return AS2 (sal%B0,%2,%0);
+    default:
+      abort ();
+    }
+}
+
+/* Given the memory address ADDR, calculate the length of the address or
+   the length of just the displacement (controlled by DISP_LENGTH).
+  
+   The length returned does not include the one-byte modrm, opcode,
+   or prefix.  */
+
+int
+memory_address_info (addr, disp_length)
+     rtx addr;
+     int disp_length;
+{
+  rtx base, index, disp, scale;
+  rtx op0, op1;
+  int len;
+
+  if (GET_CODE (addr) == PRE_DEC
+      || GET_CODE (addr) == POST_INC)
+    return 0;
+
+  /* Register Indirect.  */
+  if (register_operand (addr, Pmode))
+    {
+      /* Special cases: ebp and esp need the two-byte modrm form. 
+
+        We change [ESI] to [ESI+0] on the K6 when not optimizing
+        for size.  */
+      if (addr == stack_pointer_rtx
+         || addr == arg_pointer_rtx
+         || addr == frame_pointer_rtx
+         || (REGNO_REG_CLASS (REGNO (addr)) == SIREG
+             && ix86_cpu == PROCESSOR_K6 && !optimize_size))
+       return 1;
+      else
+       return 0;
+    }
+
+  /* Direct Addressing.  */
+  if (CONSTANT_P (addr))
+    return 4;
+
+  index = base = disp = scale = NULL_RTX;
+  op0 = XEXP (addr, 0);
+  op1 = XEXP (addr, 1);
+
+  if (GET_CODE (addr) == PLUS)
+    {
+      if (register_operand (op0, Pmode))
+       {
+         if (register_operand (op1, Pmode))
+           index = op0, base = op1;
+         else
+           base = op0, disp = op1;
+       }
+      else if (GET_CODE (op0) == MULT)
+       {
+         index = XEXP (op0, 0);
+         scale = XEXP (op0, 1);
+         if (register_operand (op1, Pmode))
+           base = op1;
+         else
+           disp = op1;
+       }
+      else if (GET_CODE (op0) == PLUS && GET_CODE (XEXP (op0, 0)) == MULT)
+       {
+         index = XEXP (XEXP (op0, 0), 0);
+         scale = XEXP (XEXP (op0, 0), 1);
+         base = XEXP (op0, 1);
+         disp = op1;
+       }
+      else if (GET_CODE (op0) == PLUS)
+       {
+         index = XEXP (op0, 0);
+         base = XEXP (op0, 1);
+         disp = op1;
+       }
+      else
+       abort ();
+    }
+  else if (GET_CODE (addr) == MULT
+          /* We're called for lea too, which implements ashift on occasion.  */
+          || GET_CODE (addr) == ASHIFT)
+    {
+      index = XEXP (addr, 0);
+      scale = XEXP (addr, 1);
+    }
+  else
+    abort ();
+      
+  /* Allow arg pointer and stack pointer as index if there is not scaling */
+  if (base && index && !scale
+      && (index == stack_pointer_rtx
+         || index == arg_pointer_rtx
+         || index == frame_pointer_rtx))
+    {
+      rtx tmp = base;
+      base = index;
+      index = tmp;
+    }
+
+  /* Special case: ebp cannot be encoded as a base without a displacement.  */
+  if (base == frame_pointer_rtx && !disp)
+    disp = const0_rtx;
+
+  /* Scaling can not be encoded without base or displacement.  
+     Except for scale == 1 where we can encode reg + reg instead of reg * 2.  */
+  if (!base && index && scale != 1)
+    disp = const0_rtx;
+
+  /* Find the length of the displacement constant.  */
+  len = 0;
+  if (disp)
+    {
+      if (GET_CODE (disp) == CONST_INT
+         && CONST_OK_FOR_LETTER_P (INTVAL (disp), 'K'))
+       len = 1;
+      else
+       len = 4;
+    }
+
+  /* An index requires the two-byte modrm form.  Not important
+     if we are computing just length of the displacement.  */
+  if (index && ! disp_length)
+    len += 1;
+
+  return len;
+}