OSDN Git Service

Remove inclusion of loop.h
[pf3gnuchains/gcc-fork.git] / gcc / config / c4x / c4x.c
index 9eaa871..aeacecb 100644 (file)
@@ -1,5 +1,5 @@
 /* Subroutines for assembler code output on the TMS320C[34]x
-   Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2003
+   Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004
    Free Software Foundation, Inc.
 
    Contributed by Michael Hayes (m.hayes@elec.canterbury.ac.nz)
@@ -42,7 +42,6 @@ Boston, MA 02111-1307, USA.  */
 #include "optabs.h"
 #include "libfuncs.h"
 #include "flags.h"
-#include "loop.h"
 #include "recog.h"
 #include "ggc.h"
 #include "cpplib.h"
@@ -50,6 +49,7 @@ Boston, MA 02111-1307, USA.  */
 #include "tm_p.h"
 #include "target.h"
 #include "target-def.h"
+#include "langhooks.h"
 
 rtx smulhi3_libfunc;
 rtx umulhi3_libfunc;
@@ -194,12 +194,15 @@ static int c4x_label_ref_used_p (rtx, rtx);
 static tree c4x_handle_fntype_attribute (tree *, tree, tree, int, bool *);
 const struct attribute_spec c4x_attribute_table[];
 static void c4x_insert_attributes (tree, tree *);
-static void c4x_asm_named_section (const char *, unsigned int);
+static void c4x_asm_named_section (const char *, unsigned int, tree);
 static int c4x_adjust_cost (rtx, rtx, rtx, int);
 static void c4x_globalize_label (FILE *, const char *);
 static bool c4x_rtx_costs (rtx, int, int, int *);
 static int c4x_address_cost (rtx);
 static void c4x_init_libfuncs (void);
+static void c4x_external_libcall (rtx);
+static rtx c4x_struct_value_rtx (tree, int);
+static tree c4x_gimplify_va_arg_expr (tree, tree, tree *, tree *);
 \f
 /* Initialize the GCC target structure.  */
 #undef TARGET_ASM_BYTE_OP
@@ -215,6 +218,9 @@ static void c4x_init_libfuncs (void);
 #undef TARGET_ASM_FILE_END
 #define TARGET_ASM_FILE_END c4x_file_end
 
+#undef TARGET_ASM_EXTERNAL_LIBCALL
+#define TARGET_ASM_EXTERNAL_LIBCALL c4x_external_libcall
+
 #undef TARGET_ATTRIBUTE_TABLE
 #define TARGET_ATTRIBUTE_TABLE c4x_attribute_table
 
@@ -244,6 +250,12 @@ static void c4x_init_libfuncs (void);
 #undef TARGET_INIT_LIBFUNCS
 #define TARGET_INIT_LIBFUNCS c4x_init_libfuncs
 
+#undef TARGET_STRUCT_VALUE_RTX
+#define TARGET_STRUCT_VALUE_RTX c4x_struct_value_rtx
+
+#undef TARGET_GIMPLIFY_VA_ARG_EXPR
+#define TARGET_GIMPLIFY_VA_ARG_EXPR c4x_gimplify_va_arg_expr
+
 struct gcc_target targetm = TARGET_INITIALIZER;
 \f
 /* Override command line options.
@@ -454,7 +466,7 @@ c4x_hard_regno_mode_ok (unsigned int regno, enum machine_mode mode)
 int
 c4x_hard_regno_rename_ok (unsigned int regno1, unsigned int regno2)
 {
-  /* We can not copy call saved registers from mode QI into QF or from
+  /* We cannot copy call saved registers from mode QI into QF or from
      mode QF into QI.  */
   if (IS_FLOAT_CALL_SAVED_REGNO (regno1) && IS_INT_CALL_SAVED_REGNO (regno2))
     return 0;
