OSDN Git Service

* config/i386/i386.md (builtin_setjmp_receiver): New pattern.
[pf3gnuchains/gcc-fork.git] / gcc / optabs.c
index b5a8423..5d80241 100644 (file)
@@ -21,18 +21,22 @@ Boston, MA 02111-1307, USA.  */
 
 #include "config.h"
 #include "system.h"
+#include "toplev.h"
 
 /* Include insn-config.h before expr.h so that HAVE_conditional_move
    is properly defined. */
 #include "insn-config.h"
 #include "rtl.h"
 #include "tree.h"
+#include "tm_p.h"
 #include "flags.h"
 #include "insn-flags.h"
 #include "insn-codes.h"
+#include "function.h"
 #include "expr.h"
 #include "recog.h"
 #include "reload.h"
+#include "ggc.h"
 
 /* Each optab contains info on how this target machine
    can perform a particular operation
@@ -43,50 +47,9 @@ Boston, MA 02111-1307, USA.  */
 
    See expr.h for documentation of these optabs.  */
 
-optab add_optab;
-optab sub_optab;
-optab smul_optab;
-optab smul_highpart_optab;
-optab umul_highpart_optab;
-optab smul_widen_optab;
-optab umul_widen_optab;
-optab sdiv_optab;
-optab sdivmod_optab;
-optab udiv_optab;
-optab udivmod_optab;
-optab smod_optab;
-optab umod_optab;
-optab flodiv_optab;
-optab ftrunc_optab;
-optab and_optab;
-optab ior_optab;
-optab xor_optab;
-optab ashl_optab;
-optab lshr_optab;
-optab ashr_optab;
-optab rotl_optab;
-optab rotr_optab;
-optab smin_optab;
-optab smax_optab;
-optab umin_optab;
-optab umax_optab;
-
-optab mov_optab;
-optab movstrict_optab;
-
-optab neg_optab;
-optab abs_optab;
-optab one_cmpl_optab;
-optab ffs_optab;
-optab sqrt_optab;
-optab sin_optab;
-optab cos_optab;
-
-optab cmp_optab;
-optab ucmp_optab;  /* Used only for libcalls for unsigned comparisons.  */
-optab tst_optab;
-
-optab strlen_optab;
+optab optab_table[OTI_MAX];
+
+rtx libfunc_table[LTI_MAX];
 
 /* Tables of patterns for extending one integer mode to another.  */
 enum insn_code extendtab[MAX_MACHINE_MODE][MAX_MACHINE_MODE][2];
@@ -99,129 +62,6 @@ enum insn_code floattab[NUM_MACHINE_MODES][NUM_MACHINE_MODES][2];
 /* Contains the optab used for each rtx code.  */
 optab code_to_optab[NUM_RTX_CODE + 1];
 
-/* SYMBOL_REF rtx's for the library functions that are called
-   implicitly and not via optabs.  */
-
-rtx extendsfdf2_libfunc;
-rtx extendsfxf2_libfunc;
-rtx extendsftf2_libfunc;
-rtx extenddfxf2_libfunc;
-rtx extenddftf2_libfunc;
-
-rtx truncdfsf2_libfunc;
-rtx truncxfsf2_libfunc;
-rtx trunctfsf2_libfunc;
-rtx truncxfdf2_libfunc;
-rtx trunctfdf2_libfunc;
-
-rtx memcpy_libfunc;
-rtx bcopy_libfunc;
-rtx memcmp_libfunc;
-rtx bcmp_libfunc;
-rtx memset_libfunc;
-rtx bzero_libfunc;
-
-rtx throw_libfunc;
-rtx rethrow_libfunc;
-rtx sjthrow_libfunc;
-rtx sjpopnthrow_libfunc;
-rtx terminate_libfunc;
-rtx setjmp_libfunc;
-rtx longjmp_libfunc;
-rtx eh_rtime_match_libfunc;
-
-rtx eqhf2_libfunc;
-rtx nehf2_libfunc;
-rtx gthf2_libfunc;
-rtx gehf2_libfunc;
-rtx lthf2_libfunc;
-rtx lehf2_libfunc;
-
-rtx eqsf2_libfunc;
-rtx nesf2_libfunc;
-rtx gtsf2_libfunc;
-rtx gesf2_libfunc;
-rtx ltsf2_libfunc;
-rtx lesf2_libfunc;
-
-rtx eqdf2_libfunc;
-rtx nedf2_libfunc;
-rtx gtdf2_libfunc;
-rtx gedf2_libfunc;
-rtx ltdf2_libfunc;
-rtx ledf2_libfunc;
-
-rtx eqxf2_libfunc;
-rtx nexf2_libfunc;
-rtx gtxf2_libfunc;
-rtx gexf2_libfunc;
-rtx ltxf2_libfunc;
-rtx lexf2_libfunc;
-
-rtx eqtf2_libfunc;
-rtx netf2_libfunc;
-rtx gttf2_libfunc;
-rtx getf2_libfunc;
-rtx lttf2_libfunc;
-rtx letf2_libfunc;
-
-rtx floatsisf_libfunc;
-rtx floatdisf_libfunc;
-rtx floattisf_libfunc;
-
-rtx floatsidf_libfunc;
-rtx floatdidf_libfunc;
-rtx floattidf_libfunc;
-
-rtx floatsixf_libfunc;
-rtx floatdixf_libfunc;
-rtx floattixf_libfunc;
-
-rtx floatsitf_libfunc;
-rtx floatditf_libfunc;
-rtx floattitf_libfunc;
-
-rtx fixsfsi_libfunc;
-rtx fixsfdi_libfunc;
-rtx fixsfti_libfunc;
-
-rtx fixdfsi_libfunc;
-rtx fixdfdi_libfunc;
-rtx fixdfti_libfunc;
-
-rtx fixxfsi_libfunc;
-rtx fixxfdi_libfunc;
-rtx fixxfti_libfunc;
-
-rtx fixtfsi_libfunc;
-rtx fixtfdi_libfunc;
-rtx fixtfti_libfunc;
-
-rtx fixunssfsi_libfunc;
-rtx fixunssfdi_libfunc;
-rtx fixunssfti_libfunc;
-
-rtx fixunsdfsi_libfunc;
-rtx fixunsdfdi_libfunc;
-rtx fixunsdfti_libfunc;
-
-rtx fixunsxfsi_libfunc;
-rtx fixunsxfdi_libfunc;
-rtx fixunsxfti_libfunc;
-
-rtx fixunstfsi_libfunc;
-rtx fixunstfdi_libfunc;
-rtx fixunstfti_libfunc;
-
-rtx chkr_check_addr_libfunc;
-rtx chkr_set_right_libfunc;
-rtx chkr_copy_bitmap_libfunc;
-rtx chkr_check_exec_libfunc;
-rtx chkr_check_str_libfunc;
-
-rtx profile_function_entry_libfunc;
-rtx profile_function_exit_libfunc;
-
 /* Indexed by the rtx-code for a conditional (eg. EQ, LT,...)
    gives the gen_function to make a branch to test that condition.  */
 
@@ -245,6 +85,14 @@ enum insn_code movcc_gen_code[NUM_MACHINE_MODES];
 static int add_equal_note      PROTO((rtx, rtx, enum rtx_code, rtx, rtx));
 static rtx widen_operand       PROTO((rtx, enum machine_mode,
                                       enum machine_mode, int, int));
