OSDN Git Service

* i386.md (ashlsi patterns): Call output_ashl instead of output_ashlsi3.
[pf3gnuchains/gcc-fork.git] / gcc / config / i386 / i386.c
index 7a5aab5..50acbb1 100644 (file)
@@ -1,5 +1,5 @@
 /* Subroutines for insn-output.c for Intel X86.
-   Copyright (C) 1988, 92, 94, 95, 96, 97, 1998 Free Software Foundation, Inc.
+   Copyright (C) 1988, 92, 94-98, 1999 Free Software Foundation, Inc.
 
 This file is part of GNU CC.
 
@@ -100,8 +100,37 @@ struct processor_costs pentiumpro_cost = {
   17                                   /* cost of a divide/mod */
 };
 
+struct processor_costs k6_cost = {
+  1,                                   /* cost of an add instruction */
+  1,                                   /* cost of a lea instruction */
+  1,                                   /* variable shift costs */
+  1,                                   /* constant shift costs */
+  2,                                   /* cost of starting a multiply */
+  0,                                   /* cost of multiply per each bit set */
+  18                                   /* cost of a divide/mod */
+};
+
 struct processor_costs *ix86_cost = &pentium_cost;
 
+/* Processor feature/optimization bitmasks.  */
+#define m_386 (1<<PROCESSOR_I386)
+#define m_486 (1<<PROCESSOR_I486)
+#define m_PENT (1<<PROCESSOR_PENTIUM)
+#define m_PPRO (1<<PROCESSOR_PENTIUMPRO)
+#define m_K6  (1<<PROCESSOR_K6)
+
+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 | 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;
+const int x86_use_any_reg = m_486;
+const int x86_cmove = m_PPRO;
+const int x86_deep_branch = m_PPRO| m_K6;
+
 #define AT_BP(mode) (gen_rtx_MEM ((mode), frame_pointer_rtx))
 
 extern FILE *asm_out_file;
@@ -129,7 +158,7 @@ enum reg_class regclass_map[FIRST_PSEUDO_REGISTER] =
   SIREG, DIREG, INDEX_REGS, GENERAL_REGS,
   /* FP registers */
   FP_TOP_REG, FP_SECOND_REG, FLOAT_REGS, FLOAT_REGS,
-  FLOAT_REGS, FLOAT_REGS, FLOAT_REGS, FLOAT_REGS,       
+  FLOAT_REGS, FLOAT_REGS, FLOAT_REGS, FLOAT_REGS,
   /* arg pointer */
   INDEX_REGS
 };
@@ -213,7 +242,8 @@ override_options ()
           {PROCESSOR_I686_STRING, PROCESSOR_PENTIUMPRO, &pentiumpro_cost,
              0, 0},
           {PROCESSOR_PENTIUMPRO_STRING, PROCESSOR_PENTIUMPRO,
-             &pentiumpro_cost, 0, 0}};
+             &pentiumpro_cost, 0, 0},
+          {PROCESSOR_K6_STRING, PROCESSOR_K6, &k6_cost, 0, 0}};
 
   int ptt_size = sizeof (processor_target_table) / sizeof (struct ptt);
 
@@ -227,7 +257,7 @@ override_options ()
       for (i = 0; (ch = i386_reg_alloc_order[i]) != '\0'; i++)
        {
          int regno = 0;
-         
+
          switch (ch)
            {
            case 'a':   regno = 0;      break;
@@ -254,7 +284,7 @@ override_options ()
       if (ix86_cpu_string == 0)
        ix86_cpu_string = PROCESSOR_DEFAULT_STRING;
     }
-  
+
   for (i = 0; i < ptt_size; i++)
     if (! strcmp (ix86_arch_string, processor_target_table[i].name))
       {
@@ -279,7 +309,7 @@ override_options ()
       {
        ix86_cpu = processor_target_table[j].processor;
        ix86_cost = processor_target_table[j].cost;
-       if (i > j && (int) ix86_arch >= (int) PROCESSOR_PENTIUMPRO)
+       if (i > j && (int) ix86_arch >= (int) PROCESSOR_K6)
          error ("-mcpu=%s does not support -march=%s",
                 ix86_cpu_string, ix86_arch_string);
 
@@ -392,7 +422,7 @@ order_regs_for_local_alloc ()
       for (i = order = 0; (ch = i386_reg_alloc_order[i]) != '\0'; i++)
        {
          int regno = 0;
-         
+
          switch (ch)
            {
            case 'a':   regno = 0;      break;
@@ -502,7 +532,7 @@ i386_aligned_p (op)
 
     case REG:
       return i386_aligned_reg_p (REGNO (op));
-    
+
     default:
       break;
     }
@@ -546,6 +576,7 @@ i386_valid_type_attribute_p (type, attributes, identifier, args)
      tree args;
 {
   if (TREE_CODE (type) != FUNCTION_TYPE
+      && TREE_CODE (type) != METHOD_TYPE
       && TREE_CODE (type) != FIELD_DECL
       && TREE_CODE (type) != TYPE_DECL)
     return 0;
@@ -591,9 +622,19 @@ i386_valid_type_attribute_p (type, attributes, identifier, args)
 
 int
 i386_comp_type_attributes (type1, type2)
-     tree type1 ATTRIBUTE_UNUSED;
-     tree type2 ATTRIBUTE_UNUSED;
+     tree type1;
+     tree type2;
 {
+  /* Check for mismatch of non-default calling convention. */
+  char *rtdstr = TARGET_RTD ? "cdecl" : "stdcall";
+
+  if (TREE_CODE (type1) != FUNCTION_TYPE)
+    return 1;
+
+  /* Check for mismatched return types (cdecl vs stdcall).  */
+  if (!lookup_attribute (rtdstr, TYPE_ATTRIBUTES (type1))
+      != !lookup_attribute (rtdstr, TYPE_ATTRIBUTES (type2)))
+    return 0;
   return 1;
 }
 
@@ -620,27 +661,27 @@ i386_return_pops_args (fundecl, funtype, size)
      tree fundecl;
      tree funtype;
      int size;
-{ 
+{
   int rtd = TARGET_RTD && (!fundecl || TREE_CODE (fundecl) != IDENTIFIER_NODE);
 
     /* Cdecl functions override -mrtd, and never pop the stack. */
   if (! lookup_attribute ("cdecl", TYPE_ATTRIBUTES (funtype))) {
-  
+
     /* Stdcall functions will pop the stack if not variable args. */
     if (lookup_attribute ("stdcall", TYPE_ATTRIBUTES (funtype)))
       rtd = 1;
-  
+
     if (rtd
         && (TYPE_ARG_TYPES (funtype) == NULL_TREE
            || (TREE_VALUE (tree_last (TYPE_ARG_TYPES (funtype)))
                == void_type_node)))
       return size;
   }
-  
+
   /* Lose any fake structure return argument.  */
   if (aggregate_value_p (TREE_TYPE (funtype)))
     return GET_MODE_SIZE (Pmode);
-  
+
     return 0;
 }
 
@@ -813,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,
@@ -891,6 +884,17 @@ output_to_reg (dest, dies, scratch_mem)
     {
       if (dies)
        output_asm_insn (AS1 (fistp%z3,%y0), xops);
+      else if (GET_MODE (xops[3]) == DImode && ! dies)
+       {
+         /* There is no DImode version of this without a stack pop, so
+            we must emulate it.  It doesn't matter much what the second
+            instruction is, because the value being pushed on the FP stack
+            is not used except for the following stack popping store.
+            This case can only happen without optimization, so it doesn't
+            matter that it is inefficient.  */
+         output_asm_insn (AS1 (fistp%z3,%0), xops);
+         output_asm_insn (AS1 (fild%z3,%0), xops);
+       }
       else
        output_asm_insn (AS1 (fist%z3,%y0), xops);
     }
@@ -927,7 +931,7 @@ output_to_reg (dest, dies, scratch_mem)
        output_asm_insn (AS1 (pop%L0,%0), &dest);
       else
        {
-         xops[0] = adj_offsettable_operand (xops[0], 4);             
+         xops[0] = adj_offsettable_operand (xops[0], 4);
          xops[3] = dest;
          output_asm_insn (AS2 (mov%L0,%0,%3), xops);
        }
@@ -939,7 +943,7 @@ output_to_reg (dest, dies, scratch_mem)
            output_asm_insn (AS1 (pop%L0,%0), &dest);
          else
            {
-             xops[0] = adj_offsettable_operand (xops[0], 4);         
+             xops[0] = adj_offsettable_operand (xops[0], 4);
              output_asm_insn (AS2 (mov%L0,%0,%3), xops);
            }
        }
@@ -1145,7 +1149,7 @@ output_move_double (operands)
          middlehalf[0] = operands[0];
          latehalf[0] = operands[0];
        }
-    
+
       if (optype1 == REGOP)
        {
           middlehalf[1] = gen_rtx_REG (SImode, REGNO (operands[1]) + 1);
@@ -1365,7 +1369,7 @@ output_move_pushmem (operands, insn, length, tmp_start, n_operands)
       char *push;
       rtx   xops[2];
     } tmp_info[MAX_TMPS];
-  
+
   rtx src = operands[1];
   int max_tmps = 0;
   int offset = 0;
@@ -1428,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;
@@ -1574,6 +1459,7 @@ standard_80387_constant_p (x)
   /* Note that on the 80387, other constants, such as pi,
      are much slower to load as standard constants
      than to load from doubles in memory!  */
+  /* ??? Not true on K6: all constants are equal cost.  */
 #endif
 
   return 0;
@@ -1634,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
@@ -1777,7 +1674,7 @@ ix86_expand_binary_operator (code, mode, operands)
          emit_move_insn (temp, operands[1]);
          operands[1] = temp;
          return TRUE;
-       }         
+       }
     }
 
   if (!ix86_binary_operator_ok (code, mode, operands))
@@ -1810,7 +1707,7 @@ ix86_expand_binary_operator (code, mode, operands)
              emit_move_insn (temp, operands[1]);
              operands[1] = temp;
              return TRUE;
-           }     
+           }
 
          if (modified && ! ix86_binary_operator_ok (code, mode, operands))
            return FALSE;