@@ -560,7 +572,7 @@ c4x_init_cumulative_args (CUMULATIVE_ARGS *cum, tree fntype, rtx libname)
 
          if ((mode = TYPE_MODE (type)))
            {
-             if (! MUST_PASS_IN_STACK (mode, type))
+             if (! targetm.calls.must_pass_in_stack (mode, type))
                {
                  /* Look for float, double, or long double argument.  */
                  if (mode == QFmode || mode == HFmode)
@@ -597,7 +609,7 @@ c4x_function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode,
   if (! TARGET_MEMPARM 
       && named
       && type
-      && ! MUST_PASS_IN_STACK (mode, type))
+      && ! targetm.calls.must_pass_in_stack (mode, type))
     {
       /* Look for float, double, or long double argument.  */
       if (mode == QFmode || mode == HFmode)
@@ -663,7 +675,7 @@ c4x_function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
   if (! TARGET_MEMPARM 
       && named 
       && type
-      && ! MUST_PASS_IN_STACK (mode, type))
+      && ! targetm.calls.must_pass_in_stack (mode, type))
     {
       /* Look for float, double, or long double argument.  */
       if (mode == QFmode || mode == HFmode)
@@ -711,16 +723,28 @@ c4x_function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
 
 /* C[34]x arguments grow in weird ways (downwards) that the standard
    varargs stuff can't handle..  */
-rtx
-c4x_va_arg (tree valist, tree type)
+
+static tree
+c4x_gimplify_va_arg_expr (tree valist, tree type,
+                         tree *pre_p ATTRIBUTE_UNUSED,
+                         tree *post_p ATTRIBUTE_UNUSED)
 {
   tree t;
+  bool indirect;
+
+  indirect = pass_by_reference (NULL, TYPE_MODE (type), type, false);
+  if (indirect)
+    type = build_pointer_type (type);
 
   t = build (PREDECREMENT_EXPR, TREE_TYPE (valist), valist,
-            build_int_2 (int_size_in_bytes (type), 0));
-  TREE_SIDE_EFFECTS (t) = 1;
+            build_int_cst (NULL_TREE, int_size_in_bytes (type)));
+  t = fold_convert (build_pointer_type (type), t);
+  t = build_fold_indirect_ref (t);
+
+  if (indirect)
+    t = build_fold_indirect_ref (t);
 
-  return expand_expr (t, NULL_RTX, Pmode, EXPAND_NORMAL);
+  return t;
 }
 
 
@@ -758,7 +782,7 @@ c4x_leaf_function_p (void)
      to save/restore the registers we actually use.
      For the global variable leaf_function to be set, we need
      to define LEAF_REGISTERS and all that it entails.
-     Let's check ourselves...   */
+     Let's check ourselves....  */
 
   if (lookup_attribute ("leaf_pretend",
                        TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl))))
@@ -791,18 +815,20 @@ c4x_naked_function_p (void)
 int
 c4x_interrupt_function_p (void)
 {
+  const char *cfun_name;
   if (lookup_attribute ("interrupt",
                        TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl))))
     return 1;
 
   /* Look for TI style c_intnn.  */
-  return current_function_name[0] == 'c'
-    && current_function_name[1] == '_'
-    && current_function_name[2] == 'i'
-    && current_function_name[3] == 'n' 
-    && current_function_name[4] == 't'
-    && ISDIGIT (current_function_name[5])
-    && ISDIGIT (current_function_name[6]);
+  cfun_name = current_function_name ();
+  return cfun_name[0] == 'c'
+    && cfun_name[1] == '_'
+    && cfun_name[2] == 'i'
+    && cfun_name[3] == 'n' 
+    && cfun_name[4] == 't'
+    && ISDIGIT (cfun_name[5])
+    && ISDIGIT (cfun_name[6]);
 }
 
 void