+static int expand_cmplxdiv_straight PROTO((rtx, rtx, rtx, rtx,
+                                          rtx, rtx, enum machine_mode,
+                                          int, enum optab_methods,
+                                          enum mode_class, optab));
+static int expand_cmplxdiv_wide PROTO((rtx, rtx, rtx, rtx,
+                                      rtx, rtx, enum machine_mode,
+                                      int, enum optab_methods,
+                                      enum mode_class, optab));
 static enum insn_code can_fix_p        PROTO((enum machine_mode, enum machine_mode,
                                       int, int *));
 static enum insn_code can_float_p PROTO((enum machine_mode, enum machine_mode,
@@ -257,6 +105,10 @@ static void init_floating_libfuncs PROTO((optab, const char *, int));
 #ifdef HAVE_conditional_trap
 static void init_traps PROTO((void));
 #endif
+static void emit_cmp_and_jump_insn_1 PROTO((rtx, rtx, enum machine_mode,
+                                           enum rtx_code, int, rtx));
+static void prepare_float_lib_cmp PROTO((rtx *, rtx *, enum rtx_code *,
+                                        enum machine_mode *, int *));
 \f
 /* Add a REG_EQUAL note to the last insn in SEQ.  TARGET is being set to
    the result of operation CODE applied to OP0 (and OP1 if it is a binary
@@ -347,6 +199,379 @@ widen_operand (op, mode, oldmode, unsignedp, no_extend)
   return result;
 }
 \f
+/* Generate code to perform a straightforward complex divide.  */
+
+static int
+expand_cmplxdiv_straight (real0, real1, imag0, imag1, realr, imagr, submode,
+                         unsignedp, methods, class, binoptab)
+  rtx real0, real1, imag0, imag1, realr, imagr;
+  enum machine_mode submode;
+  int unsignedp;
+  enum optab_methods methods;
+  enum mode_class class;
+  optab binoptab;
+{
+  rtx divisor;
+  rtx real_t, imag_t;
+  rtx temp1, temp2;
+  rtx res;
+             
+  /* Don't fetch these from memory more than once.  */
+  real0 = force_reg (submode, real0);
+  real1 = force_reg (submode, real1);
+
+  if (imag0 != 0)
+    imag0 = force_reg (submode, imag0);
+
+  imag1 = force_reg (submode, imag1);
+
+  /* Divisor: c*c + d*d.  */
+  temp1 = expand_binop (submode, smul_optab, real1, real1,
+                       NULL_RTX, unsignedp, methods);
+
+  temp2 = expand_binop (submode, smul_optab, imag1, imag1,
+                       NULL_RTX, unsignedp, methods);
+
+  if (temp1 == 0 || temp2 == 0)
+    return 0;
+
+  divisor = expand_binop (submode, add_optab, temp1, temp2,
+                         NULL_RTX, unsignedp, methods);
+  if (divisor == 0)
+    return 0;
+
+  if (imag0 == 0)
+    {
+      /* Mathematically, ((a)(c-id))/divisor.  */
+      /* Computationally, (a+i0) / (c+id) = (ac/(cc+dd)) + i(-ad/(cc+dd)).  */
+
+      /* Calculate the dividend.  */
+      real_t = expand_binop (submode, smul_optab, real0, real1,
+                            NULL_RTX, unsignedp, methods);
+                 
+      imag_t = expand_binop (submode, smul_optab, real0, imag1,
+                            NULL_RTX, unsignedp, methods);
+
+      if (real_t == 0 || imag_t == 0)
+       return 0;
+
+      imag_t = expand_unop (submode, neg_optab, imag_t,
+                           NULL_RTX, unsignedp);
+    }
+  else
+    {
+      /* Mathematically, ((a+ib)(c-id))/divider.  */
+      /* Calculate the dividend.  */
+      temp1 = expand_binop (submode, smul_optab, real0, real1,
+                           NULL_RTX, unsignedp, methods);
+
+      temp2 = expand_binop (submode, smul_optab, imag0, imag1,
+                           NULL_RTX, unsignedp, methods);
+
+      if (temp1 == 0 || temp2 == 0)
+       return 0;
+
+      real_t = expand_binop (submode, add_optab, temp1, temp2,
+                            NULL_RTX, unsignedp, methods);
+                 
+      temp1 = expand_binop (submode, smul_optab, imag0, real1,
+                           NULL_RTX, unsignedp, methods);
+
+      temp2 = expand_binop (submode, smul_optab, real0, imag1,
+                           NULL_RTX, unsignedp, methods);
+
+      if (temp1 == 0 || temp2 == 0)
+       return 0;
+
+      imag_t = expand_binop (submode, sub_optab, temp1, temp2,
+                            NULL_RTX, unsignedp, methods);
+
+      if (real_t == 0 || imag_t == 0)
+       return 0;
+    }
+
+  if (class == MODE_COMPLEX_FLOAT)
+    res = expand_binop (submode, binoptab, real_t, divisor,
+                       realr, unsignedp, methods);
+  else
+    res = expand_divmod (0, TRUNC_DIV_EXPR, submode,
+                        real_t, divisor, realr, unsignedp);
+
+  if (res == 0)
+    return 0;
+
+  if (res != realr)
+    emit_move_insn (realr, res);
+
+  if (class == MODE_COMPLEX_FLOAT)
+    res = expand_binop (submode, binoptab, imag_t, divisor,
+                       imagr, unsignedp, methods);
+  else
+    res = expand_divmod (0, TRUNC_DIV_EXPR, submode,
+                        imag_t, divisor, imagr, unsignedp);
+
+  if (res == 0)
+    return 0;
+
+  if (res != imagr)
+    emit_move_insn (imagr, res);
+
+  return 1;
+}
+\f
+/* Generate code to perform a wide-input-range-acceptable complex divide.  */
+
+static int
+expand_cmplxdiv_wide (real0, real1, imag0, imag1, realr, imagr, submode,
+                     unsignedp, methods, class, binoptab)
+  rtx real0, real1, imag0, imag1, realr, imagr;
+  enum machine_mode submode;
+  int unsignedp;
+  enum optab_methods methods;
+  enum mode_class class;
+  optab binoptab;
+{
+  rtx ratio, divisor;
+  rtx real_t, imag_t;
+  rtx temp1, temp2, lab1, lab2;
+  enum machine_mode mode;
+  int align;
+  rtx res;
+             
+  /* Don't fetch these from memory more than once.  */
+  real0 = force_reg (submode, real0);
+  real1 = force_reg (submode, real1);
+
+  if (imag0 != 0)
+    imag0 = force_reg (submode, imag0);
+
+  imag1 = force_reg (submode, imag1);
+
+  /* XXX What's an "unsigned" complex number?  */
+  if (unsignedp)
+    {
+      temp1 = real1;
+      temp2 = imag1;
+    }
+  else
+    {
+      temp1 = expand_abs (submode, real1, NULL_RTX, 1);
+      temp2 = expand_abs (submode, imag1, NULL_RTX, 1);
+    }
+
+  if (temp1 == 0 || temp2 == 0)
+    return 0;
+
+  mode = GET_MODE (temp1);
+  align = GET_MODE_ALIGNMENT (mode);
+  lab1 = gen_label_rtx ();
+  emit_cmp_and_jump_insns (temp1, temp2, LT, NULL_RTX,
+                          mode, unsignedp, align, lab1);
+
+  /* |c| >= |d|; use ratio d/c to scale dividend and divisor.  */
+
+  if (class == MODE_COMPLEX_FLOAT)
+    ratio = expand_binop (submode, binoptab, imag1, real1,
+                         NULL_RTX, unsignedp, methods);
+  else
+    ratio = expand_divmod (0, TRUNC_DIV_EXPR, submode,
+                          imag1, real1, NULL_RTX, unsignedp);
+
+  if (ratio == 0)
+    return 0;
+
+  /* Calculate divisor.  */
+
+  temp1 = expand_binop (submode, smul_optab, imag1, ratio,
+                       NULL_RTX, unsignedp, methods);
+
+  if (temp1 == 0)
+    return 0;
+
+  divisor = expand_binop (submode, add_optab, temp1, real1,
+                         NULL_RTX, unsignedp, methods);
+
+  if (divisor == 0)
+    return 0;
+
+  /* Calculate dividend.  */
+
+  if (imag0 == 0)
+    {
+      real_t = real0;
+
+      /* Compute a / (c+id) as a / (c+d(d/c)) + i (-a(d/c)) / (c+d(d/c)).  */
+
+      imag_t = expand_binop (submode, smul_optab, real0, ratio,
+                            NULL_RTX, unsignedp, methods);
+
+      if (imag_t == 0)
+       return 0;
+
+      imag_t = expand_unop (submode, neg_optab, imag_t,
+                           NULL_RTX, unsignedp);
+
+      if (real_t == 0 || imag_t == 0)
+       return 0;
+    }
+  else
+    {
+      /* Compute (a+ib)/(c+id) as
+        (a+b(d/c))/(c+d(d/c) + i(b-a(d/c))/(c+d(d/c)).  */
+
+      temp1 = expand_binop (submode, smul_optab, imag0, ratio,
+                           NULL_RTX, unsignedp, methods);
+
+      if (temp1 == 0)
+       return 0;
+
+      real_t = expand_binop (submode, add_optab, temp1, real0,
+                            NULL_RTX, unsignedp, methods);
+
+      temp1 = expand_binop (submode, smul_optab, real0, ratio,
+                           NULL_RTX, unsignedp, methods);
+
+      if (temp1 == 0)
+       return 0;
+
+      imag_t = expand_binop (submode, sub_optab, imag0, temp1,
+                            NULL_RTX, unsignedp, methods);
+
+      if (real_t == 0 || imag_t == 0)
+       return 0;
+    }
+
+  if (class == MODE_COMPLEX_FLOAT)
+    res = expand_binop (submode, binoptab, real_t, divisor,
+                       realr, unsignedp, methods);
+  else
+    res = expand_divmod (0, TRUNC_DIV_EXPR, submode,
+                        real_t, divisor, realr, unsignedp);
+
+  if (res == 0)
+    return 0;
+
+  if (res != realr)
+    emit_move_insn (realr, res);
+
+  if (class == MODE_COMPLEX_FLOAT)
+    res = expand_binop (submode, binoptab, imag_t, divisor,
+                       imagr, unsignedp, methods);
+  else
+    res = expand_divmod (0, TRUNC_DIV_EXPR, submode,
+                        imag_t, divisor, imagr, unsignedp);
+
+  if (res == 0)
+    return 0;
+
+  if (res != imagr)
+    emit_move_insn (imagr, res);
+
+  lab2 = gen_label_rtx ();
+  emit_jump_insn (gen_jump (lab2));
+  emit_barrier ();
+
+  emit_label (lab1);
+
+  /* |d| > |c|; use ratio c/d to scale dividend and divisor.  */
+
+  if (class == MODE_COMPLEX_FLOAT)
+    ratio = expand_binop (submode, binoptab, real1, imag1,
+                         NULL_RTX, unsignedp, methods);
+  else
+    ratio = expand_divmod (0, TRUNC_DIV_EXPR, submode,
+                          real1, imag1, NULL_RTX, unsignedp);
+
+  if (ratio == 0)
+    return 0;
+
+  /* Calculate divisor.  */
+
+  temp1 = expand_binop (submode, smul_optab, real1, ratio,
+                       NULL_RTX, unsignedp, methods);
+
+  if (temp1 == 0)
+    return 0;
+
+  divisor = expand_binop (submode, add_optab, temp1, imag1,
+                         NULL_RTX, unsignedp, methods);
+
+  if (divisor == 0)
+    return 0;
+
+  /* Calculate dividend.  */
+
+  if (imag0 == 0)
+    {
+      /* Compute a / (c+id) as a(c/d) / (c(c/d)+d) + i (-a) / (c(c/d)+d).  */
+
+      real_t = expand_binop (submode, smul_optab, real0, ratio,
+                            NULL_RTX, unsignedp, methods);
+
+      imag_t = expand_unop (submode, neg_optab, real0,
+                           NULL_RTX, unsignedp);
+
+      if (real_t == 0 || imag_t == 0)
+       return 0;
+    }
+  else
+    {
+      /* Compute (a+ib)/(c+id) as
+        (a(c/d)+b)/(c(c/d)+d) + i (b(c/d)-a)/(c(c/d)+d).  */
+
+      temp1 = expand_binop (submode, smul_optab, real0, ratio,
+                           NULL_RTX, unsignedp, methods);
+
+      if (temp1 == 0)
+       return 0;
+
+      real_t = expand_binop (submode, add_optab, temp1, imag0,
+                            NULL_RTX, unsignedp, methods);
+
+      temp1 = expand_binop (submode, smul_optab, imag0, ratio,
+                           NULL_RTX, unsignedp, methods);
+
+      if (temp1 == 0)
+       return 0;
+
+      imag_t = expand_binop (submode, sub_optab, temp1, real0,
+                            NULL_RTX, unsignedp, methods);
+
+      if (real_t == 0 || imag_t == 0)
+       return 0;
+    }
+
+  if (class == MODE_COMPLEX_FLOAT)
+    res = expand_binop (submode, binoptab, real_t, divisor,
+                       realr, unsignedp, methods);
+  else
+    res = expand_divmod (0, TRUNC_DIV_EXPR, submode,
+                        real_t, divisor, realr, unsignedp);
+
+  if (res == 0)
+    return 0;
+
+  if (res != realr)
+    emit_move_insn (realr, res);
+
+  if (class == MODE_COMPLEX_FLOAT)
+    res = expand_binop (submode, binoptab, imag_t, divisor,
+                       imagr, unsignedp, methods);
+  else
+    res = expand_divmod (0, TRUNC_DIV_EXPR, submode,
+                        imag_t, divisor, imagr, unsignedp);
+
+  if (res == 0)
+    return 0;
+
+  if (res != imagr)
+    emit_move_insn (imagr, res);
+
+  emit_label (lab2);
+
+  return 1;
+}
+\f
 /* Generate code to perform an operation specified by BINOPTAB
    on operands OP0 and OP1, with result having machine-mode MODE.
 
@@ -448,8 +673,8 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
       && binoptab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
     {
       int icode = (int) binoptab->handlers[(int) mode].insn_code;
-      enum machine_mode mode0 = insn_operand_mode[icode][1];
-      enum machine_mode mode1 = insn_operand_mode[icode][2];
+      enum machine_mode mode0 = insn_data[icode].operand[1].mode;
+      enum machine_mode mode1 = insn_data[icode].operand[2].mode;
       rtx pat;
       rtx xop0 = op0, xop1 = op1;
 
@@ -488,15 +713,15 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
       /* Now, if insn's predicates don't allow our operands, put them into
         pseudo regs.  */
 
-      if (! (*insn_operand_predicate[icode][1]) (xop0, mode0)
+      if (! (*insn_data[icode].operand[1].predicate) (xop0, mode0)
          && mode0 != VOIDmode)
        xop0 = copy_to_mode_reg (mode0, xop0);
 
-      if (! (*insn_operand_predicate[icode][2]) (xop1, mode1)
+      if (! (*insn_data[icode].operand[2].predicate) (xop1, mode1)
          && mode1 != VOIDmode)
        xop1 = copy_to_mode_reg (mode1, xop1);
 
-      if (! (*insn_operand_predicate[icode][0]) (temp, mode))
+      if (! (*insn_data[icode].operand[0].predicate) (temp, mode))
        temp = gen_reg_rtx (mode);
 
       pat = GEN_FCN (icode) (temp, xop0, xop1);
@@ -897,7 +1122,7 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
       rtx carry_tmp = gen_reg_rtx (word_mode);
       optab otheroptab = binoptab == add_optab ? sub_optab : add_optab;
       int nwords = GET_MODE_BITSIZE (mode) / BITS_PER_WORD;
-      rtx carry_in, carry_out;
+      rtx carry_in = NULL_RTX, carry_out = NULL_RTX;
       rtx xop0, xop1;
 
       /* We can handle either a 1 or -1 value for the carry.  If STORE_FLAG
@@ -943,7 +1168,7 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
              carry_out = gen_reg_rtx (word_mode);
              carry_out = emit_store_flag_force (carry_out,
                                                 (binoptab == add_optab
-                                                 ? LTU : GTU),
+                                                 ? LT : GT),
                                                 x, op0_piece,
                                                 word_mode, 1, normalizep);
            }
@@ -966,7 +1191,7 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
                  /* Get out carry from adding/subtracting carry in.  */
                  carry_tmp = emit_store_flag_force (carry_tmp,
                                                     binoptab == add_optab
-                                                    ? LTU : GTU,
+                                                    ? LT : GT,
                                                     x, carry_in,
                                                     word_mode, 1, normalizep);
 
@@ -994,8 +1219,10 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
                                                   copy_rtx (xop0),
                                                   copy_rtx (xop1)));
            }
+
          return target;
        }