@@ -1914,9 +1811,16 @@ asm_output_function_prefix (file, name)
 
       prologue_node = make_node (FUNCTION_DECL);
       DECL_RESULT (prologue_node) = 0;
-#ifdef ASM_DECLARE_FUNCTION_NAME
-      ASM_DECLARE_FUNCTION_NAME (file, pic_label_name, prologue_node);
-#endif
+
+      /* This used to call ASM_DECLARE_FUNCTION_NAME() but since it's an
+        internal (non-global) label that's being emitted, it didn't make
+        sense to have .type information for local labels.   This caused
+        the SCO OpenServer 5.0.4 ELF assembler grief (why are you giving
+        me debug info for a label that you're declaring non-global?) this
+        was changed to call ASM_OUTPUT_LABEL() instead. */
+
+
+      ASM_OUTPUT_LABEL (file, pic_label_name); 
       output_asm_insn ("movl (%1),%0", xops);
       output_asm_insn ("ret", xops);
     }
@@ -1936,7 +1840,7 @@ function_prologue (file, size)
       pic_label_rtx = 0;
       return;
     }
-  
+
   ix86_prologue (0);
 }
 
@@ -1947,7 +1851,7 @@ ix86_expand_prologue ()
 {
   if (! TARGET_SCHEDULE_PROLOGUE)
       return;
+
   ix86_prologue (1);
 }
 
