OSDN Git Service

2005-07-07 Khem Raj <kraj@mvista.com>
[pf3gnuchains/gcc-fork.git] / gcc / config / arm / arm.c
index 7345510..31fa08e 100644 (file)
@@ -19,8 +19,8 @@
 
    You should have received a copy of the GNU General Public License
    along with GCC; see the file COPYING.  If not, write to
-   the Free Software Foundation, 59 Temple Place - Suite 330,
-   Boston, MA 02111-1307, USA.  */
+   the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301, USA.  */
 
 #include "config.h"
 #include "system.h"
@@ -73,6 +73,7 @@ static int thumb_far_jump_used_p (void);
 static bool thumb_force_lr_save (void);
 static int const_ok_for_op (HOST_WIDE_INT, enum rtx_code);
 static rtx emit_sfm (int, int);
+static int arm_size_return_regs (void);
 #ifndef AOF_ASSEMBLER
 static bool arm_assemble_integer (rtx, unsigned int, int);
 #endif
@@ -144,6 +145,9 @@ static void emit_constant_insn (rtx cond, rtx pattern);
 static int arm_arg_partial_bytes (CUMULATIVE_ARGS *, enum machine_mode,
                                  tree, bool);
 
+#ifdef OBJECT_FORMAT_ELF
+static void arm_elf_asm_constructor (rtx, int);
+#endif
 #ifndef ARM_PE
 static void arm_encode_section_info (tree, rtx, int);
 #endif
@@ -167,6 +171,10 @@ static bool arm_default_short_enums (void);
 static bool arm_align_anon_bitfield (void);
 static bool arm_return_in_msb (tree);
 static bool arm_must_pass_in_stack (enum machine_mode, tree);
+#ifdef TARGET_UNWIND_INFO
+static void arm_unwind_emit (FILE *, rtx);
+static bool arm_output_ttype (rtx);
+#endif
 
 static tree arm_cxx_guard_type (void);
 static bool arm_cxx_guard_mask_bit (void);
@@ -178,6 +186,7 @@ static void arm_cxx_determine_class_data_visibility (tree);
 static bool arm_cxx_class_data_always_comdat (void);
 static bool arm_cxx_use_aeabi_atexit (void);
 static void arm_init_libfuncs (void);
+static bool arm_handle_option (size_t, const char *, int);
 static unsigned HOST_WIDE_INT arm_shift_truncation_mask (enum machine_mode);
 \f
 /* Initialize the GCC target structure.  */
@@ -218,6 +227,11 @@ static unsigned HOST_WIDE_INT arm_shift_truncation_mask (enum machine_mode);
 #undef  TARGET_ASM_FUNCTION_EPILOGUE
 #define TARGET_ASM_FUNCTION_EPILOGUE arm_output_function_epilogue
 
+#undef  TARGET_DEFAULT_TARGET_FLAGS
+#define TARGET_DEFAULT_TARGET_FLAGS (TARGET_DEFAULT | MASK_SCHED_PROLOG)
+#undef  TARGET_HANDLE_OPTION
+#define TARGET_HANDLE_OPTION arm_handle_option
+
 #undef  TARGET_COMP_TYPE_ATTRIBUTES
 #define TARGET_COMP_TYPE_ATTRIBUTES arm_comp_type_attributes
 
@@ -327,6 +341,18 @@ static unsigned HOST_WIDE_INT arm_shift_truncation_mask (enum machine_mode);
 #undef TARGET_MUST_PASS_IN_STACK
 #define TARGET_MUST_PASS_IN_STACK arm_must_pass_in_stack
 
+#ifdef TARGET_UNWIND_INFO
+#undef TARGET_UNWIND_EMIT
+#define TARGET_UNWIND_EMIT arm_unwind_emit
+
+/* EABI unwinding tables use a different format for the typeinfo tables.  */
+#undef TARGET_ASM_TTYPE
+#define TARGET_ASM_TTYPE arm_output_ttype
+
+#undef TARGET_ARM_EABI_UNWINDER
+#define TARGET_ARM_EABI_UNWINDER true
+#endif /* TARGET_UNWIND_INFO */
+
 struct gcc_target targetm = TARGET_INITIALIZER;
 \f
 /* Obstack for minipool constant handling.  */
@@ -364,23 +390,7 @@ enum float_abi_type arm_float_abi;
 /* Which ABI to use.  */
 enum arm_abi_type arm_abi;
 
-/* Set by the -mfpu=... option.  */
-const char * target_fpu_name = NULL;
-
-/* Set by the -mfpe=... option.  */
-const char * target_fpe_name = NULL;
-
-/* Set by the -mfloat-abi=... option.  */
-const char * target_float_abi_name = NULL;
-
-/* Set by the legacy -mhard-float and -msoft-float options.  */
-const char * target_float_switch = NULL;
-
-/* Set by the -mabi=... option.  */
-const char * target_abi_name = NULL;
-
 /* Used to parse -mstructure_size_boundary command line option.  */