+
       else
        delete_insns_since (last);
     }
@@ -1067,8 +1294,8 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
       rtx op1_high = operand_subword_force (op1, high, mode);
       rtx op1_low = operand_subword_force (op1, low, mode);
       rtx product = 0;
-      rtx op0_xhigh;
-      rtx op1_xhigh;
+      rtx op0_xhigh = NULL_RTX;
+      rtx op1_xhigh = NULL_RTX;
 
       /* If the target is the same as one of the inputs, don't use it.  This
         prevents problems with the REG_EQUAL note.  */
@@ -1176,6 +1403,7 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
                                                       copy_rtx (op0),
                                                       copy_rtx (op1)));
                }
+
              return product;
            }
        }
@@ -1218,12 +1446,12 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
 
       start_sequence ();
 
-      realr = gen_realpart  (submode, target);
+      realr = gen_realpart (submode, target);
       imagr = gen_imagpart (submode, target);
 
       if (GET_MODE (op0) == mode)
        {
-         real0 = gen_realpart  (submode, op0);
+         real0 = gen_realpart (submode, op0);
          imag0 = gen_imagpart (submode, op0);
        }
       else
@@ -1231,7 +1459,7 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
 
       if (GET_MODE (op1) == mode)
        {
-         real1 = gen_realpart  (submode, op1);
+         real1 = gen_realpart (submode, op1);
          imag1 = gen_imagpart (submode, op1);
        }
       else
@@ -1389,111 +1617,25 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
            }
          else
            {
-             /* Divisor is of complex type:
-                X/(a+ib) */
-             rtx divisor;
-             rtx real_t, imag_t;
-             rtx temp1, temp2;
-             
-             /* Don't fetch these from memory more than once.  */
-             real0 = force_reg (submode, real0);
-             real1 = force_reg (submode, real1);
-
-             if (imag0 != 0)
-               imag0 = force_reg (submode, imag0);
-
-             imag1 = force_reg (submode, imag1);
-
-             /* Divisor: c*c + d*d */
-             temp1 = expand_binop (submode, smul_optab, real1, real1,
-                                   NULL_RTX, unsignedp, methods);
-
-             temp2 = expand_binop (submode, smul_optab, imag1, imag1,
-                                   NULL_RTX, unsignedp, methods);
-
-             if (temp1 == 0 || temp2 == 0)
-               break;
-
-             divisor = expand_binop (submode, add_optab, temp1, temp2,
-                                     NULL_RTX, unsignedp, methods);
-             if (divisor == 0)
-               break;
-
-             if (imag0 == 0)
+             switch (flag_complex_divide_method)
                {
-                 /* ((a)(c-id))/divisor */
-                 /* (a+i0) / (c+id) = (ac/(cc+dd)) + i(-ad/(cc+dd)) */
-
-                 /* Calculate the dividend */
-                 real_t = expand_binop (submode, smul_optab, real0, real1,
-                                        NULL_RTX, unsignedp, methods);
-                 
-                 imag_t = expand_binop (submode, smul_optab, real0, imag1,
-                                        NULL_RTX, unsignedp, methods);
-
-                 if (real_t == 0 || imag_t == 0)
-                   break;
-
-                 imag_t = expand_unop (submode, neg_optab, imag_t,
-                                       NULL_RTX, unsignedp);
-               }
-             else
-               {
-                 /* ((a+ib)(c-id))/divider */
-                 /* Calculate the dividend */
-                 temp1 = expand_binop (submode, smul_optab, real0, real1,
-                                       NULL_RTX, unsignedp, methods);
-
-                 temp2 = expand_binop (submode, smul_optab, imag0, imag1,
-                                       NULL_RTX, unsignedp, methods);
-
-                 if (temp1 == 0 || temp2 == 0)
-                   break;
-
-                 real_t = expand_binop (submode, add_optab, temp1, temp2,
-                                        NULL_RTX, unsignedp, methods);
-                 
-                 temp1 = expand_binop (submode, smul_optab, imag0, real1,
-                                       NULL_RTX, unsignedp, methods);
-
-                 temp2 = expand_binop (submode, smul_optab, real0, imag1,
-                                       NULL_RTX, unsignedp, methods);
-
-                 if (temp1 == 0 || temp2 == 0)
-                   break;
+               case 0:
+                 ok = expand_cmplxdiv_straight (real0, real1, imag0, imag1,
+                                                realr, imagr, submode,
+                                                unsignedp, methods,
+                                                class, binoptab);
+                 break;
 
-                 imag_t = expand_binop (submode, sub_optab, temp1, temp2,
-                                        NULL_RTX, unsignedp, methods);
+               case 1:
+                 ok = expand_cmplxdiv_wide (real0, real1, imag0, imag1,
+                                            realr, imagr, submode,
+                                            unsignedp, methods,
+                                            class, binoptab);
+                 break;
 
-                 if (real_t == 0 || imag_t == 0)
-                   break;
+               default:
+                 abort ();
                }
-
-             if (class == MODE_COMPLEX_FLOAT)
-               res = expand_binop (submode, binoptab, real_t, divisor,
-                                   realr, unsignedp, methods);
-             else
-               res = expand_divmod (0, TRUNC_DIV_EXPR, submode,
-                                    real_t, divisor, realr, unsignedp);
-
-             if (res == 0)
-               break;
-             else if (res != realr)
-               emit_move_insn (realr, res);
-
-             if (class == MODE_COMPLEX_FLOAT)
-               res = expand_binop (submode, binoptab, imag_t, divisor,
-                                   imagr, unsignedp, methods);
-             else
-               res = expand_divmod (0, TRUNC_DIV_EXPR, submode,
-                                    imag_t, divisor, imagr, unsignedp);
-
-             if (res == 0)
-               break;
-             else if (res != imagr)
-               emit_move_insn (imagr, res);
-
-             ok = 1;
            }
          break;
          