@@ -1974,9 +1878,9 @@ load_pic_register (do_rtl)
       if (do_rtl)
        {
          emit_insn (gen_prologue_get_pc (xops[0], xops[1]));
-         emit_insn (gen_prologue_set_got (xops[0], 
+         emit_insn (gen_prologue_set_got (xops[0],
                                           gen_rtx (SYMBOL_REF, Pmode,
-                                                   "$_GLOBAL_OFFSET_TABLE_"), 
+                                                   "$_GLOBAL_OFFSET_TABLE_"),
                                           xops[1]));
        }
       else
@@ -1991,7 +1895,7 @@ load_pic_register (do_rtl)
     {
       xops[0] = pic_offset_table_rtx;
       xops[1] = gen_label_rtx ();
+
       if (do_rtl)
        {
          /* We can't put a raw CODE_LABEL into the RTL, and we can't emit
@@ -2002,12 +1906,12 @@ load_pic_register (do_rtl)
       else
        {
          output_asm_insn (AS1 (call,%P1), xops);
-         ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", 
+         ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L",
                                     CODE_LABEL_NUMBER (xops[1]));
          output_asm_insn (AS1 (pop%L0,%0), xops);
          output_asm_insn ("addl $_GLOBAL_OFFSET_TABLE_+[.-%P1],%0", xops);
        }
-    } 
+    }
 
   /* When -fpic, we must emit a scheduling barrier, so that the instruction
      that restores %ebx (which is PIC_OFFSET_TABLE_REGNUM), does not get
@@ -2017,6 +1921,62 @@ load_pic_register (do_rtl)
     emit_insn (gen_blockage ());
 }
 
+/* Compute the size of local storage taking into consideration the
+   desired stack alignment which is to be maintained.  Also determine
+   the number of registers saved below the local storage.  */
+
+HOST_WIDE_INT
+ix86_compute_frame_size (size, nregs_on_stack)
+     HOST_WIDE_INT size;
+     int *nregs_on_stack;
+{
+  int limit;
+  int nregs;
+  int regno;
+  int padding;
+  int pic_reg_used = flag_pic && (current_function_uses_pic_offset_table
+                                 || current_function_uses_const_pool);
+  HOST_WIDE_INT total_size;
+
+  limit = frame_pointer_needed
+         ? FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM;
+
+  nregs = 0;
+
+  for (regno = limit - 1; regno >= 0; regno--)
+    if ((regs_ever_live[regno] && ! call_used_regs[regno])
+       || (regno == PIC_OFFSET_TABLE_REGNUM && pic_reg_used))
+      nregs++;
+
+  padding = 0;
+  total_size = size + (nregs * UNITS_PER_WORD);
+
+#ifdef PREFERRED_STACK_BOUNDARY
+  {
+    int offset;
+    int preferred_alignment = PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT;
+
+    offset = 4;
+    if (frame_pointer_needed)
+      offset += UNITS_PER_WORD;
+
+    total_size += offset;
+    
+    padding = ((total_size + preferred_alignment - 1)
+              & -preferred_alignment) - total_size;
+
+    if (padding < (((offset + preferred_alignment - 1)
+                   & -preferred_alignment) - offset))
+      padding += preferred_alignment;
+  }
+#endif
+
+  if (nregs_on_stack)
+    *nregs_on_stack = nregs;
+
+  return size + padding;
+}
+
 static void
 ix86_prologue (do_rtl)
      int do_rtl;
@@ -2026,10 +1986,10 @@ ix86_prologue (do_rtl)
   rtx xops[4];
   int pic_reg_used = flag_pic && (current_function_uses_pic_offset_table
                                  || current_function_uses_const_pool);
-  long tsize = get_frame_size ();
+  HOST_WIDE_INT tsize = ix86_compute_frame_size (get_frame_size (), (int *)0);
   rtx insn;
   int cfa_offset = INCOMING_FRAME_SP_OFFSET, cfa_store_offset = cfa_offset;
-  
+
   xops[0] = stack_pointer_rtx;
   xops[1] = frame_pointer_rtx;
   xops[2] = GEN_INT (tsize);
@@ -2051,7 +2011,7 @@ ix86_prologue (do_rtl)
 
       else
        {
-         output_asm_insn ("push%L1 %1", xops); 
+         output_asm_insn ("push%L1 %1", xops);
 #ifdef INCOMING_RETURN_ADDR_RTX
          if (dwarf2out_do_frame ())
            {
@@ -2064,7 +2024,7 @@ ix86_prologue (do_rtl)
            }
 #endif
 
-         output_asm_insn (AS2 (mov%L0,%0,%1), xops); 
+         output_asm_insn (AS2 (mov%L0,%0,%1), xops);
 #ifdef INCOMING_RETURN_ADDR_RTX
          if (dwarf2out_do_frame ())
            dwarf2out_def_cfa ("", FRAME_POINTER_REGNUM, cfa_offset);
@@ -2081,7 +2041,7 @@ ix86_prologue (do_rtl)
          insn = emit_insn (gen_prologue_set_stack_ptr (xops[2]));
          RTX_FRAME_RELATED_P (insn) = 1;
        }
-      else 
+      else
        {
          output_asm_insn (AS2 (sub%L0,%2,%0), xops);
 #ifdef INCOMING_RETURN_ADDR_RTX
@@ -2097,7 +2057,7 @@ ix86_prologue (do_rtl)
 #endif
        }
     }
-  else 
+  else
     {
       xops[3] = gen_rtx_REG (SImode, 0);
       if (do_rtl)
@@ -2161,6 +2121,10 @@ ix86_prologue (do_rtl)
          }
       }
 
+#ifdef SUBTARGET_PROLOGUE
+  SUBTARGET_PROLOGUE;
+#endif  
+
   if (pic_reg_used)
     load_pic_register (do_rtl);
 
@@ -2175,7 +2139,7 @@ ix86_prologue (do_rtl)
 /* Return 1 if it is appropriate to emit `ret' instructions in the
    body of a function.  Do this only if the epilogue is simple, needing a
    couple of insns.  Prior to reloading, we can't tell how many registers
-   must be saved, so return 0 then.  Return 0 if there is no frame 
+   must be saved, so return 0 then.  Return 0 if there is no frame
    marker to de-allocate.
 
    If NON_SAVING_SETJMP is defined and true, then it is not possible
@@ -2222,7 +2186,7 @@ function_epilogue (file, size)
     return;
 }
 
-/* Restore function stack, frame, and registers. */ 
+/* Restore function stack, frame, and registers. */
 
 void
 ix86_expand_epilogue ()
@@ -2235,32 +2199,18 @@ ix86_epilogue (do_rtl)
      int do_rtl;
 {
   register int regno;
-  register int nregs, limit;
-  int offset;
+  register int limit;
+  int nregs;
   rtx xops[3];
   int pic_reg_used = flag_pic && (current_function_uses_pic_offset_table
                                  || current_function_uses_const_pool);
-  long tsize = get_frame_size ();
-
-  /* Compute the number of registers to pop */
-
-  limit = (frame_pointer_needed ? FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM);
-
-  nregs = 0;
-
-  for (regno = limit - 1; regno >= 0; regno--)
-    if ((regs_ever_live[regno] && ! call_used_regs[regno])
-       || (regno == PIC_OFFSET_TABLE_REGNUM && pic_reg_used))
-      nregs++;
+  int sp_valid = !frame_pointer_needed || current_function_sp_is_unchanging;
+  HOST_WIDE_INT offset;
+  HOST_WIDE_INT tsize = ix86_compute_frame_size (get_frame_size (), &nregs);
 
-  /* sp is often  unreliable so we must go off the frame pointer.
+  /* sp is often unreliable so we may have to go off the frame pointer. */
 
-     In reality, we may not care if sp is unreliable, because we can restore
-     the register relative to the frame pointer.  In theory, since each move
-     is the same speed as a pop, and we don't need the leal, this is faster.
-     For now restore multiple registers the old way. */
-
-  offset = - tsize - (nregs * UNITS_PER_WORD);
+  offset = -(tsize + nregs * UNITS_PER_WORD);
 
   xops[2] = stack_pointer_rtx;
 
@@ -2275,9 +2225,17 @@ ix86_epilogue (do_rtl)
   if (flag_pic || profile_flag || profile_block_flag)
     emit_insn (gen_blockage ());
 
-  if (nregs > 1 || ! frame_pointer_needed)
+  /* If we're only restoring one register and sp is not valid then
+     using a move instruction to restore the register since it's
+     less work than reloading sp and popping the register.  Otherwise,
+     restore sp (if necessary) and pop the registers. */
+
+  limit = frame_pointer_needed
+         ? FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM;
+
+  if (nregs > 1 || sp_valid)
     {
-      if (frame_pointer_needed)
+      if ( !sp_valid )
        {
          xops[0] = adj_offsettable_operand (AT_BP (QImode), offset);
          if (do_rtl)
@@ -2346,14 +2304,57 @@ ix86_epilogue (do_rtl)
 
   else if (tsize)
     {
-      /* If there is no frame pointer, we must still release the frame. */
-      xops[0] = GEN_INT (tsize);
+      /* Intel's docs say that for 4 or 8 bytes of stack frame one should
+        use `pop' and not `add'.  */
+      int use_pop = tsize == 4;
 
-      if (do_rtl)
-       emit_insn (gen_rtx (SET, VOIDmode, xops[2],
-                           gen_rtx (PLUS, SImode, xops[2], xops[0])));
+      /* Use two pops only for the Pentium processors.  */
+      if (tsize == 8 && !TARGET_386 && !TARGET_486)
+       {
+         rtx retval = current_function_return_rtx;
+
+         xops[1] = gen_rtx_REG (SImode, 1);    /* %edx */
+
+         /* This case is a bit more complex.  Since we cannot pop into
+            %ecx twice we need a second register.  But this is only
+            available if the return value is not of DImode in which
+            case the %edx register is not available.  */
+         use_pop = (retval == NULL
+                    || ! reg_overlap_mentioned_p (xops[1], retval));
+       }
+
+      if (use_pop)
+       {
+         xops[0] = gen_rtx_REG (SImode, 2);    /* %ecx */
+
+         if (do_rtl)
+           {
+             /* We have to prevent the two pops here from being scheduled.
+                GCC otherwise would try in some situation to put other
+                instructions in between them which has a bad effect.  */
+             emit_insn (gen_blockage ());
+             emit_insn (gen_pop (xops[0]));
+             if (tsize == 8)
+               emit_insn (gen_pop (xops[1]));
+           }
+         else
+           {
+             output_asm_insn ("pop%L0 %0", xops);
+             if (tsize == 8)
+               output_asm_insn ("pop%L1 %1", xops);
+           }
+       }
       else
-       output_asm_insn (AS2 (add%L2,%0,%2), xops);
+       {
+         /* If there is no frame pointer, we must still release the frame. */
+         xops[0] = GEN_INT (tsize);
+
+         if (do_rtl)
+           emit_insn (gen_rtx (SET, VOIDmode, xops[2],
+                               gen_rtx (PLUS, SImode, xops[2], xops[0])));
+         else
+           output_asm_insn (AS2 (add%L2,%0,%2), xops);
+       }
     }
 
 #ifdef FUNCTION_BLOCK_PROFILER_EXIT
@@ -2390,7 +2391,7 @@ ix86_epilogue (do_rtl)
              output_asm_insn ("jmp %*%0", xops);
            }
        }
-      else 
+      else
        {
          if (do_rtl)
            emit_jump_insn (gen_return_pop_internal (xops[1]));
@@ -2442,6 +2443,37 @@ do {                                                                     \
 } while (0)
 
 int
+legitimate_pic_address_disp_p (disp)
+     register rtx disp;
+{
+  if (GET_CODE (disp) != CONST)
+    return 0;
+  disp = XEXP (disp, 0);
+
+  if (GET_CODE (disp) == PLUS)
+    {
+      if (GET_CODE (XEXP (disp, 1)) != CONST_INT)
+       return 0;
+      disp = XEXP (disp, 0);
+    }
+
+  if (GET_CODE (disp) != UNSPEC
+      || XVECLEN (disp, 0) != 1)
+    return 0;
+
+  /* Must be @GOT or @GOTOFF.  */
+  if (XINT (disp, 1) != 6
+      && XINT (disp, 1) != 7)
+    return 0;
+
+  if (GET_CODE (XVECEXP (disp, 0, 0)) != SYMBOL_REF
+      && GET_CODE (XVECEXP (disp, 0, 0)) != LABEL_REF)
+    return 0;
+
+  return 1;
+}
+
+int
 legitimate_address_p (mode, addr, strict)
      enum machine_mode mode;
      register rtx addr;
@@ -2462,7 +2494,7 @@ legitimate_address_p (mode, addr, strict)
     }
 
   if (GET_CODE (addr) == REG || GET_CODE (addr) == SUBREG)
-      base = addr;
+    base = addr;
 
   else if (GET_CODE (addr) == PLUS)
     {
@@ -2552,6 +2584,12 @@ legitimate_address_p (mode, addr, strict)
          return FALSE;
        }
 
+      if (GET_MODE (base) != Pmode)
+       {
+         ADDR_INVALID ("Base is not in Pmode.\n", base);
+         return FALSE;
+       }
+
       if ((strict && ! REG_OK_FOR_BASE_STRICT_P (base))
          || (! strict && ! REG_OK_FOR_BASE_NONSTRICT_P (base)))
        {
@@ -2573,6 +2611,12 @@ legitimate_address_p (mode, addr, strict)
          return FALSE;
        }
 
+      if (GET_MODE (indx) != Pmode)
+       {
+         ADDR_INVALID ("Index is not in Pmode.\n", indx);
+         return FALSE;
+       }
+
       if ((strict && ! REG_OK_FOR_INDEX_STRICT_P (indx))
          || (! strict && ! REG_OK_FOR_INDEX_NONSTRICT_P (indx)))
        {
@@ -2602,20 +2646,10 @@ legitimate_address_p (mode, addr, strict)
        }
     }
 
-  /* Validate displacement
-     Constant pool addresses must be handled special.  They are
-     considered legitimate addresses, but only if not used with regs.
-     When printed, the output routines know to print the reference with the
-     PIC reg, even though the PIC reg doesn't appear in the RTL. */
+  /* Validate displacement.  */
   if (disp)
     {
-      if (GET_CODE (disp) == SYMBOL_REF
-         && CONSTANT_POOL_ADDRESS_P (disp)
-         && base == 0
-         && indx == 0)
-       ;
-
-      else if (!CONSTANT_ADDRESS_P (disp))
+      if (!CONSTANT_ADDRESS_P (disp))
        {
          ADDR_INVALID ("Displacement is not valid.\n", disp);
          return FALSE;
@@ -2627,20 +2661,32 @@ legitimate_address_p (mode, addr, strict)
          return FALSE;
        }
 
-      else if (flag_pic && SYMBOLIC_CONST (disp)
-              && base != pic_offset_table_rtx
-              && (indx != pic_offset_table_rtx || scale != NULL_RTX))
+      if (flag_pic && SYMBOLIC_CONST (disp))
        {
-         ADDR_INVALID ("Displacement is an invalid pic reference.\n", disp);
-         return FALSE;
+         if (! legitimate_pic_address_disp_p (disp))
+           {
+             ADDR_INVALID ("Displacement is an invalid PIC construct.\n",
+                           disp);
+             return FALSE;
+           }
+
+         if (base != pic_offset_table_rtx
+             && (indx != pic_offset_table_rtx || scale != NULL_RTX))
+           {
+             ADDR_INVALID ("PIC displacement against invalid base.\n", disp);
+             return FALSE;
+           }
        }
 
-      else if (HALF_PIC_P () && HALF_PIC_ADDRESS_P (disp)
-              && (base != NULL_RTX || indx != NULL_RTX))
+      else if (HALF_PIC_P ())
        {
-         ADDR_INVALID ("Displacement is an invalid half-pic reference.\n",
-                       disp);
-         return FALSE;
+         if (! HALF_PIC_ADDRESS_P (disp)
+             || (base != NULL_RTX || indx != NULL_RTX))
+           {
+             ADDR_INVALID ("Displacement is an invalid half-pic reference.\n",
+                           disp);
+             return FALSE;
+           }
        }
     }
 
@@ -2654,29 +2700,20 @@ legitimate_address_p (mode, addr, strict)
 /* Return a legitimate reference for ORIG (an address) using the
    register REG.  If REG is 0, a new pseudo is generated.
 
-   There are three types of references that must be handled:
+   There are two types of references that must be handled:
 
    1. Global data references must load the address from the GOT, via
       the PIC reg.  An insn is emitted to do this load, and the reg is
       returned.
 
-   2. Static data references must compute the address as an offset
-      from the GOT, whose base is in the PIC reg.  An insn is emitted to
-      compute the address into a reg, and the reg is returned.  Static
-      data objects have SYMBOL_REF_FLAG set to differentiate them from
-      global data objects.
-
-   3. Constant pool addresses must be handled special.  They are
-      considered legitimate addresses, but only if not used with regs.
-      When printed, the output routines know to print the reference with the
-      PIC reg, even though the PIC reg doesn't appear in the RTL.
+   2. Static data references, constant pool addresses, and code labels
+      compute the address as an offset from the GOT, whose base is in
+      the PIC reg.  Static data objects have SYMBOL_REF_FLAG set to
+      differentiate them from global data objects.  The returned
+      address is the PIC reg + an unspec constant.
 
    GO_IF_LEGITIMATE_ADDRESS rejects symbolic references unless the PIC
-   reg also appears in the address (except for constant pool references,
-   noted above).
-
-   "switch" statements also require special handling when generating
-   PIC code.  See comments by the `casesi' insn in i386.md for details.  */
+   reg also appears in the address.  */
 
 rtx
 legitimize_pic_address (orig, reg)
@@ -2685,60 +2722,99 @@ legitimize_pic_address (orig, reg)
 {
   rtx addr = orig;
   rtx new = orig;
+  rtx base;
 
-  if (GET_CODE (addr) == SYMBOL_REF || GET_CODE (addr) == LABEL_REF)
+  if (GET_CODE (addr) == LABEL_REF
+      || (GET_CODE (addr) == SYMBOL_REF
+         && (CONSTANT_POOL_ADDRESS_P (addr)
+             || SYMBOL_REF_FLAG (addr))))
     {
-      if (GET_CODE (addr) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (addr))
-       reg = new = orig;
-      else
-       {
-         if (reg == 0)
-           reg = gen_reg_rtx (Pmode);
+      /* This symbol may be referenced via a displacement from the PIC
+        base address (@GOTOFF).  */
 
-         if ((GET_CODE (addr) == SYMBOL_REF && SYMBOL_REF_FLAG (addr))
-             || GET_CODE (addr) == LABEL_REF)
-           new = gen_rtx (PLUS, Pmode, pic_offset_table_rtx, orig);
-         else
-           new = gen_rtx_MEM (Pmode,
-                          gen_rtx (PLUS, Pmode, pic_offset_table_rtx, orig));
+      current_function_uses_pic_offset_table = 1;
+      new = gen_rtx_UNSPEC (VOIDmode, gen_rtvec (1, addr), 7);
+      new = gen_rtx_CONST (VOIDmode, new);
+      new = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, new);
 
+      if (reg != 0)
+       {
          emit_move_insn (reg, new);
+         new = reg;
        }
-      current_function_uses_pic_offset_table = 1;
-      return reg;
     }
-
-  else if (GET_CODE (addr) == CONST || GET_CODE (addr) == PLUS)
+  else if (GET_CODE (addr) == SYMBOL_REF)
     {
-      rtx base;
+      /* This symbol must be referenced via a load from the
+        Global Offset Table (@GOT). */
 
-      if (GET_CODE (addr) == CONST)
-       {
-         addr = XEXP (addr, 0);
-         if (GET_CODE (addr) != PLUS)
-           abort ();
-       }
-
-      if (XEXP (addr, 0) == pic_offset_table_rtx)
-       return orig;
+      current_function_uses_pic_offset_table = 1;
+      new = gen_rtx_UNSPEC (VOIDmode, gen_rtvec (1, addr), 6);
+      new = gen_rtx_CONST (VOIDmode, new);
+      new = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, new);
+      new = gen_rtx_MEM (Pmode, new);
+      RTX_UNCHANGING_P (new) = 1;
 
       if (reg == 0)
        reg = gen_reg_rtx (Pmode);
-
-      base = legitimize_pic_address (XEXP (addr, 0), reg);
-      addr = legitimize_pic_address (XEXP (addr, 1),
-                                    base == reg ? NULL_RTX : reg);
-
-      if (GET_CODE (addr) == CONST_INT)
-       return plus_constant (base, INTVAL (addr));
-
-      if (GET_CODE (addr) == PLUS && CONSTANT_P (XEXP (addr, 1)))
+      emit_move_insn (reg, new);
+      new = reg;
+    }      
+  else
+    {
+      if (GET_CODE (addr) == CONST)
        {
-         base = gen_rtx (PLUS, Pmode, base, XEXP (addr, 0));
-         addr = XEXP (addr, 1);
+         addr = XEXP (addr, 0);
+         if (GET_CODE (addr) == UNSPEC)
+           {
+             /* Check that the unspec is one of the ones we generate?  */
+           }
+         else if (GET_CODE (addr) != PLUS)
+           abort();
        }
+      if (GET_CODE (addr) == PLUS)
+       {
+         rtx op0 = XEXP (addr, 0), op1 = XEXP (addr, 1);
+
+         /* Check first to see if this is a constant offset from a @GOTOFF
+            symbol reference.  */
+         if ((GET_CODE (op0) == LABEL_REF
+              || (GET_CODE (op0) == SYMBOL_REF
+                  && (CONSTANT_POOL_ADDRESS_P (op0)
+                      || SYMBOL_REF_FLAG (op0))))
+             && GET_CODE (op1) == CONST_INT)
+           {
+             current_function_uses_pic_offset_table = 1;
+             new = gen_rtx_UNSPEC (VOIDmode, gen_rtvec (1, op0), 7);
+             new = gen_rtx_PLUS (VOIDmode, new, op1);
+             new = gen_rtx_CONST (VOIDmode, new);
+             new = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, new);
+
+             if (reg != 0)
+               {
+                 emit_move_insn (reg, new);
+                 new = reg;
+               }
+           }
+         else
+           {
+             base = legitimize_pic_address (XEXP (addr, 0), reg);
+             new  = legitimize_pic_address (XEXP (addr, 1),
+                                            base == reg ? NULL_RTX : reg);
 
-      return gen_rtx (PLUS, Pmode, base, addr);
+             if (GET_CODE (new) == CONST_INT)
+               new = plus_constant (base, INTVAL (new));
+             else
+               {
+                 if (GET_CODE (new) == PLUS && CONSTANT_P (XEXP (new, 1)))
+                   {
+                     base = gen_rtx_PLUS (Pmode, base, XEXP (new, 0));
+                     new = XEXP (new, 1);
+                   }
+                 new = gen_rtx_PLUS (Pmode, base, new);
+               }
+           }
+       }
     }
   return new;
 }