@@ -846,7 +872,7 @@ c4x_expand_prologue (void)
             storage!  */
          if (size > 32767)
            error ("ISR %s requires %d words of local vars, max is 32767",
-                  current_function_name, size);
+                  current_function_name (), size);
 
          insn = emit_insn (gen_addqi3 (gen_rtx_REG (QImode, SP_REGNO),
                                        gen_rtx_REG (QImode, SP_REGNO),
@@ -919,7 +945,7 @@ c4x_expand_prologue (void)
        }
       else
        {
-         /* If we use ar3, we need to push it.   */
+         /* If we use ar3, we need to push it.  */
          dont_push_ar3 = 0;
          if ((size != 0) || (current_function_args_size != 0))
            {
@@ -1067,7 +1093,7 @@ c4x_expand_epilogue(void)
                                         gen_rtx_PLUS 
                                         (QImode, gen_rtx_REG (QImode,
                                                               AR3_REGNO),
-                                         GEN_INT(-1)))));
+                                         constm1_rtx))));
              RTX_FRAME_RELATED_P (insn) = 1;
              
              /* We already have the return value and the fp,
@@ -1234,10 +1260,11 @@ c4x_emit_move_sequence (rtx *operands, enum machine_mode mode)
       && dp_reg_operand (XEXP (op1, 0), mode))
     {
       /* expand_increment will sometimes create a LO_SUM immediate
-        address.  */
+        address.  Undo this silliness.  */
       op1 = XEXP (op1, 1);
     }