@@ -1753,8 +1895,8 @@ expand_twoval_binop (binoptab, op0, op1, targ0, targ1, unsignedp)
   if (binoptab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
     {
       int icode = (int) binoptab->handlers[(int) mode].insn_code;
-      enum machine_mode mode0 = insn_operand_mode[icode][1];
-      enum machine_mode mode1 = insn_operand_mode[icode][2];
+      enum machine_mode mode0 = insn_data[icode].operand[1].mode;
+      enum machine_mode mode1 = insn_data[icode].operand[2].mode;
       rtx pat;
       rtx xop0 = op0, xop1 = op1;
 
@@ -1767,16 +1909,16 @@ expand_twoval_binop (binoptab, op0, op1, targ0, targ1, unsignedp)
        xop1 = convert_to_mode (mode1, xop1, unsignedp);
 
       /* Now, if insn doesn't accept these operands, put them into pseudos.  */
-      if (! (*insn_operand_predicate[icode][1]) (xop0, mode0))
+      if (! (*insn_data[icode].operand[1].predicate) (xop0, mode0))
        xop0 = copy_to_mode_reg (mode0, xop0);
 
-      if (! (*insn_operand_predicate[icode][2]) (xop1, mode1))
+      if (! (*insn_data[icode].operand[2].predicate) (xop1, mode1))
        xop1 = copy_to_mode_reg (mode1, xop1);
 
       /* We could handle this, but we should always be called with a pseudo
         for our targets and all insns should take them as outputs.  */
-      if (! (*insn_operand_predicate[icode][0]) (targ0, mode)
-         || ! (*insn_operand_predicate[icode][3]) (targ1, mode))
+      if (! (*insn_data[icode].operand[0].predicate) (targ0, mode)
+         || ! (*insn_data[icode].operand[3].predicate) (targ1, mode))
        abort ();
        
       pat = GEN_FCN (icode) (targ0, xop0, xop1, targ1);
@@ -1863,7 +2005,7 @@ expand_unop (mode, unoptab, op0, target, unsignedp)
   if (unoptab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
     {
       int icode = (int) unoptab->handlers[(int) mode].insn_code;
-      enum machine_mode mode0 = insn_operand_mode[icode][1];
+      enum machine_mode mode0 = insn_data[icode].operand[1].mode;
       rtx xop0 = op0;
 
       if (target)
@@ -1877,10 +2019,10 @@ expand_unop (mode, unoptab, op0, target, unsignedp)
 
       /* Now, if insn doesn't accept our operand, put it into a pseudo.  */
 
-      if (! (*insn_operand_predicate[icode][1]) (xop0, mode0))
+      if (! (*insn_data[icode].operand[1].predicate) (xop0, mode0))
        xop0 = copy_to_mode_reg (mode0, xop0);
 
-      if (! (*insn_operand_predicate[icode][0]) (temp, mode))
+      if (! (*insn_data[icode].operand[0].predicate) (temp, mode))
        temp = gen_reg_rtx (mode);
 
       pat = GEN_FCN (icode) (temp, xop0);
@@ -2121,10 +2263,29 @@ expand_abs (mode, op0, target, safe)
   if (temp != 0)
     return temp;
 
+  /* If we have a MAX insn, we can do this as MAX (x, -x).  */
+  if (smax_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
+    {
+      rtx last = get_last_insn ();
+
+      temp = expand_unop (mode, neg_optab, op0, NULL_RTX, 0);
+      if (temp != 0)
+       temp = expand_binop (mode, smax_optab, op0, temp, target, 0,
+                            OPTAB_WIDEN);
+
+      if (temp != 0)
+       return temp;
+
+      delete_insns_since (last);
+    }
+
   /* If this machine has expensive jumps, we can do integer absolute
      value of X as (((signed) x >> (W-1)) ^ x) - ((signed) x >> (W-1)),
-     where W is the width of MODE.  */
+     where W is the width of MODE.  But don't do this if the machine has
+     conditional arithmetic since the branches will be converted into
+     a conditional negation insn.  */
 
+#ifndef HAVE_conditional_arithmetic
   if (GET_MODE_CLASS (mode) == MODE_INT && BRANCH_COST >= 2)
     {
       rtx extended = expand_shift (RSHIFT_EXPR, mode, op0,
@@ -2140,6 +2301,7 @@ expand_abs (mode, op0, target, safe)
       if (temp != 0)
        return temp;
     }
+#endif
 
   /* If that does not win, use conditional jump and negate.  */
 
@@ -2162,23 +2324,12 @@ expand_abs (mode, op0, target, safe)
 
   /* If this mode is an integer too wide to compare properly,
      compare word by word.  Rely on CSE to optimize constant cases.  */
-  if (GET_MODE_CLASS (mode) == MODE_INT && ! can_compare_p (mode))
+  if (GET_MODE_CLASS (mode) == MODE_INT && ! can_compare_p (mode, ccp_jump))
     do_jump_by_parts_greater_rtx (mode, 0, target, const0_rtx, 
                                  NULL_RTX, op1);
   else
-    {
-      temp = compare_from_rtx (target, CONST0_RTX (mode), GE, 0, mode,
-                              NULL_RTX, 0);
-      if (temp == const1_rtx)
-       return target;
-      else if (temp != const0_rtx)
-       {
-         if (bcc_gen_fctn[(int) GET_CODE (temp)] != 0)
-           emit_jump_insn ((*bcc_gen_fctn[(int) GET_CODE (temp)]) (op1));
-         else
-           abort ();
-       }
-    }
+    do_compare_rtx_and_jump (target, CONST0_RTX (mode), GE, 0, mode,
+                            NULL_RTX, 0, NULL_RTX, op1);
 
   op0 = expand_unop (mode, neg_optab, target, target, 0);
   if (op0 != target)
@@ -2235,7 +2386,7 @@ expand_complex_abs (mode, op0, target, unsignedp)
   if (abs_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
     {
       int icode = (int) abs_optab->handlers[(int) mode].insn_code;
-      enum machine_mode mode0 = insn_operand_mode[icode][1];
+      enum machine_mode mode0 = insn_data[icode].operand[1].mode;
       rtx xop0 = op0;
 
       if (target)
@@ -2249,10 +2400,10 @@ expand_complex_abs (mode, op0, target, unsignedp)
 
       /* Now, if insn doesn't accept our operand, put it into a pseudo.  */
 
-      if (! (*insn_operand_predicate[icode][1]) (xop0, mode0))
+      if (! (*insn_data[icode].operand[1].predicate) (xop0, mode0))
        xop0 = copy_to_mode_reg (mode0, xop0);
 
-      if (! (*insn_operand_predicate[icode][0]) (temp, submode))
+      if (! (*insn_data[icode].operand[0].predicate) (temp, submode))
        temp = gen_reg_rtx (submode);
 
       pat = GEN_FCN (icode) (temp, xop0);
@@ -2399,7 +2550,7 @@ emit_unop_insn (icode, target, op0, code)
      enum rtx_code code;
 {
   register rtx temp;
-  enum machine_mode mode0 = insn_operand_mode[icode][1];
+  enum machine_mode mode0 = insn_data[icode].operand[1].mode;
   rtx pat;
 
   temp = target = protect_from_queue (target, 1);
@@ -2414,10 +2565,10 @@ emit_unop_insn (icode, target, op0, code)
 
   /* Now, if insn does not accept our operands, put them into pseudos.  */
 
-  if (! (*insn_operand_predicate[icode][1]) (op0, mode0))
+  if (! (*insn_data[icode].operand[1].predicate) (op0, mode0))
     op0 = copy_to_mode_reg (mode0, op0);
 
-  if (! (*insn_operand_predicate[icode][0]) (temp, GET_MODE (temp))
+  if (! (*insn_data[icode].operand[0].predicate) (temp, GET_MODE (temp))
       || (flag_force_mem && GET_CODE (temp) == MEM))
     temp = gen_reg_rtx (GET_MODE (temp));
 
@@ -2486,7 +2637,8 @@ emit_no_conflict_block (insns, target, op0, op1, equiv)
 
       next = NEXT_INSN (insn);
 
-      if (GET_CODE (PATTERN (insn)) == SET)
+      if (GET_CODE (PATTERN (insn)) == SET || GET_CODE (PATTERN (insn)) == USE
+         || GET_CODE (PATTERN (insn)) == CLOBBER)
        set = PATTERN (insn);
       else if (GET_CODE (PATTERN (insn)) == PARALLEL)
        {
@@ -2603,7 +2755,7 @@ emit_libcall_block (insns, target, result, equiv)
         {
           rtx note = find_reg_note (insn, REG_EH_REGION, NULL_RTX);
           if (note == NULL_RTX)
-            REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_EH_REGION, GEN_INT (0),
+            REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_EH_REGION, GEN_INT (-1),
                                                   REG_NOTES (insn));
         }
     }
@@ -2686,31 +2838,68 @@ emit_0_to_1_insn (x)
   emit_move_insn (x, const1_rtx);
 }
 
-/* Generate code to compare X with Y
-   so that the condition codes are set.
+/* Nonzero if we can perform a comparison of mode MODE straightforwardly.
+   If FOR_JUMP is nonzero, we will be generating a jump based on this
+   comparison, otherwise a store-flags operation.  */
+  
+int
+can_compare_p (mode, purpose)
+     enum machine_mode mode;
+     enum can_compare_purpose purpose;
+{
+  do
+    {
+      if (cmp_optab->handlers[(int)mode].insn_code != CODE_FOR_nothing)
+       return 1;
+      if (purpose == ccp_jump
+         && cbranch_optab->handlers[(int)mode].insn_code != CODE_FOR_nothing)
+       return 1;
+      if (purpose == ccp_cmov
+         && cmov_optab->handlers[(int)mode].insn_code != CODE_FOR_nothing)
+       return 1;
+      if (purpose == ccp_store_flag
+         && cstore_optab->handlers[(int)mode].insn_code != CODE_FOR_nothing)
+       return 1;
 
-   MODE is the mode of the inputs (in case they are const_int).
-   UNSIGNEDP nonzero says that X and Y are unsigned;
+      mode = GET_MODE_WIDER_MODE (mode);
+    }
+  while (mode != VOIDmode);
+
+  return 0;
+}
+
+/* This function is called when we are going to emit a compare instruction that
+   compares the values found in *PX and *PY, using the rtl operator COMPARISON.
+
+   *PMODE is the mode of the inputs (in case they are const_int).
+   *PUNSIGNEDP nonzero says that the operands are unsigned;
    this matters if they need to be widened.
 
-   If they have mode BLKmode, then SIZE specifies the size of both X and Y,
-   and ALIGN specifies the known shared alignment of X and Y.
+   If they have mode BLKmode, then SIZE specifies the size of both operands,
+   and ALIGN specifies the known shared alignment of the operands.
 
-   COMPARISON is the rtl operator to compare with (EQ, NE, GT, etc.).
-   It is ignored for fixed-point and block comparisons;
-   it is used only for floating-point comparisons.  */
+   This function performs all the setup necessary so that the caller only has
+   to emit a single comparison insn.  This setup can involve doing a BLKmode
+   comparison or emitting a library call to perform the comparison if no insn
+   is available to handle it.
+   The values which are passed in through pointers can be modified; the caller
+   should perform the comparison on the modified values.  */
 
 void
-emit_cmp_insn (x, y, comparison, size, mode, unsignedp, align)
-     rtx x, y;
-     enum rtx_code comparison;
+prepare_cmp_insn (px, py, pcomparison, size, pmode, punsignedp, align,
+                 purpose)
+     rtx *px, *py;
+     enum rtx_code *pcomparison;
      rtx size;
-     enum machine_mode mode;
-     int unsignedp;
+     enum machine_mode *pmode;
+     int *punsignedp;
      int align;
+     enum can_compare_purpose purpose;
 {
+  enum machine_mode mode = *pmode;
+  rtx x = *px, y = *py;
+  int unsignedp = *punsignedp;
   enum mode_class class;
-  enum machine_mode wider_mode;
 
   class = GET_MODE_CLASS (mode);
 
@@ -2749,6 +2938,9 @@ emit_cmp_insn (x, y, comparison, size, mode, unsignedp, align)
 
   if (mode == BLKmode)
     {
+      rtx result;
+      enum machine_mode result_mode;
+
       emit_queue ();
       x = protect_from_queue (x, 0);
       y = protect_from_queue (y, 0);
@@ -2760,12 +2952,9 @@ emit_cmp_insn (x, y, comparison, size, mode, unsignedp, align)
          && GET_CODE (size) == CONST_INT
          && INTVAL (size) < (1 << GET_MODE_BITSIZE (QImode)))
        {
-         enum machine_mode result_mode
-           = insn_operand_mode[(int) CODE_FOR_cmpstrqi][0];
-         rtx result = gen_reg_rtx (result_mode);
+         result_mode = insn_data[(int) CODE_FOR_cmpstrqi].operand[0].mode;
+         result = gen_reg_rtx (result_mode);
          emit_insn (gen_cmpstrqi (result, x, y, size, GEN_INT (align)));
-         emit_cmp_insn (result, const0_rtx, comparison, NULL_RTX,
-                        result_mode, 0, 0);
        }
       else
 #endif
@@ -2774,33 +2963,25 @@ emit_cmp_insn (x, y, comparison, size, mode, unsignedp, align)
          && GET_CODE (size) == CONST_INT
          && INTVAL (size) < (1 << GET_MODE_BITSIZE (HImode)))
        {
-         enum machine_mode result_mode
-           = insn_operand_mode[(int) CODE_FOR_cmpstrhi][0];
-         rtx result = gen_reg_rtx (result_mode);
+         result_mode = insn_data[(int) CODE_FOR_cmpstrhi].operand[0].mode;
+         result = gen_reg_rtx (result_mode);
          emit_insn (gen_cmpstrhi (result, x, y, size, GEN_INT (align)));
-         emit_cmp_insn (result, const0_rtx, comparison, NULL_RTX,
-                        result_mode, 0, 0);
        }
       else
 #endif
 #ifdef HAVE_cmpstrsi
       if (HAVE_cmpstrsi)
        {
-         enum machine_mode result_mode
-           = insn_operand_mode[(int) CODE_FOR_cmpstrsi][0];
-         rtx result = gen_reg_rtx (result_mode);
+         result_mode = insn_data[(int) CODE_FOR_cmpstrsi].operand[0].mode;
+         result = gen_reg_rtx (result_mode);
          size = protect_from_queue (size, 0);
          emit_insn (gen_cmpstrsi (result, x, y,
                                   convert_to_mode (SImode, size, 1),
                                   GEN_INT (align)));
-         emit_cmp_insn (result, const0_rtx, comparison, NULL_RTX,
-                        result_mode, 0, 0);
        }
       else
 #endif
        {
-         rtx result;
-
 #ifdef TARGET_MEM_FUNCTIONS
          emit_library_call (memcmp_libfunc, 0,
                             TYPE_MODE (integer_type_node), 3,
@@ -2822,83 +3003,24 @@ emit_cmp_insn (x, y, comparison, size, mode, unsignedp, align)
             register so reload doesn't clobber the value if it needs
             the return register for a spill reg.  */
          result = gen_reg_rtx (TYPE_MODE (integer_type_node));
+         result_mode = TYPE_MODE (integer_type_node);
          emit_move_insn (result,
-                         hard_libcall_value (TYPE_MODE (integer_type_node)));
-         emit_cmp_insn (result,
-                        const0_rtx, comparison, NULL_RTX,
-                        TYPE_MODE (integer_type_node), 0, 0);
+                         hard_libcall_value (result_mode));
        }
+      *px = result;
+      *py = const0_rtx;
+      *pmode = result_mode;
       return;
     }
 
-  /* Handle some compares against zero.  */
-
-  if (y == CONST0_RTX (mode)
-      && tst_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
-    {
-      int icode = (int) tst_optab->handlers[(int) mode].insn_code;
-
-      emit_queue ();
-      x = protect_from_queue (x, 0);
-      y = protect_from_queue (y, 0);
-
-      /* Now, if insn does accept these operands, put them into pseudos.  */
-      if (! (*insn_operand_predicate[icode][0])
-         (x, insn_operand_mode[icode][0]))
-       x = copy_to_mode_reg (insn_operand_mode[icode][0], x);
-
-      emit_insn (GEN_FCN (icode) (x));
-      return;
-    }
-
-  /* Handle compares for which there is a directly suitable insn.  */
-
-  if (cmp_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
-    {
-      int icode = (int) cmp_optab->handlers[(int) mode].insn_code;
-
-      emit_queue ();
-      x = protect_from_queue (x, 0);
-      y = protect_from_queue (y, 0);
-
-      /* Now, if insn doesn't accept these operands, put them into pseudos.  */
-      if (! (*insn_operand_predicate[icode][0])
-         (x, insn_operand_mode[icode][0]))
-       x = copy_to_mode_reg (insn_operand_mode[icode][0], x);
-
-      if (! (*insn_operand_predicate[icode][1])
-         (y, insn_operand_mode[icode][1]))
-       y = copy_to_mode_reg (insn_operand_mode[icode][1], y);
-
-      emit_insn (GEN_FCN (icode) (x, y));
-      return;
-    }
-
-  /* Try widening if we can find a direct insn that way.  */
-
-  if (class == MODE_INT || class == MODE_FLOAT || class == MODE_COMPLEX_FLOAT)
-    {
-      for (wider_mode = GET_MODE_WIDER_MODE (mode); wider_mode != VOIDmode;
-          wider_mode = GET_MODE_WIDER_MODE (wider_mode))
-       {
-         if (cmp_optab->handlers[(int) wider_mode].insn_code
-             != CODE_FOR_nothing)
-           {
-             x = protect_from_queue (x, 0);
-             y = protect_from_queue (y, 0);
-             x = convert_modes (wider_mode, mode, x, unsignedp);
-             y = convert_modes (wider_mode, mode, y, unsignedp);
-             emit_cmp_insn (x, y, comparison, NULL_RTX,
-                            wider_mode, unsignedp, align);
-             return;
-           }
-       }
-    }
+  *px = x;
+  *py = y;
+  if (can_compare_p (mode, purpose))
+    return;
 
   /* Handle a lib call just for the mode we are using.  */
 
-  if (cmp_optab->handlers[(int) mode].libfunc
-      && class != MODE_FLOAT)
+  if (cmp_optab->handlers[(int) mode].libfunc && class != MODE_FLOAT)
     {
       rtx libfunc = cmp_optab->handlers[(int) mode].libfunc;
       rtx result;
@@ -2920,18 +3042,113 @@ emit_cmp_insn (x, y, comparison, size, mode, unsignedp, align)
       /* Integer comparison returns a result that must be compared against 1,
         so that even if we do an unsigned compare afterward,
         there is still a value that can represent the result "less than".  */
-      emit_cmp_insn (result, const1_rtx,
-                    comparison, NULL_RTX, word_mode, unsignedp, 0);
+      *px = result;
+      *py = const1_rtx;
+      *pmode = word_mode;
       return;
     }
 
   if (class == MODE_FLOAT)
-    emit_float_lib_cmp (x, y, comparison);
+    prepare_float_lib_cmp (px, py, pcomparison, pmode, punsignedp);
 
   else
     abort ();
 }
 
+/* Before emitting an insn with code ICODE, make sure that X, which is going
+   to be used for operand OPNUM of the insn, is converted from mode MODE to
+   WIDER_MODE (UNSIGNEDP determines whether it is a unsigned conversion), and
+   that it is accepted by the operand predicate.  Return the new value.  */
+rtx
+prepare_operand (icode, x, opnum, mode, wider_mode, unsignedp)
+     int icode;
+     rtx x;
+     int opnum;
+     enum machine_mode mode, wider_mode;
+     int unsignedp;
+{
+  x = protect_from_queue (x, 0);
+
+  if (mode != wider_mode)
+    x = convert_modes (wider_mode, mode, x, unsignedp);
+
+  if (! (*insn_data[icode].operand[opnum].predicate)
+      (x, insn_data[icode].operand[opnum].mode))
+    x = copy_to_mode_reg (insn_data[icode].operand[opnum].mode, x);
+  return x;
+}
+
+/* Subroutine of emit_cmp_and_jump_insns; this function is called when we know
+   we can do the comparison.
+   The arguments are the same as for emit_cmp_and_jump_insns; but LABEL may
+   be NULL_RTX which indicates that only a comparison is to be generated.  */
+
+static void
+emit_cmp_and_jump_insn_1 (x, y, mode, comparison, unsignedp, label)
+     rtx x, y;
+     enum machine_mode mode;
+     enum rtx_code comparison;
+     int unsignedp;
+     rtx label;
+{
+  rtx test = gen_rtx_fmt_ee (comparison, mode, x, y);
+  enum mode_class class = GET_MODE_CLASS (mode);
+  enum machine_mode wider_mode = mode;
+
+  /* Try combined insns first.  */
+  do
+    {
+      enum insn_code icode;
+      PUT_MODE (test, wider_mode);
+
+      if (label)
+       {         
+         icode = cbranch_optab->handlers[(int)wider_mode].insn_code;
+         
+         if (icode != CODE_FOR_nothing
+             && (*insn_data[icode].operand[0].predicate) (test, wider_mode))
+           {
+             x = prepare_operand (icode, x, 1, mode, wider_mode, unsignedp);
+             y = prepare_operand (icode, y, 2, mode, wider_mode, unsignedp);
+             emit_jump_insn (GEN_FCN (icode) (test, x, y, label));
+             return;
+           }
+       }
+
+      /* Handle some compares against zero.  */
+      icode = (int) tst_optab->handlers[(int) wider_mode].insn_code;
+      if (y == CONST0_RTX (mode) && icode != CODE_FOR_nothing)
+       {
+         x = prepare_operand (icode, x, 0, mode, wider_mode, unsignedp);
+         emit_insn (GEN_FCN (icode) (x));
+         if (label)
+           emit_jump_insn ((*bcc_gen_fctn[(int) comparison]) (label));
+         return;
+       }
+
+      /* Handle compares for which there is a directly suitable insn.  */
+
+      icode = (int) cmp_optab->handlers[(int) wider_mode].insn_code;
+      if (icode != CODE_FOR_nothing)
+       {
+         x = prepare_operand (icode, x, 0, mode, wider_mode, unsignedp);
+         y = prepare_operand (icode, y, 1, mode, wider_mode, unsignedp);
+         emit_insn (GEN_FCN (icode) (x, y));
+         if (label)
+           emit_jump_insn ((*bcc_gen_fctn[(int) comparison]) (label));
+         return;
+       }
+
+      if (class != MODE_INT && class != MODE_FLOAT
+         && class != MODE_COMPLEX_FLOAT)
+       break;
+
+      wider_mode = GET_MODE_WIDER_MODE (wider_mode);
+    } while (wider_mode != VOIDmode);
+
+  abort ();
+}
+
 /* Generate code to compare X with Y so that the condition codes are
    set and to jump to LABEL if the condition is true.  If X is a
    constant and Y is not a constant, then the comparison is swapped to
@@ -2963,7 +3180,8 @@ emit_cmp_and_jump_insns (x, y, comparison, size, mode, unsignedp, align, label)
   rtx op0;
   rtx op1;
          
-  if (CONSTANT_P (x))
+  if ((CONSTANT_P (x) && ! CONSTANT_P (y))
+      || (GET_CODE (x) == CONST_INT && GET_CODE (y) != CONST_INT))
     {
       /* Swap operands and condition to ensure canonical RTL.  */
       op0 = y;
@@ -2975,39 +3193,48 @@ emit_cmp_and_jump_insns (x, y, comparison, size, mode, unsignedp, align, label)
       op0 = x;
       op1 = y;
     }
-  emit_cmp_insn (op0, op1, comparison, size, mode, unsignedp, align);
 
+#ifdef HAVE_cc0
+  /* If OP0 is still a constant, then both X and Y must be constants.  Force
+     X into a register to avoid aborting in emit_cmp_insn due to non-canonical
+     RTL.  */
+  if (CONSTANT_P (op0))
+    op0 = force_reg (mode, op0);
+#endif
+
+  emit_queue ();
   if (unsignedp)
     comparison = unsigned_condition (comparison);
-  emit_jump_insn ((*bcc_gen_fctn[(int) comparison]) (label));
+  prepare_cmp_insn (&op0, &op1, &comparison, size, &mode, &unsignedp, align,
+                   ccp_jump);
+  emit_cmp_and_jump_insn_1 (op0, op1, mode, comparison, unsignedp, label);
 }
 
-
-/* Nonzero if a compare of mode MODE can be done straightforwardly
-   (without splitting it into pieces).  */
-
-int
-can_compare_p (mode)
+/* Like emit_cmp_and_jump_insns, but generate only the comparison.  */
+void
+emit_cmp_insn (x, y, comparison, size, mode, unsignedp, align)
+     rtx x, y;
+     enum rtx_code comparison;
+     rtx size;
      enum machine_mode mode;
+     int unsignedp;
+     int align;
 {
-  do
-    {
-      if (cmp_optab->handlers[(int)mode].insn_code != CODE_FOR_nothing)
-       return 1;
-      mode = GET_MODE_WIDER_MODE (mode);
-    } while (mode != VOIDmode);
-
-  return 0;
+  emit_cmp_and_jump_insns (x, y, comparison, size, mode, unsignedp, align, 0);
 }
 \f
 /* Emit a library call comparison between floating point X and Y.
    COMPARISON is the rtl operator to compare with (EQ, NE, GT, etc.).  */
 
-void
-emit_float_lib_cmp (x, y, comparison)
-     rtx x, y;
-     enum rtx_code comparison;
+static void
+prepare_float_lib_cmp (px, py, pcomparison, pmode, punsignedp)
+     rtx *px, *py;
+     enum rtx_code *pcomparison;
+     enum machine_mode *pmode;
+     int *punsignedp;
 {
+  enum rtx_code comparison = *pcomparison;
+  rtx x = *px, y = *py;
   enum machine_mode mode = GET_MODE (x);
   rtx libfunc = 0;
   rtx result;
@@ -3175,9 +3402,9 @@ emit_float_lib_cmp (x, y, comparison)
            {
              x = protect_from_queue (x, 0);
              y = protect_from_queue (y, 0);
-             x = convert_to_mode (wider_mode, x, 0);
-             y = convert_to_mode (wider_mode, y, 0);
-             emit_float_lib_cmp (x, y, comparison);
+             *px = convert_to_mode (wider_mode, x, 0);
+             *py = convert_to_mode (wider_mode, y, 0);
+             prepare_float_lib_cmp (px, py, pcomparison, pmode, punsignedp);
              return;
            }
        }
@@ -3195,9 +3422,14 @@ emit_float_lib_cmp (x, y, comparison)
      the return register for a spill reg.  */
   result = gen_reg_rtx (word_mode);
   emit_move_insn (result, hard_libcall_value (word_mode));
-
-  emit_cmp_insn (result, const0_rtx, comparison,
-                NULL_RTX, word_mode, 0, 0);
+  *px = result;
+  *py = const0_rtx;
+  *pmode = word_mode;
+#ifdef FLOAT_LIB_COMPARE_RETURNS_BOOL
+  if (FLOAT_LIB_COMPARE_RETURNS_BOOL (mode, comparison))
+    *pcomparison = NE;
+#endif
+  *punsignedp = 0;
 }
 \f
 /* Generate code to indirectly jump to a location given in the rtx LOC.  */
@@ -3206,7 +3438,7 @@ void
 emit_indirect_jump (loc)
      rtx loc;
 {
-  if (! ((*insn_operand_predicate[(int)CODE_FOR_indirect_jump][0])
+  if (! ((*insn_data[(int)CODE_FOR_indirect_jump].operand[0].predicate)
         (loc, Pmode)))
     loc = copy_to_mode_reg (Pmode, loc);
 
@@ -3256,6 +3488,14 @@ emit_conditional_move (target, code, op0, op1, cmode, op2, op3, mode,
       code = swap_condition (code);
     }
 
+  /* get_condition will prefer to generate LT and GT even if the old
+     comparison was against zero, so undo that canonicalization here since
+     comparisons against zero are cheaper.  */
+  if (code == LT && GET_CODE (op1) == CONST_INT && INTVAL (op1) == 1)
+    code = LE, op1 = const0_rtx;
+  else if (code == GT && GET_CODE (op1) == CONST_INT && INTVAL (op1) == -1)
+    code = GE, op1 = const0_rtx;
+
   if (cmode == VOIDmode)
     cmode = GET_MODE (op0);
 
@@ -3298,17 +3538,17 @@ emit_conditional_move (target, code, op0, op1, cmode, op2, op3, mode,
 
   /* If the insn doesn't accept these operands, put them in pseudos.  */
 
-  if (! (*insn_operand_predicate[icode][0])
-      (subtarget, insn_operand_mode[icode][0]))
-    subtarget = gen_reg_rtx (insn_operand_mode[icode][0]);
+  if (! (*insn_data[icode].operand[0].predicate)
+      (subtarget, insn_data[icode].operand[0].mode))
+    subtarget = gen_reg_rtx (insn_data[icode].operand[0].mode);
 
-  if (! (*insn_operand_predicate[icode][2])
-      (op2, insn_operand_mode[icode][2]))
-    op2 = copy_to_mode_reg (insn_operand_mode[icode][2], op2);
+  if (! (*insn_data[icode].operand[2].predicate)
+      (op2, insn_data[icode].operand[2].mode))
+    op2 = copy_to_mode_reg (insn_data[icode].operand[2].mode, op2);
 
-  if (! (*insn_operand_predicate[icode][3])
-      (op3, insn_operand_mode[icode][3]))
-    op3 = copy_to_mode_reg (insn_operand_mode[icode][3], op3);
+  if (! (*insn_data[icode].operand[3].predicate)
+      (op3, insn_data[icode].operand[3].mode))
+    op3 = copy_to_mode_reg (insn_data[icode].operand[3].mode, op3);
 
   /* Everything should now be in the suitable form, so emit the compare insn
      and then the conditional move.  */
@@ -3370,9 +3610,12 @@ gen_add2_insn (x, y)
 {
   int icode = (int) add_optab->handlers[(int) GET_MODE (x)].insn_code; 
 
-  if (! (*insn_operand_predicate[icode][0]) (x, insn_operand_mode[icode][0])
-      || ! (*insn_operand_predicate[icode][1]) (x, insn_operand_mode[icode][1])
-      || ! (*insn_operand_predicate[icode][2]) (y, insn_operand_mode[icode][2]))
+  if (! ((*insn_data[icode].operand[0].predicate)
+        (x, insn_data[icode].operand[0].mode))
+      || ! ((*insn_data[icode].operand[1].predicate)
+           (x, insn_data[icode].operand[1].mode))
+      || ! ((*insn_data[icode].operand[2].predicate)
+           (y, insn_data[icode].operand[2].mode)))
     abort ();
 
   return (GEN_FCN (icode) (x, x, y));
@@ -3393,9 +3636,12 @@ gen_sub2_insn (x, y)
 {
   int icode = (int) sub_optab->handlers[(int) GET_MODE (x)].insn_code; 
 
-  if (! (*insn_operand_predicate[icode][0]) (x, insn_operand_mode[icode][0])
-      || ! (*insn_operand_predicate[icode][1]) (x, insn_operand_mode[icode][1])
-      || ! (*insn_operand_predicate[icode][2]) (y, insn_operand_mode[icode][2]))
+  if (! ((*insn_data[icode].operand[0].predicate)
+        (x, insn_data[icode].operand[0].mode))
+      || ! ((*insn_data[icode].operand[1].predicate)
+           (x, insn_data[icode].operand[1].mode))
+      || ! ((*insn_data[icode].operand[2].predicate)
+           (y, insn_data[icode].operand[2].mode)))
     abort ();
 
   return (GEN_FCN (icode) (x, x, y));
@@ -3654,8 +3900,8 @@ expand_float (to, from, unsignedp)
              do_pending_stack_adjust ();
 
              /* Test whether the sign bit is set.  */
-             emit_cmp_insn (from, const0_rtx, GE, NULL_RTX, imode, 0, 0);
-             emit_jump_insn (gen_blt (neglabel));
+             emit_cmp_and_jump_insns (from, const0_rtx, LT, NULL_RTX, imode,
+                                      0, 0, neglabel);
 
              /* The sign bit is not set.  Convert as signed.  */
              expand_float (target, from, 0);
@@ -3945,6 +4191,7 @@ expand_fix (to, from, unsignedp)
                                                  GET_MODE (to),
                                                  copy_rtx (from)));
            }
+
          return;
        }
 #endif
@@ -4089,10 +4336,10 @@ init_libfuncs (optable, first_mode, last_mode, opname, suffix)
   for (mode = first_mode; (int) mode <= (int) last_mode;
        mode = (enum machine_mode) ((int) mode + 1))
     {
-      register char *mname = mode_name[(int) mode];
+      register const char *mname = GET_MODE_NAME(mode);
       register unsigned mname_len = strlen (mname);
       register char *libfunc_name
-       = (char *) xmalloc (2 + opname_len + mname_len + 1 + 1);
+       = ggc_alloc_string (NULL, 2 + opname_len + mname_len + 1 + 1);
       register char *p;
       register const char *q;
 
@@ -4102,9 +4349,10 @@ init_libfuncs (optable, first_mode, last_mode, opname, suffix)
       for (q = opname; *q; )
        *p++ = *q++;
       for (q = mname; *q; q++)
-       *p++ = tolower ((unsigned char)*q);
+       *p++ = TOLOWER (*q);
       *p++ = suffix;
       *p++ = '\0';
+
       optable->handlers[(int) mode].libfunc
        = gen_rtx_SYMBOL_REF (Pmode, libfunc_name);
     }
@@ -4138,6 +4386,27 @@ init_floating_libfuncs (optable, opname, suffix)
   init_libfuncs (optable, SFmode, TFmode, opname, suffix);
 }
 
+rtx
+init_one_libfunc (name)
+     register const char *name;
+{
+  if (ggc_p)
+    name = ggc_alloc_string (name, -1);
+  return gen_rtx_SYMBOL_REF (Pmode, name);
+}
+
+/* Mark ARG (which is really an OPTAB *) for GC.  */
+
+void
+mark_optab (arg)
+     void *arg;
+{
+  optab o = *(optab *) arg;
+  int i;
+
+  for (i = 0; i < NUM_MACHINE_MODES; ++i)
+    ggc_mark_rtx (o->handlers[i].libfunc);
+}
 
 /* Call this once to initialize the contents of the optabs
    appropriately for the current target machine.  */
@@ -4222,6 +4491,9 @@ init_optabs ()
   sin_optab = init_optab (UNKNOWN);
   cos_optab = init_optab (UNKNOWN);
   strlen_optab = init_optab (UNKNOWN);
+  cbranch_optab = init_optab (UNKNOWN);
+  cmov_optab = init_optab (UNKNOWN);
+  cstore_optab = init_optab (UNKNOWN);
 
   for (i = 0; i < NUM_MACHINE_MODES; i++)
     {
@@ -4244,10 +4516,6 @@ init_optabs ()
       fixtrunctab[i][j][1] = fixtrunctab[i][j][0];
 #endif
 
-#ifdef EXTRA_CC_MODES
-  init_mov_optab ();
-#endif
-
   /* Initialize the optabs with the names of the library functions.  */
   init_integral_libfuncs (add_optab, "add", '3');
   init_floating_libfuncs (add_optab, "add", '3');
@@ -4287,189 +4555,189 @@ init_optabs ()
 
 #ifdef MULSI3_LIBCALL
   smul_optab->handlers[(int) SImode].libfunc
-    = gen_rtx_SYMBOL_REF (Pmode, MULSI3_LIBCALL);
+    = init_one_libfunc (MULSI3_LIBCALL);
 #endif
 #ifdef MULDI3_LIBCALL
   smul_optab->handlers[(int) DImode].libfunc
-    = gen_rtx_SYMBOL_REF (Pmode, MULDI3_LIBCALL);
+    = init_one_libfunc (MULDI3_LIBCALL);
 #endif
 
 #ifdef DIVSI3_LIBCALL
   sdiv_optab->handlers[(int) SImode].libfunc
-    = gen_rtx_SYMBOL_REF (Pmode, DIVSI3_LIBCALL);
+    = init_one_libfunc (DIVSI3_LIBCALL);
 #endif
 #ifdef DIVDI3_LIBCALL
   sdiv_optab->handlers[(int) DImode].libfunc
-    = gen_rtx_SYMBOL_REF (Pmode, DIVDI3_LIBCALL);
+    = init_one_libfunc (DIVDI3_LIBCALL);
 #endif
 
 #ifdef UDIVSI3_LIBCALL
   udiv_optab->handlers[(int) SImode].libfunc
-    = gen_rtx_SYMBOL_REF (Pmode, UDIVSI3_LIBCALL);
+    = init_one_libfunc (UDIVSI3_LIBCALL);
 #endif
 #ifdef UDIVDI3_LIBCALL
   udiv_optab->handlers[(int) DImode].libfunc
-    = gen_rtx_SYMBOL_REF (Pmode, UDIVDI3_LIBCALL);
+    = init_one_libfunc (UDIVDI3_LIBCALL);
 #endif
 
 #ifdef MODSI3_LIBCALL
   smod_optab->handlers[(int) SImode].libfunc
-    = gen_rtx_SYMBOL_REF (Pmode, MODSI3_LIBCALL);
+    = init_one_libfunc (MODSI3_LIBCALL);
 #endif
 #ifdef MODDI3_LIBCALL
   smod_optab->handlers[(int) DImode].libfunc
-    = gen_rtx_SYMBOL_REF (Pmode, MODDI3_LIBCALL);
+    = init_one_libfunc (MODDI3_LIBCALL);
 #endif
 
 #ifdef UMODSI3_LIBCALL
   umod_optab->handlers[(int) SImode].libfunc
-    = gen_rtx_SYMBOL_REF (Pmode, UMODSI3_LIBCALL);
+    = init_one_libfunc (UMODSI3_LIBCALL);
 #endif
 #ifdef UMODDI3_LIBCALL
   umod_optab->handlers[(int) DImode].libfunc
-    = gen_rtx_SYMBOL_REF (Pmode, UMODDI3_LIBCALL);
+    = init_one_libfunc (UMODDI3_LIBCALL);
 #endif
 
   /* Use cabs for DC complex abs, since systems generally have cabs.
      Don't define any libcall for SCmode, so that cabs will be used.  */
   abs_optab->handlers[(int) DCmode].libfunc
-    = gen_rtx_SYMBOL_REF (Pmode, "cabs");
+    = init_one_libfunc ("cabs");
 
   /* The ffs function operates on `int'.  */
 #ifndef INT_TYPE_SIZE
 #define INT_TYPE_SIZE BITS_PER_WORD
 #endif
-  ffs_optab->handlers[(int) mode_for_size (INT_TYPE_SIZE, MODE_INT, 0)] .libfunc
-    = gen_rtx_SYMBOL_REF (Pmode, "ffs");
-
-  extendsfdf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__extendsfdf2");
-  extendsfxf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__extendsfxf2");
-  extendsftf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__extendsftf2");
-  extenddfxf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__extenddfxf2");
-  extenddftf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__extenddftf2");
-
-  truncdfsf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__truncdfsf2");
-  truncxfsf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__truncxfsf2");
-  trunctfsf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__trunctfsf2");
-  truncxfdf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__truncxfdf2");
-  trunctfdf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__trunctfdf2");
-
-  memcpy_libfunc = gen_rtx_SYMBOL_REF (Pmode, "memcpy");
-  bcopy_libfunc = gen_rtx_SYMBOL_REF (Pmode, "bcopy");
-  memcmp_libfunc = gen_rtx_SYMBOL_REF (Pmode, "memcmp");
-  bcmp_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__gcc_bcmp");
-  memset_libfunc = gen_rtx_SYMBOL_REF (Pmode, "memset");
-  bzero_libfunc = gen_rtx_SYMBOL_REF (Pmode, "bzero");
-
-  throw_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__throw");
-  rethrow_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__rethrow");
-  sjthrow_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__sjthrow");
-  sjpopnthrow_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__sjpopnthrow");
-  terminate_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__terminate");
-  eh_rtime_match_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__eh_rtime_match");
+  ffs_optab->handlers[(int) mode_for_size (INT_TYPE_SIZE, MODE_INT, 0)].libfunc
+    = init_one_libfunc ("ffs");
+
+  extendsfdf2_libfunc = init_one_libfunc ("__extendsfdf2");
+  extendsfxf2_libfunc = init_one_libfunc ("__extendsfxf2");
+  extendsftf2_libfunc = init_one_libfunc ("__extendsftf2");
+  extenddfxf2_libfunc = init_one_libfunc ("__extenddfxf2");
+  extenddftf2_libfunc = init_one_libfunc ("__extenddftf2");
+
+  truncdfsf2_libfunc = init_one_libfunc ("__truncdfsf2");
+  truncxfsf2_libfunc = init_one_libfunc ("__truncxfsf2");
+  trunctfsf2_libfunc = init_one_libfunc ("__trunctfsf2");
+  truncxfdf2_libfunc = init_one_libfunc ("__truncxfdf2");
+  trunctfdf2_libfunc = init_one_libfunc ("__trunctfdf2");
+
+  memcpy_libfunc = init_one_libfunc ("memcpy");
+  bcopy_libfunc = init_one_libfunc ("bcopy");
+  memcmp_libfunc = init_one_libfunc ("memcmp");
+  bcmp_libfunc = init_one_libfunc ("__gcc_bcmp");
+  memset_libfunc = init_one_libfunc ("memset");
+  bzero_libfunc = init_one_libfunc ("bzero");
+
+  throw_libfunc = init_one_libfunc ("__throw");
+  rethrow_libfunc = init_one_libfunc ("__rethrow");
+  sjthrow_libfunc = init_one_libfunc ("__sjthrow");
+  sjpopnthrow_libfunc = init_one_libfunc ("__sjpopnthrow");
+  terminate_libfunc = init_one_libfunc ("__terminate");
+  eh_rtime_match_libfunc = init_one_libfunc ("__eh_rtime_match");
 #ifndef DONT_USE_BUILTIN_SETJMP
-  setjmp_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__builtin_setjmp");
-  longjmp_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__builtin_longjmp");
+  setjmp_libfunc = init_one_libfunc ("__builtin_setjmp");
+  longjmp_libfunc = init_one_libfunc ("__builtin_longjmp");
 #else
-  setjmp_libfunc = gen_rtx_SYMBOL_REF (Pmode, "setjmp");
-  longjmp_libfunc = gen_rtx_SYMBOL_REF (Pmode, "longjmp");
+  setjmp_libfunc = init_one_libfunc ("setjmp");
+  longjmp_libfunc = init_one_libfunc ("longjmp");
 #endif
 
-  eqhf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__eqhf2");
-  nehf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__nehf2");
-  gthf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__gthf2");
-  gehf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__gehf2");
-  lthf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__lthf2");
-  lehf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__lehf2");
-
-  eqsf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__eqsf2");
-  nesf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__nesf2");
-  gtsf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__gtsf2");
-  gesf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__gesf2");
-  ltsf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__ltsf2");
-  lesf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__lesf2");
-
-  eqdf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__eqdf2");
-  nedf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__nedf2");
-  gtdf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__gtdf2");
-  gedf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__gedf2");
-  ltdf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__ltdf2");
-  ledf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__ledf2");
-
-  eqxf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__eqxf2");
-  nexf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__nexf2");
-  gtxf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__gtxf2");
-  gexf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__gexf2");
-  ltxf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__ltxf2");
-  lexf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__lexf2");
-
-  eqtf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__eqtf2");
-  netf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__netf2");
-  gttf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__gttf2");
-  getf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__getf2");
-  lttf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__lttf2");
-  letf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__letf2");
-
-  floatsisf_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__floatsisf");
-  floatdisf_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__floatdisf");
-  floattisf_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__floattisf");
-
-  floatsidf_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__floatsidf");
-  floatdidf_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__floatdidf");
-  floattidf_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__floattidf");
-
-  floatsixf_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__floatsixf");
-  floatdixf_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__floatdixf");
-  floattixf_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__floattixf");
-
-  floatsitf_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__floatsitf");
-  floatditf_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__floatditf");
-  floattitf_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__floattitf");
-
-  fixsfsi_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__fixsfsi");
-  fixsfdi_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__fixsfdi");
-  fixsfti_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__fixsfti");
-
-  fixdfsi_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__fixdfsi");
-  fixdfdi_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__fixdfdi");
-  fixdfti_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__fixdfti");
-
-  fixxfsi_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__fixxfsi");
-  fixxfdi_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__fixxfdi");
-  fixxfti_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__fixxfti");
-
-  fixtfsi_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__fixtfsi");
-  fixtfdi_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__fixtfdi");
-  fixtfti_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__fixtfti");
-
-  fixunssfsi_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__fixunssfsi");
-  fixunssfdi_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__fixunssfdi");
-  fixunssfti_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__fixunssfti");
-
-  fixunsdfsi_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__fixunsdfsi");
-  fixunsdfdi_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__fixunsdfdi");
-  fixunsdfti_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__fixunsdfti");
-
-  fixunsxfsi_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__fixunsxfsi");
-  fixunsxfdi_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__fixunsxfdi");
-  fixunsxfti_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__fixunsxfti");
-
-  fixunstfsi_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__fixunstfsi");
-  fixunstfdi_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__fixunstfdi");
-  fixunstfti_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__fixunstfti");
+  eqhf2_libfunc = init_one_libfunc ("__eqhf2");
+  nehf2_libfunc = init_one_libfunc ("__nehf2");
+  gthf2_libfunc = init_one_libfunc ("__gthf2");
+  gehf2_libfunc = init_one_libfunc ("__gehf2");
+  lthf2_libfunc = init_one_libfunc ("__lthf2");
+  lehf2_libfunc = init_one_libfunc ("__lehf2");
+
+  eqsf2_libfunc = init_one_libfunc ("__eqsf2");
+  nesf2_libfunc = init_one_libfunc ("__nesf2");
+  gtsf2_libfunc = init_one_libfunc ("__gtsf2");
+  gesf2_libfunc = init_one_libfunc ("__gesf2");
+  ltsf2_libfunc = init_one_libfunc ("__ltsf2");
+  lesf2_libfunc = init_one_libfunc ("__lesf2");
+
+  eqdf2_libfunc = init_one_libfunc ("__eqdf2");
+  nedf2_libfunc = init_one_libfunc ("__nedf2");
+  gtdf2_libfunc = init_one_libfunc ("__gtdf2");
+  gedf2_libfunc = init_one_libfunc ("__gedf2");
+  ltdf2_libfunc = init_one_libfunc ("__ltdf2");
+  ledf2_libfunc = init_one_libfunc ("__ledf2");
+
+  eqxf2_libfunc = init_one_libfunc ("__eqxf2");
+  nexf2_libfunc = init_one_libfunc ("__nexf2");
+  gtxf2_libfunc = init_one_libfunc ("__gtxf2");
+  gexf2_libfunc = init_one_libfunc ("__gexf2");
+  ltxf2_libfunc = init_one_libfunc ("__ltxf2");
+  lexf2_libfunc = init_one_libfunc ("__lexf2");
+
+  eqtf2_libfunc = init_one_libfunc ("__eqtf2");
+  netf2_libfunc = init_one_libfunc ("__netf2");
+  gttf2_libfunc = init_one_libfunc ("__gttf2");
+  getf2_libfunc = init_one_libfunc ("__getf2");
+  lttf2_libfunc = init_one_libfunc ("__lttf2");
+  letf2_libfunc = init_one_libfunc ("__letf2");
+
+  floatsisf_libfunc = init_one_libfunc ("__floatsisf");
+  floatdisf_libfunc = init_one_libfunc ("__floatdisf");
+  floattisf_libfunc = init_one_libfunc ("__floattisf");
+
+  floatsidf_libfunc = init_one_libfunc ("__floatsidf");
+  floatdidf_libfunc = init_one_libfunc ("__floatdidf");
+  floattidf_libfunc = init_one_libfunc ("__floattidf");
+
+  floatsixf_libfunc = init_one_libfunc ("__floatsixf");
+  floatdixf_libfunc = init_one_libfunc ("__floatdixf");
+  floattixf_libfunc = init_one_libfunc ("__floattixf");
+
+  floatsitf_libfunc = init_one_libfunc ("__floatsitf");
+  floatditf_libfunc = init_one_libfunc ("__floatditf");
+  floattitf_libfunc = init_one_libfunc ("__floattitf");
+
+  fixsfsi_libfunc = init_one_libfunc ("__fixsfsi");
+  fixsfdi_libfunc = init_one_libfunc ("__fixsfdi");
+  fixsfti_libfunc = init_one_libfunc ("__fixsfti");
+
+  fixdfsi_libfunc = init_one_libfunc ("__fixdfsi");
+  fixdfdi_libfunc = init_one_libfunc ("__fixdfdi");
+  fixdfti_libfunc = init_one_libfunc ("__fixdfti");
+
+  fixxfsi_libfunc = init_one_libfunc ("__fixxfsi");
+  fixxfdi_libfunc = init_one_libfunc ("__fixxfdi");
+  fixxfti_libfunc = init_one_libfunc ("__fixxfti");
+
+  fixtfsi_libfunc = init_one_libfunc ("__fixtfsi");
+  fixtfdi_libfunc = init_one_libfunc ("__fixtfdi");
+  fixtfti_libfunc = init_one_libfunc ("__fixtfti");
+
+  fixunssfsi_libfunc = init_one_libfunc ("__fixunssfsi");
+  fixunssfdi_libfunc = init_one_libfunc ("__fixunssfdi");
+  fixunssfti_libfunc = init_one_libfunc ("__fixunssfti");
+
+  fixunsdfsi_libfunc = init_one_libfunc ("__fixunsdfsi");
+  fixunsdfdi_libfunc = init_one_libfunc ("__fixunsdfdi");
+  fixunsdfti_libfunc = init_one_libfunc ("__fixunsdfti");
+
+  fixunsxfsi_libfunc = init_one_libfunc ("__fixunsxfsi");
+  fixunsxfdi_libfunc = init_one_libfunc ("__fixunsxfdi");
+  fixunsxfti_libfunc = init_one_libfunc ("__fixunsxfti");
+
+  fixunstfsi_libfunc = init_one_libfunc ("__fixunstfsi");
+  fixunstfdi_libfunc = init_one_libfunc ("__fixunstfdi");
+  fixunstfti_libfunc = init_one_libfunc ("__fixunstfti");
 
   /* For check-memory-usage.  */
-  chkr_check_addr_libfunc = gen_rtx_SYMBOL_REF (Pmode, "chkr_check_addr");
-  chkr_set_right_libfunc = gen_rtx_SYMBOL_REF (Pmode, "chkr_set_right");
-  chkr_copy_bitmap_libfunc = gen_rtx_SYMBOL_REF (Pmode, "chkr_copy_bitmap");
-  chkr_check_exec_libfunc = gen_rtx_SYMBOL_REF (Pmode, "chkr_check_exec");
-  chkr_check_str_libfunc = gen_rtx_SYMBOL_REF (Pmode, "chkr_check_str");
+  chkr_check_addr_libfunc = init_one_libfunc ("chkr_check_addr");
+  chkr_set_right_libfunc = init_one_libfunc ("chkr_set_right");
+  chkr_copy_bitmap_libfunc = init_one_libfunc ("chkr_copy_bitmap");
+  chkr_check_exec_libfunc = init_one_libfunc ("chkr_check_exec");
+  chkr_check_str_libfunc = init_one_libfunc ("chkr_check_str");
 
   /* For function entry/exit instrumentation.  */
   profile_function_entry_libfunc
-    = gen_rtx_SYMBOL_REF (Pmode, "__cyg_profile_func_enter");
+    = init_one_libfunc ("__cyg_profile_func_enter");
   profile_function_exit_libfunc
-    = gen_rtx_SYMBOL_REF (Pmode, "__cyg_profile_func_exit");
+    = init_one_libfunc ("__cyg_profile_func_exit");
 
 #ifdef HAVE_conditional_trap
   init_traps ();
@@ -4479,6 +4747,10 @@ init_optabs ()
   /* Allow the target to add more libcalls or rename some, etc.  */
   INIT_TARGET_OPTABS;
 #endif
+
+  /* Add these GC roots.  */
+  ggc_add_root (optab_table, OTI_MAX, sizeof(optab), mark_optab);
+  ggc_add_rtx_root (libfunc_table, LTI_MAX);
 }
 \f
 #ifdef BROKEN_LDEXP
@@ -4502,16 +4774,17 @@ ldexp(x,n)
 /* The insn generating function can not take an rtx_code argument.
    TRAP_RTX is used as an rtx argument.  Its code is replaced with
    the code to be used in the trap insn and all other fields are
-   ignored.
-
-   ??? Will need to change to support garbage collection.  */
+   ignored.  */
 static rtx trap_rtx;
 
 static void
 init_traps ()
 {
   if (HAVE_conditional_trap)
-    trap_rtx = gen_rtx_fmt_ee (EQ, VOIDmode, NULL_RTX, NULL_RTX);
+    {
+      trap_rtx = gen_rtx_fmt_ee (EQ, VOIDmode, NULL_RTX, NULL_RTX);
+      ggc_add_rtx_root (&trap_rtx, 1);
+    }
 }
 #endif
 
@@ -4533,11 +4806,17 @@ gen_cond_trap (code, op1, op2, tcode)
       && cmp_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
     {
       rtx insn;
+      start_sequence();
       emit_insn (GEN_FCN (cmp_optab->handlers[(int) mode].insn_code) (op1, op2));
       PUT_CODE (trap_rtx, code);
       insn = gen_conditional_trap (trap_rtx, tcode);
       if (insn)
-       return insn;
+       {
+         emit_insn (insn);
+         insn = gen_sequence ();
+       }
+      end_sequence();
+      return insn;
     }
 #endif