@@ -2753,7 +2829,7 @@ emit_pic_move (operands, mode)
   rtx temp = reload_in_progress ? operands[0] : gen_reg_rtx (Pmode);
 
   if (GET_CODE (operands[0]) == MEM && SYMBOLIC_CONST (operands[1]))
-    operands[1] = force_reg (SImode, operands[1]);
+    operands[1] = force_reg (Pmode, operands[1]);
   else
     operands[1] = legitimize_pic_address (operands[1], temp);
 }
@@ -2804,8 +2880,8 @@ legitimize_address (x, oldx, mode)
       && (log = (unsigned)exact_log2 (INTVAL (XEXP (x, 1)))) < 4)
     {
       changed = 1;
-      x = gen_rtx (MULT, Pmode, force_reg (Pmode, XEXP (x, 0)),
-                  GEN_INT (1 << log));
+      x = gen_rtx_MULT (Pmode, force_reg (Pmode, XEXP (x, 0)),
+                       GEN_INT (1 << log));
     }
 
   if (GET_CODE (x) == PLUS)
@@ -2966,31 +3042,14 @@ output_pic_addr_const (file, x, code)
       break;
 
     case SYMBOL_REF:
-    case LABEL_REF:
-      if (GET_CODE (x) == SYMBOL_REF)
-       assemble_name (file, XSTR (x, 0));
-      else
-       {
-         ASM_GENERATE_INTERNAL_LABEL (buf, "L",
-                                      CODE_LABEL_NUMBER (XEXP (x, 0)));
-         assemble_name (asm_out_file, buf);
-       }
-
-      if (code == 'X')
-       ; /* No suffix, dammit. */
-      else if (GET_CODE (x) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (x))
-       fprintf (file, "@GOTOFF(%%ebx)");
-      else if (code == 'P')
-       fprintf (file, "@PLT");
-      else if (GET_CODE (x) == LABEL_REF)
-       fprintf (file, "@GOTOFF");
-      else if (! SYMBOL_REF_FLAG (x))
-       fprintf (file, "@GOT");
-      else
-       fprintf (file, "@GOTOFF");
-
+      assemble_name (file, XSTR (x, 0));
+      if (code == 'P' && ! SYMBOL_REF_FLAG (x))
+       fputs ("@PLT", file);
       break;
 