-  else if (symbolic_address_operand (op1, mode))
+  
+  if (symbolic_address_operand (op1, mode))
     {
       if (TARGET_LOAD_ADDRESS)
        {
@@ -1379,13 +1406,13 @@ c4x_emit_libcall (rtx libcall, enum rtx_code code,
     case 2:
       ret = emit_library_call_value (libcall, NULL_RTX, 1, dmode, 1,
                                     operands[1], smode);
-      equiv = gen_rtx (code, dmode, operands[1]);
+      equiv = gen_rtx_fmt_e (code, dmode, operands[1]);
       break;
 
     case 3:
       ret = emit_library_call_value (libcall, NULL_RTX, 1, dmode, 2,
                                     operands[1], smode, operands[2], smode);
-      equiv = gen_rtx (code, dmode, operands[1], operands[2]);
+      equiv = gen_rtx_fmt_ee (code, dmode, operands[1], operands[2]);
       break;
 
     default:
@@ -1420,8 +1447,8 @@ c4x_emit_libcall_mulhi (rtx libcall, enum rtx_code code,
   equiv = gen_rtx_TRUNCATE (mode,
                    gen_rtx_LSHIFTRT (HImode,
                             gen_rtx_MULT (HImode,
-                                     gen_rtx (code, HImode, operands[1]),
-                                     gen_rtx (code, HImode, operands[2])),
+                                     gen_rtx_fmt_e (code, HImode, operands[1]),
+                                     gen_rtx_fmt_e (code, HImode, operands[2])),
                                      GEN_INT (32)));
   insns = get_insns ();
   end_sequence ();
@@ -1430,7 +1457,7 @@ c4x_emit_libcall_mulhi (rtx libcall, enum rtx_code code,
 
 
 int
-c4x_check_legit_addr (enum machine_mode mode, rtx addr, int strict)
+c4x_legitimate_address_p (enum machine_mode mode, rtx addr, int strict)
 {
   rtx base = NULL_RTX;         /* Base register (AR0-AR7).  */
   rtx indx = NULL_RTX;         /* Index register (IR0,IR1).  */
@@ -1469,7 +1496,9 @@ c4x_check_legit_addr (enum machine_mode mode, rtx addr, int strict)
            || (GET_CODE (op1) != PLUS && GET_CODE (op1) != MINUS))
          return 0;
        base = XEXP (op1, 0);
-       if (base != op0)
+       if (! REG_P (base))
+           return 0;
+       if (REGNO (base) != REGNO (op0))
          return 0;
        if (REG_P (XEXP (op1, 1)))
          indx = XEXP (op1, 1);
@@ -2230,7 +2259,7 @@ c4x_rptb_nop_p (rtx insn)
   Before we can create a repeat block looping instruction we have to
   verify that there are no jumps outside the loop and no jumps outside
   the loop go into this loop. This can happen in the basic blocks reorder
-  pass. The C4x cpu can not handle this.  */
+  pass. The C4x cpu cannot handle this.  */
 
 static int
 c4x_label_ref_used_p (rtx x, rtx code_label)
@@ -2275,7 +2304,7 @@ c4x_rptb_valid_p (rtx insn, rtx start_label)
     if (insn == start_label)
       break;
 
-  /* Note found then we can not use a rptb or rpts.  The label was
+  /* Note found then we cannot use a rptb or rpts.  The label was
      probably moved by the basic block reorder pass.  */
   if (! insn)
     return 0;
@@ -2332,7 +2361,7 @@ c4x_rptb_insert (rtx insn)
   rtx count_reg;
 
   /* If the count register has not been allocated to RC, say if
-     there is a movstr pattern in the loop, then do not insert a
+     there is a movmem pattern in the loop, then do not insert a
      RPTB instruction.  Instead we emit a decrement and branch
      at the end of the loop.  */
   count_reg = XEXP (XEXP (SET_SRC (XVECEXP (PATTERN (insn), 0, 0)), 0), 0);
@@ -2344,10 +2373,10 @@ c4x_rptb_insert (rtx insn)
   
   if (! c4x_rptb_valid_p (insn, start_label))
     {
-      /* We can not use the rptb insn.  Replace it so reorg can use
+      /* We cannot use the rptb insn.  Replace it so reorg can use
          the delay slots of the jump insn.  */
-      emit_insn_before (gen_addqi3 (count_reg, count_reg, GEN_INT (-1)), insn);
-      emit_insn_before (gen_cmpqi (count_reg, GEN_INT (0)), insn);
+      emit_insn_before (gen_addqi3 (count_reg, count_reg, constm1_rtx), insn);
+      emit_insn_before (gen_cmpqi (count_reg, const0_rtx), insn);
       emit_insn_before (gen_bge (start_label), insn);
       LABEL_NUSES (start_label)++;
       delete_insn (insn);
@@ -2927,9 +2956,6 @@ const_operand (register rtx op, register enum machine_mode mode)
     case Pmode:
 #endif
     case QImode:
-      if (GET_CODE (op) == CONSTANT_P_RTX)
-       return 1;
-
       if (GET_CODE (op) != CONST_INT
          || (GET_MODE (op) != VOIDmode && GET_MODE (op) != mode)
          || GET_MODE_CLASS (mode) != MODE_INT)
@@ -3263,7 +3289,8 @@ src_operand (rtx op, enum machine_mode mode)
       && ((GET_CODE (XEXP (op, 0)) == SYMBOL_REF
           || GET_CODE (XEXP (op, 0)) == LABEL_REF
           || GET_CODE (XEXP (op, 0)) == CONST)))
-    return ! TARGET_LOAD_DIRECT_MEMS && GET_MODE (op) == mode;
+    return !TARGET_EXPOSE_LDP && 
+      ! TARGET_LOAD_DIRECT_MEMS && GET_MODE (op) == mode;
 
   return general_operand (op, mode);
 }
@@ -3314,7 +3341,7 @@ tsrc_operand (rtx op, enum machine_mode mode)
 }
 
 
-/* Check src operand of two operand non immedidate instructions.  */
+/* Check src operand of two operand non immediate instructions.  */
 
 int
 nonimmediate_src_operand (rtx op, enum machine_mode mode)
@@ -3326,7 +3353,7 @@ nonimmediate_src_operand (rtx op, enum machine_mode mode)
 }
 
 
-/* Check logical src operand of two operand non immedidate instructions.  */
+/* Check logical src operand of two operand non immediate instructions.  */
 
 int
 nonimmediate_lsrc_operand (rtx op, enum machine_mode mode)
@@ -3461,7 +3488,7 @@ c4x_S_address_parse (rtx op, int *base, int *incdec, int *index, int *disp)
            return;
          }
       }
-      /* Fallthrough.  */
+      /* Fall through.  */
 
     default:
       fatal_insn ("invalid indirect (S) memory address", op);