-const char * structure_size_string = NULL;
 int    arm_structure_size_boundary = DEFAULT_STRUCTURE_SIZE_BOUNDARY;
 
 /* Used for Thumb call_via trampolines.  */
@@ -491,7 +501,6 @@ int arm_cpp_interwork = 0;
 enum machine_mode output_memory_reference_mode;
 
 /* The register number to be used for the PIC offset register.  */
-const char * arm_pic_register_string = NULL;
 int arm_pic_register = INVALID_REGNUM;
 
 /* Set to 1 when a return insn is output, this means that the epilogue
@@ -571,11 +580,18 @@ static const struct processors all_architectures[] =
   {NULL, arm_none, NULL, 0 , NULL}
 };
 
+struct arm_cpu_select
+{
+  const char *              string;
+  const char *              name;
+  const struct processors * processors;
+};
+
 /* This is a magic structure.  The 'string' field is magically filled in
    with a pointer to the value specified by the user on the command line
    assuming that the user has specified such a value.  */
 
-struct arm_cpu_select arm_select[] =
+static struct arm_cpu_select arm_select[] =
 {
   /* string      name            processors  */
   { NULL,      "-mcpu=",       all_cores  },
@@ -583,6 +599,10 @@ struct arm_cpu_select arm_select[] =
   { NULL,      "-mtune=",      all_cores }
 };
 
+/* Defines representing the indexes into the above table.  */
+#define ARM_OPT_SET_CPU 0
+#define ARM_OPT_SET_ARCH 1
+#define ARM_OPT_SET_TUNE 2
 
 /* The name of the proprocessor macro to define for this architecture.  */
 
@@ -776,12 +796,45 @@ arm_init_libfuncs (void)
   set_optab_libfunc (umod_optab, SImode, NULL);
 }
 