+    case LABEL_REF:
+      x = XEXP (x, 0);
+      /* FALLTHRU */
     case CODE_LABEL:
       ASM_GENERATE_INTERNAL_LABEL (buf, "L", CODE_LABEL_NUMBER (x));
       assemble_name (asm_out_file, buf);
@@ -3028,17 +3087,17 @@ output_pic_addr_const (file, x, code)
       if (GET_CODE (XEXP (x, 0)) == CONST_INT)
        {
          output_pic_addr_const (file, XEXP (x, 0), code);
-         if (INTVAL (XEXP (x, 1)) >= 0)
-           fprintf (file, "+");
+         fprintf (file, "+");
          output_pic_addr_const (file, XEXP (x, 1), code);
        }
-      else
+      else if (GET_CODE (XEXP (x, 1)) == CONST_INT)
        {
          output_pic_addr_const (file, XEXP (x, 1), code);
-         if (INTVAL (XEXP (x, 0)) >= 0)
-           fprintf (file, "+");
+         fprintf (file, "+");
          output_pic_addr_const (file, XEXP (x, 0), code);
        }
+      else
+       abort ();
       break;
 
     case MINUS:
@@ -3047,11 +3106,140 @@ output_pic_addr_const (file, x, code)
       output_pic_addr_const (file, XEXP (x, 1), code);
       break;
 