@@ -3520,7 +3547,7 @@ c4x_address_conflict (rtx op0, rtx op1, int store0, int store1)
   if (! TARGET_DEVEL && base0 == base1 && (incdec0 || incdec1))
     return 1;
 
-  /* We can not optimize the case where op1 and op2 refer to the same
+  /* We cannot optimize the case where op1 and op2 refer to the same
      address.  */
   if (base0 == base1 && disp0 == disp1 && index0 == index1)
     return 1;
@@ -3731,11 +3758,17 @@ c4x_valid_operands (enum rtx_code code, rtx *operands,
                    enum machine_mode mode ATTRIBUTE_UNUSED,
                    int force)
 {
+  rtx op0;
   rtx op1;
   rtx op2;
   enum rtx_code code1;
   enum rtx_code code2;
 
+
+  /* FIXME, why can't we tighten the operands for IF_THEN_ELSE?  */
+  if (code == IF_THEN_ELSE)
+      return 1 || (operands[0] == operands[2] || operands[0] == operands[3]);
+
   if (code == COMPARE)
     {
       op1 = operands[0];
@@ -3747,6 +3780,10 @@ c4x_valid_operands (enum rtx_code code, rtx *operands,
       op2 = operands[2];
     }
 
+  op0 = operands[0];
+
+  if (GET_CODE (op0) == SUBREG)
+    op0 = SUBREG_REG (op0);
   if (GET_CODE (op1) == SUBREG)
     op1 = SUBREG_REG (op1);
   if (GET_CODE (op2) == SUBREG)
@@ -3755,6 +3792,7 @@ c4x_valid_operands (enum rtx_code code, rtx *operands,
   code1 = GET_CODE (op1);
   code2 = GET_CODE (op2);
 
+  
   if (code1 == REG && code2 == REG)
     return 1;
 
@@ -3765,6 +3803,7 @@ c4x_valid_operands (enum rtx_code code, rtx *operands,
       return c4x_R_indirect (op1) && c4x_R_indirect (op2);
     }
 
+  /* We cannot handle two MEMs or two CONSTS, etc.  */
   if (code1 == code2)
     return 0;
 
@@ -3784,11 +3823,6 @@ c4x_valid_operands (enum rtx_code code, rtx *operands,
 
          /* Any valid memory operand screened by src_operand is OK.  */
        case MEM:
-         
-         /* After CSE, any remaining (ADDRESSOF:P reg) gets converted
-            into a stack slot memory address comprising a PLUS and a
-            constant.  */
-       case ADDRESSOF:
          break;
          
        default:
@@ -3796,54 +3830,65 @@ c4x_valid_operands (enum rtx_code code, rtx *operands,
          break;
        }
       
+      if (GET_CODE (op0) == SCRATCH)
+         return 1;
+
+      if (!REG_P (op0))
+         return 0;
+
       /* Check that we have a valid destination register for a two operand
         instruction.  */
-      return ! force || code == COMPARE || REGNO (op1) == REGNO (operands[0]);
+      return ! force || code == COMPARE || REGNO (op1) == REGNO (op0);
     }
 
-  /* We assume MINUS is commutative since the subtract patterns
-     also support the reverse subtract instructions.  Since op1
-     is not a register, and op2 is a register, op1 can only
-     be a restricted memory operand for a shift instruction.  */
+
+  /* Check non-commutative operators.  */
   if (code == ASHIFTRT || code == LSHIFTRT
       || code == ASHIFT || code == COMPARE)
     return code2 == REG
       && (c4x_S_indirect (op1) || c4x_R_indirect (op1));
-  
-  switch (code1)
+
+
+  /* Assume MINUS is commutative since the subtract patterns
+     also support the reverse subtract instructions.  Since op1
+     is not a register, and op2 is a register, op1 can only
+     be a restricted memory operand for a shift instruction.  */
+  if (code2 == REG)
     {
-    case CONST_INT:
-      if (c4x_J_constant (op1) && c4x_R_indirect (op2))
-       return 1;
-      break;
+      switch (code1)
+       {
+       case CONST_INT:
+         break;
       
-    case CONST_DOUBLE:
-      if (! c4x_H_constant (op1))
-       return 0;
-      break;
+       case CONST_DOUBLE:
+         if (! c4x_H_constant (op1))
+           return 0;
+         break;
 
-      /* Any valid memory operand screened by src_operand is OK.  */      
-    case MEM:
-#if 0
-      if (code2 != REG)
-       return 0;
-#endif
-      break;
+         /* Any valid memory operand screened by src_operand is OK.  */      
+       case MEM:
+         break;
+         
+       default:
+         abort ();
+         break;
+       }
 
-      /* After CSE, any remaining (ADDRESSOF:P reg) gets converted
-        into a stack slot memory address comprising a PLUS and a
-        constant.  */
-    case ADDRESSOF:
-      break;
-      
-    default:
-      abort ();
-      break;
+      if (GET_CODE (op0) == SCRATCH)
+         return 1;
+
+      if (!REG_P (op0))
+         return 0;
+
+      /* Check that we have a valid destination register for a two operand
+        instruction.  */
+      return ! force || REGNO (op1) == REGNO (op0);
     }
       
-  /* Check that we have a valid destination register for a two operand
-     instruction.  */
-  return ! force || REGNO (op1) == REGNO (operands[0]);
+  if (c4x_J_constant (op1) && c4x_R_indirect (op2))
+    return 1;
+
+  return 0;
 }
 
 
@@ -3886,7 +3931,6 @@ legitimize_operands (enum rtx_code code, rtx *operands, enum machine_mode mode)
          && TARGET_HOIST
          && optimize > 0
          && GET_CODE (operands[1]) == CONST_INT 
-         && preserve_subexpressions_p ()
          && rtx_cost (operands[1], code) > 1)
        operands[1] = force_reg (mode, operands[1]);
       
@@ -3904,13 +3948,12 @@ legitimize_operands (enum rtx_code code, rtx *operands, enum machine_mode mode)
       && TARGET_HOIST
       && optimize > 1
       && GET_CODE (operands[2]) == CONST_INT
-      && preserve_subexpressions_p ()
       && rtx_cost (operands[2], code) > 1)
     operands[2] = force_reg (mode, operands[2]);
 
   /* We can get better code on a C30 if we force constant shift counts
      into a register.  This way they can get hoisted out of loops,
-     tying up a register, but saving an instruction.  The downside is
+     tying up a register but saving an instruction.  The downside is
      that they may get allocated to an address or index register, and
      thus we will get a pipeline conflict if there is a nearby
      indirect address using an address register. 
@@ -3944,6 +3987,17 @@ legitimize_operands (enum rtx_code code, rtx *operands, enum machine_mode mode)
       && (GET_CODE (operands[2]) != CONST_INT))
     operands[2] = gen_rtx_NEG (mode, negate_rtx (mode, operands[2]));
   
+
+  /* When the shift count is greater than 32 then the result 
+     can be implementation dependent.  We truncate the result to
+     fit in 5 bits so that we do not emit invalid code when
+     optimizing---such as trying to generate lhu2 with 20021124-1.c.  */
+  if (((code == ASHIFTRT || code == LSHIFTRT || code == ASHIFT)
+      && (GET_CODE (operands[2]) == CONST_INT))
+      && INTVAL (operands[2]) > (GET_MODE_BITSIZE (mode) - 1))
+      operands[2]
+         = GEN_INT (INTVAL (operands[2]) & (GET_MODE_BITSIZE (mode) - 1));
+
   return 1;
 }
 