+/* Implement TARGET_HANDLE_OPTION.  */
+
+static bool
+arm_handle_option (size_t code, const char *arg, int value ATTRIBUTE_UNUSED)
+{
+  switch (code)
+    {
+    case OPT_march_:
+      arm_select[1].string = arg;
+      return true;
+
+    case OPT_mcpu_:
+      arm_select[0].string = arg;
+      return true;
+
+    case OPT_mhard_float:
+      target_float_abi_name = "hard";
+      return true;
+
+    case OPT_msoft_float:
+      target_float_abi_name = "soft";
+      return true;
+
+    case OPT_mtune_:
+      arm_select[2].string = arg;
+      return true;
+
+    default:
+      return true;
+    }
+}
+
 /* Fix up any incompatible options that the user has specified.
    This has now turned into a maze.  */
 void
 arm_override_options (void)
 {
   unsigned i;
+  enum processor_type target_arch_cpu = arm_none;
 
   /* Set up the flags based on the cpu/architecture selected by the user.  */
   for (i = ARRAY_SIZE (arm_select); i--;)
@@ -796,22 +849,25 @@ arm_override_options (void)
             if (streq (ptr->string, sel->name))
               {
                /* Set the architecture define.  */
-               if (i != 2)
+               if (i != ARM_OPT_SET_TUNE)
                  sprintf (arm_arch_name, "__ARM_ARCH_%s__", sel->arch);
 
                /* Determine the processor core for which we should
                   tune code-generation.  */
                if (/* -mcpu= is a sensible default.  */
-                   i == 0
-                   /* If -march= is used, and -mcpu= has not been used,
-                      assume that we should tune for a representative
-                      CPU from that architecture.  */
-                   || i == 1
+                   i == ARM_OPT_SET_CPU
                    /* -mtune= overrides -mcpu= and -march=.  */
-                   || i == 2)
+                   || i == ARM_OPT_SET_TUNE)
                  arm_tune = (enum processor_type) (sel - ptr->processors);
 
-               if (i != 2)
+               /* Remember the CPU associated with this architecture.
+                  If no other option is used to set the CPU type,
+                  we'll use this to guess the most suitable tuning
+                  options.  */
+               if (i == ARM_OPT_SET_ARCH)
+                 target_arch_cpu = sel->core;
+               
+               if (i != ARM_OPT_SET_TUNE)
                  {
                    /* If we have been given an architecture and a processor
                       make sure that they are compatible.  We only generate
@@ -832,6 +888,10 @@ arm_override_options (void)
         }
     }
 
+  /* Guess the tuning options from the architecture if necessary.  */
+  if (arm_tune == arm_none)
+    arm_tune = target_arch_cpu;
+
   /* If the user did not specify a processor, choose one for them.  */
   if (insn_flags == 0)
     {
@@ -935,25 +995,24 @@ arm_override_options (void)
   if (TARGET_INTERWORK && !(insn_flags & FL_THUMB))
     {
       warning (0, "target CPU does not support interworking" );
-      target_flags &= ~ARM_FLAG_INTERWORK;
+      target_flags &= ~MASK_INTERWORK;
     }
 
   if (TARGET_THUMB && !(insn_flags & FL_THUMB))
     {
       warning (0, "target CPU does not support THUMB instructions");
-      target_flags &= ~ARM_FLAG_THUMB;
+      target_flags &= ~MASK_THUMB;
     }
 
   if (TARGET_APCS_FRAME && TARGET_THUMB)
     {
       /* warning (0, "ignoring -mapcs-frame because -mthumb was used"); */
-      target_flags &= ~ARM_FLAG_APCS_FRAME;
+      target_flags &= ~MASK_APCS_FRAME;
     }
 
   /* TARGET_BACKTRACE calls leaf_function_p, which causes a crash if done
      from here where no function is being compiled currently.  */
-  if ((target_flags & (THUMB_FLAG_LEAF_BACKTRACE | THUMB_FLAG_BACKTRACE))
-      && TARGET_ARM)
+  if ((TARGET_TPCS_FRAME || TARGET_TPCS_LEAF_FRAME) && TARGET_ARM)
     warning (0, "enabling backtrace support is only meaningful when compiling for the Thumb");
 
   if (TARGET_ARM && TARGET_CALLEE_INTERWORKING)
@@ -965,11 +1024,11 @@ arm_override_options (void)
   if (TARGET_APCS_STACK && !TARGET_APCS_FRAME)
     {
       warning (0, "-mapcs-stack-check incompatible with -mno-apcs-frame");
-      target_flags |= ARM_FLAG_APCS_FRAME;
+      target_flags |= MASK_APCS_FRAME;
     }
 
   if (TARGET_POKE_FUNCTION_NAME)
-    target_flags |= ARM_FLAG_APCS_FRAME;
+    target_flags |= MASK_APCS_FRAME;
 
   if (TARGET_APCS_REENT && flag_pic)
     error ("-fpic and -mapcs-reent are incompatible");
@@ -982,7 +1041,7 @@ arm_override_options (void)
   if (TARGET_ARM
       && write_symbols != NO_DEBUG
       && !TARGET_APCS_FRAME
-      && (TARGET_DEFAULT & ARM_FLAG_APCS_FRAME))
+      && (TARGET_DEFAULT & MASK_APCS_FRAME))
     warning (0, "-g with -mno-apcs-frame may not give sensible debugging");
 
   /* If stack checking is disabled, we can use r10 as the PIC register,
@@ -1019,7 +1078,7 @@ arm_override_options (void)
     arm_cpp_interwork = 1;
 
   if (arm_arch5)
-    target_flags &= ~ARM_FLAG_INTERWORK;
+    target_flags &= ~MASK_INTERWORK;
 
   if (target_abi_name)
     {
@@ -1111,14 +1170,6 @@ arm_override_options (void)
        error ("invalid floating point abi: -mfloat-abi=%s",
               target_float_abi_name);
     }
-  else if (target_float_switch)
-    {
-      /* This is a bit of a hack to avoid needing target flags for these.  */
-      if (target_float_switch[0] == 'h')
-       arm_float_abi = ARM_FLOAT_ABI_HARD;
-      else
-       arm_float_abi = ARM_FLOAT_ABI_SOFT;
-    }
   else
     arm_float_abi = TARGET_DEFAULT_FLOAT_ABI;
 
@@ -1392,9 +1443,11 @@ use_return_insn (int iscond, rtx sibling)
       if (!call_used_regs[3])
        return 0;
 
-      /* ... that it isn't being used for a return value (always true
-        until we implement return-in-regs), or for a tail-call
-        argument ...  */
+      /* ... that it isn't being used for a return value ... */
+      if (arm_size_return_regs () >= (4 * UNITS_PER_WORD))
+       return 0;
+
+      /* ... or for a tail-call argument ...  */
       if (sibling)
        {
          gcc_assert (GET_CODE (sibling) == CALL_INSN);
@@ -1458,8 +1511,8 @@ use_return_insn (int iscond, rtx sibling)
 int
 const_ok_for_arm (HOST_WIDE_INT i)
 {
-  unsigned HOST_WIDE_INT mask = ~(unsigned HOST_WIDE_INT)0xFF;
-
+  int lowbit;
+  
   /* For machines with >32 bit HOST_WIDE_INT, the bits above bit 31 must
      be all zero, or all one.  */
   if ((i & ~(unsigned HOST_WIDE_INT) 0xffffffff) != 0
@@ -1468,19 +1521,24 @@ const_ok_for_arm (HOST_WIDE_INT i)
              & ~(unsigned HOST_WIDE_INT) 0xffffffff)))
     return FALSE;
 
-  /* Fast return for 0 and powers of 2 */
-  if ((i & (i - 1)) == 0)
+  i &= (unsigned HOST_WIDE_INT) 0xffffffff;
+  
+  /* Fast return for 0 and small values.  We must do this for zero, since
+     the code below can't handle that one case.  */
+  if ((i & ~(unsigned HOST_WIDE_INT) 0xff) == 0)
     return TRUE;
 
-  do
-    {
-      if ((i & mask & (unsigned HOST_WIDE_INT) 0xffffffff) == 0)
-        return TRUE;
-      mask =
-         (mask << 2) | ((mask & (unsigned HOST_WIDE_INT) 0xffffffff)
-                         >> (32 - 2)) | ~(unsigned HOST_WIDE_INT) 0xffffffff;
-    }
-  while (mask != ~(unsigned HOST_WIDE_INT) 0xFF);
+  /* Get the number of trailing zeros, rounded down to the nearest even
+     number.  */
+  lowbit = (ffs ((int) i) - 1) & ~1;
+
+  if ((i & ~(((unsigned HOST_WIDE_INT) 0xff) << lowbit)) == 0)
+    return TRUE;
+  else if (lowbit <= 4
+          && ((i & ~0xc000003f) == 0
+              || (i & ~0xf000000f) == 0
+              || (i & ~0xfc000003) == 0))
+    return TRUE;
 
   return FALSE;
 }