+     case UNSPEC:
+       if (XVECLEN (x, 0) != 1)
+       abort ();
+       output_pic_addr_const (file, XVECEXP (x, 0, 0), code);
+       switch (XINT (x, 1))
+       {
+       case 6:
+         fputs ("@GOT", file);
+         break;
+       case 7:
+         fputs ("@GOTOFF", file);
+         break;
+       case 8:
+         fputs ("@PLT", file);
+         break;
+       default:
+         output_operand_lossage ("invalid UNSPEC as operand");
+         break;
+       }
+       break;
+
     default:
       output_operand_lossage ("invalid expression as operand");
     }
 }
 \f
+static void
+put_jump_code (code, reverse, file)
+     enum rtx_code code;
+     int reverse;
+     FILE *file;
+{
+  int flags = cc_prev_status.flags;
+  int ieee = (TARGET_IEEE_FP && (flags & CC_IN_80387));
+  const char *suffix;
+
+  if (flags & CC_Z_IN_NOT_C)
+    switch (code)
+      {
+      case EQ:
+       fputs (reverse ? "c" : "nc", file);
+       return;
+
+      case NE:
+       fputs (reverse ? "nc" : "c", file);
+       return;
+
+      default:
+       abort ();
+      }
+  if (ieee)
+    {
+      switch (code)
+       {
+       case LE:
+         suffix = reverse ? "ae" : "b";
+         break;
+       case GT:
+       case LT:
+       case GE:
+         suffix = reverse ? "ne" : "e";
+         break;
+       case EQ:
+         suffix = reverse ? "ne" : "e";
+         break;
+       case NE:
+         suffix = reverse ? "e" : "ne";
+         break;
+       default:
+         abort ();
+       }
+      fputs (suffix, file);
+      return;
+    }
+  if (flags & CC_TEST_AX)
+    abort();
+  if ((flags & CC_NO_OVERFLOW) && (code == LE || code == GT))
+    abort ();
+  if (reverse)
+    code = reverse_condition (code);
+  switch (code)
+    {
+    case EQ:
+      suffix = "e";
+      break;
+
+    case NE:
+      suffix = "ne";
+      break;
+
+    case GT:
+      suffix = flags & CC_IN_80387 ? "a" : "g";
+      break;
+
+    case GTU:
+      suffix = "a";
+      break;
+
+    case LT:
+      if (flags & CC_NO_OVERFLOW)
+       suffix = "s";
+      else
+       suffix = flags & CC_IN_80387 ? "b" : "l";
+      break;
+
+    case LTU:
+      suffix = "b";
+      break;
+
+    case GE:
+      if (flags & CC_NO_OVERFLOW)
+       suffix = "ns";
+      else
+       suffix = flags & CC_IN_80387 ? "ae" : "ge";
+      break;
+
+    case GEU:
+      suffix = "ae";
+      break;
+
+    case LE:
+      suffix = flags & CC_IN_80387 ? "be" : "le";
+      break;
+
+    case LEU:
+      suffix = "be";
+      break;
+
+    default:
+      abort ();
+    }
+  fputs (suffix, file);
+}
+
 /* Append the correct conditional move suffix which corresponds to CODE.  */
 
 static void