@@ -4708,41 +4762,52 @@ c4x_init_builtins (void)
 {
   tree endlink = void_list_node;
 
-  builtin_function ("fast_ftoi",
-                   build_function_type 
-                   (integer_type_node,
-                    tree_cons (NULL_TREE, double_type_node, endlink)),
-                   C4X_BUILTIN_FIX, BUILT_IN_MD, NULL, NULL_TREE);
-  builtin_function ("ansi_ftoi",
-                   build_function_type 
-                   (integer_type_node, 
-                    tree_cons (NULL_TREE, double_type_node, endlink)),
-                   C4X_BUILTIN_FIX_ANSI, BUILT_IN_MD, NULL, NULL_TREE);
+  lang_hooks.builtin_function ("fast_ftoi",
+                              build_function_type 
+                              (integer_type_node,
+                               tree_cons (NULL_TREE, double_type_node,
+                                          endlink)),
+                              C4X_BUILTIN_FIX, BUILT_IN_MD, NULL, NULL_TREE);
+  lang_hooks.builtin_function ("ansi_ftoi",
+                              build_function_type 
+                              (integer_type_node, 
+                               tree_cons (NULL_TREE, double_type_node,
+                                          endlink)),
+                              C4X_BUILTIN_FIX_ANSI, BUILT_IN_MD, NULL,
+                              NULL_TREE);
   if (TARGET_C3X)
-    builtin_function ("fast_imult",
-                     build_function_type
-                     (integer_type_node, 
-                      tree_cons (NULL_TREE, integer_type_node,
-                                 tree_cons (NULL_TREE,
-                                            integer_type_node, endlink))),
-                     C4X_BUILTIN_MPYI, BUILT_IN_MD, NULL, NULL_TREE);
+    lang_hooks.builtin_function ("fast_imult",
+                                build_function_type
+                                (integer_type_node, 
+                                 tree_cons (NULL_TREE, integer_type_node,
+                                            tree_cons (NULL_TREE,
+                                                       integer_type_node,
+                                                       endlink))),
+                                C4X_BUILTIN_MPYI, BUILT_IN_MD, NULL,
+                                NULL_TREE);
   else
     {
-      builtin_function ("toieee",
-                       build_function_type 
-                       (double_type_node,
-                        tree_cons (NULL_TREE, double_type_node, endlink)),
-                       C4X_BUILTIN_TOIEEE, BUILT_IN_MD, NULL, NULL_TREE);
-      builtin_function ("frieee",
-                       build_function_type
-                       (double_type_node, 
-                        tree_cons (NULL_TREE, double_type_node, endlink)),
-                       C4X_BUILTIN_FRIEEE, BUILT_IN_MD, NULL, NULL_TREE);
-      builtin_function ("fast_invf",
-                       build_function_type 
-                       (double_type_node, 
-                        tree_cons (NULL_TREE, double_type_node, endlink)),
-                       C4X_BUILTIN_RCPF, BUILT_IN_MD, NULL, NULL_TREE);
+      lang_hooks.builtin_function ("toieee",
+                                  build_function_type 
+                                  (double_type_node,
+                                   tree_cons (NULL_TREE, double_type_node,
+                                              endlink)),
+                                  C4X_BUILTIN_TOIEEE, BUILT_IN_MD, NULL,
+                                  NULL_TREE);
+      lang_hooks.builtin_function ("frieee",
+                                  build_function_type
+                                  (double_type_node, 
+                                   tree_cons (NULL_TREE, double_type_node,
+                                              endlink)),
+                                  C4X_BUILTIN_FRIEEE, BUILT_IN_MD, NULL,
+                                  NULL_TREE);
+      lang_hooks.builtin_function ("fast_invf",
+                                  build_function_type 
+                                  (double_type_node, 
+                                   tree_cons (NULL_TREE, double_type_node,
+                                              endlink)),
+                                  C4X_BUILTIN_RCPF, BUILT_IN_MD, NULL,
+                                  NULL_TREE);
     }
 }
 