@@ -1836,6 +1894,41 @@ arm_gen_constant (enum rtx_code code, enum machine_mode mode, rtx cond,
            }
        }
 
+      /* See if we can calculate the value as the difference between two
+        valid immediates.  */
+      if (clear_sign_bit_copies + clear_zero_bit_copies <= 16)
+       {
+         int topshift = clear_sign_bit_copies & ~1;
+
+         temp1 = ARM_SIGN_EXTEND ((remainder + (0x00800000 >> topshift))
+                                  & (0xff000000 >> topshift));
+
+         /* If temp1 is zero, then that means the 9 most significant
+            bits of remainder were 1 and we've caused it to overflow.
+            When topshift is 0 we don't need to do anything since we
+            can borrow from 'bit 32'.  */
+         if (temp1 == 0 && topshift != 0)
+           temp1 = 0x80000000 >> (topshift - 1);
+
+         temp2 = ARM_SIGN_EXTEND (temp1 - remainder);
+         
+         if (const_ok_for_arm (temp2))
+           {
+             if (generate)
+               {
+                 rtx new_src = subtargets ? gen_reg_rtx (mode) : target;
+                 emit_constant_insn (cond,
+                                     gen_rtx_SET (VOIDmode, new_src,
+                                                  GEN_INT (temp1)));
+                 emit_constant_insn (cond,
+                                     gen_addsi3 (target, new_src,
+                                                 GEN_INT (-temp2)));
+               }
+
+             return 2;
+           }
+       }
+
       /* See if we can generate this by setting the bottom (or the top)
         16 bits, and then shifting these into the other half of the
         word.  We only look for the simplest cases, to do more would cost
@@ -2354,6 +2447,7 @@ arm_return_in_memory (tree type)
   HOST_WIDE_INT size;
 
   if (!AGGREGATE_TYPE_P (type) &&
+      (TREE_CODE (type) != VECTOR_TYPE) &&
       !(TARGET_AAPCS_BASED && TREE_CODE (type) == COMPLEX_TYPE))
     /* All simple types are returned in registers.
        For AAPCS, complex types are treated the same as aggregates.  */
@@ -2368,6 +2462,11 @@ arm_return_in_memory (tree type)
       return (size < 0 || size > UNITS_PER_WORD);
     }
 
+  /* To maximize backwards compatibility with previous versions of gcc,
+     return vectors up to 4 words in registers.  */
+  if (TREE_CODE (type) == VECTOR_TYPE)
+    return (size < 0 || size > (4 * UNITS_PER_WORD));
+
   /* For the arm-wince targets we choose to be compatible with Microsoft's
      ARM and Thumb compilers, which always return aggregates in memory.  */
 #ifndef ARM_WINCE