@@ -3069,7 +3257,7 @@ put_condition_code (code, reverse_cc, mode, file)
   if (mode == MODE_INT)
     switch (code)
       {
-      case NE: 
+      case NE:
        if (cc_prev_status.flags & CC_Z_IN_NOT_C)
          fputs ("b", file);
        else
@@ -3128,34 +3316,34 @@ put_condition_code (code, reverse_cc, mode, file)
   else if (mode == MODE_FLOAT)
     switch (code)
       {
-      case NE: 
+      case NE:
        fputs (ieee ? (reverse_cc ? "ne" : "e") : "ne", file);
        return;
-      case EQ: 
+      case EQ:
        fputs (ieee ? (reverse_cc ? "ne" : "e") : "e", file);
        return;
-      case GE: 
+      case GE:
        fputs (ieee ? (reverse_cc ? "ne" : "e") : "nb", file);
        return;
-      case GT: 
+      case GT:
        fputs (ieee ? (reverse_cc ? "ne" : "e") : "nbe", file);
        return;
-      case LE: 
+      case LE:
        fputs (ieee ? (reverse_cc ? "nb" : "b") : "be", file);
        return;
-      case LT: 
+      case LT:
        fputs (ieee ? (reverse_cc ? "ne" : "e") : "b", file);
        return;
-      case GEU: 
+      case GEU:
        fputs (ieee ? (reverse_cc ? "ne" : "e") : "nb", file);
        return;
-      case GTU: 
+      case GTU:
        fputs (ieee ? (reverse_cc ? "ne" : "e") : "nbe", file);
        return;
-      case LEU: 
+      case LEU:
        fputs (ieee ? (reverse_cc ? "nb" : "b") : "be", file);
        return;
-      case LTU: 
+      case LTU:
        fputs (ieee ? (reverse_cc ? "ne" : "e") : "b", file);
        return;
       default:
@@ -3168,12 +3356,13 @@ put_condition_code (code, reverse_cc, mode, file)
    C -- print opcode suffix for set/cmov insn.
    c -- like C, but print reversed condition
    F -- print opcode suffix for fcmov insn.
-   f -- like C, but print reversed condition
+   f -- like F, but print reversed condition
+   D -- print the opcode suffix for a jump
+   d -- like D, but print reversed condition
    R -- print the prefix for register names.
    z -- print the opcode suffix for the size of the current operand.
    * -- print a star (in certain assembler syntax)
    w -- print the operand as if it's a "word" (HImode) even if it isn't.
-   c -- don't print special prefixes before constant operands.
    J -- print the appropriate jump operand.
    s -- print a shift double count, followed by the assemblers argument
        delimiter.
@@ -3295,9 +3484,9 @@ print_operand (file, x, code)
            case GTU: fputs ("jne",  file); return;
            case LEU: fputs ("je", file); return;
            case LTU: fputs ("#branch never",  file); return;
-           
+
            /* no matching branches for GT nor LE */
-           
+
            default:
              abort ();
            }
@@ -3311,6 +3500,14 @@ print_operand (file, x, code)
 
          return;
 
+       case 'D':
+         put_jump_code (GET_CODE (x), 0, file);
+         return;
+
+       case 'd':
+         put_jump_code (GET_CODE (x), 1, file);
+         return;
+
          /* This is used by the conditional move instructions.  */
        case 'C':
          put_condition_code (GET_CODE (x), 0, MODE_INT, file);
@@ -3389,7 +3586,7 @@ print_operand (file, x, code)
       REAL_VALUE_TO_DECIMAL (r, "%.22e", dstr);
       fprintf (file, "%s", dstr);
     }
-  else 
+  else
     {
       if (code != 'P')
        {
@@ -3419,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);
@@ -3534,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;
 
@@ -3584,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))
@@ -3719,7 +3928,12 @@ split_di (operands, num, lo_half, hi_half)
   while (num--)
     {
       rtx op = operands[num];
-      if (GET_CODE (op) == REG)
+      if (! reload_completed)
+       {
+         lo_half[num] = gen_lowpart (SImode, op);
+         hi_half[num] = gen_highpart (SImode, op);
+       }
+      else if (GET_CODE (op) == REG)
        {
          lo_half[num] = gen_rtx_REG (SImode, REGNO (op));
          hi_half[num] = gen_rtx_REG (SImode, REGNO (op) + 1);
@@ -3817,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:
@@ -3868,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])))
        {
@@ -3901,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 ();
 
@@ -4002,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
@@ -4030,7 +4267,7 @@ output_float_compare (insn, operands)
       operands[1] = tmp;
       cc_status.flags |= CC_REVERSED;
     }
-    
+
   if (! STACK_TOP_P (operands[0]))
     abort ();
 
@@ -4072,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 "";
@@ -4124,7 +4356,7 @@ output_fp_cc0_set (insn)
       if (!(cc_status.flags & CC_REVERSED))
         {
           next = next_cc0_user (insn);
-        
+
           if (GET_CODE (next) == JUMP_INSN
               && GET_CODE (PATTERN (next)) == SET
               && SET_DEST (PATTERN (next)) == pc_rtx
@@ -4387,7 +4619,7 @@ copy_all_rtx (orig)
   copy->integrated = orig->integrated;
   /* intel1 */
   copy->is_spill_rtx = orig->is_spill_rtx;
-  
+
   format_ptr = GET_RTX_FORMAT (GET_CODE (copy));
 
   for (i = 0; i < GET_RTX_LENGTH (GET_CODE (copy)); i++)
@@ -4439,7 +4671,7 @@ copy_all_rtx (orig)
 \f
 /* Try to rewrite a memory address to make it valid */
 
-void 
+void
 rewrite_address (mem_rtx)
      rtx mem_rtx;
 {
@@ -4476,7 +4708,7 @@ rewrite_address (mem_rtx)
       obfree (storage);
     }
 
-  /* This part is utilized by loop.c.  
+  /* This part is utilized by loop.c.
      If the address contains PLUS (reg,const) and this pattern is invalid
      in this case - try to rewrite the address to make it valid. */
   storage = oballoc (0);
@@ -4833,7 +5065,7 @@ is_fp_dest (insn)
          || GET_MODE (SET_DEST (PATTERN (insn))) == XFmode)
       && GET_CODE (SET_DEST (PATTERN (insn))) == REG
       && REGNO (SET_DEST (PATTERN (insn))) >= FIRST_FLOAT_REG
-      && GET_CODE (SET_SRC (insn)) != MEM)
+      && GET_CODE (SET_SRC (PATTERN (insn))) != MEM)
     return 1;
 
   return 0;
