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"
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
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
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);
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. */
#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
#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. */
/* 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. */
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
{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 },
{ 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. */
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--;)
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
}
}
+ /* 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)
{
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)
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");
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,
arm_cpp_interwork = 1;
if (arm_arch5)
- target_flags &= ~ARM_FLAG_INTERWORK;
+ target_flags &= ~MASK_INTERWORK;
if (target_abi_name)
{
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;
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);
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
& ~(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;
}
}
}
+ /* 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
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. */
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
{
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;
}
{
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;
}
{
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;
}
}
}
else
{
- warning (0, "%qs attribute ignored", IDENTIFIER_POINTER (name));
+ warning (OPT_Wattributes, "%qs attribute ignored",
+ IDENTIFIER_POINTER (name));
}
}
}
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);
}
/* 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;
/* 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)
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)
{
/* 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,
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
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. */
int high_regs_pushed = 0;
int had_to_push_lr;
int size;
- int mode;
if (return_used_this_function)
return "";
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:
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 ();
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,
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;
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);
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;
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. */
}
-/* 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)
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);
+}