@@ -4764,7 +4829,6 @@ c4x_expand_builtin (tree exp, rtx target,
     case C4X_BUILTIN_FIX:
       arg0 = TREE_VALUE (arglist);
       r0 = expand_expr (arg0, NULL_RTX, QFmode, 0);
-      r0 = protect_from_queue (r0, 0);
       if (! target || ! register_operand (target, QImode))
        target = gen_reg_rtx (QImode);
       emit_insn (gen_fixqfqi_clobber (target, r0));
@@ -4773,7 +4837,6 @@ c4x_expand_builtin (tree exp, rtx target,
     case C4X_BUILTIN_FIX_ANSI:
       arg0 = TREE_VALUE (arglist);
       r0 = expand_expr (arg0, NULL_RTX, QFmode, 0);
-      r0 = protect_from_queue (r0, 0);
       if (! target || ! register_operand (target, QImode))
        target = gen_reg_rtx (QImode);
       emit_insn (gen_fix_truncqfqi2 (target, r0));
@@ -4786,8 +4849,6 @@ c4x_expand_builtin (tree exp, rtx target,
       arg1 = TREE_VALUE (TREE_CHAIN (arglist));
       r0 = expand_expr (arg0, NULL_RTX, QImode, 0);
       r1 = expand_expr (arg1, NULL_RTX, QImode, 0);
-      r0 = protect_from_queue (r0, 0);
-      r1 = protect_from_queue (r1, 0);
       if (! target || ! register_operand (target, QImode))
        target = gen_reg_rtx (QImode);
       emit_insn (gen_mulqi3_24_clobber (target, r0, r1));
@@ -4798,7 +4859,6 @@ c4x_expand_builtin (tree exp, rtx target,
        break;
       arg0 = TREE_VALUE (arglist);
       r0 = expand_expr (arg0, NULL_RTX, QFmode, 0);
-      r0 = protect_from_queue (r0, 0);
       if (! target || ! register_operand (target, QFmode))
        target = gen_reg_rtx (QFmode);
       emit_insn (gen_toieee (target, r0));
@@ -4808,10 +4868,7 @@ c4x_expand_builtin (tree exp, rtx target,
       if (TARGET_C3X)
        break;
       arg0 = TREE_VALUE (arglist);
-      if (TREE_CODE (arg0) == VAR_DECL || TREE_CODE (arg0) == PARM_DECL)
-       put_var_into_stack (arg0, /*rescan=*/true);
       r0 = expand_expr (arg0, NULL_RTX, QFmode, 0);
-      r0 = protect_from_queue (r0, 0);
       if (register_operand (r0, QFmode))
        {
          r1 = assign_stack_local (QFmode, GET_MODE_SIZE (QFmode), 0);
@@ -4828,7 +4885,6 @@ c4x_expand_builtin (tree exp, rtx target,
        break;
       arg0 = TREE_VALUE (arglist);
       r0 = expand_expr (arg0, NULL_RTX, QFmode, 0);
-      r0 = protect_from_queue (r0, 0);
       if (! target || ! register_operand (target, QFmode))
        target = gen_reg_rtx (QFmode);
       emit_insn (gen_rcpfqf_clobber (target, r0));
@@ -4867,7 +4923,8 @@ c4x_init_libfuncs (void)
 }
 
 static void
-c4x_asm_named_section (const char *name, unsigned int flags ATTRIBUTE_UNUSED)
+c4x_asm_named_section (const char *name, unsigned int flags ATTRIBUTE_UNUSED,
+                      tree decl ATTRIBUTE_UNUSED)
 {
   fprintf (asm_out_file, "\t.sect\t\"%s\"\n", name);
 }
@@ -4970,3 +5027,21 @@ c4x_rtx_costs (rtx x, int code, int outer_code, int *total)
       return false;
     }
 }
+\f
+/* Worker function for TARGET_ASM_EXTERNAL_LIBCALL.  */
+
+static void
+c4x_external_libcall (rtx fun)
+{
+  /* This is only needed to keep asm30 happy for ___divqf3 etc.  */
+  c4x_external_ref (XSTR (fun, 0));
+}
+
+/* Worker function for TARGET_STRUCT_VALUE_RTX.  */
+
+static rtx
+c4x_struct_value_rtx (tree fntype ATTRIBUTE_UNUSED,
+                     int incoming ATTRIBUTE_UNUSED)
+{
+  return gen_rtx_REG (Pmode, AR0_REGNO);
+}