@@ -4865,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;
 }
@@ -5109,7 +5361,7 @@ output_strlen_unroll (operands)
   /* Check third byte. */
   output_asm_insn (AS2 (test%L1,%16,%1), xops);
   output_asm_insn (AS1 (je,%l10), xops);
-  
+
   /* Check fourth byte and increment address. */
   output_asm_insn (AS2 (add%L0,%5,%0), xops);
   output_asm_insn (AS2 (test%L1,%17,%1), xops);
@@ -5134,26 +5386,12 @@ output_fp_conditional_move (which_alternative, operands)
      int which_alternative;
      rtx operands[];
 {
-  int code = GET_CODE (operands[1]);
-
-  /* This is very tricky. We have to do it right. For a code segement
-     like:
-
-       int foo;
-       double bar;
-       ....
-       foo = foo - x;
-       if (foo >= 0)
-         bar = y;
+  enum rtx_code code = GET_CODE (operands[1]);
 
-     final_scan_insn () may delete the insn which sets CC. We have to
-     tell final_scan_insn () if it should be reinserted. When CODE is
-     GT or LE, we have to check the CC_NO_OVERFLOW bit and return
-     NULL_PTR to tell final to reinsert the test insn because the
-     conditional move cannot be handled properly without it. */
-  if ((code == GT || code == LE)
-      && (cc_prev_status.flags & CC_NO_OVERFLOW))
-    return NULL_PTR;
+  /* This should never happen.  */
+  if (!(cc_prev_status.flags & CC_IN_80387)
+      && (code == GT || code == LE || code == GE || code == LT))
+    abort ();
 
   switch (which_alternative)
     {
@@ -5161,15 +5399,9 @@ output_fp_conditional_move (which_alternative, operands)
       /* r <- cond ? arg : r */
       output_asm_insn (AS2 (fcmov%F1,%2,%0), operands);
       break;
-  
-    case 1:
-      /* r <- cond ? r : arg */
-      output_asm_insn (AS2 (fcmov%f1,%3,%0), operands);
-      break;
 
-    case 2:
+    case 1:
       /* r <- cond ? r : arg */
-      output_asm_insn (AS2 (fcmov%F1,%2,%0), operands);
       output_asm_insn (AS2 (fcmov%f1,%3,%0), operands);
       break;
 
@@ -5185,9 +5417,7 @@ output_int_conditional_move (which_alternative, operands)
      int which_alternative;
      rtx operands[];
 {
-  int code = GET_CODE (operands[1]);
-  enum machine_mode mode;
-  rtx xops[4];
+  enum rtx_code code = GET_CODE (operands[1]);
 
   /* This is very tricky. We have to do it right. For a code segement
      like:
@@ -5207,45 +5437,364 @@ output_int_conditional_move (which_alternative, operands)
       && (cc_prev_status.flags & CC_NO_OVERFLOW))
     return NULL_PTR;
 
-  mode = GET_MODE (operands [0]);
-  if (mode == DImode)
-    {
-      xops [0] = gen_rtx_SUBREG (SImode, operands [0], 1);
-      xops [1] = operands [1];
-      xops [2] = gen_rtx_SUBREG (SImode, operands [2], 1);
-      xops [3] = gen_rtx_SUBREG (SImode, operands [3], 1);
-    }
-
   switch (which_alternative)
     {
     case 0:
       /* r <- cond ? arg : r */
       output_asm_insn (AS2 (cmov%C1,%2,%0), operands);
-      if (mode == DImode)
-       output_asm_insn (AS2 (cmov%C1,%2,%0), xops);
       break;
 
     case 1:
       /* r <- cond ? r : arg */
       output_asm_insn (AS2 (cmov%c1,%3,%0), operands);
-      if (mode == DImode)
-       output_asm_insn (AS2 (cmov%c1,%3,%0), xops);
       break;
 
-    case 2:
-      /* rm <- cond ? arg1 : arg2 */
-      output_asm_insn (AS2 (cmov%C1,%2,%0), operands);
-      output_asm_insn (AS2 (cmov%c1,%3,%0), operands);
-      if (mode == DImode)
+    default:
+      abort ();
+    }
+
+  return "";
+}
+
+int
+x86_adjust_cost (insn, link, dep_insn, cost)
+     rtx insn, link, dep_insn;
+     int cost;
+{
+  rtx next_inst;
+
+  if (GET_CODE (dep_insn) == CALL_INSN || GET_CODE (insn) == JUMP_INSN)
+    return 0;
+
+  if (GET_CODE (dep_insn) == INSN
+      && GET_CODE (PATTERN (dep_insn)) == SET
+      && GET_CODE (SET_DEST (PATTERN (dep_insn))) == REG
+      && GET_CODE (insn) == INSN
+      && GET_CODE (PATTERN (insn)) == SET
+      && !reg_overlap_mentioned_p (SET_DEST (PATTERN (dep_insn)),
+                                  SET_SRC (PATTERN (insn))))
+    return 0;  /* ??? */
+
+
+  switch (ix86_cpu)
+    {
+    case PROCESSOR_PENTIUM:
+      if (cost != 0 && is_fp_insn (insn) && is_fp_insn (dep_insn)
+         && !is_fp_dest (dep_insn))
+       return 0;
+
+      if (agi_dependent (insn, dep_insn))
+       return cost ? cost + 1 : 2;
+
+      if (GET_CODE (insn) == INSN
+         && GET_CODE (PATTERN (insn)) == SET
+         && SET_DEST (PATTERN (insn)) == cc0_rtx
+         && (next_inst = next_nonnote_insn (insn))
+         && GET_CODE (next_inst) == JUMP_INSN)
+       /* compare probably paired with jump */
+       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))
        {
-         output_asm_insn (AS2 (cmov%C1,%2,%0), xops);
-         output_asm_insn (AS2 (cmov%c1,%3,%0), xops);
+         if(!agi_dependent (insn, dep_insn))
+           return 0;
+         if (TARGET_486)
+           return 2;
        }
+      else
+       if (is_fp_store (insn) && is_fp_insn (dep_insn)
+           && NEXT_INSN (insn) && NEXT_INSN (NEXT_INSN (insn))
+           && NEXT_INSN (NEXT_INSN (NEXT_INSN (insn)))
+           && (GET_CODE (NEXT_INSN (insn)) == INSN)
+           && (GET_CODE (NEXT_INSN (NEXT_INSN (insn))) == JUMP_INSN)
+           && (GET_CODE (NEXT_INSN (NEXT_INSN (NEXT_INSN (insn)))) == NOTE)
+           && (NOTE_LINE_NUMBER (NEXT_INSN (NEXT_INSN (NEXT_INSN (insn))))
+               == NOTE_INSN_LOOP_END))
+         return 3;
       break;
+    }
+
+  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 ();
     }
+}
 
-  return "";
+/* 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;
 }