Add port done awhile ago for the ARC cpu.
authorJeff Law <law@gcc.gnu.org>
Tue, 9 Sep 1997 21:51:14 +0000 (15:51 -0600)
committerJeff Law <law@gcc.gnu.org>
Tue, 9 Sep 1997 21:51:14 +0000 (15:51 -0600)
        * arc/arc.h: New file.
        * arc/arc.c: New file.
        * arc/arc.md: New file.
        * arc/initfini.c: New file.
        * arc/lib1funcs.asm: New file.
        * arc/t-arc: New file.
        * arc/xm-arc.h: New file.
        * ginclude/va-arc.h: New file.
        * ginclude/stdarg.h: Include va-arc.h ifdef __arc__.
        * ginclude/varargs.h: Likewise.
        * Makefile.in (USER_H): Add va-arc.h.
        * configure.in (arc-*-elf*): Recognize.
        * longlong.h: Add ARC support.
Mostly so I can test changes in snapshot scripts.

        * expr.c (clear_storage): Use CONST0_RTX instead of const0_rtx.
        when clearing non-BLKmode data.
Fixes sparc problem.

From-SVN: r15186

gcc/config/arc/arc.c [new file with mode: 0644]

diff --git a/gcc/config/arc/arc.c b/gcc/config/arc/arc.c
new file mode 100644 (file)
index 0000000..c9e5411
--- /dev/null
@@ -0,0 +1,2212 @@
+/* Subroutines used for code generation on the ARC cpu.
+   Copyright (C) 1994, 1995, 1997 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING.  If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+/* ??? This is an old port, and is undoubtedly suffering from bit rot.  */
+
+#include <stdio.h>
+#include "config.h"
+#include "tree.h"
+#include "rtl.h"
+#include "regs.h"
+#include "hard-reg-set.h"
+#include "real.h"
+#include "insn-config.h"
+#include "conditions.h"
+#include "insn-flags.h"
+#include "output.h"
+#include "insn-attr.h"
+#include "flags.h"
+#include "expr.h"
+#include "recog.h"
+
+/* Which cpu we're compiling for (NULL(=base), ???).  */
+char *arc_cpu_string;
+int arc_cpu_type;
+
+/* Name of mangle string to add to symbols to separate code compiled for each
+   cpu (or NULL).  */
+char *arc_mangle_cpu;
+
+/* Save the operands last given to a compare for use when we
+   generate a scc or bcc insn.  */
+rtx arc_compare_op0, arc_compare_op1;
+
+/* Name of text, data, and rodata sections, as specified on command line.
+   Selected by -m{text,data,rodata} flags.  */
+char *arc_text_string = ARC_DEFAULT_TEXT_SECTION;
+char *arc_data_string = ARC_DEFAULT_DATA_SECTION;
+char *arc_rodata_string = ARC_DEFAULT_RODATA_SECTION;
+
+/* Name of text, data, and rodata sections used in varasm.c.  */
+char *arc_text_section;
+char *arc_data_section;
+char *arc_rodata_section;
+
+/* Array of valid operand punctuation characters.  */
+char arc_punct_chars[256];
+
+/* Variables used by arc_final_prescan_insn to implement conditional
+   execution.  */
+static int arc_ccfsm_state;
+static int arc_ccfsm_current_cc;
+static rtx arc_ccfsm_target_insn;
+static int arc_ccfsm_target_label;
+
+/* The maximum number of insns skipped which will be conditionalised if
+   possible.  */
+#define MAX_INSNS_SKIPPED 3
+
+/* A nop is needed between a 4 byte insn that sets the condition codes and
+   a branch that uses them (the same isn't true for an 8 byte insn that sets
+   the condition codes).  Set by arc_final_prescan_insn.  Used by
+   arc_print_operand.  */
+static int last_insn_set_cc_p;
+static int current_insn_set_cc_p;
+static void record_cc_ref ();
+
+void arc_init_reg_tables ();
+
+/* Called by OVERRIDE_OPTIONS to initialize various things.  */
+
+void
+arc_init (void)
+{
+  if (arc_cpu_string == 0
+      || !strcmp (arc_cpu_string, "base"))
+    {
+      /* Ensure we have a printable value for the .cpu pseudo-op.  */
+      arc_cpu_string = "base";
+      arc_cpu_type = 0;
+      arc_mangle_cpu = NULL;
+    }
+  else if (ARC_EXTENSION_CPU (arc_cpu_string))
+    ; /* nothing to do */
+  else
+    {
+      error ("bad value (%s) for -mcpu switch", arc_cpu_string);
+      arc_cpu_string = "base";
+      arc_cpu_type = 0;
+      arc_mangle_cpu = NULL;
+    }
+
+  /* Set the pseudo-ops for the various standard sections.  */
+  arc_text_section = xmalloc (strlen (arc_text_string) + sizeof (ARC_SECTION_FORMAT) + 1);
+  sprintf (arc_text_section, ARC_SECTION_FORMAT, arc_text_string);
+  arc_data_section = xmalloc (strlen (arc_data_string) + sizeof (ARC_SECTION_FORMAT) + 1);
+  sprintf (arc_data_section, ARC_SECTION_FORMAT, arc_data_string);
+  arc_rodata_section = xmalloc (strlen (arc_rodata_string) + sizeof (ARC_SECTION_FORMAT) + 1);
+  sprintf (arc_rodata_section, ARC_SECTION_FORMAT, arc_rodata_string);
+
+  arc_init_reg_tables ();
+
+  /* Initialize array for PRINT_OPERAND_PUNCT_VALID_P.  */
+  memset (arc_punct_chars, 0, sizeof (arc_punct_chars));
+  arc_punct_chars['#'] = 1;
+  arc_punct_chars['*'] = 1;
+  arc_punct_chars['?'] = 1;
+  arc_punct_chars['!'] = 1;
+  arc_punct_chars['~'] = 1;
+}
+\f
+/* The condition codes of the ARC, and the inverse function.  */
+static char *arc_condition_codes[] =
+{
+  "al", 0, "eq", "ne", "p", "n", "c", "nc", "v", "nv",
+  "gt", "le", "ge", "lt", "hi", "ls", "pnz", 0
+};
+
+#define ARC_INVERSE_CONDITION_CODE(X)  ((X) ^ 1)
+
+/* Returns the index of the ARC condition code string in
+   `arc_condition_codes'.  COMPARISON should be an rtx like
+   `(eq (...) (...))'.  */
+
+static int
+get_arc_condition_code (comparison)
+     rtx comparison;
+{
+  switch (GET_CODE (comparison))
+    {
+    case EQ : return 2;
+    case NE : return 3;
+    case GT : return 10;
+    case LE : return 11;
+    case GE : return 12;
+    case LT : return 13;
+    case GTU : return 14;
+    case LEU : return 15;
+    case LTU : return 6;
+    case GEU : return 7;
+    default : abort ();
+    }
+  /*NOTREACHED*/
+  return (42);
+}
+
+/* Given a comparison code (EQ, NE, etc.) and the first operand of a COMPARE,
+   return the mode to be used for the comparison.  */
+
+enum machine_mode
+arc_select_cc_mode (op, x, y)
+     enum rtx_code op;
+     rtx x, y;
+{
+  switch (op)
+    {
+    case EQ :
+    case NE :
+      return CCZNmode;
+    default :
+      switch (GET_CODE (x))
+       {
+       case AND :
+       case IOR :
+       case XOR :
+       case SIGN_EXTEND :
+       case ZERO_EXTEND :
+         return CCZNmode;
+       case ASHIFT :
+       case ASHIFTRT :
+       case LSHIFTRT :
+         return CCZNCmode;
+       }
+    }
+  return CCmode;
+}
+\f
+/* Vectors to keep interesting information about registers where it can easily
+   be got.  We use to use the actual mode value as the bit number, but there
+   is (or may be) more than 32 modes now.  Instead we use two tables: one
+   indexed by hard register number, and one indexed by mode.  */
+
+/* The purpose of arc_mode_class is to shrink the range of modes so that
+   they all fit (as bit numbers) in a 32 bit word (again).  Each real mode is
+   mapped into one arc_mode_class mode.  */
+
+enum arc_mode_class {
+  C_MODE,
+  S_MODE, D_MODE, T_MODE, O_MODE,
+  SF_MODE, DF_MODE, TF_MODE, OF_MODE
+};
+
+/* Modes for condition codes.  */
+#define C_MODES (1 << (int) C_MODE)
+
+/* Modes for single-word and smaller quantities.  */
+#define S_MODES ((1 << (int) S_MODE) | (1 << (int) SF_MODE))
+
+/* Modes for double-word and smaller quantities.  */
+#define D_MODES (S_MODES | (1 << (int) D_MODE) | (1 << DF_MODE))
+
+/* Modes for quad-word and smaller quantities.  */
+#define T_MODES (D_MODES | (1 << (int) T_MODE) | (1 << (int) TF_MODE))
+
+/* Value is 1 if register/mode pair is acceptable on arc.  */
+
+unsigned int arc_hard_regno_mode_ok[] = {
+  T_MODES, T_MODES, T_MODES, T_MODES, T_MODES, T_MODES, T_MODES, T_MODES,
+  T_MODES, T_MODES, T_MODES, T_MODES, T_MODES, T_MODES, T_MODES, T_MODES,
+  T_MODES, T_MODES, T_MODES, T_MODES, T_MODES, T_MODES, T_MODES, D_MODES,
+  D_MODES, S_MODES, S_MODES, S_MODES, S_MODES, S_MODES, S_MODES, S_MODES,
+
+  /* ??? Leave these as S_MODES for now.  */
+  S_MODES, S_MODES, S_MODES, S_MODES, S_MODES, S_MODES, S_MODES, S_MODES,
+  S_MODES, S_MODES, S_MODES, S_MODES, S_MODES, S_MODES, S_MODES, S_MODES,
+  S_MODES, S_MODES, S_MODES, S_MODES, S_MODES, S_MODES, S_MODES, S_MODES,
+  S_MODES, S_MODES, S_MODES, S_MODES, S_MODES, C_MODES
+};
+
+unsigned int arc_mode_class [NUM_MACHINE_MODES];
+
+enum reg_class arc_regno_reg_class[FIRST_PSEUDO_REGISTER];
+
+void
+arc_init_reg_tables ()
+{
+  int i;
+
+  for (i = 0; i < NUM_MACHINE_MODES; i++)
+    {
+      switch (GET_MODE_CLASS (i))
+       {
+       case MODE_INT:
+       case MODE_PARTIAL_INT:
+       case MODE_COMPLEX_INT:
+         if (GET_MODE_SIZE (i) <= 4)
+           arc_mode_class[i] = 1 << (int) S_MODE;
+         else if (GET_MODE_SIZE (i) == 8)
+           arc_mode_class[i] = 1 << (int) D_MODE;
+         else if (GET_MODE_SIZE (i) == 16)
+           arc_mode_class[i] = 1 << (int) T_MODE;
+         else if (GET_MODE_SIZE (i) == 32)
+           arc_mode_class[i] = 1 << (int) O_MODE;
+         else 
+           arc_mode_class[i] = 0;
+         break;
+       case MODE_FLOAT:
+       case MODE_COMPLEX_FLOAT:
+         if (GET_MODE_SIZE (i) <= 4)
+           arc_mode_class[i] = 1 << (int) SF_MODE;
+         else if (GET_MODE_SIZE (i) == 8)
+           arc_mode_class[i] = 1 << (int) DF_MODE;
+         else if (GET_MODE_SIZE (i) == 16)
+           arc_mode_class[i] = 1 << (int) TF_MODE;
+         else if (GET_MODE_SIZE (i) == 32)
+           arc_mode_class[i] = 1 << (int) OF_MODE;
+         else 
+           arc_mode_class[i] = 0;
+         break;
+       case MODE_CC:
+       default:
+         /* mode_class hasn't been initialized yet for EXTRA_CC_MODES, so
+            we must explicitly check for them here.  */
+         if (i == (int) CCmode || i == (int) CCZNmode || i == (int) CCZNCmode)
+           arc_mode_class[i] = 1 << (int) C_MODE;
+         else
+           arc_mode_class[i] = 0;
+         break;
+       }
+    }
+
+  for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+    {
+      if (i < 60)
+       arc_regno_reg_class[i] = GENERAL_REGS;
+      else if (i == 60)
+       arc_regno_reg_class[i] = LPCOUNT_REG;
+      else if (i == 61)
+       arc_regno_reg_class[i] = NO_REGS /* CC_REG: must be NO_REGS */;
+      else
+       arc_regno_reg_class[i] = NO_REGS;
+    }
+}
+\f
+/* ARC specific attribute support.
+
+   The ARC has these attributes:
+   interrupt - for interrupt functions
+*/
+
+/* Return nonzero if IDENTIFIER is a valid decl attribute.  */
+
+int
+arc_valid_machine_decl_attribute (type, attributes, identifier, args)
+     tree type;
+     tree attributes;
+     tree identifier;
+     tree args;
+{
+  if (identifier == get_identifier ("__interrupt__")
+      && list_length (args) == 1
+      && TREE_CODE (TREE_VALUE (args)) == STRING_CST)
+    {
+      tree value = TREE_VALUE (args);
+
+      if (!strcmp (TREE_STRING_POINTER (value), "ilink1")
+          || !strcmp (TREE_STRING_POINTER (value), "ilink2"))
+       return 1;
+    }
+  return 0;
+}
+
+/* Return zero if TYPE1 and TYPE are incompatible, one if they are compatible,
+   and two if they are nearly compatible (which causes a warning to be
+   generated).  */
+
+int
+arc_comp_type_attributes (type1, type2)
+     tree type1, type2;
+{
+  return 1;
+}
+
+/* Set the default attributes for TYPE.  */
+
+void
+arc_set_default_type_attributes (type)
+     tree type;
+{
+}
+\f
+/* Acceptable arguments to the call insn.  */
+
+int
+call_address_operand (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  return (symbolic_operand (op, mode)
+         || (GET_CODE (op) == CONST_INT && LEGITIMATE_CONSTANT_P (op))
+         || (GET_CODE (op) == REG));
+}
+
+int
+call_operand (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  if (GET_CODE (op) != MEM)
+    return 0;
+  op = XEXP (op, 0);
+  return call_address_operand (op, mode);
+}
+
+/* Returns 1 if OP is a symbol reference.  */
+
+int
+symbolic_operand (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  switch (GET_CODE (op))
+    {
+    case SYMBOL_REF:
+    case LABEL_REF:
+    case CONST :
+      return 1;
+    default:
+      return 0;
+    }
+}
+
+/* Return truth value of statement that OP is a symbolic memory
+   operand of mode MODE.  */
+
+int
+symbolic_memory_operand (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  if (GET_CODE (op) == SUBREG)
+    op = SUBREG_REG (op);
+  if (GET_CODE (op) != MEM)
+    return 0;
+  op = XEXP (op, 0);
+  return (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == CONST
+         || GET_CODE (op) == LABEL_REF);
+}
+
+/* Return true if OP is a short immediate (shimm) value.  */
+
+int
+short_immediate_operand (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  if (GET_CODE (op) != CONST_INT)
+    return 0;
+  return SMALL_INT (INTVAL (op));
+}
+
+/* Return true if OP will require a long immediate (limm) value.
+   This is currently only used when calculating length attributes.  */
+
+int
+long_immediate_operand (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  switch (GET_CODE (op))
+    {
+    case SYMBOL_REF :
+    case LABEL_REF :
+    case CONST :
+      return 1;
+    case CONST_INT :
+      return !SMALL_INT (INTVAL (op));
+    case CONST_DOUBLE :
+      /* These can happen because large unsigned 32 bit constants are
+        represented this way (the multiplication patterns can cause these
+        to be generated).  They also occur for SFmode values.  */
+      return 1;
+    }
+  return 0;
+}
+
+/* Return true if OP is a MEM that when used as a load or store address will
+   require an 8 byte insn.
+   Load and store instructions don't allow the same possibilities but they're
+   similar enough that this one function will do.
+   This is currently only used when calculating length attributes.  */
+
+int
+long_immediate_loadstore_operand (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  if (GET_CODE (op) != MEM)
+    return 0;
+
+  op = XEXP (op, 0);
+  switch (GET_CODE (op))
+    {
+    case SYMBOL_REF :
+    case LABEL_REF :
+    case CONST :
+      return 1;
+    case CONST_INT :
+      /* This must be handled as "st c,[limm]".  Ditto for load.
+        Technically, the assembler could translate some possibilities to
+        "st c,[limm/2 + limm/2]" if limm/2 will fit in a shimm, but we don't
+        assume that it does.  */
+      return 1;
+    case CONST_DOUBLE :
+      /* These can happen because large unsigned 32 bit constants are
+        represented this way (the multiplication patterns can cause these
+        to be generated).  They also occur for SFmode values.  */
+      return 1;
+    case REG :
+      return 0;
+    case PLUS :
+      if (GET_CODE (XEXP (op, 1)) == CONST_INT
+         && !SMALL_INT (INTVAL (XEXP (op, 1))))
+       return 1;
+      return 0;
+    }
+  return 0;
+}
+
+/* Return true if OP is an acceptable argument for a single word
+   move source.  */
+
+int
+move_src_operand (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  switch (GET_CODE (op))
+    {
+    case SYMBOL_REF :
+    case LABEL_REF :
+    case CONST :
+      return 1;
+    case CONST_INT :
+      return (LARGE_INT (INTVAL (op)));
+    case CONST_DOUBLE :
+      /* We can handle DImode integer constants in SImode if the value
+        (signed or unsigned) will fit in 32 bits.  This is needed because
+        large unsigned 32 bit constants are represented as CONST_DOUBLEs.  */
+      if (mode == SImode)
+       return arc_double_limm_p (op);
+      /* We can handle 32 bit floating point constants.  */
+      if (mode == SFmode)
+       return GET_MODE (op) == SFmode;
+      return 0;
+    case REG :
+      return register_operand (op, mode);
+    case SUBREG :
+      /* (subreg (mem ...) ...) can occur here if the inner part was once a
+        pseudo-reg and is now a stack slot.  */
+      if (GET_CODE (SUBREG_REG (op)) == MEM)
+       return address_operand (XEXP (SUBREG_REG (op), 0), mode);
+      else
+       return register_operand (op, mode);
+    case MEM :
+      return address_operand (XEXP (op, 0), mode);
+    default :
+      return 0;
+    }
+}
+
+/* Return true if OP is an acceptable argument for a double word
+   move source.  */
+
+int
+move_double_src_operand (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  switch (GET_CODE (op))
+    {
+    case REG :
+      return register_operand (op, mode);
+    case SUBREG :
+      /* (subreg (mem ...) ...) can occur here if the inner part was once a
+        pseudo-reg and is now a stack slot.  */
+      if (GET_CODE (SUBREG_REG (op)) == MEM)
+       return move_double_src_operand (SUBREG_REG (op), mode);
+      else
+       return register_operand (op, mode);
+    case MEM :
+      /* Disallow auto inc/dec for now.  */
+      if (GET_CODE (XEXP (op, 0)) == PRE_DEC
+         || GET_CODE (XEXP (op, 0)) == PRE_INC)
+       return 0;
+      return address_operand (XEXP (op, 0), mode);
+    case CONST_INT :
+    case CONST_DOUBLE :
+      return 1;
+    default :
+      return 0;
+    }
+}
+
+/* Return true if OP is an acceptable argument for a move destination.  */
+
+int
+move_dest_operand (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  switch (GET_CODE (op))
+    {
+    case REG :
+      return register_operand (op, mode);
+    case SUBREG :
+      /* (subreg (mem ...) ...) can occur here if the inner part was once a
+        pseudo-reg and is now a stack slot.  */
+      if (GET_CODE (SUBREG_REG (op)) == MEM)
+       return address_operand (XEXP (SUBREG_REG (op), 0), mode);
+      else
+       return register_operand (op, mode);
+    case MEM :
+      return address_operand (XEXP (op, 0), mode);
+    default :
+      return 0;
+    }
+}
+
+/* Return true if OP is valid load with update operand.  */
+
+int
+load_update_operand (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  if (GET_CODE (op) != MEM
+      || GET_MODE (op) != mode)
+    return 0;
+  op = XEXP (op, 0);
+  if (GET_CODE (op) != PLUS
+      || GET_MODE (op) != Pmode
+      || !register_operand (XEXP (op, 0), Pmode)
+      || !nonmemory_operand (XEXP (op, 1), Pmode))
+    return 0;
+  return 1;
+}
+
+/* Return true if OP is valid store with update operand.  */
+
+int
+store_update_operand (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  if (GET_CODE (op) != MEM
+      || GET_MODE (op) != mode)
+    return 0;
+  op = XEXP (op, 0);
+  if (GET_CODE (op) != PLUS
+      || GET_MODE (op) != Pmode
+      || !register_operand (XEXP (op, 0), Pmode)
+      || !(GET_CODE (XEXP (op, 1)) == CONST_INT
+          && SMALL_INT (INTVAL (XEXP (op, 1)))))
+    return 0;
+  return 1;
+}
+
+/* Return true if OP is a non-volatile non-immediate operand.
+   Volatile memory refs require a special "cache-bypass" instruction
+   and only the standard movXX patterns are set up to handle them.  */
+
+int
+nonvol_nonimm_operand (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  if (GET_CODE (op) == MEM && MEM_VOLATILE_P (op))
+    return 0;
+  return nonimmediate_operand (op, mode);
+}
+
+/* Accept integer operands in the range -0x80000000..0x7fffffff.  We have
+   to check the range carefully since this predicate is used in DImode
+   contexts.  */
+
+int
+const_sint32_operand (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  /* All allowed constants will fit a CONST_INT.  */
+  return (GET_CODE (op) == CONST_INT
+         && (INTVAL (op) >= (-0x7fffffff - 1) && INTVAL (op) <= 0x7fffffff));
+}
+
+/* Accept integer operands in the range 0..0xffffffff.  We have to check the
+   range carefully since this predicate is used in DImode contexts.  Also, we
+   need some extra crud to make it work when hosted on 64-bit machines.  */
+
+int
+const_uint32_operand (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+#if HOST_BITS_PER_WIDE_INT > 32
+  /* All allowed constants will fit a CONST_INT.  */
+  return (GET_CODE (op) == CONST_INT
+         && (INTVAL (op) >= 0 && INTVAL (op) <= 0xffffffffL));
+#else
+  return ((GET_CODE (op) == CONST_INT && INTVAL (op) >= 0)
+         || (GET_CODE (op) == CONST_DOUBLE && CONST_DOUBLE_HIGH (op) == 0));
+#endif
+}
+
+/* Return 1 if OP is a comparison operator valid for the mode of CC.
+   This allows the use of MATCH_OPERATOR to recognize all the branch insns.
+
+   Some insns only set a few bits in the condition code.  So only allow those
+   comparisons that use the bits that are valid.  */
+
+int
+proper_comparison_operator (op, mode)
+    rtx op;
+    enum machine_mode mode;
+{
+  enum rtx_code code = GET_CODE (op);
+
+  if (GET_RTX_CLASS (code) != '<')
+    return 0;
+
+  if (GET_MODE (XEXP (op, 0)) == CCZNmode)
+    return (code == EQ || code == NE);
+  if (GET_MODE (XEXP (op, 0)) == CCZNCmode)
+    return (code == EQ || code == NE
+           || code == LTU || code == GEU || code == GTU || code == LEU);
+  return 1;
+}
+\f
+/* Misc. utilities.  */
+
+/* X and Y are two things to compare using CODE.  Emit the compare insn and
+   return the rtx for the cc reg in the proper mode.  */
+
+rtx
+gen_compare_reg (code, x, y)
+     enum rtx_code code;
+     rtx x, y;
+{
+  enum machine_mode mode = SELECT_CC_MODE (code, x, y);
+  rtx cc_reg;
+
+  cc_reg = gen_rtx (REG, mode, 61);
+
+  emit_insn (gen_rtx (SET, VOIDmode, cc_reg,
+                     gen_rtx (COMPARE, mode, x, y)));
+
+  return cc_reg;
+}
+
+/* Return 1 if VALUE, a const_double, will fit in a limm (4 byte number).
+   We assume the value can be either signed or unsigned.  */
+
+int
+arc_double_limm_p (value)
+     rtx value;
+{
+  HOST_WIDE_INT low, high;
+
+  if (GET_CODE (value) != CONST_DOUBLE)
+    abort ();
+
+  low = CONST_DOUBLE_LOW (value);
+  high = CONST_DOUBLE_HIGH (value);
+
+  if (low & 0x80000000)
+    {
+      return (((unsigned HOST_WIDE_INT) low <= 0xffffffff && high == 0)
+             || (((low & - (unsigned HOST_WIDE_INT) 0x80000000)
+                  == - (unsigned HOST_WIDE_INT) 0x80000000)
+                 && high == -1));
+    }
+  else
+    {
+      return (unsigned HOST_WIDE_INT) low <= 0x7fffffff && high == 0;
+    }
+}
+\f
+/* Do any needed setup for a variadic function.  For the ARC, we must
+   create a register parameter block, and then copy any anonymous arguments
+   in registers to memory.
+
+   CUM has not been updated for the last named argument which has type TYPE
+   and mode MODE, and we rely on this fact.
+
+   We do things a little weird here.  We're supposed to only allocate space
+   for the anonymous arguments.  However we need to keep the stack eight byte
+   aligned.  So we round the space up if necessary, and leave it to va-arc.h
+   to compensate.  */
+
+void
+arc_setup_incoming_varargs (cum, mode, type, pretend_size, no_rtl)
+     CUMULATIVE_ARGS *cum;
+     enum machine_mode mode;
+     tree type;
+     int *pretend_size;
+     int no_rtl;
+{
+  int first_anon_arg;
+
+  /* All BLKmode values are passed by reference.  */
+  if (mode == BLKmode)
+    abort ();
+
+  /* We must treat `__builtin_va_alist' as an anonymous arg.  */
+  if (current_function_varargs)
+    first_anon_arg = *cum;
+  else
+    first_anon_arg = *cum + ((GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1)
+                            / UNITS_PER_WORD);
+
+  if (first_anon_arg < MAX_ARC_PARM_REGS && !no_rtl)
+    {
+      /* Note that first_reg_offset < MAX_ARC_PARM_REGS.  */
+      int first_reg_offset = first_anon_arg;
+      /* Size in words to "pretend" allocate.  */
+      int size = MAX_ARC_PARM_REGS - first_reg_offset;
+      /* Extra slop to keep stack eight byte aligned.  */
+      int align_slop = size & 1;
+      rtx regblock;
+
+      regblock = gen_rtx (MEM, BLKmode,
+                         plus_constant (arg_pointer_rtx,
+                                        FIRST_PARM_OFFSET (0)
+                                        + align_slop * UNITS_PER_WORD));
+      move_block_from_reg (first_reg_offset, regblock,
+                          MAX_ARC_PARM_REGS - first_reg_offset,
+                          ((MAX_ARC_PARM_REGS - first_reg_offset)
+                           * UNITS_PER_WORD));
+
+      *pretend_size = ((MAX_ARC_PARM_REGS - first_reg_offset + align_slop)
+                      * UNITS_PER_WORD);
+    }
+}
+\f
+/* Cost functions.  */
+
+/* Provide the costs of an addressing mode that contains ADDR.
+   If ADDR is not a valid address, its cost is irrelevant.  */
+
+int
+arc_address_cost (addr)
+     rtx addr;
+{
+  switch (GET_CODE (addr))
+    {
+    case REG :
+      /* This is handled in the macro that calls us.
+        It's here for documentation.  */
+      return 1;
+
+    case LABEL_REF :
+    case SYMBOL_REF :
+    case CONST :
+      return 2;
+
+    case PLUS :
+      {
+       register rtx plus0 = XEXP (addr, 0);
+       register rtx plus1 = XEXP (addr, 1);
+
+       if (GET_CODE (plus0) != REG)
+         break;
+
+       switch (GET_CODE (plus1))
+         {
+         case CONST_INT :
+           return SMALL_INT (plus1) ? 1 : 2;
+         case CONST :
+         case SYMBOL_REF :
+         case LABEL_REF :
+           return 2;
+         default:
+           break;
+         }
+       break;
+      }
+    }
+
+  return 4;
+}
+\f
+/* Function prologue/epilogue handlers.  */
+
+/* ARC stack frames look like:
+
+             Before call                       After call
+        +-----------------------+       +-----------------------+
+        |                       |       |                       |
+   high |  local variables,     |       |  local variables,     |
+   mem  |  reg save area, etc.  |       |  reg save area, etc.  |
+        |                       |       |                       |
+        +-----------------------+       +-----------------------+
+        |                       |       |                       |
+        |  arguments on stack.  |       |  arguments on stack.  |
+        |                       |       |                       |
+ SP+16->+-----------------------+FP+48->+-----------------------+
+        | 4 word save area for  |       |  reg parm save area,  |
+        | return addr, prev %fp |       |  only created for     |    
+  SP+0->+-----------------------+       |  variable argument    |    
+                                        |  functions            |    
+                                 FP+16->+-----------------------+    
+                                        | 4 word save area for  |    
+                                        | return addr, prev %fp |    
+                                  FP+0->+-----------------------+    
+                                        |                       |    
+                                        |  local variables      |    
+                                        |                       |    
+                                        +-----------------------+    
+                                        |                       |    
+                                        |  register save area   |    
+                                        |                       |    
+                                        +-----------------------+    
+                                        |                       |    
+                                        |  alloca allocations   |    
+                                        |                       |    
+                                        +-----------------------+    
+                                        |                       |    
+                                        |  arguments on stack   |    
+                                        |                       |    
+                                 SP+16->+-----------------------+
+   low                                  | 4 word save area for  |    
+   memory                               | return addr, prev %fp |    
+                                  SP+0->+-----------------------+    
+
+Notes:
+1) The "reg parm save area" does not exist for non variable argument fns.
+   The "reg parm save area" can be eliminated completely if we created our
+   own va-arc.h, but that has tradeoffs as well (so it's not done).  */
+
+/* Structure to be filled in by arc_compute_frame_size with register
+   save masks, and offsets for the current function.  */
+struct arc_frame_info
+{
+  unsigned int total_size;     /* # bytes that the entire frame takes up.  */
+  unsigned int extra_size;     /* # bytes of extra stuff.  */
+  unsigned int pretend_size;   /* # bytes we push and pretend caller did.  */
+  unsigned int args_size;      /* # bytes that outgoing arguments take up.  */
+  unsigned int reg_size;       /* # bytes needed to store regs.  */
+  unsigned int var_size;       /* # bytes that variables take up.  */
+  unsigned int reg_offset;     /* Offset from new sp to store regs.  */
+  unsigned int gmask;          /* Mask of saved gp registers.  */
+  int          initialized;    /* Nonzero if frame size already calculated.  */
+};
+
+/* Current frame information calculated by arc_compute_frame_size.  */
+static struct arc_frame_info current_frame_info;
+
+/* Zero structure to initialize current_frame_info.  */
+static struct arc_frame_info zero_frame_info;
+
+/* Type of function DECL.
+
+   The result is cached.  To reset the cache at the end of a function,
+   call with DECL = NULL_TREE.  */
+
+enum arc_function_type
+arc_compute_function_type (decl)
+     tree decl;
+{
+  tree a;
+  /* Cached value.  */
+  static enum arc_function_type fn_type = ARC_FUNCTION_UNKNOWN;
+  /* Last function we were called for.  */
+  static tree last_fn = NULL_TREE;
+
+  /* Resetting the cached value?  */
+  if (decl == NULL_TREE)
+    {
+      fn_type = ARC_FUNCTION_UNKNOWN;
+      last_fn = NULL_TREE;
+      return fn_type;
+    }
+
+  if (decl == last_fn && fn_type != ARC_FUNCTION_UNKNOWN)
+    return fn_type;
+
+  /* Assume we have a normal function (not an interrupt handler).  */
+  fn_type = ARC_FUNCTION_NORMAL;
+
+  /* Now see if this is an interrupt handler.  */
+  for (a = DECL_MACHINE_ATTRIBUTES (current_function_decl);
+       a;
+       a = TREE_CHAIN (a))
+    {
+      tree name = TREE_PURPOSE (a), args = TREE_VALUE (a);
+
+      if (name == get_identifier ("__interrupt__")
+         && list_length (args) == 1
+         && TREE_CODE (TREE_VALUE (args)) == STRING_CST)
+       {
+         tree value = TREE_VALUE (args);
+
+         if (!strcmp (TREE_STRING_POINTER (value), "ilink1"))
+           fn_type = ARC_FUNCTION_ILINK1;
+         else if (!strcmp (TREE_STRING_POINTER (value), "ilink2"))
+           fn_type = ARC_FUNCTION_ILINK2;
+         else
+           abort ();
+         break;
+       }
+    }
+
+  last_fn = decl;
+  return fn_type;
+}
+
+#define ILINK1_REGNUM 29
+#define ILINK2_REGNUM 30
+#define RETURN_ADDR_REGNUM 31
+#define FRAME_POINTER_MASK (1 << (FRAME_POINTER_REGNUM))
+#define RETURN_ADDR_MASK (1 << (RETURN_ADDR_REGNUM))
+
+/* Tell prologue and epilogue if register REGNO should be saved / restored.
+   The return address and frame pointer are treated separately.
+   Don't consider them here.  */
+#define MUST_SAVE_REGISTER(regno, interrupt_p) \
+((regno) != RETURN_ADDR_REGNUM && (regno) != FRAME_POINTER_REGNUM \
+ && (regs_ever_live[regno] && (!call_used_regs[regno] || interrupt_p)))
+
+#define MUST_SAVE_RETURN_ADDR (regs_ever_live[RETURN_ADDR_REGNUM])
+
+/* Return the bytes needed to compute the frame pointer from the current
+   stack pointer.
+
+   SIZE is the size needed for local variables.  */
+
+unsigned int
+arc_compute_frame_size (size)
+     int size;                 /* # of var. bytes allocated.  */
+{
+  int regno;
+  unsigned int total_size, var_size, args_size, pretend_size, extra_size;
+  unsigned int reg_size, reg_offset;
+  unsigned int gmask;
+  enum arc_function_type fn_type;
+  int interrupt_p;
+
+  var_size     = size;
+  args_size    = current_function_outgoing_args_size;
+  pretend_size = current_function_pretend_args_size;
+  extra_size   = FIRST_PARM_OFFSET (0);
+  total_size   = extra_size + pretend_size + args_size + var_size;
+  reg_offset   = FIRST_PARM_OFFSET(0) + current_function_outgoing_args_size;
+  reg_size     = 0;
+  gmask                = 0;
+
+  /* See if this is an interrupt handler.  Call used registers must be saved
+     for them too.  */
+  fn_type = arc_compute_function_type (current_function_decl);
+  interrupt_p = ARC_INTERRUPT_P (fn_type);
+
+  /* Calculate space needed for registers.
+     ??? We ignore the extension registers for now.  */
+
+  for (regno = 0; regno <= 31; regno++)
+    {
+      if (MUST_SAVE_REGISTER (regno, interrupt_p))
+       {
+         reg_size += UNITS_PER_WORD;
+         gmask |= 1 << regno;
+       }
+    }
+
+  total_size += reg_size;
+
+  /* If the only space to allocate is the fp/blink save area this is an
+     empty frame.  However, if we'll be making a function call we need to
+     allocate a stack frame for our callee's fp/blink save area.  */
+  if (total_size == extra_size
+      && !MUST_SAVE_RETURN_ADDR)
+    total_size = extra_size = 0;
+
+  total_size = ARC_STACK_ALIGN (total_size);
+
+  /* Save computed information.  */
+  current_frame_info.total_size   = total_size;
+  current_frame_info.extra_size   = extra_size;
+  current_frame_info.pretend_size = pretend_size;
+  current_frame_info.var_size     = var_size;
+  current_frame_info.args_size    = args_size;
+  current_frame_info.reg_size    = reg_size;
+  current_frame_info.reg_offset          = reg_offset;
+  current_frame_info.gmask       = gmask;
+  current_frame_info.initialized  = reload_completed;
+
+  /* Ok, we're done.  */
+  return total_size;
+}
+\f
+/* Common code to save/restore registers.  */
+
+void
+arc_save_restore (file, base_reg, offset, gmask, op)
+     FILE *file;
+     char *base_reg;
+     unsigned int offset;
+     unsigned int gmask;
+     char *op;
+{
+  int regno;
+
+  if (gmask == 0)
+    return;
+
+  for (regno = 0; regno <= 31; regno++)
+    {
+      if ((gmask & (1L << regno)) != 0)
+       {
+         fprintf (file, "\t%s %s,[%s,%d]\n",
+                    op, reg_names[regno], base_reg, offset);
+         offset += UNITS_PER_WORD;
+       }
+    }
+}
+\f
+/* Set up the stack and frame pointer (if desired) for the function.  */
+
+void
+arc_output_function_prologue (file, size)
+     FILE *file;
+     int size;
+{
+  char *sp_str = reg_names[STACK_POINTER_REGNUM];
+  char *fp_str = reg_names[FRAME_POINTER_REGNUM];
+  unsigned int gmask = current_frame_info.gmask;
+  enum arc_function_type fn_type = arc_compute_function_type (current_function_decl);
+
+  /* If this is an interrupt handler, set up our stack frame.
+     ??? Optimize later.  */
+  if (ARC_INTERRUPT_P (fn_type))
+    {
+      fprintf (file, "\t%s interrupt handler\n",
+              ASM_COMMENT_START);
+      fprintf (file, "\tsub %s,%s,16\n", sp_str, sp_str);
+    }
+
+  /* This is only for the human reader.  */
+  fprintf (file, "\t%s BEGIN PROLOGUE %s vars= %d, regs= %d, args= %d, extra= %d\n",
+          ASM_COMMENT_START, ASM_COMMENT_START,
+          current_frame_info.var_size,
+          current_frame_info.reg_size / 4,
+          current_frame_info.args_size,
+          current_frame_info.extra_size);
+
+  size = ARC_STACK_ALIGN (size);
+  size = (! current_frame_info.initialized
+          ? arc_compute_frame_size (size)
+          : current_frame_info.total_size);
+
+  /* These cases shouldn't happen.  Catch them now.  */
+  if (size == 0 && gmask)
+    abort ();
+
+  /* Allocate space for register argumenets if this is a variadic function.  */
+  if (current_frame_info.pretend_size != 0)
+    fprintf (file, "\tsub %s,%s,%d\n",
+            sp_str, sp_str, current_frame_info.pretend_size);
+
+  /* The home-grown ABI says link register is saved first.  */
+  if (MUST_SAVE_RETURN_ADDR)
+    fprintf (file, "\tst %s,[%s,%d]\n",
+            reg_names[RETURN_ADDR_REGNUM], sp_str, UNITS_PER_WORD);
+
+  /* Set up the previous frame pointer next (if we need to).  */
+  if (frame_pointer_needed)
+    {
+      fprintf (file, "\tst %s,[%s]\n", fp_str, sp_str);
+      fprintf (file, "\tmov %s,%s\n", fp_str, sp_str);
+    }
+
+  /* ??? We don't handle the case where the saved regs are more than 252
+     bytes away from sp.  This can be handled by decrementing sp once, saving
+     the regs, and then decrementing it again.  The epilogue doesn't have this
+     problem as the `ld' insn takes reg+limm values (though it would be more
+     efficient to avoid reg+limm).  */
+
+  /* Allocate the stack frame.  */
+  if (size - current_frame_info.pretend_size > 0)
+    fprintf (file, "\tsub %s,%s,%d\n",
+            sp_str, sp_str, size - current_frame_info.pretend_size);
+
+  /* Save any needed call-saved regs (and call-used if this is an
+     interrupt handler).  */
+  arc_save_restore (file, sp_str, current_frame_info.reg_offset,
+                   /* The zeroing of these two bits is unnecessary,
+                      but leave this in for clarity.  */
+                   gmask & ~(FRAME_POINTER_MASK | RETURN_ADDR_MASK),
+                   "st");
+
+  fprintf (file, "\t%s END PROLOGUE\n", ASM_COMMENT_START);
+}
+\f
+/* Do any necessary cleanup after a function to restore stack, frame,
+   and regs. */
+
+void
+arc_output_function_epilogue (file, size)
+     FILE *file;
+     int size;
+{
+  rtx epilogue_delay = current_function_epilogue_delay_list;
+  int noepilogue = FALSE;
+  enum arc_function_type fn_type = arc_compute_function_type (current_function_decl);
+
+  /* This is only for the human reader.  */
+  fprintf (file, "\t%s EPILOGUE\n", ASM_COMMENT_START);
+
+  size = ARC_STACK_ALIGN (size);
+  size = (!current_frame_info.initialized
+          ? arc_compute_frame_size (size)
+          : current_frame_info.total_size);
+
+  if (size == 0 && epilogue_delay == 0)
+    {
+      rtx insn = get_last_insn ();
+
+      /* If the last insn was a BARRIER, we don't have to write any code
+        because a jump (aka return) was put there.  */
+      if (GET_CODE (insn) == NOTE)
+       insn = prev_nonnote_insn (insn);
+      if (insn && GET_CODE (insn) == BARRIER)
+       noepilogue = TRUE;
+    }
+
+  if (!noepilogue)
+    {
+      unsigned int pretend_size = current_frame_info.pretend_size;
+      unsigned int frame_size = size - pretend_size;
+      int restored, fp_restored_p;
+      int can_trust_sp_p = !current_function_calls_alloca;
+      char *sp_str = reg_names[STACK_POINTER_REGNUM];
+      char *fp_str = reg_names[FRAME_POINTER_REGNUM];
+
+      /* ??? There are lots of optimizations that can be done here.
+        EG: Use fp to restore regs if it's closer.
+        Maybe in time we'll do them all.  For now, always restore regs from
+        sp, but don't restore sp if we don't have to.  */
+
+      if (!can_trust_sp_p)
+       {
+         if (!frame_pointer_needed)
+           abort ();
+         fprintf (file,"\tsub %s,%s,%d\t\t%s sp not trusted here\n",
+                  sp_str, fp_str, frame_size, ASM_COMMENT_START);
+       }
+
+      /* Restore any saved registers.  */
+      arc_save_restore (file, sp_str, current_frame_info.reg_offset,
+                       /* The zeroing of these two bits is unnecessary,
+                          but leave this in for clarity.  */
+                       current_frame_info.gmask & ~(FRAME_POINTER_MASK | RETURN_ADDR_MASK),
+                       "ld");
+
+      if (MUST_SAVE_RETURN_ADDR)
+       fprintf (file, "\tld %s,[%s,%d]\n",
+                reg_names[RETURN_ADDR_REGNUM],
+                frame_pointer_needed ? fp_str : sp_str,
+                UNITS_PER_WORD + (frame_pointer_needed ? 0 : frame_size));
+
+      /* Keep track of how much of the stack pointer we've restored.
+        It makes the following a lot more readable.  */
+      restored = 0;
+      fp_restored_p = 0;
+
+      /* We try to emit the epilogue delay slot insn right after the load
+        of the return address register so that it can execute with the
+        stack intact.  Secondly, loads are delayed.  */
+      /* ??? If stack intactness is important, always emit now.  */
+      if (MUST_SAVE_RETURN_ADDR && epilogue_delay != NULL_RTX)
+       {
+         final_scan_insn (XEXP (epilogue_delay, 0), file, 1, -2, 1);
+         epilogue_delay = NULL_RTX;
+       }
+
+      if (frame_pointer_needed)
+       {
+         /* Try to restore the frame pointer in the delay slot.  We can't,
+            however, if any of these is true.  */
+         if (epilogue_delay != NULL_RTX
+             || !SMALL_INT (frame_size)
+             || pretend_size
+             || ARC_INTERRUPT_P (fn_type))
+           {
+             /* Note that we restore fp and sp here!  */
+             fprintf (file, "\tld.a %s,[%s,%d]\n", fp_str, sp_str, frame_size);
+             restored += frame_size;
+             fp_restored_p = 1;
+           }
+       }
+      else if (!SMALL_INT (size /* frame_size + pretend_size */)
+              || ARC_INTERRUPT_P (fn_type))
+       {
+         fprintf (file, "\tadd %s,%s,%d\n", sp_str, sp_str, frame_size);
+         restored += frame_size;
+       }
+
+      /* These must be done before the return insn because the delay slot
+        does the final stack restore.  */
+      if (ARC_INTERRUPT_P (fn_type))
+       {
+         if (epilogue_delay)
+           {
+             final_scan_insn (XEXP (epilogue_delay, 0), file, 1, -2, 1);
+           }
+       }
+
+      /* Emit the return instruction.  */
+      {
+       static int regs[4] = {
+         0, RETURN_ADDR_REGNUM, ILINK1_REGNUM, ILINK2_REGNUM
+       };
+       fprintf (file, "\tj.d %s\n", reg_names[regs[fn_type]]);
+      }
+
+      /* If the only register saved is the return address, we need a
+        nop, unless we have an instruction to put into it.  Otherwise
+        we don't since reloading multiple registers doesn't reference
+        the register being loaded.  */
+
+      if (ARC_INTERRUPT_P (fn_type))
+       fprintf (file, "\tadd %s,%s,16\n", sp_str, sp_str);
+      else if (epilogue_delay != NULL_RTX)
+       {
+         if (frame_pointer_needed && !fp_restored_p)
+           abort ();
+         if (restored < size)
+           abort ();
+         final_scan_insn (XEXP (epilogue_delay, 0), file, 1, -2, 1);
+       }
+      else if (frame_pointer_needed && !fp_restored_p)
+       {
+         if (!SMALL_INT (frame_size))
+           abort ();
+         /* Note that we restore fp and sp here!  */
+         fprintf (file, "\tld.a %s,[%s,%d]\n", fp_str, sp_str, frame_size);
+       }
+      else if (restored < size)
+       {
+         if (!SMALL_INT (size - restored))
+           abort ();
+         fprintf (file, "\tadd %s,%s,%d\n",
+                  sp_str, sp_str, size - restored);
+       }
+      else
+       fprintf (file, "\tnop\n");
+    }
+
+  /* Reset state info for each function.  */
+  current_frame_info = zero_frame_info;
+  arc_compute_function_type (NULL_TREE);
+}
+\f
+/* Define the number of delay slots needed for the function epilogue.
+
+   Interrupt handlers can't have any epilogue delay slots (it's always needed
+   for something else, I think).  For normal functions, we have to worry about
+   using call-saved regs as they'll be restored before the delay slot insn.
+   Functions with non-empty frames already have enough choices for the epilogue
+   delay slot so for now we only consider functions with empty frames.  */
+
+int
+arc_delay_slots_for_epilogue ()
+{
+  if (arc_compute_function_type (current_function_decl) != ARC_FUNCTION_NORMAL)
+    return 0;
+  if (!current_frame_info.initialized)
+    (void) arc_compute_frame_size (get_frame_size ());
+  if (current_frame_info.total_size == 0)
+    return 1;
+  return 0;
+}
+
+/* Return true if TRIAL is a valid insn for the epilogue delay slot.
+   Any single length instruction which doesn't reference the stack or frame
+   pointer or any call-saved register is OK.  SLOT will always be 0.  */
+
+int
+arc_eligible_for_epilogue_delay (trial, slot)
+     rtx trial;
+     int slot;
+{
+  if (slot != 0)
+    abort ();
+
+  if (get_attr_length (trial) == 1
+      /* If registers where saved, presumably there's more than enough
+        possibilities for the delay slot.  The alternative is something
+        more complicated (of course, if we expanded the epilogue as rtl
+        this problem would go away).  */
+      /* ??? Note that this will always be true since only functions with
+        empty frames have epilogue delay slots.  See
+        arc_delay_slots_for_epilogue.  */
+      && current_frame_info.gmask == 0
+      && ! reg_mentioned_p (stack_pointer_rtx, PATTERN (trial))
+      && ! reg_mentioned_p (frame_pointer_rtx, PATTERN (trial)))
+    return 1;
+  return 0;
+}
+\f
+/* PIC */
+
+/* Set up PIC-specific rtl.  This should not cause any insns
+   to be emitted.  */
+
+void
+arc_initialize_pic ()
+{
+  /* nothing to do */
+}
+
+/* Emit special PIC prologues and epilogues.  */
+
+void
+arc_finalize_pic ()
+{
+  /* nothing to do */
+}
+\f
+/* Return true if OP is a shift operator.  */
+
+int
+shift_operator (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  switch (GET_CODE (op))
+    {
+    case ASHIFTRT:
+    case LSHIFTRT:
+    case ASHIFT:
+      return 1;
+    default:
+      return 0;
+    }
+}
+
+/* Output the assembler code for doing a shift.
+   We go to a bit of trouble to generate efficient code as the ARC only has
+   single bit shifts.  This is taken from the h8300 port.  We only have one
+   mode of shifting and can't access individual bytes like the h8300 can, so
+   this is greatly simplified (at the expense of not generating hyper-
+   efficient code).
+
+   This function is not used if the variable shift insns are present.  */
+
+/* ??? We assume the output operand is the same as operand 1.
+   This can be optimized (deleted) in the case of 1 bit shifts.  */
+/* ??? We use the loop register here.  We don't use it elsewhere (yet) and
+   using it here will give us a chance to play with it.  */
+
+char *
+output_shift (operands)
+     rtx *operands;
+{
+  static int loopend_lab;
+  rtx shift = operands[3];
+  enum machine_mode mode = GET_MODE (shift);
+  enum rtx_code code = GET_CODE (shift);
+  char *shift_one;
+
+  if (mode != SImode)
+    abort ();
+
+  switch (code)
+    {
+    case ASHIFT:   shift_one = "asl %0,%0"; break;
+    case ASHIFTRT: shift_one = "asr %0,%0"; break;
+    case LSHIFTRT: shift_one = "lsr %0,%0"; break;
+    default:       abort ();
+    }
+
+  if (GET_CODE (operands[2]) != CONST_INT)
+    {
+      if (optimize)
+       output_asm_insn ("mov lp_count,%2", operands);
+      else
+       output_asm_insn ("mov %4,%2", operands);
+      goto shiftloop;
+    }
+  else
+    {
+      int n = INTVAL (operands[2]);
+
+      /* If the count is negative, make it 0.  */
+      if (n < 0)
+       n = 0;
+      /* If the count is too big, truncate it.
+         ANSI says shifts of GET_MODE_BITSIZE are undefined - we choose to
+        do the intuitive thing.  */
+      else if (n > GET_MODE_BITSIZE (mode))
+       n = GET_MODE_BITSIZE (mode);
+
+      /* First see if we can do them inline.  */
+      if (n <= 8)
+       {
+         while (--n >= 0)
+           output_asm_insn (shift_one, operands);
+       }
+      /* See if we can use a rotate/and.  */
+      else if (n == BITS_PER_WORD - 1)
+       {
+         switch (code)
+           {
+           case ASHIFT :
+             output_asm_insn ("and %0,%0,1\n\tror %0,%0", operands);
+             break;
+           case ASHIFTRT :
+             /* The ARC doesn't have a rol insn.  Use something else.  */
+             output_asm_insn ("asl.f 0,%0\n\tsbc %0,0,0", operands);
+             break;
+           case LSHIFTRT :
+             /* The ARC doesn't have a rol insn.  Use something else.  */
+             output_asm_insn ("asl.f 0,%0\n\tadc %0,0,0", operands);
+             break;
+           }
+       }
+      /* Must loop.  */
+      else
+       {
+         char buf[100];
+
+         if (optimize)
+           output_asm_insn ("mov lp_count,%c2", operands);
+         else
+           output_asm_insn ("mov %4,%c2", operands);
+       shiftloop:
+         if (optimize)
+           {
+             if (flag_pic)
+               sprintf ("lr %%4,[status]\n\tadd %%4,%%4,6\t%s single insn loop start",
+                        ASM_COMMENT_START);
+             else
+               sprintf (buf, "mov %%4,%%%%st(1f)\t%s (single insn loop start) >> 2",
+                        ASM_COMMENT_START);
+             output_asm_insn (buf, operands);
+             output_asm_insn ("sr %4,[lp_start]", operands);
+             output_asm_insn ("add %4,%4,1", operands);
+             output_asm_insn ("sr %4,[lp_end]", operands);
+             output_asm_insn ("nop\n\tnop", operands);
+             if (flag_pic)
+               asm_fprintf (asm_out_file, "\t%s single insn loop\n",
+                            ASM_COMMENT_START);
+             else
+               asm_fprintf (asm_out_file, "1:\t%s single insn loop\n",
+                            ASM_COMMENT_START);
+             output_asm_insn (shift_one, operands);
+           }
+         else 
+           {
+             asm_fprintf (asm_out_file, "1:\t%s begin shift loop\n",
+                          ASM_COMMENT_START);
+             output_asm_insn ("sub.f %4,%4,1", operands);
+             output_asm_insn ("nop", operands);
+             output_asm_insn ("bn.nd 2f", operands);
+             output_asm_insn (shift_one, operands);
+             output_asm_insn ("b.nd 1b", operands);
+             asm_fprintf (asm_out_file, "2:\t%s end shift loop\n",
+                          ASM_COMMENT_START);
+           }
+       }
+    }
+
+  return "";
+}
+\f
+/* Nested function support.  */
+
+/* Emit RTL insns to initialize the variable parts of a trampoline.
+   FNADDR is an RTX for the address of the function's pure code.
+   CXT is an RTX for the static chain value for the function.  */
+
+void
+arc_initialize_trampoline (tramp, fnaddr, cxt)
+     rtx tramp, fnaddr, cxt;
+{
+}
+\f
+/* Set the cpu type and print out other fancy things,
+   at the top of the file.  */
+
+void
+arc_asm_file_start (file)
+     FILE *file;
+{
+  fprintf (file, "\t.cpu %s\n", arc_cpu_string);
+}
+\f
+/* Print operand X (an rtx) in assembler syntax to file FILE.
+   CODE is a letter or dot (`z' in `%z0') or 0 if no letter was specified.
+   For `%' followed by punctuation, CODE is the punctuation and X is null.  */
+
+void
+arc_print_operand (file, x, code)
+     FILE *file;
+     rtx x;
+     int code;
+{
+  switch (code)
+    {
+    case '#' :
+      /* Conditional brances.  For now these are equivalent.  */
+    case '*' :
+      /* Unconditional branches.  Output the appropriate delay slot suffix.  */
+      if (!final_sequence || XVECLEN (final_sequence, 0) == 1)
+       {
+         /* There's nothing in the delay slot.  */
+         fputs (".nd", file);
+       }
+      else
+       {
+         rtx jump = XVECEXP (final_sequence, 0, 0);
+         rtx delay = XVECEXP (final_sequence, 0, 1);
+         if (INSN_ANNULLED_BRANCH_P (jump))
+           fputs (INSN_FROM_TARGET_P (delay) ? ".jd" : ".nd", file);
+         else
+           fputs (".d", file);
+       }
+      return;
+    case '?' : /* with leading "." */
+    case '!' : /* without leading "." */
+      /* This insn can be conditionally executed.  See if the ccfsm machinery
+        says it should be conditionalized.  */
+      if (arc_ccfsm_state == 3 || arc_ccfsm_state == 4)
+       {
+         /* Is this insn in a delay slot?  */
+         if (final_sequence && XVECLEN (final_sequence, 0) == 2)
+           {
+             rtx insn = XVECEXP (final_sequence, 0, 1);
+
+             /* If the insn is annulled and is from the target path, we need
+                to inverse the condition test.  */
+             if (INSN_ANNULLED_BRANCH_P (insn))
+               {
+                 if (INSN_FROM_TARGET_P (insn))
+                   fprintf (file, "%s%s",
+                            code == '?' ? "." : "",
+                            arc_condition_codes[ARC_INVERSE_CONDITION_CODE (arc_ccfsm_current_cc)]);
+                 else
+                   fprintf (file, "%s%s",
+                            code == '?' ? "." : "",
+                            arc_condition_codes[arc_ccfsm_current_cc]);
+               }
+             else
+               /* This insn is executed for either path, so don't
+                  conditionalize it at all.  */
+               ; /* nothing to do */
+           }
+         else
+           {
+             /* This insn isn't in a delay slot.  */
+             fprintf (file, "%s%s",
+                      code == '?' ? "." : "",
+                      arc_condition_codes[arc_ccfsm_current_cc]);
+           }
+       }
+      return;
+    case '~' :
+      /* Output a nop if we're between a set of the condition codes,
+        and a conditional branch.  */
+      if (last_insn_set_cc_p)
+       fputs ("nop\n\t", file);
+      return;
+    case 'd' :
+      fputs (arc_condition_codes[get_arc_condition_code (x)], file);
+      return;
+    case 'D' :
+      fputs (arc_condition_codes[ARC_INVERSE_CONDITION_CODE
+                                (get_arc_condition_code (x))],
+            file);
+      return;
+    case 'R' :
+      /* Write second word of DImode or DFmode reference,
+        register or memory.  */
+      if (GET_CODE (x) == REG)
+       fputs (reg_names[REGNO (x)+1], file);
+      else if (GET_CODE (x) == MEM)
+       {
+         fputc ('[', file);
+         /* Handle possible auto-increment.  Since it is pre-increment and
+            we have already done it, we can just use an offset of four.  */
+         /* ??? This is taken from rs6000.c I think.  I don't think it is
+            currently necessary, but keep it around.  */
+         if (GET_CODE (XEXP (x, 0)) == PRE_INC
+             || GET_CODE (XEXP (x, 0)) == PRE_DEC)
+           output_address (plus_constant (XEXP (XEXP (x, 0), 0), 4));
+         else
+           output_address (plus_constant (XEXP (x, 0), 4));
+         fputc (']', file);
+       }
+      else
+       output_operand_lossage ("invalid operand to %R code");
+      return;
+    case 'S' :
+      if ((GET_CODE (x) == SYMBOL_REF && SYMBOL_REF_FLAG (x))
+         || GET_CODE (x) == LABEL_REF)
+       {
+         fprintf (file, "%%st(");
+         output_addr_const (file, x);
+         fprintf (file, ")");
+         return;
+       }
+      break;
+    case 'H' :
+    case 'L' :
+      if (GET_CODE (x) == REG)
+       {
+         /* L = least significant word, H = most significant word */
+         if ((TARGET_BIG_ENDIAN != 0) ^ (code == 'L'))
+           fputs (reg_names[REGNO (x)], file);
+         else
+           fputs (reg_names[REGNO (x)+1], file);
+       }
+      else if (GET_CODE (x) == CONST_INT
+              || GET_CODE (x) == CONST_DOUBLE)
+       {
+         rtx first, second;
+
+         split_double (x, &first, &second);
+         fprintf (file, "0x%08lx",
+                  code == 'L' ? INTVAL (first) : INTVAL (second));
+       }
+      else
+       output_operand_lossage ("invalid operand to %H/%L code");
+      return;
+    case 'A' :
+      {
+       REAL_VALUE_TYPE d;
+       char str[30];
+
+       if (GET_CODE (x) != CONST_DOUBLE
+           || GET_MODE_CLASS (GET_MODE (x)) != MODE_FLOAT)
+         abort ();
+       REAL_VALUE_FROM_CONST_DOUBLE (d, x);
+       REAL_VALUE_TO_DECIMAL (d, "%.20e", str);
+       fprintf (file, "%s", str);
+       return;
+      }
+    case 'U' :
+      /* Output a load/store with update indicator if appropriate.  */
+      if (GET_CODE (x) == MEM)
+       {
+         if (GET_CODE (XEXP (x, 0)) == PRE_INC
+             || GET_CODE (XEXP (x, 0)) == PRE_DEC)
+           fputs (".a", file);
+       }
+      else
+       output_operand_lossage ("invalid operand to %U code");
+      return;
+    case 'V' :
+      /* Output cache bypass indicator for a load/store insn.  Volatile memory
+        refs are defined to use the cache bypass mechanism.  */
+      if (GET_CODE (x) == MEM)
+       {
+         if (MEM_VOLATILE_P (x))
+           fputs (".di", file);
+       }
+      else
+       output_operand_lossage ("invalid operand to %V code");
+      return;
+    case 0 :
+      /* Do nothing special.  */
+      break;
+    default :
+      /* Unknown flag.  */
+      output_operand_lossage ("invalid operand output code");
+    }
+
+  switch (GET_CODE (x))
+    {
+    case REG :
+      fputs (reg_names[REGNO (x)], file);
+      break;
+    case MEM :
+      fputc ('[', file);
+      if (GET_CODE (XEXP (x, 0)) == PRE_INC)
+       output_address (plus_constant (XEXP (XEXP (x, 0), 0),
+                                      GET_MODE_SIZE (GET_MODE (x))));
+      else if (GET_CODE (XEXP (x, 0)) == PRE_DEC)
+       output_address (plus_constant (XEXP (XEXP (x, 0), 0),
+                                      - GET_MODE_SIZE (GET_MODE (x))));
+      else
+       output_address (XEXP (x, 0));
+      fputc (']', file);
+      break;
+    case CONST_DOUBLE :
+      /* We handle SFmode constants here as output_addr_const doesn't.  */
+      if (GET_MODE (x) == SFmode)
+       {
+         REAL_VALUE_TYPE d;
+         long l;
+
+         REAL_VALUE_FROM_CONST_DOUBLE (d, x);
+         REAL_VALUE_TO_TARGET_SINGLE (d, l);
+         fprintf (file, "0x%08lx", l);
+         break;
+       }
+      /* Fall through.  Let output_addr_const deal with it.  */
+    default :
+      output_addr_const (file, x);
+      break;
+    }
+}
+
+/* Print a memory address as an operand to reference that memory location.  */
+
+void
+arc_print_operand_address (file, addr)
+     FILE *file;
+     rtx addr;
+{
+  register rtx base, index = 0;
+  int offset = 0;
+
+  switch (GET_CODE (addr))
+    {
+    case REG :
+      fputs (reg_names[REGNO (addr)], file);
+      break;
+    case SYMBOL_REF :
+      if (/*???*/ 0 && SYMBOL_REF_FLAG (addr))
+       {
+         fprintf (file, "%%st(");
+         output_addr_const (file, addr);
+         fprintf (file, ")");
+       }
+      else
+       output_addr_const (file, addr);
+      break;
+    case PLUS :
+      if (GET_CODE (XEXP (addr, 0)) == CONST_INT)
+       offset = INTVAL (XEXP (addr, 0)), base = XEXP (addr, 1);
+      else if (GET_CODE (XEXP (addr, 1)) == CONST_INT)
+       offset = INTVAL (XEXP (addr, 1)), base = XEXP (addr, 0);
+      else
+       base = XEXP (addr, 0), index = XEXP (addr, 1);
+      if (GET_CODE (base) != REG)
+       abort ();
+      fputs (reg_names[REGNO (base)], file);
+      if (index == 0)
+       {
+         if (offset != 0)
+           fprintf (file, ",%d", offset);
+       }
+      else if (GET_CODE (index) == REG)
+       fprintf (file, ",%s", reg_names[REGNO (index)]);
+      else if (GET_CODE (index) == SYMBOL_REF)
+       fputc (',', file), output_addr_const (file, index);
+      else
+       abort ();
+      break;
+    case PRE_INC :
+    case PRE_DEC :
+      /* We shouldn't get here as we've lost the mode of the memory object
+        (which says how much to inc/dec by.  */
+      abort ();
+      break;
+    default :
+      output_addr_const (file, addr);
+      break;
+    }
+}
+
+/* Update compare/branch separation marker.  */
+
+static void
+record_cc_ref (insn)
+     rtx insn;
+{
+  last_insn_set_cc_p = current_insn_set_cc_p;
+
+  switch (get_attr_cond (insn))
+    {
+    case COND_SET :
+    case COND_SET_ZN :
+    case COND_SET_ZNC :
+      if (get_attr_length (insn) == 1)
+       current_insn_set_cc_p = 1;
+      else
+       current_insn_set_cc_p = 0;
+      break;
+    default :
+      current_insn_set_cc_p = 0;
+      break;
+    }
+}
+\f
+/* Conditional execution support.
+
+   This is based on the ARM port but for now is much simpler.
+
+   A finite state machine takes care of noticing whether or not instructions
+   can be conditionally executed, and thus decrease execution time and code
+   size by deleting branch instructions.  The fsm is controlled by
+   final_prescan_insn, and controls the actions of PRINT_OPERAND.  The patterns
+   in the .md file for the branch insns also have a hand in this.  */
+
+/* The state of the fsm controlling condition codes are:
+   0: normal, do nothing special
+   1: don't output this insn
+   2: don't output this insn
+   3: make insns conditional
+   4: make insns conditional
+
+   State transitions (state->state by whom, under what condition):
+   0 -> 1 final_prescan_insn, if insn is conditional branch
+   0 -> 2 final_prescan_insn, if the `target' is an unconditional branch
+   1 -> 3 branch patterns, after having not output the conditional branch
+   2 -> 4 branch patterns, after having not output the conditional branch
+   3 -> 0 ASM_OUTPUT_INTERNAL_LABEL, if the `target' label is reached
+          (the target label has CODE_LABEL_NUMBER equal to
+         arc_ccfsm_target_label).
+   4 -> 0 final_prescan_insn, if `target' unconditional branch is reached
+
+   If the jump clobbers the conditions then we use states 2 and 4.
+
+   A similar thing can be done with conditional return insns.
+
+   We also handle separating branches from sets of the condition code.
+   This is done here because knowledge of the ccfsm state is required,
+   we may not be outputting the branch.  */
+
+void
+arc_final_prescan_insn (insn, opvec, noperands)
+     rtx insn;
+     rtx *opvec;
+     int noperands;
+{
+  /* BODY will hold the body of INSN.  */
+  register rtx body = PATTERN (insn);
+
+  /* This will be 1 if trying to repeat the trick (ie: do the `else' part of
+     an if/then/else), and things need to be reversed.  */
+  int reverse = 0;
+
+  /* If we start with a return insn, we only succeed if we find another one. */
+  int seeking_return = 0;
+  
+  /* START_INSN will hold the insn from where we start looking.  This is the
+     first insn after the following code_label if REVERSE is true.  */
+  rtx start_insn = insn;
+
+  /* Update compare/branch separation marker.  */
+  record_cc_ref (insn);
+
+  /* Allow -mdebug-ccfsm to turn this off so we can see how well it does.
+     We can't do this in macro FINAL_PRESCAN_INSN because it's called from
+     final_scan_insn which has `optimize' as a local.  */
+  if (optimize < 2 || TARGET_NO_COND_EXEC)
+    return;
+
+  /* If in state 4, check if the target branch is reached, in order to
+     change back to state 0.  */
+  if (arc_ccfsm_state == 4)
+    {
+      if (insn == arc_ccfsm_target_insn)
+       {
+         arc_ccfsm_target_insn = NULL;
+         arc_ccfsm_state = 0;
+       }
+      return;
+    }
+
+  /* If in state 3, it is possible to repeat the trick, if this insn is an
+     unconditional branch to a label, and immediately following this branch
+     is the previous target label which is only used once, and the label this
+     branch jumps to is not too far off.  Or in other words "we've done the
+     `then' part, see if we can do the `else' part."  */
+  if (arc_ccfsm_state == 3)
+    {
+      if (simplejump_p (insn))
+       {
+         start_insn = next_nonnote_insn (start_insn);
+         if (GET_CODE (start_insn) == BARRIER)
+           {
+             /* ??? Isn't this always a barrier?  */
+             start_insn = next_nonnote_insn (start_insn);
+           }
+         if (GET_CODE (start_insn) == CODE_LABEL
+             && CODE_LABEL_NUMBER (start_insn) == arc_ccfsm_target_label
+             && LABEL_NUSES (start_insn) == 1)
+           reverse = TRUE;
+         else
+           return;
+       }
+      else if (GET_CODE (body) == RETURN)
+        {
+         start_insn = next_nonnote_insn (start_insn);
+         if (GET_CODE (start_insn) == BARRIER)
+           start_insn = next_nonnote_insn (start_insn);
+         if (GET_CODE (start_insn) == CODE_LABEL
+             && CODE_LABEL_NUMBER (start_insn) == arc_ccfsm_target_label
+             && LABEL_NUSES (start_insn) == 1)
+           {
+             reverse = TRUE;
+             seeking_return = 1;
+           }
+         else
+           return;
+        }
+      else
+       return;
+    }
+
+  if (GET_CODE (insn) != JUMP_INSN)
+    return;
+
+  /* This jump might be paralled with a clobber of the condition codes,
+     the jump should always come first.  */
+  if (GET_CODE (body) == PARALLEL && XVECLEN (body, 0) > 0)
+    body = XVECEXP (body, 0, 0);
+
+  if (reverse
+      || (GET_CODE (body) == SET && GET_CODE (SET_DEST (body)) == PC
+         && GET_CODE (SET_SRC (body)) == IF_THEN_ELSE))
+    {
+      int insns_skipped = 0, fail = FALSE, succeed = FALSE;
+      /* Flag which part of the IF_THEN_ELSE is the LABEL_REF.  */
+      int then_not_else = TRUE;
+      /* Nonzero if next insn must be the target label.  */
+      int next_must_be_target_label_p;
+      rtx this_insn = start_insn, label = 0;
+
+      /* Register the insn jumped to.  */
+      if (reverse)
+        {
+         if (!seeking_return)
+           label = XEXP (SET_SRC (body), 0);
+        }
+      else if (GET_CODE (XEXP (SET_SRC (body), 1)) == LABEL_REF)
+       label = XEXP (XEXP (SET_SRC (body), 1), 0);
+      else if (GET_CODE (XEXP (SET_SRC (body), 2)) == LABEL_REF)
+       {
+         label = XEXP (XEXP (SET_SRC (body), 2), 0);
+         then_not_else = FALSE;
+       }
+      else if (GET_CODE (XEXP (SET_SRC (body), 1)) == RETURN)
+       seeking_return = 1;
+      else if (GET_CODE (XEXP (SET_SRC (body), 2)) == RETURN)
+        {
+         seeking_return = 1;
+         then_not_else = FALSE;
+        }
+      else
+       abort ();
+
+      /* See how many insns this branch skips, and what kind of insns.  If all
+        insns are okay, and the label or unconditional branch to the same
+        label is not too far away, succeed.  */
+      for (insns_skipped = 0, next_must_be_target_label_p = FALSE;
+          !fail && !succeed && insns_skipped < MAX_INSNS_SKIPPED;
+          insns_skipped++)
+       {
+         rtx scanbody;
+
+         this_insn = next_nonnote_insn (this_insn);
+         if (!this_insn)
+           break;
+
+         if (next_must_be_target_label_p)
+           {
+             if (GET_CODE (this_insn) == BARRIER)
+               continue;
+             if (GET_CODE (this_insn) == CODE_LABEL
+                 && this_insn == label)
+               {
+                 arc_ccfsm_state = 1;
+                 succeed = TRUE;
+               }
+             else
+               fail = TRUE;
+             break;
+           }
+
+         scanbody = PATTERN (this_insn);
+
+         switch (GET_CODE (this_insn))
+           {
+           case CODE_LABEL:
+             /* Succeed if it is the target label, otherwise fail since
+                control falls in from somewhere else.  */
+             if (this_insn == label)
+               {
+                 arc_ccfsm_state = 1;
+                 succeed = TRUE;
+               }
+             else
+               fail = TRUE;
+             break;
+
+           case BARRIER:
+             /* Succeed if the following insn is the target label.
+                Otherwise fail.  
+                If return insns are used then the last insn in a function 
+                will be a barrier. */
+             next_must_be_target_label_p = TRUE;
+             break;
+
+           case CALL_INSN:
+             /* Can handle a call insn if there are no insns after it.
+                IE: The next "insn" is the target label.  We don't have to
+                worry about delay slots as such insns are SEQUENCE's inside
+                INSN's.  ??? It is possible to handle such insns though.  */
+             if (get_attr_cond (this_insn) == COND_CANUSE)
+               next_must_be_target_label_p = TRUE;
+             else
+               fail = TRUE;
+             break;
+
+           case JUMP_INSN:
+             /* If this is an unconditional branch to the same label, succeed.
+                If it is to another label, do nothing.  If it is conditional,
+                fail.  */
+             /* ??? Probably, the test for the SET and the PC are unnecessary. */
+
+             if (GET_CODE (scanbody) == SET
+                 && GET_CODE (SET_DEST (scanbody)) == PC)
+               {
+                 if (GET_CODE (SET_SRC (scanbody)) == LABEL_REF
+                     && XEXP (SET_SRC (scanbody), 0) == label && !reverse)
+                   {
+                     arc_ccfsm_state = 2;
+                     succeed = TRUE;
+                   }
+                 else if (GET_CODE (SET_SRC (scanbody)) == IF_THEN_ELSE)
+                   fail = TRUE;
+               }
+             else if (GET_CODE (scanbody) == RETURN
+                      && seeking_return)
+               {
+                 arc_ccfsm_state = 2;
+                 succeed = TRUE;
+               }
+             else if (GET_CODE (scanbody) == PARALLEL)
+               {
+                 if (get_attr_cond (this_insn) != COND_CANUSE)
+                   fail = TRUE;
+               }
+             break;
+
+           case INSN:
+             /* We can only do this with insns that can use the condition
+                codes (and don't set them).  */
+             if (GET_CODE (scanbody) == SET
+                 || GET_CODE (scanbody) == PARALLEL)
+               {
+                 if (get_attr_cond (this_insn) != COND_CANUSE)
+                   fail = TRUE;
+               }
+             /* We can't handle other insns like sequences.  */
+             else
+               fail = TRUE;
+             break;
+
+           default:
+             break;
+           }
+       }
+
+      if (succeed)
+       {
+         if ((!seeking_return) && (arc_ccfsm_state == 1 || reverse))
+           arc_ccfsm_target_label = CODE_LABEL_NUMBER (label);
+         else if (seeking_return || arc_ccfsm_state == 2)
+           {
+             while (this_insn && GET_CODE (PATTERN (this_insn)) == USE)
+               {
+                 this_insn = next_nonnote_insn (this_insn);
+                 if (this_insn && (GET_CODE (this_insn) == BARRIER
+                                   || GET_CODE (this_insn) == CODE_LABEL))
+                   abort ();
+               }
+             if (!this_insn)
+               {
+                 /* Oh dear! we ran off the end, give up.  */
+                 insn_extract (insn);
+                 arc_ccfsm_state = 0;
+                 arc_ccfsm_target_insn = NULL;
+                 return;
+               }
+             arc_ccfsm_target_insn = this_insn;
+           }
+         else
+           abort ();
+
+         /* If REVERSE is true, ARM_CURRENT_CC needs to be inverted from
+            what it was.  */
+         if (!reverse)
+           arc_ccfsm_current_cc = get_arc_condition_code (XEXP (SET_SRC (body),
+                                                                0));
+
+         if (reverse || then_not_else)
+           arc_ccfsm_current_cc = ARC_INVERSE_CONDITION_CODE (arc_ccfsm_current_cc);
+       }
+
+      /* Restore recog_operand.  Getting the attributes of other insns can
+        destroy this array, but final.c assumes that it remains intact
+        accross this call; since the insn has been recognized already we
+        call insn_extract direct. */
+      insn_extract (insn);
+    }
+}
+
+/* Record that we are currently outputting label NUM with prefix PREFIX.
+   It it's the label we're looking for, reset the ccfsm machinery.
+
+   Called from ASM_OUTPUT_INTERNAL_LABEL.  */
+
+void
+arc_ccfsm_at_label (prefix, num)
+     char *prefix;
+     int num;
+{
+  if (arc_ccfsm_state == 3 && arc_ccfsm_target_label == num
+      && !strcmp (prefix, "L"))
+    {
+      arc_ccfsm_state = 0;
+      arc_ccfsm_target_insn = NULL_RTX;
+    }
+}
+
+/* See if the current insn, which is a conditional branch, is to be
+   deleted.  */
+
+int
+arc_ccfsm_branch_deleted_p ()
+{
+  if (arc_ccfsm_state == 1 || arc_ccfsm_state == 2)
+    return 1;
+  return 0;
+}
+
+/* Record a branch isn't output because subsequent insns can be
+   conditionalized.  */
+
+void
+arc_ccfsm_record_branch_deleted ()
+{
+  /* Indicate we're conditionalizing insns now.  */
+  arc_ccfsm_state += 2;
+
+  /* If the next insn is a subroutine call, we still need a nop between the
+     cc setter and user.  We need to undo the effect of calling record_cc_ref
+     for the just deleted branch.  */
+  current_insn_set_cc_p = last_insn_set_cc_p;
+}