@@ -2690,7 +2789,7 @@ arm_handle_fndecl_attribute (tree *node, tree name, tree args ATTRIBUTE_UNUSED,
 {
   if (TREE_CODE (*node) != FUNCTION_DECL)
     {
-      warning (0, "%qs attribute only applies to functions",
+      warning (OPT_Wattributes, "%qs attribute only applies to functions",
               IDENTIFIER_POINTER (name));
       *no_add_attrs = true;
     }
@@ -2708,7 +2807,7 @@ arm_handle_isr_attribute (tree *node, tree name, tree args, int flags,
     {
       if (TREE_CODE (*node) != FUNCTION_DECL)
        {
-         warning (0, "%qs attribute only applies to functions",
+         warning (OPT_Wattributes, "%qs attribute only applies to functions",
                   IDENTIFIER_POINTER (name));
          *no_add_attrs = true;
        }
@@ -2722,7 +2821,8 @@ arm_handle_isr_attribute (tree *node, tree name, tree args, int flags,
        {
          if (arm_isr_value (args) == ARM_FT_UNKNOWN)
            {
-             warning (0, "%qs attribute ignored", IDENTIFIER_POINTER (name));
+             warning (OPT_Wattributes, "%qs attribute ignored",
+                      IDENTIFIER_POINTER (name));
              *no_add_attrs = true;
            }
        }
@@ -2749,7 +2849,8 @@ arm_handle_isr_attribute (tree *node, tree name, tree args, int flags,
            }
          else
            {
-             warning (0, "%qs attribute ignored", IDENTIFIER_POINTER (name));
+             warning (OPT_Wattributes, "%qs attribute ignored",
+                      IDENTIFIER_POINTER (name));
            }
        }
     }
@@ -3094,7 +3195,7 @@ legitimize_pic_address (rtx orig, enum machine_mode mode, rtx reg)
             test the index for the appropriate mode.  */
          if (!arm_legitimate_index_p (mode, offset, SET, 0))
            {
-             gcc_assert (no_new_pseudos);
+             gcc_assert (!no_new_pseudos);
              offset = force_reg (Pmode, offset);
            }
 
@@ -6557,13 +6658,15 @@ arm_must_pass_in_stack (enum machine_mode mode, tree type)
 
 /* For use by FUNCTION_ARG_PADDING (MODE, TYPE).
    Return true if an argument passed on the stack should be padded upwards,
-   i.e. if the least-significant byte has useful data.  */
+   i.e. if the least-significant byte has useful data.
+   For legacy APCS ABIs we use the default.  For AAPCS based ABIs small
+   aggregate types are placed in the lowest memory address.  */
 
 bool
 arm_pad_arg_upward (enum machine_mode mode, tree type)
 {
   if (!TARGET_AAPCS_BASED)
-    return DEFAULT_FUNCTION_ARG_PADDING(mode, type);
+    return DEFAULT_FUNCTION_ARG_PADDING(mode, type) == upward;
 
   if (type && BYTES_BIG_ENDIAN && INTEGRAL_TYPE_P (type))
     return false;
@@ -6891,7 +6994,7 @@ add_minipool_forward_ref (Mfix *fix)
   /* If this fix's address is greater than the address of the first
      entry, then we can't put the fix in this pool.  We subtract the
      size of the current fix to ensure that if the table is fully
-     packed we still have enough room to insert this value by suffling
+     packed we still have enough room to insert this value by shuffling
      the other fixes forwards.  */
   if (minipool_vector_head &&
       fix->address >= minipool_vector_head->max_address - fix->fix_size)
@@ -9732,6 +9835,20 @@ emit_multi_reg_push (unsigned long mask)
   return par;
 }
 
+/* Calculate the size of the return value that is passed in registers.  */
+static int
+arm_size_return_regs (void)
+{
+  enum machine_mode mode;
+
+  if (current_function_return_rtx != 0)
+    mode = GET_MODE (current_function_return_rtx);
+  else
+    mode = DECL_MODE (DECL_RESULT (current_function_decl));
+
+  return GET_MODE_SIZE (mode);
+}
+
 static rtx
 emit_sfm (int base_reg, int count)
 {
@@ -10347,7 +10464,7 @@ arm_expand_prologue (void)
   /* If we are profiling, make sure no instructions are scheduled before
      the call to mcount.  Similarly if the user has requested no
      scheduling in the prolog.  */
-  if (current_function_profile || TARGET_NO_SCHED_PRO)
+  if (current_function_profile || !TARGET_SCHED_PROLOG)
     emit_insn (gen_blockage ());
 
   /* If the link register is being kept alive, with the return address in it,
@@ -10780,6 +10897,26 @@ arm_assemble_integer (rtx x, unsigned int size, int aligned_p)
 
   return default_assemble_integer (x, size, aligned_p);
 }
+
+
+/* Add a function to the list of static constructors.  */
+
+static void
+arm_elf_asm_constructor (rtx symbol, int priority ATTRIBUTE_UNUSED)
+{
+  if (!TARGET_AAPCS_BASED)
+    {
+      default_named_section_asm_out_constructor (symbol, priority);
+      return;
+    }
+
+  /* Put these in the .init_array section, using a special relocation.  */
+  ctors_section ();
+  assemble_align (POINTER_SIZE);
+  fputs ("\t.word\t", asm_out_file);
+  output_addr_const (asm_out_file, symbol);
+  fputs ("(target1)\n", asm_out_file);
+}
 #endif
 \f
 /* A finite state machine takes care of noticing whether or not instructions
@@ -12298,6 +12435,21 @@ thumb_pushpop (FILE *f, unsigned long mask, int push, int *cfa_offset,
       return;
     }
 
+  if (ARM_EABI_UNWIND_TABLES && push)
+    {
+      fprintf (f, "\t.save\t{");
+      for (regno = 0; regno < 15; regno++)
+       {
+         if (real_regs & (1 << regno))
+           {
+             if (real_regs & ((1 << regno) -1))
+               fprintf (f, ", ");
+             asm_fprintf (f, "%r", regno);
+           }
+       }
+      fprintf (f, "}\n");
+    }
+
   fprintf (f, "\t%s\t{", push ? "push" : "pop");
 
   /* Look at the low registers first.  */
@@ -12766,7 +12918,6 @@ thumb_unexpanded_epilogue (void)
   int high_regs_pushed = 0;
   int had_to_push_lr;
   int size;
-  int mode;
 
   if (return_used_this_function)
     return "";
@@ -12781,13 +12932,7 @@ thumb_unexpanded_epilogue (void)
      This is more reliable that examining regs_ever_live[] because that
      will be set if the register is ever used in the function, not just if
      the register is used to hold a return value.  */
-
-  if (current_function_return_rtx != 0)
-    mode = GET_MODE (current_function_return_rtx);
-  else
-    mode = DECL_MODE (DECL_RESULT (current_function_decl));
-
-  size = GET_MODE_SIZE (mode);
+  size = arm_size_return_regs ();
 
   /* The prolog may have pushed some high registers to use as
      work registers.  e.g. the testsuite file:
@@ -13148,7 +13293,7 @@ thumb_expand_prologue (void)
                                  hard_frame_pointer_rtx));
     }
 
-  if (current_function_profile || TARGET_NO_SCHED_PRO)
+  if (current_function_profile || !TARGET_SCHED_PROLOG)
     emit_insn (gen_blockage ());
 
   cfun->machine->lr_save_eliminated = !thumb_force_lr_save ();
@@ -13197,7 +13342,7 @@ thumb_expand_epilogue (void)
      the stack adjustment will not be deleted.  */
   emit_insn (gen_prologue_use (stack_pointer_rtx));
 
-  if (current_function_profile || TARGET_NO_SCHED_PRO)
+  if (current_function_profile || !TARGET_SCHED_PROLOG)
     emit_insn (gen_blockage ());
 
   /* Emit a clobber for each insn that will be restored in the epilogue,
@@ -13258,6 +13403,11 @@ thumb_output_function_prologue (FILE *f, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
 
   if (current_function_pretend_args_size)
     {
+      /* Output unwind directive for the stack adjustment.  */
+      if (ARM_EABI_UNWIND_TABLES)
+       fprintf (f, "\t.pad #%d\n",
+                current_function_pretend_args_size);
+
       if (cfun->machine->uses_anonymous_args)
        {
          int num_pushes;
@@ -13323,6 +13473,9 @@ thumb_output_function_prologue (FILE *f, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
 
       work_register = thumb_find_work_register (live_regs_mask);
 
+      if (ARM_EABI_UNWIND_TABLES)
+       asm_fprintf (f, "\t.pad #16\n");
+
       asm_fprintf
        (f, "\tsub\t%r, %r, #16\t%@ Create stack backtrace structure\n",
         SP_REGNUM, SP_REGNUM);
@@ -13338,7 +13491,7 @@ thumb_output_function_prologue (FILE *f, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
       if (l_mask)
        {
          thumb_pushpop (f, l_mask, 1, &cfa_offset, l_mask);
-         offset = bit_count (l_mask);
+         offset = bit_count (l_mask) * UNITS_PER_WORD;
        }
       else
        offset = 0;
@@ -13380,7 +13533,7 @@ thumb_output_function_prologue (FILE *f, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
       asm_fprintf (f, "\tmov\t%r, %r\t\t%@ Backtrace structure created\n",
                   ARM_HARD_FRAME_POINTER_REGNUM, work_register);
     }
-  /* Optimisation:  If we are not pushing any low registers but we are going
+  /* Optimization:  If we are not pushing any low registers but we are going
      to push some high registers then delay our first push.  This will just
      be a push of LR and we can combine it with the push of the first high
      register.  */
@@ -14324,7 +14477,7 @@ arm_cxx_guard_type (void)
 }
 
 
-/* The EABI says test the least significan bit of a guard variable.  */
+/* The EABI says test the least significant bit of a guard variable.  */
 
 static bool
 arm_cxx_guard_mask_bit (void)
@@ -14550,3 +14703,261 @@ arm_dbx_register_number (unsigned int regno)
   gcc_unreachable ();
 }
 
+
+#ifdef TARGET_UNWIND_INFO
+/* Emit unwind directives for a store-multiple instruction.  This should
+   only ever be generated by the function prologue code, so we expect it
+   to have a particular form.  */
+
+static void
+arm_unwind_emit_stm (FILE * asm_out_file, rtx p)
+{
+  int i;
+  HOST_WIDE_INT offset;
+  HOST_WIDE_INT nregs;
+  int reg_size;
+  unsigned reg;
+  unsigned lastreg;
+  rtx e;
+
+  /* First insn will adjust the stack pointer.  */
+  e = XVECEXP (p, 0, 0);
+  if (GET_CODE (e) != SET
+      || GET_CODE (XEXP (e, 0)) != REG
+      || REGNO (XEXP (e, 0)) != SP_REGNUM
+      || GET_CODE (XEXP (e, 1)) != PLUS)
+    abort ();
+
+  offset = -INTVAL (XEXP (XEXP (e, 1), 1));
+  nregs = XVECLEN (p, 0) - 1;
+
+  reg = REGNO (XEXP (XVECEXP (p, 0, 1), 1));
+  if (reg < 16)
+    {
+      /* The function prologue may also push pc, but not annotate it as it is
+        never restored.  We turn this into an stack pointer adjustment.  */
+      if (nregs * 4 == offset - 4)
+       {
+         fprintf (asm_out_file, "\t.pad #4\n");
+         offset -= 4;
+       }
+      reg_size = 4;
+    }
+  else if (IS_VFP_REGNUM (reg))
+    {
+      /* FPA register saves use an additional word.  */
+      offset -= 4;
+      reg_size = 8;
+    }
+  else if (reg >= FIRST_FPA_REGNUM && reg <= LAST_FPA_REGNUM)
+    {
+      /* FPA registers are done differently.  */
+      asm_fprintf (asm_out_file, "\t.save %r, %wd\n", reg, nregs);
+      return;
+    }
+  else
+    /* Unknown register type.  */
+    abort ();
+
+  /* If the stack increment doesn't match the size of the saved registers,
+     something has gone horribly wrong.  */
+  if (offset != nregs * reg_size)
+    abort ();
+
+  fprintf (asm_out_file, "\t.save {");
+
+  offset = 0;
+  lastreg = 0;
+  /* The remaining insns will describe the stores.  */
+  for (i = 1; i <= nregs; i++)
+    {
+      /* Expect (set (mem <addr>) (reg)).
+         Where <addr> is (reg:SP) or (plus (reg:SP) (const_int)).  */
+      e = XVECEXP (p, 0, i);
+      if (GET_CODE (e) != SET
+         || GET_CODE (XEXP (e, 0)) != MEM
+         || GET_CODE (XEXP (e, 1)) != REG)
+       abort ();
+      
+      reg = REGNO (XEXP (e, 1));
+      if (reg < lastreg)
+       abort ();
+         
+      if (i != 1)
+       fprintf (asm_out_file, ", ");
+      /* We can't use %r for vfp because we need to use the
+        double precision register names.  */
+      if (IS_VFP_REGNUM (reg))
+       asm_fprintf (asm_out_file, "d%d", (reg - FIRST_VFP_REGNUM) / 2);
+      else
+       asm_fprintf (asm_out_file, "%r", reg);
+
+#ifdef ENABLE_CHECKING
+      /* Check that the addresses are consecutive.  */
+      e = XEXP (XEXP (e, 0), 0);
+      if (GET_CODE (e) == PLUS)
+       {
+         offset += reg_size;
+         if (GET_CODE (XEXP (e, 0)) != REG
+             || REGNO (XEXP (e, 0)) != SP_REGNUM
+             || GET_CODE (XEXP (e, 1)) != CONST_INT
+             || offset != INTVAL (XEXP (e, 1)))
+           abort ();
+       }
+      else if (i != 1
+              || GET_CODE (e) != REG
+              || REGNO (e) != SP_REGNUM)
+       abort ();
+#endif
+    }
+  fprintf (asm_out_file, "}\n");
+}
+
+/*  Emit unwind directives for a SET.  */
+
+static void
+arm_unwind_emit_set (FILE * asm_out_file, rtx p)
+{
+  rtx e0;
+  rtx e1;
+
+  e0 = XEXP (p, 0);
+  e1 = XEXP (p, 1);
+  switch (GET_CODE (e0))
+    {
+    case MEM:
+      /* Pushing a single register.  */
+      if (GET_CODE (XEXP (e0, 0)) != PRE_DEC
+         || GET_CODE (XEXP (XEXP (e0, 0), 0)) != REG
+         || REGNO (XEXP (XEXP (e0, 0), 0)) != SP_REGNUM)
+       abort ();
+
+      asm_fprintf (asm_out_file, "\t.save ");
+      if (IS_VFP_REGNUM (REGNO (e1)))
+       asm_fprintf(asm_out_file, "{d%d}\n",
+                   (REGNO (e1) - FIRST_VFP_REGNUM) / 2);
+      else
+       asm_fprintf(asm_out_file, "{%r}\n", REGNO (e1));
+      break;
+
+    case REG:
+      if (REGNO (e0) == SP_REGNUM)
+       {
+         /* A stack increment.  */
+         if (GET_CODE (e1) != PLUS
+             || GET_CODE (XEXP (e1, 0)) != REG
+             || REGNO (XEXP (e1, 0)) != SP_REGNUM
+             || GET_CODE (XEXP (e1, 1)) != CONST_INT)
+           abort ();
+
+         asm_fprintf (asm_out_file, "\t.pad #%wd\n",
+                      -INTVAL (XEXP (e1, 1)));
+       }
+      else if (REGNO (e0) == HARD_FRAME_POINTER_REGNUM)
+       {
+         HOST_WIDE_INT offset;
+         unsigned reg;
+         
+         if (GET_CODE (e1) == PLUS)
+           {
+             if (GET_CODE (XEXP (e1, 0)) != REG
+                 || GET_CODE (XEXP (e1, 1)) != CONST_INT)
+               abort ();
+             reg = REGNO (XEXP (e1, 0));
+             offset = INTVAL (XEXP (e1, 1));
+             asm_fprintf (asm_out_file, "\t.setfp %r, %r, #%wd\n",
+                          HARD_FRAME_POINTER_REGNUM, reg,
+                          INTVAL (XEXP (e1, 1)));
+           }
+         else if (GET_CODE (e1) == REG)
+           {
+             reg = REGNO (e1);
+             asm_fprintf (asm_out_file, "\t.setfp %r, %r\n",
+                          HARD_FRAME_POINTER_REGNUM, reg);
+           }
+         else
+           abort ();
+       }
+      else if (GET_CODE (e1) == REG && REGNO (e1) == SP_REGNUM)
+       {
+         /* Move from sp to reg.  */
+         asm_fprintf (asm_out_file, "\t.movsp %r\n", REGNO (e0));
+       }
+      else
+       abort ();
+      break;
+
+    default:
+      abort ();
+    }
+}
+
+
+/* Emit unwind directives for the given insn.  */
+
+static void
+arm_unwind_emit (FILE * asm_out_file, rtx insn)
+{
+  rtx pat;
+
+  if (!ARM_EABI_UNWIND_TABLES)
+    return;
+
+  if (GET_CODE (insn) == NOTE || !RTX_FRAME_RELATED_P (insn))
+    return;
+
+  pat = find_reg_note (insn, REG_FRAME_RELATED_EXPR, NULL_RTX);
+  if (pat)
+    pat = XEXP (pat, 0);
+  else
+    pat = PATTERN (insn);
+
+  switch (GET_CODE (pat))
+    {
+    case SET:
+      arm_unwind_emit_set (asm_out_file, pat);
+      break;
+
+    case SEQUENCE:
+      /* Store multiple.  */
+      arm_unwind_emit_stm (asm_out_file, pat);
+      break;
+
+    default:
+      abort();
+    }
+}
+
+
+/* Output a reference from a function exception table to the type_info
+   object X.  The EABI specifies that the symbol should be relocated by
+   an R_ARM_TARGET2 relocation.  */
+
+static bool
+arm_output_ttype (rtx x)
+{
+  fputs ("\t.word\t", asm_out_file);
+  output_addr_const (asm_out_file, x);
+  /* Use special relocations for symbol references.  */
+  if (GET_CODE (x) != CONST_INT)
+    fputs ("(TARGET2)", asm_out_file);
+  fputc ('\n', asm_out_file);
+
+  return TRUE;
+}
+#endif /* TARGET_UNWIND_INFO */
+
+
+/* Output unwind directives for the start/end of a function.  */
+
+void
+arm_output_fn_unwind (FILE * f, bool prologue)
+{
+  if (!ARM_EABI_UNWIND_TABLES)
+    return;
+
+  if (prologue)
+    fputs ("\t.fnstart\n", f);
+  else
+    fputs ("\t.fnend\n", f);
+}