/* Output routines for GCC for ARM.
- Copyright (C) 1991, 93, 94, 95, 96, 97, 98, 99, 2000 Free Software Foundation, Inc.
+ Copyright (C) 1991, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+ Free Software Foundation, Inc.
Contributed by Pieter `Tiggr' Schoenmakers (rcpieter@win.tue.nl)
and Martin Simmons (@harleqn.co.uk).
More major hacks by Richard Earnshaw (rearnsha@arm.com).
#include "system.h"
#include "rtl.h"
#include "tree.h"
-#include "tm_p.h"
+#include "obstack.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 "recog.h"
#include "ggc.h"
#include "except.h"
+#include "c-pragma.h"
+#include "integrate.h"
#include "tm_p.h"
+#include "target.h"
+#include "target-def.h"
/* Forward definitions of types. */
typedef struct minipool_node Mnode;
#define Hint HOST_WIDE_INT
#define Mmode enum machine_mode
#define Ulong unsigned long
+#define Ccstar const char *
/* Forward function declarations. */
static void arm_add_gc_roots PARAMS ((void));
static int arm_gen_constant PARAMS ((enum rtx_code, Mmode, Hint, rtx, rtx, int, int));
-static int arm_naked_function_p PARAMS ((tree));
static Ulong bit_count PARAMS ((signed int));
static int const_ok_for_op PARAMS ((Hint, enum rtx_code));
static int eliminate_lr2ip PARAMS ((rtx *));
static rtx emit_multi_reg_push PARAMS ((int));
static rtx emit_sfm PARAMS ((int, int));
-static const char * fp_const_from_val PARAMS ((REAL_VALUE_TYPE *));
+static Ccstar fp_const_from_val PARAMS ((REAL_VALUE_TYPE *));
static arm_cc get_arm_condition_code PARAMS ((rtx));
static void init_fpa_table PARAMS ((void));
static Hint int_log2 PARAMS ((Hint));
static rtx is_jump_table PARAMS ((rtx));
-static const char * output_multi_immediate PARAMS ((rtx *, const char *, const char *, int, Hint));
-static void print_multi_reg PARAMS ((FILE *, const char *, int, int, int));
+static Ccstar output_multi_immediate PARAMS ((rtx *, Ccstar, Ccstar, int, Hint));
+static void print_multi_reg PARAMS ((FILE *, Ccstar, int, int));
static Mmode select_dominance_cc_mode PARAMS ((rtx, rtx, Hint));
-static const char * shift_op PARAMS ((rtx, Hint *));
+static Ccstar shift_op PARAMS ((rtx, Hint *));
static void arm_init_machine_status PARAMS ((struct function *));
static void arm_mark_machine_status PARAMS ((struct function *));
+static void arm_free_machine_status PARAMS ((struct function *));
static int number_of_first_bit_set PARAMS ((int));
static void replace_symbols_in_block PARAMS ((tree, rtx, rtx));
static void thumb_exit PARAMS ((FILE *, int, rtx));
static void thumb_pushpop PARAMS ((FILE *, int, int));
-static const char * thumb_condition_code PARAMS ((rtx, int));
+static Ccstar thumb_condition_code PARAMS ((rtx, int));
static rtx is_jump_table PARAMS ((rtx));
static Hint get_jump_table_size PARAMS ((rtx));
static Mnode * move_minipool_fix_forward_ref PARAMS ((Mnode *, Mnode *, Hint));
static void push_minipool_fix PARAMS ((rtx, Hint, rtx *, Mmode, rtx));
static void note_invalid_constants PARAMS ((rtx, Hint));
static int current_file_function_operand PARAMS ((rtx));
-\f
+static Ulong arm_compute_save_reg_mask PARAMS ((void));
+static Ulong arm_isr_value PARAMS ((tree));
+static Ulong arm_compute_func_type PARAMS ((void));
+static int arm_valid_type_attribute_p PARAMS ((tree, tree,
+ tree, tree));
+static int arm_valid_decl_attribute_p PARAMS ((tree, tree,
+ tree, tree));
+static int arm_comp_type_attributes PARAMS ((tree, tree));
+static void arm_set_default_type_attributes PARAMS ((tree));
#undef Hint
#undef Mmode
#undef Ulong
+#undef Ccstar
+\f
+/* Initialize the GCC target structure. */
+#ifdef TARGET_DLLIMPORT_DECL_ATTRIBUTES
+#undef TARGET_MERGE_DECL_ATTRIBUTES
+#define TARGET_MERGE_DECL_ATTRIBUTES merge_dllimport_decl_attributes
+#endif
+
+#undef TARGET_VALID_TYPE_ATTRIBUTE
+#define TARGET_VALID_TYPE_ATTRIBUTE arm_valid_type_attribute_p
+
+#undef TARGET_VALID_DECL_ATTRIBUTE
+#ifdef ARM_PE
+ static int arm_pe_valid_decl_attribute_p PARAMS ((tree, tree, tree, tree));
+# define TARGET_VALID_DECL_ATTRIBUTE arm_pe_valid_decl_attribute_p
+#else
+# define TARGET_VALID_DECL_ATTRIBUTE arm_valid_decl_attribute_p
+#endif
+
+#undef TARGET_COMP_TYPE_ATTRIBUTES
+#define TARGET_COMP_TYPE_ATTRIBUTES arm_comp_type_attributes
+
+#undef TARGET_SET_DEFAULT_TYPE_ATTRIBUTES
+#define TARGET_SET_DEFAULT_TYPE_ATTRIBUTES arm_set_default_type_attributes
+
+struct gcc_target target = TARGET_INITIALIZER;
+\f
+/* Obstack for minipool constant handling. */
+static struct obstack minipool_obstack;
+static char *minipool_startobj;
+
+#define obstack_chunk_alloc xmalloc
+#define obstack_chunk_free free
/* The maximum number of insns skipped which will be conditionalised if
possible. */
#define FL_THUMB (1 << 6) /* Thumb aware */
#define FL_LDSCHED (1 << 7) /* Load scheduling necessary */
#define FL_STRONG (1 << 8) /* StrongARM */
+#define FL_ARCH5E (1 << 9) /* DSP extenstions to v5 */
+#define FL_XSCALE (1 << 10) /* XScale */
/* The bits in this mask specify which instructions we are
allowed to generate. */
/* Nonzero if this chip supports the ARM Architecture 5 extensions. */
int arm_arch5 = 0;
+/* Nonzero if this chip supports the ARM Architecture 5E extensions. */
+int arm_arch5e = 0;
+
/* Nonzero if this chip can benefit from load scheduling. */
int arm_ld_sched = 0;
/* Nonzero if this chip is a StrongARM. */
int arm_is_strong = 0;
+/* Nonzero if this chip is an XScale. */
+int arm_is_xscale = 0;
+
/* Nonzero if this chip is a an ARM6 or an ARM7. */
int arm_is_6_or_7 = 0;
{"arm700", FL_CO_PROC | FL_MODE26 | FL_MODE32 },
{"arm700i", FL_CO_PROC | FL_MODE26 | FL_MODE32 },
{"arm710", FL_MODE26 | FL_MODE32 },
+ {"arm710t", FL_MODE26 | FL_MODE32 | FL_THUMB },
{"arm720", FL_MODE26 | FL_MODE32 },
+ {"arm720t", FL_MODE26 | FL_MODE32 | FL_THUMB },
+ {"arm740t", FL_MODE26 | FL_MODE32 | FL_THUMB },
{"arm710c", FL_MODE26 | FL_MODE32 },
{"arm7100", FL_MODE26 | FL_MODE32 },
{"arm7500", FL_MODE26 | FL_MODE32 },
{"arm9", FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_LDSCHED },
{"arm920", FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_LDSCHED },
{"arm920t", FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_LDSCHED },
+ {"arm940t", FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_LDSCHED },
{"arm9tdmi", FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_LDSCHED },
+ {"arm9e", FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_LDSCHED },
{"strongarm", FL_MODE26 | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_LDSCHED | FL_STRONG },
{"strongarm110", FL_MODE26 | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_LDSCHED | FL_STRONG },
{"strongarm1100", FL_MODE26 | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_LDSCHED | FL_STRONG },
+ {"strongarm1110", FL_MODE26 | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_LDSCHED | FL_STRONG },
+ {"arm10tdmi", FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_LDSCHED | FL_ARCH5 },
+ {"arm1020t", FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_LDSCHED | FL_ARCH5 },
+ {"xscale", FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_LDSCHED | FL_STRONG | FL_ARCH5 | FL_ARCH5E | FL_XSCALE },
{NULL, 0}
};
implementations that support it, so we will leave it out for now. */
{ "armv4t", FL_CO_PROC | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB },
{ "armv5", FL_CO_PROC | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_ARCH5 },
+ { "armv5t", FL_CO_PROC | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_ARCH5 },
+ { "armv5te", FL_CO_PROC | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_ARCH5 | FL_ARCH5E },
{ NULL, 0 }
};
while (value)
{
- value &= ~(value & - value);
- ++ count;
+ value &= ~(value & -value);
+ ++count;
}
return count;
unsigned i;
/* Set up the flags based on the cpu/architecture selected by the user. */
- for (i = sizeof (arm_select) / sizeof (arm_select[0]); i--;)
+ for (i = ARRAY_SIZE (arm_select); i--;)
{
struct arm_cpu_select * ptr = arm_select + i;
{
const struct processors * sel;
- for (sel = ptr->processors; sel->name != NULL; sel ++)
+ for (sel = ptr->processors; sel->name != NULL; sel++)
if (streq (ptr->string, sel->name))
{
if (i == 2)
{ TARGET_CPU_arm810, "arm810" },
{ TARGET_CPU_arm9, "arm9" },
{ TARGET_CPU_strongarm, "strongarm" },
+ { TARGET_CPU_xscale, "xscale" },
{ TARGET_CPU_generic, "arm" },
{ 0, 0 }
};
struct cpu_default * def;
/* Find the default. */
- for (def = cpu_defaults; def->name; def ++)
+ for (def = cpu_defaults; def->name; def++)
if (def->cpu == TARGET_CPU_DEFAULT)
break;
abort ();
/* Find the default CPU's flags. */
- for (sel = all_cores; sel->name != NULL; sel ++)
+ for (sel = all_cores; sel->name != NULL; sel++)
if (streq (def->name, sel->name))
break;
interworking. Therefore we force FL_MODE26 to be removed
from insn_flags here (if it was set), so that the search
below will always be able to find a compatible processor. */
- insn_flags &= ~ FL_MODE26;
+ insn_flags &= ~FL_MODE26;
}
- else if (! TARGET_APCS_32)
+ else if (!TARGET_APCS_32)
sought |= FL_MODE26;
if (sought != 0 && ((sought & insn_flags) != sought))
/* Try to locate a CPU type that supports all of the abilities
of the default CPU, plus the extra abilities requested by
the user. */
- for (sel = all_cores; sel->name != NULL; sel ++)
+ for (sel = all_cores; sel->name != NULL; sel++)
if ((sel->flags & sought) == (sought | insn_flags))
break;
options. Instead if we cannot find a cpu that has both the
characteristics of the default cpu and the given command line
options we scan the array again looking for a best match. */
- for (sel = all_cores; sel->name != NULL; sel ++)
+ for (sel = all_cores; sel->name != NULL; sel++)
if ((sel->flags & sought) == sought)
{
unsigned int count;
"-mapcs-32 -mcpu=arm2" then we loose here. */
if ((TARGET_DEFAULT & ARM_FLAG_APCS_32) == 0)
warning ("target CPU does not support APCS-32" );
- target_flags &= ~ ARM_FLAG_APCS_32;
+ target_flags &= ~ARM_FLAG_APCS_32;
}
- else if (! TARGET_APCS_32 && !(insn_flags & FL_MODE26))
+ else if (!TARGET_APCS_32 && !(insn_flags & FL_MODE26))
{
warning ("target CPU does not support APCS-26" );
target_flags |= ARM_FLAG_APCS_32;
if (TARGET_THUMB && !(insn_flags & FL_THUMB))
{
- warning ("target CPU does not supoport THUMB instructions.");
+ warning ("target CPU does not support THUMB instructions.");
target_flags &= ~ARM_FLAG_THUMB;
}
/* warning ("ignoring -mapcs-frame because -mthumb was used."); */
target_flags &= ~ARM_FLAG_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))
/* If interworking is enabled then APCS-32 must be selected as well. */
if (TARGET_INTERWORK)
{
- if (! TARGET_APCS_32)
+ if (!TARGET_APCS_32)
warning ("interworking forces APCS-32 to be used" );
target_flags |= ARM_FLAG_APCS_32;
}
- if (TARGET_APCS_STACK && ! TARGET_APCS_FRAME)
+ if (TARGET_APCS_STACK && !TARGET_APCS_FRAME)
{
warning ("-mapcs-stack-check incompatible with -mno-apcs-frame");
target_flags |= ARM_FLAG_APCS_FRAME;
target_flags |= ARM_FLAG_APCS_FRAME;
if (TARGET_APCS_REENT && flag_pic)
- fatal ("-fpic and -mapcs-reent are incompatible");
+ error ("-fpic and -mapcs-reent are incompatible");
if (TARGET_APCS_REENT)
warning ("APCS reentrant code not supported. Ignored");
are turned off and debugging is turned on. */
if (TARGET_ARM
&& write_symbols != NO_DEBUG
- && ! TARGET_APCS_FRAME
+ && !TARGET_APCS_FRAME
&& (TARGET_DEFAULT & ARM_FLAG_APCS_FRAME))
warning ("-g with -mno-apcs-frame may not give sensible debugging");
/* If stack checking is disabled, we can use r10 as the PIC register,
which keeps r9 available. */
- if (flag_pic && ! TARGET_APCS_STACK)
+ if (flag_pic && !TARGET_APCS_STACK)
arm_pic_register = 10;
if (TARGET_APCS_FLOAT)
arm_fast_multiply = (insn_flags & FL_FAST_MULT) != 0;
arm_arch4 = (insn_flags & FL_ARCH4) != 0;
arm_arch5 = (insn_flags & FL_ARCH5) != 0;
-
+ arm_arch5e = (insn_flags & FL_ARCH5E) != 0;
+ arm_is_xscale = (insn_flags & FL_XSCALE) != 0;
+
arm_ld_sched = (tune_flags & FL_LDSCHED) != 0;
arm_is_strong = (tune_flags & FL_STRONG) != 0;
thumb_code = (TARGET_ARM == 0);
arm_is_6_or_7 = (((tune_flags & (FL_MODE26 | FL_MODE32))
&& !(tune_flags & FL_ARCH4))) != 0;
-
+
/* Default value for floating point code... if no co-processor
bus, then schedule for emulated floating point. Otherwise,
assume the user has an FPA.
else if (streq (target_fp_name, "3"))
arm_fpu_arch = FP_SOFT3;
else
- fatal ("Invalid floating point emulation option: -mfpe-%s",
+ error ("Invalid floating point emulation option: -mfpe-%s",
target_fp_name);
}
else
{
int pic_register;
- if (! flag_pic)
+ if (!flag_pic)
warning ("-mpic-register= is useless without -fpic");
pic_register = decode_reg_name (arm_pic_register_string);
if (optimize_size || (tune_flags & FL_LDSCHED))
arm_constant_limit = 1;
+ if (arm_is_xscale)
+ arm_constant_limit = 2;
+
/* If optimizing for size, bump the number of instructions that we
are prepared to conditionally execute (even on a StrongARM).
Otherwise for the StrongARM, which has early execution of branches,
{
ggc_add_rtx_root (&arm_compare_op0, 1);
ggc_add_rtx_root (&arm_compare_op1, 1);
- ggc_add_rtx_root (&arm_target_insn, 1); /* Not sure this is really a root */
- /* XXX: What about the minipool tables? */
+ ggc_add_rtx_root (&arm_target_insn, 1); /* Not sure this is really a root. */
+
+ gcc_obstack_init(&minipool_obstack);
+ minipool_startobj = (char *) obstack_alloc (&minipool_obstack, 0);
+}
+\f
+/* A table of known ARM exception types.
+ For use with the interrupt function attribute. */
+
+typedef struct
+{
+ const char * arg;
+ unsigned long return_value;
+}
+isr_attribute_arg;
+
+static isr_attribute_arg isr_attribute_args [] =
+{
+ { "IRQ", ARM_FT_ISR },
+ { "irq", ARM_FT_ISR },
+ { "FIQ", ARM_FT_FIQ },
+ { "fiq", ARM_FT_FIQ },
+ { "ABORT", ARM_FT_ISR },
+ { "abort", ARM_FT_ISR },
+ { "ABORT", ARM_FT_ISR },
+ { "abort", ARM_FT_ISR },
+ { "UNDEF", ARM_FT_EXCEPTION },
+ { "undef", ARM_FT_EXCEPTION },
+ { "SWI", ARM_FT_EXCEPTION },
+ { "swi", ARM_FT_EXCEPTION },
+ { NULL, ARM_FT_NORMAL }
+};
+
+/* Returns the (interrupt) function type of the current
+ function, or ARM_FT_UNKNOWN if the type cannot be determined. */
+
+static unsigned long
+arm_isr_value (argument)
+ tree argument;
+{
+ isr_attribute_arg * ptr;
+ const char * arg;
+
+ /* No argument - default to IRQ. */
+ if (argument == NULL_TREE)
+ return ARM_FT_ISR;
+
+ /* Get the value of the argument. */
+ if (TREE_VALUE (argument) == NULL_TREE
+ || TREE_CODE (TREE_VALUE (argument)) != STRING_CST)
+ return ARM_FT_UNKNOWN;
+
+ arg = TREE_STRING_POINTER (TREE_VALUE (argument));
+
+ /* Check it against the list of known arguments. */
+ for (ptr = isr_attribute_args; ptr->arg != NULL; ptr ++)
+ if (strcmp (arg, ptr->arg) == 0)
+ return ptr->return_value;
+
+ /* An unrecognised interrupt type. */
+ return ARM_FT_UNKNOWN;
+}
+
+/* Computes the type of the current function. */
+
+static unsigned long
+arm_compute_func_type ()
+{
+ unsigned long type = ARM_FT_UNKNOWN;
+ tree a;
+ tree attr;
+
+ if (TREE_CODE (current_function_decl) != FUNCTION_DECL)
+ abort ();
+
+ /* Decide if the current function is volatile. Such functions
+ never return, and many memory cycles can be saved by not storing
+ register values that will never be needed again. This optimization
+ was added to speed up context switching in a kernel application. */
+ if (optimize > 0
+ && current_function_nothrow
+ && TREE_THIS_VOLATILE (current_function_decl))
+ type |= ARM_FT_VOLATILE;
+
+ if (current_function_needs_context)
+ type |= ARM_FT_NESTED;
+
+ attr = DECL_MACHINE_ATTRIBUTES (current_function_decl);
+
+ a = lookup_attribute ("naked", attr);
+ if (a != NULL_TREE)
+ type |= ARM_FT_NAKED;
+
+ if (cfun->machine->eh_epilogue_sp_ofs != NULL_RTX)
+ type |= ARM_FT_EXCEPTION_HANDLER;
+ else
+ {
+ a = lookup_attribute ("isr", attr);
+ if (a == NULL_TREE)
+ a = lookup_attribute ("interrupt", attr);
+
+ if (a == NULL_TREE)
+ type |= TARGET_INTERWORK ? ARM_FT_INTERWORKED : ARM_FT_NORMAL;
+ else
+ type |= arm_isr_value (TREE_VALUE (a));
+ }
+
+ return type;
+}
+
+/* Returns the type of the current function. */
+
+unsigned long
+arm_current_func_type ()
+{
+ if (ARM_FUNC_TYPE (cfun->machine->func_type) == ARM_FT_UNKNOWN)
+ cfun->machine->func_type = arm_compute_func_type ();
+
+ return cfun->machine->func_type;
}
\f
/* Return 1 if it is possible to return using a single instruction. */
+
int
use_return_insn (iscond)
int iscond;
{
int regno;
+ unsigned int func_type;
/* Never use a return instruction before reload has run. */
- if (! reload_completed
- /* Or if the function is variadic. */
- || current_function_pretend_args_size
+ if (!reload_completed)
+ return 0;
+
+ func_type = arm_current_func_type ();
+
+ /* Naked functions, volatile functiond and interrupt
+ functions all need special consideration. */
+ if (func_type & (ARM_FT_INTERRUPT | ARM_FT_VOLATILE | ARM_FT_NAKED))
+ return 0;
+
+ /* As do variadic functions. */
+ if (current_function_pretend_args_size
|| current_function_anonymous_args
/* Of if the function calls __builtin_eh_return () */
- || cfun->machine->eh_epilogue_sp_ofs != NULL
+ || ARM_FUNC_TYPE (func_type) == ARM_FT_EXCEPTION_HANDLER
/* Or if there is no frame pointer and there is a stack adjustment. */
|| ((get_frame_size () + current_function_outgoing_args_size != 0)
- && ! frame_pointer_needed))
+ && !frame_pointer_needed))
return 0;
/* Can't be done if interworking with Thumb, and any registers have been
|| TARGET_INTERWORK)
{
for (regno = 0; regno <= LAST_ARM_REGNUM; regno++)
- if (regs_ever_live[regno] && ! call_used_regs[regno])
+ if (regs_ever_live[regno] && !call_used_regs[regno])
return 0;
if (flag_pic && regs_ever_live[PIC_OFFSET_TABLE_REGNUM])
return 0;
}
- /* Can't be done if any of the FPU regs are pushed, since this also
- requires an insn. */
+ /* Can't be done if any of the FPU regs are pushed,
+ since this also requires an insn. */
if (TARGET_HARD_FLOAT)
for (regno = FIRST_ARM_FP_REGNUM; regno <= LAST_ARM_FP_REGNUM; regno++)
- if (regs_ever_live[regno] && ! call_used_regs[regno])
+ if (regs_ever_live[regno] && !call_used_regs[regno])
return 0;
- /* If a function is naked, don't use the "return" insn. */
- if (arm_naked_function_p (current_function_decl))
- return 0;
-
return 1;
}
const_ok_for_arm (i)
HOST_WIDE_INT i;
{
- unsigned HOST_WIDE_INT mask = ~ HOST_UINT (0xFF);
+ unsigned HOST_WIDE_INT mask = ~HOST_UINT (0xFF);
/* For machines with >32 bit HOST_WIDE_INT, the bits above bit 31 must
be all zero, or all one. */
- if ((i & ~ HOST_UINT (0xffffffff)) != 0
- && ((i & ~ HOST_UINT (0xffffffff))
- != ((~ HOST_UINT (0))
- & ~ HOST_UINT (0xffffffff))))
+ if ((i & ~HOST_UINT (0xffffffff)) != 0
+ && ((i & ~HOST_UINT (0xffffffff))
+ != ((~HOST_UINT (0))
+ & ~HOST_UINT (0xffffffff))))
return FALSE;
/* Fast return for 0 and powers of 2 */
mask =
(mask << 2) | ((mask & HOST_UINT (0xffffffff))
>> (32 - 2)) | ~(HOST_UINT (0xffffffff));
- } while (mask != ~ HOST_UINT (0xFF));
+ } while (mask != ~HOST_UINT (0xFF));
return FALSE;
}
Ref: gcc -O1 -mcpu=strongarm gcc.c-torture/compile/980506-2.c
*/
- if (! after_arm_reorg
+ if (!after_arm_reorg
&& (arm_gen_constant (code, mode, val, target, source, 1, 0)
> arm_constant_limit + (code != SET)))
{
return arm_gen_constant (code, mode, val, target, source, subtargets, 1);
}
+static int
+count_insns_for_constant (HOST_WIDE_INT remainder, int i)
+{
+ HOST_WIDE_INT temp1;
+ int num_insns = 0;
+ do
+ {
+ int end;
+
+ if (i <= 0)
+ i += 32;
+ if (remainder & (3 << (i - 2)))
+ {
+ end = i - 8;
+ if (end < 0)
+ end += 32;
+ temp1 = remainder & ((0x0ff << end)
+ | ((i < end) ? (0xff >> (32 - end)) : 0));
+ remainder &= ~temp1;
+ num_insns++;
+ i -= 6;
+ }
+ i -= 2;
+ } while (remainder);
+ return num_insns;
+}
+
/* As above, but extra parameter GENERATE which, if clear, suppresses
RTL generation. */
static int
{
if ((((temp2 | (temp2 << i))
& HOST_UINT (0xffffffff)) == remainder)
- && ! const_ok_for_arm (temp2))
+ && !const_ok_for_arm (temp2))
{
rtx new_src = (subtargets
? (generate ? gen_reg_rtx (mode) : NULL_RTX)
for (i = 17; i < 24; i++)
{
if (((temp1 | (temp1 >> i)) == remainder)
- && ! const_ok_for_arm (temp1))
+ && !const_ok_for_arm (temp1))
{
rtx new_src = (subtargets
? (generate ? gen_reg_rtx (mode) : NULL_RTX)
then this can be done in two instructions instead of 3-4. */
if (subtargets
/* TARGET can't be NULL if SUBTARGETS is 0 */
- || (reload_completed && ! reg_mentioned_p (target, source)))
+ || (reload_completed && !reg_mentioned_p (target, source)))
{
- if (const_ok_for_arm (ARM_SIGN_EXTEND (~ val)))
+ if (const_ok_for_arm (ARM_SIGN_EXTEND (~val)))
{
if (generate)
{
return 2;
}
- if (const_ok_for_arm (temp1 = ARM_SIGN_EXTEND (~ val)))
+ if (const_ok_for_arm (temp1 = ARM_SIGN_EXTEND (~val)))
{
if (generate)
{
{
int consecutive_zeros = 0;
- if (! (remainder & (3 << i)))
+ if (!(remainder & (3 << i)))
{
- while ((i < 32) && ! (remainder & (3 << i)))
+ while ((i < 32) && !(remainder & (3 << i)))
{
consecutive_zeros += 2;
i += 2;
}
}
- /* Now start emitting the insns, starting with the one with the highest
- bit set: we do this so that the smallest number will be emitted last;
- this is more likely to be combinable with addressing insns. */
+ /* So long as it won't require any more insns to do so, it's
+ desirable to emit a small constant (in bits 0...9) in the last
+ insn. This way there is more chance that it can be combined with
+ a later addressing insn to form a pre-indexed load or store
+ operation. Consider:
+
+ *((volatile int *)0xe0000100) = 1;
+ *((volatile int *)0xe0000110) = 2;
+
+ We want this to wind up as:
+
+ mov rA, #0xe0000000
+ mov rB, #1
+ str rB, [rA, #0x100]
+ mov rB, #2
+ str rB, [rA, #0x110]
+
+ rather than having to synthesize both large constants from scratch.
+
+ Therefore, we calculate how many insns would be required to emit
+ the constant starting from `best_start', and also starting from
+ zero (ie with bit 31 first to be output). If `best_start' doesn't
+ yield a shorter sequence, we may as well use zero. */
+ if (best_start != 0
+ && ((((unsigned HOST_WIDE_INT) 1) << best_start) < remainder)
+ && (count_insns_for_constant (remainder, 0) <=
+ count_insns_for_constant (remainder, best_start)))
+ best_start = 0;
+
+ /* Now start emitting the insns. */
i = best_start;
do
{
case GT:
case LE:
- if (i != (((HOST_UINT (1)) << (HOST_BITS_PER_WIDE_INT - 1))
- - 1)
- && (const_ok_for_arm (i+1) || const_ok_for_arm (- (i+1))))
+ if (i != (((HOST_UINT (1)) << (HOST_BITS_PER_WIDE_INT - 1)) - 1)
+ && (const_ok_for_arm (i + 1) || const_ok_for_arm (-(i + 1))))
{
- *op1 = GEN_INT (i+1);
+ *op1 = GEN_INT (i + 1);
return code == GT ? GE : LT;
}
break;
case GE:
case LT:
if (i != ((HOST_UINT (1)) << (HOST_BITS_PER_WIDE_INT - 1))
- && (const_ok_for_arm (i-1) || const_ok_for_arm (- (i-1))))
+ && (const_ok_for_arm (i - 1) || const_ok_for_arm (-(i - 1))))
{
- *op1 = GEN_INT (i-1);
+ *op1 = GEN_INT (i - 1);
return code == GE ? GT : LE;
}
break;
case GTU:
case LEU:
- if (i != ~ (HOST_UINT (0))
- && (const_ok_for_arm (i+1) || const_ok_for_arm (- (i+1))))
+ if (i != ~(HOST_UINT (0))
+ && (const_ok_for_arm (i + 1) || const_ok_for_arm (-(i + 1))))
{
*op1 = GEN_INT (i + 1);
return code == GTU ? GEU : LTU;
case GEU:
case LTU:
if (i != 0
- && (const_ok_for_arm (i - 1) || const_ok_for_arm (- (i - 1))))
+ && (const_ok_for_arm (i - 1) || const_ok_for_arm (-(i - 1))))
{
*op1 = GEN_INT (i - 1);
return code == GEU ? GTU : LEU;
arm_return_in_memory (type)
tree type;
{
- if (! AGGREGATE_TYPE_P (type))
+ if (!AGGREGATE_TYPE_P (type))
/* All simple types are returned in registers. */
return 0;
a register are not allowed. */
if (RETURN_IN_MEMORY (TREE_TYPE (field)))
return 1;
-
+
/* Now check the remaining fields, if any. Only bitfields are allowed,
since they are not addressable. */
for (field = TREE_CHAIN (field);
if (TREE_CODE (field) != FIELD_DECL)
continue;
- if (! DECL_BIT_FIELD_TYPE (field))
+ if (!DECL_BIT_FIELD_TYPE (field))
return 1;
}
/* Compute operand 2 of the call insn. */
return GEN_INT (pcum->call_cookie);
- if (! named || pcum->nregs >= NUM_ARG_REGS)
+ if (!named || pcum->nregs >= NUM_ARG_REGS)
return NULL_RTX;
return gen_rtx_REG (mode, pcum->nregs);
static arm_pragma_enum arm_pragma_long_calls = OFF;
-/* Handle pragmas for compatibility with Intel's compilers.
- FIXME: This is incomplete, since it does not handle all
- the pragmas that the Intel compilers understand. */
-int
-arm_process_pragma (p_getc, p_ungetc, pname)
- int (* p_getc) PARAMS ((void)) ATTRIBUTE_UNUSED;
- void (* p_ungetc) PARAMS ((int)) ATTRIBUTE_UNUSED;
- char * pname;
-{
- /* Should be pragma 'far' or equivalent for callx/balx here. */
- if (strcmp (pname, "long_calls") == 0)
- arm_pragma_long_calls = LONG;
- else if (strcmp (pname, "no_long_calls") == 0)
- arm_pragma_long_calls = SHORT;
- else if (strcmp (pname, "long_calls_off") == 0)
- arm_pragma_long_calls = OFF;
- else
- return 0;
-
- return 1;
+void
+arm_pr_long_calls (pfile)
+ cpp_reader *pfile ATTRIBUTE_UNUSED;
+{
+ arm_pragma_long_calls = LONG;
+}
+
+void
+arm_pr_no_long_calls (pfile)
+ cpp_reader *pfile ATTRIBUTE_UNUSED;
+{
+ arm_pragma_long_calls = SHORT;
+}
+
+void
+arm_pr_long_calls_off (pfile)
+ cpp_reader *pfile ATTRIBUTE_UNUSED;
+{
+ arm_pragma_long_calls = OFF;
}
+
\f
-/* Return nonzero if IDENTIFIER with arguments ARGS is a valid machine specific
- attribute for TYPE. The attributes in ATTRIBUTES have previously been
- assigned to TYPE. */
-int
+/* Return nonzero if IDENTIFIER with arguments ARGS is a valid machine
+ specific attribute for TYPE. The attributes in ATTRIBUTES have
+ previously been assigned to TYPE. */
+static int
arm_valid_type_attribute_p (type, attributes, identifier, args)
tree type;
tree attributes ATTRIBUTE_UNUSED;
if (is_attribute_p ("short_call", identifier))
return (args == NULL_TREE);
+ /* Interrupt Service Routines have special prologue and epilogue requirements. */
+ if (is_attribute_p ("isr", identifier)
+ || is_attribute_p ("interrupt", identifier))
+ return arm_isr_value (args);
+
return 0;
}
/* Return 0 if the attributes for two types are incompatible, 1 if they
are compatible, and 2 if they are nearly compatible (which causes a
warning to be generated). */
-int
+static int
arm_comp_type_attributes (type1, type2)
tree type1;
tree type2;
return 0;
}
+ /* Check for mismatched ISR attribute. */
+ l1 = lookup_attribute ("isr", TYPE_ATTRIBUTES (type1)) != NULL;
+ if (! l1)
+ l1 = lookup_attribute ("interrupt", TYPE_ATTRIBUTES (type1)) != NULL;
+ l2 = lookup_attribute ("isr", TYPE_ATTRIBUTES (type2)) != NULL;
+ if (! l2)
+ l1 = lookup_attribute ("interrupt", TYPE_ATTRIBUTES (type2)) != NULL;
+ if (l1 != l2)
+ return 0;
+
return 1;
}
/* Do not allow weak functions to be treated as short call. */
if (DECL_WEAK (decl) && flag == SHORT_CALL_FLAG_CHAR)
return;
-
- if (ggc_p)
- newstr = ggc_alloc_string (NULL, len + 2);
- else
- newstr = permalloc (len + 2);
- sprintf (newstr, "%c%s", flag, str);
+ newstr = alloca (len + 2);
+ newstr[0] = flag;
+ strcpy (newstr + 1, str);
+ newstr = (char *) ggc_alloc_string (newstr, len + 1);
XSTR (XEXP (DECL_RTL (decl), 0), 0) = newstr;
}
/* Assigns default attributes to newly defined type. This is used to
set short_call/long_call attributes for function types of
functions defined inside corresponding #pragma scopes. */
-void
+static void
arm_set_default_type_attributes (type)
tree type;
{
unit. if it s a weak defintion however, then this may not be the real
defintion of the function, and so we have to say no. */
if (sym_ref == XEXP (DECL_RTL (current_function_decl), 0)
- && ! DECL_WEAK (current_function_decl))
+ && !DECL_WEAK (current_function_decl))
return 1;
/* We cannot make the determination - default to returning 0. */
int call_cookie;
int call_symbol;
{
- if (! call_symbol)
+ if (!call_symbol)
{
if (GET_CODE (sym_ref) != MEM)
return 0;
/* Cannot tail-call to long calls, since these are out of range of
a branch instruction. However, if not compiling PIC, we know
we can reach the symbol if it is in this compilation unit. */
- if (call_type == CALL_LONG && (flag_pic || ! TREE_ASM_WRITTEN (decl)))
+ if (call_type == CALL_LONG && (flag_pic || !TREE_ASM_WRITTEN (decl)))
return 0;
/* If we are interworking and the function is not declared static
then we can't tail-call it unless we know that it exists in this
compilation unit (since it might be a Thumb routine). */
- if (TARGET_INTERWORK && TREE_PUBLIC (decl) && ! TREE_ASM_WRITTEN (decl))
+ if (TARGET_INTERWORK && TREE_PUBLIC (decl) && !TREE_ASM_WRITTEN (decl))
+ return 0;
+
+ /* Never tailcall from an ISR routine - it needs a special exit sequence. */
+ if (IS_INTERRUPT (arm_current_func_type ()))
return 0;
/* Everything else is ok. */
if (reg == 0)
{
- if (reload_in_progress || reload_completed)
+ if (no_new_pseudos)
abort ();
else
reg = gen_reg_rtx (Pmode);
else
address = reg;
- emit_insn (gen_pic_load_addr (address, orig));
+ if (TARGET_ARM)
+ emit_insn (gen_pic_load_addr_arm (address, orig));
+ else
+ emit_insn (gen_pic_load_addr_thumb (address, orig));
pic_ref = gen_rtx_MEM (Pmode,
gen_rtx_PLUS (Pmode, pic_offset_table_rtx,
if (reg == 0)
{
- if (reload_in_progress || reload_completed)
+ if (no_new_pseudos)
abort ();
else
reg = gen_reg_rtx (Pmode);
{
/* The base register doesn't really matter, we only want to
test the index for the appropriate mode. */
- GO_IF_LEGITIMATE_INDEX (mode, 0, offset, win);
+ ARM_GO_IF_LEGITIMATE_INDEX (mode, 0, offset, win);
- if (! reload_in_progress && ! reload_completed)
+ if (!no_new_pseudos)
offset = force_reg (Pmode, offset);
else
abort ();
win:
if (GET_CODE (offset) == CONST_INT)
- return plus_constant_for_output (base, INTVAL (offset));
+ return plus_constant (base, INTVAL (offset));
}
if (GET_MODE_SIZE (mode) > 4
if (NEED_GOT_RELOC)
{
rtx pic_ref, address = gen_reg_rtx (Pmode);
-
- emit_insn (gen_pic_load_addr (address, orig));
+
+ if (TARGET_ARM)
+ emit_insn (gen_pic_load_addr_arm (address, orig));
+ else
+ emit_insn (gen_pic_load_addr_thumb (address, orig));
+
pic_ref = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, address);
emit_move_insn (address, pic_ref);
return orig;
}
-static rtx pic_rtx;
-
-int
-is_pic (x)
- rtx x;
-{
- if (x == pic_rtx)
- return 1;
- return 0;
-}
+/* Generate code to load the PIC register. PROLOGUE is true if
+ called from arm_expand_prologue (in which case we want the
+ generated insns at the start of the function); false if called
+ by an exception receiver that needs the PIC register reloaded
+ (in which case the insns are just dumped at the current location). */
void
-arm_finalize_pic ()
+arm_finalize_pic (prologue)
+ int prologue;
{
#ifndef AOF_ASSEMBLER
- rtx l1, pic_tmp, pic_tmp2, seq;
+ rtx l1, pic_tmp, pic_tmp2, seq, pic_rtx;
rtx global_offset_table;
if (current_function_uses_pic_offset_table == 0 || TARGET_SINGLE_PIC_BASE)
return;
- if (! flag_pic)
+ if (!flag_pic)
abort ();
start_sequence ();
pic_rtx = gen_rtx_CONST (Pmode, gen_rtx_MINUS (Pmode, pic_tmp2, pic_tmp));
- emit_insn (gen_pic_load_addr (pic_offset_table_rtx, pic_rtx));
if (TARGET_ARM)
- emit_insn (gen_pic_add_dot_plus_eight (pic_offset_table_rtx, l1));
+ {
+ emit_insn (gen_pic_load_addr_arm (pic_offset_table_rtx, pic_rtx));
+ emit_insn (gen_pic_add_dot_plus_eight (pic_offset_table_rtx, l1));
+ }
else
- emit_insn (gen_pic_add_dot_plus_four (pic_offset_table_rtx, l1));
+ {
+ emit_insn (gen_pic_load_addr_thumb (pic_offset_table_rtx, pic_rtx));
+ emit_insn (gen_pic_add_dot_plus_four (pic_offset_table_rtx, l1));
+ }
seq = gen_sequence ();
end_sequence ();
- emit_insn_after (seq, get_insns ());
+ if (prologue)
+ emit_insn_after (seq, get_insns ());
+ else
+ emit_insn (seq);
/* Need to emit this whether or not we obey regdecls,
since setjmp/longjmp can cause life info to screw up. */
while (i)
{
i >>= 2;
- cycles ++;
+ cycles++;
}
return COSTS_N_INSNS (2) + cycles;
}
if (const_ok_for_arm (INTVAL (x)))
return outer == SET ? 2 : -1;
else if (outer == AND
- && const_ok_for_arm (~ INTVAL (x)))
+ && const_ok_for_arm (~INTVAL (x)))
return -1;
else if ((outer == COMPARE
|| outer == PLUS || outer == MINUS)
- && const_ok_for_arm (- INTVAL (x)))
+ && const_ok_for_arm (-INTVAL (x)))
return -1;
else
return 5;
{
rtx i_pat, d_pat;
+ /* Some true dependencies can have a higher cost depending
+ on precisely how certain input operands are used. */
+ if (arm_is_xscale
+ && REG_NOTE_KIND (link) == 0
+ && recog_memoized (insn) < 0
+ && recog_memoized (dep) < 0)
+ {
+ int shift_opnum = get_attr_shift (insn);
+ enum attr_type attr_type = get_attr_type (dep);
+
+ /* If nonzero, SHIFT_OPNUM contains the operand number of a shifted
+ operand for INSN. If we have a shifted input operand and the
+ instruction we depend on is another ALU instruction, then we may
+ have to account for an additional stall. */
+ if (shift_opnum != 0 && attr_type == TYPE_NORMAL)
+ {
+ rtx shifted_operand;
+ int opno;
+
+ /* Get the shifted operand. */
+ extract_insn (insn);
+ shifted_operand = recog_data.operand[shift_opnum];
+
+ /* Iterate over all the operands in DEP. If we write an operand
+ that overlaps with SHIFTED_OPERAND, then we have increase the
+ cost of this dependency. */
+ extract_insn (dep);
+ preprocess_constraints ();
+ for (opno = 0; opno < recog_data.n_operands; opno++)
+ {
+ /* We can ignore strict inputs. */
+ if (recog_data.operand_type[opno] == OP_IN)
+ continue;
+
+ if (reg_overlap_mentioned_p (recog_data.operand[opno],
+ shifted_operand))
+ return 2;
+ }
+ }
+ }
+
/* XXX This is not strictly true for the FPA. */
if (REG_NOTE_KIND (link) == REG_DEP_ANTI
|| REG_NOTE_KIND (link) == REG_DEP_OUTPUT)
|| REGNO_REG_CLASS (REGNO (op)) != NO_REGS));
}
+/* A hard register operand (even before reload. */
+int
+arm_hard_register_operand (op, mode)
+ register rtx op;
+ enum machine_mode mode;
+{
+ if (GET_MODE (op) != mode && mode != VOIDmode)
+ return 0;
+
+ return (GET_CODE (op) == REG
+ && REGNO (op) < FIRST_PSEUDO_REGISTER);
+}
+
/* Only accept reg, subreg(reg), const_int. */
int
{
int regno = true_regnum (op);
- return (! CONSTANT_P (op)
+ return (!CONSTANT_P (op)
&& (regno == -1
|| (GET_CODE (op) == REG
&& REGNO (op) >= FIRST_PSEUDO_REGISTER)));
enum machine_mode mode ATTRIBUTE_UNUSED;
{
#if 0
- if ((mode == QImode && ! memory_operand (op, mode)) || GET_CODE (op) != MEM)
+ if ((mode == QImode && !memory_operand (op, mode)) || GET_CODE (op) != MEM)
return 0;
#endif
if (GET_CODE (op) != MEM)
/* A sum of anything more complex than reg + reg or reg + const is bad. */
if ((GET_CODE (op) == PLUS || GET_CODE (op) == MINUS)
- && (! s_register_operand (XEXP (op, 0), VOIDmode)
- || (! s_register_operand (XEXP (op, 1), VOIDmode)
+ && (!s_register_operand (XEXP (op, 0), VOIDmode)
+ || (!s_register_operand (XEXP (op, 1), VOIDmode)
&& GET_CODE (XEXP (op, 1)) != CONST_INT)))
return 1;
|| GET_CODE (SET_SRC (elt)) != MEM
|| GET_MODE (SET_SRC (elt)) != SImode
|| GET_CODE (XEXP (SET_SRC (elt), 0)) != PLUS
- || ! rtx_equal_p (XEXP (XEXP (SET_SRC (elt), 0), 0), src_addr)
+ || !rtx_equal_p (XEXP (XEXP (SET_SRC (elt), 0), 0), src_addr)
|| GET_CODE (XEXP (XEXP (SET_SRC (elt), 0), 1)) != CONST_INT
|| INTVAL (XEXP (XEXP (SET_SRC (elt), 0), 1)) != (i - base) * 4)
return 0;
|| GET_CODE (SET_DEST (elt)) != MEM
|| GET_MODE (SET_DEST (elt)) != SImode
|| GET_CODE (XEXP (SET_DEST (elt), 0)) != PLUS
- || ! rtx_equal_p (XEXP (XEXP (SET_DEST (elt), 0), 0), dest_addr)
+ || !rtx_equal_p (XEXP (XEXP (SET_DEST (elt), 0), 0), dest_addr)
|| GET_CODE (XEXP (XEXP (SET_DEST (elt), 0), 1)) != CONST_INT
|| INTVAL (XEXP (XEXP (SET_DEST (elt), 0), 1)) != (i - base) * 4)
return 0;
if (GET_CODE (op) != PARALLEL
|| (GET_CODE (XVECEXP (op, 0, 0)) != SET)
|| (GET_CODE (SET_SRC (XVECEXP (op, 0, 0))) != UNSPEC)
- || (XINT (SET_SRC (XVECEXP (op, 0, 0)), 1) != 2))
+ || (XINT (SET_SRC (XVECEXP (op, 0, 0)), 1) != UNSPEC_PUSH_MULT))
return 0;
return 1;
don't output any prologue or epilogue code, the user is assumed
to do the right thing.
+ isr or interrupt:
+ Interrupt Service Routine.
+
interfacearm:
Always assume that this function will be entered in ARM mode,
not Thumb mode, and that the caller wishes to be returned to in
ARM mode. */
-int
-arm_valid_machine_decl_attribute (decl, attr, args)
+static int
+arm_valid_decl_attribute_p (decl, attributes, attr, args)
tree decl;
+ tree attributes ATTRIBUTE_UNUSED;
tree attr;
tree args;
{
+ /* The interrupt attribute can take args, so check for it before
+ rejecting other attributes on the grounds that they did have args. */
+ if (is_attribute_p ("isr", attr)
+ || is_attribute_p ("interrupt", attr))
+ return TREE_CODE (decl) == FUNCTION_DECL;
+
if (args != NULL_TREE)
return 0;
if (is_attribute_p ("naked", attr))
return TREE_CODE (decl) == FUNCTION_DECL;
-
+
#ifdef ARM_PE
if (is_attribute_p ("interfacearm", attr))
return TREE_CODE (decl) == FUNCTION_DECL;
return 0;
}
-/* Return non-zero if FUNC is a naked function. */
+#ifdef ARM_PE
+
+/* ARM/PE has three new attributes:
+ naked - for interrupt functions
+ dllexport - for exporting a function/variable that will live in a dll
+ dllimport - for importing a function/variable from a dll
+
+ Microsoft allows multiple declspecs in one __declspec, separating
+ them with spaces. We do NOT support this. Instead, use __declspec
+ multiple times.
+*/
+
static int
-arm_naked_function_p (func)
- tree func;
+arm_pe_valid_decl_attribute_p (decl, attributes, attr, args)
+ tree decl;
+ tree attributes;
+ tree attr;
+ tree args;
{
- tree a;
+ if (args != NULL_TREE)
+ return 0;
- if (TREE_CODE (func) != FUNCTION_DECL)
- abort ();
+ if (is_attribute_p ("dllexport", attr))
+ return 1;
- a = lookup_attribute ("naked", DECL_MACHINE_ATTRIBUTES (func));
- return a != NULL_TREE;
+ if (is_attribute_p ("dllimport", attr))
+ return 1;
+
+ return arm_valid_decl_attribute_p (decl, attributes, attr, args);
}
+
+#endif /* ARM_PE */
\f
/* Routines for use in generating RTL. */
rtx
int sign = up ? 1 : -1;
rtx mem;
+ /* XScale has load-store double instructions, but they have stricter
+ alignment requirements than load-store multiple, so we can not
+ use them.
+
+ For XScale ldm requires 2 + NREGS cycles to complete and blocks
+ the pipeline until completion.
+
+ NREGS CYCLES
+ 1 3
+ 2 4
+ 3 5
+ 4 6
+
+ An ldr instruction takes 1-3 cycles, but does not block the
+ pipeline.
+
+ NREGS CYCLES
+ 1 1-3
+ 2 2-6
+ 3 3-9
+ 4 4-12
+
+ Best case ldr will always win. However, the more ldr instructions
+ we issue, the less likely we are to be able to schedule them well.
+ Using ldr instructions also increases code size.
+
+ As a compromise, we use ldr for counts of 1 or 2 regs, and ldm
+ for counts of 3 or 4 regs. */
+ if (arm_is_xscale && count <= 2 && ! optimize_size)
+ {
+ rtx seq;
+
+ start_sequence ();
+
+ for (i = 0; i < count; i++)
+ {
+ mem = gen_rtx_MEM (SImode, plus_constant (from, i * 4 * sign));
+ RTX_UNCHANGING_P (mem) = unchanging_p;
+ MEM_IN_STRUCT_P (mem) = in_struct_p;
+ MEM_SCALAR_P (mem) = scalar_p;
+ emit_move_insn (gen_rtx_REG (SImode, base_regno + i), mem);
+ }
+
+ if (write_back)
+ emit_move_insn (from, plus_constant (from, count * 4 * sign));
+
+ seq = gen_sequence ();
+ end_sequence ();
+
+ return seq;
+ }
+
result = gen_rtx_PARALLEL (VOIDmode,
rtvec_alloc (count + (write_back ? 1 : 0)));
if (write_back)
int sign = up ? 1 : -1;
rtx mem;
+ /* See arm_gen_load_multiple for discussion of
+ the pros/cons of ldm/stm usage for XScale. */
+ if (arm_is_xscale && count <= 2 && ! optimize_size)
+ {
+ rtx seq;
+
+ start_sequence ();
+
+ for (i = 0; i < count; i++)
+ {
+ mem = gen_rtx_MEM (SImode, plus_constant (to, i * 4 * sign));
+ RTX_UNCHANGING_P (mem) = unchanging_p;
+ MEM_IN_STRUCT_P (mem) = in_struct_p;
+ MEM_SCALAR_P (mem) = scalar_p;
+ emit_move_insn (mem, gen_rtx_REG (SImode, base_regno + i));
+ }
+
+ if (write_back)
+ emit_move_insn (to, plus_constant (to, count * 4 * sign));
+
+ seq = gen_sequence ();
+ end_sequence ();
+
+ return seq;
+ }
+
result = gen_rtx_PARALLEL (VOIDmode,
rtvec_alloc (count + (write_back ? 1 : 0)));
if (write_back)
return gen_rtx_ROTATE (SImode, base, GEN_INT (16));
}
+/* Select a dominance comparison mode if possible. We support three forms.
+ COND_OR == 0 => (X && Y)
+ COND_OR == 1 => ((! X( || Y)
+ COND_OR == 2 => (X || Y)
+ If we are unable to support a dominance comparsison we return CC mode.
+ This will then fail to match for the RTL expressions that generate this
+ call. */
+
static enum machine_mode
select_dominance_cc_mode (x, y, cond_or)
rtx x;
!= CCmode))
return CCmode;
- if (cond_or)
+ /* The if_then_else variant of this tests the second condition if the
+ first passes, but is true if the first fails. Reverse the first
+ condition to get a true "inclusive-or" expression. */
+ if (cond_or == 1)
cond1 = reverse_condition (cond1);
/* If the comparisons are not equal, and one doesn't dominate the other,
then we can't do this. */
if (cond1 != cond2
- && ! comparison_dominates_p (cond1, cond2)
- && (swapped = 1, ! comparison_dominates_p (cond2, cond1)))
+ && !comparison_dominates_p (cond1, cond2)
+ && (swapped = 1, !comparison_dominates_p (cond2, cond1)))
return CCmode;
if (swapped)
switch (cond1)
{
case EQ:
- if (cond2 == EQ || ! cond_or)
+ if (cond2 == EQ || !cond_or)
return CC_DEQmode;
switch (cond2)
break;
case LT:
- if (cond2 == LT || ! cond_or)
+ if (cond2 == LT || !cond_or)
return CC_DLTmode;
if (cond2 == LE)
return CC_DLEmode;
break;
case GT:
- if (cond2 == GT || ! cond_or)
+ if (cond2 == GT || !cond_or)
return CC_DGTmode;
if (cond2 == GE)
return CC_DGEmode;
break;
case LTU:
- if (cond2 == LTU || ! cond_or)
+ if (cond2 == LTU || !cond_or)
return CC_DLTUmode;
if (cond2 == LEU)
return CC_DLEUmode;
break;
case GTU:
- if (cond2 == GTU || ! cond_or)
+ if (cond2 == GTU || !cond_or)
return CC_DGTUmode;
if (cond2 == GEU)
return CC_DGEUmode;
&& GET_CODE (y) == CONST_INT)
return CC_Zmode;
+ /* A construct for a conditional compare, if the false arm contains
+ 0, then both conditions must be true, otherwise either condition
+ must be true. Not all conditions are possible, so CCmode is
+ returned if it can't be done. */
+ if (GET_CODE (x) == IF_THEN_ELSE
+ && (XEXP (x, 2) == const0_rtx
+ || XEXP (x, 2) == const1_rtx)
+ && GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) == '<'
+ && GET_RTX_CLASS (GET_CODE (XEXP (x, 1))) == '<')
+ return select_dominance_cc_mode (XEXP (x, 0), XEXP (x, 1),
+ INTVAL (XEXP (x, 2)));
+
+ /* Alternate canonicalizations of the above. These are somewhat cleaner. */
+ if (GET_CODE (x) == AND
+ && GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) == '<'
+ && GET_RTX_CLASS (GET_CODE (XEXP (x, 1))) == '<')
+ return select_dominance_cc_mode (XEXP (x, 0), XEXP (x, 1), 0);
+
+ if (GET_CODE (x) == IOR
+ && GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) == '<'
+ && GET_RTX_CLASS (GET_CODE (XEXP (x, 1))) == '<')
+ return select_dominance_cc_mode (XEXP (x, 0), XEXP (x, 1), 2);
+
/* An operation that sets the condition codes as a side-effect, the
V flag is not set correctly, so we can only use comparisons where
this doesn't matter. (For LT and GE we can use "mi" and "pl"
|| GET_CODE (x) == ROTATERT || GET_CODE (x) == ZERO_EXTRACT))
return CC_NOOVmode;
- /* A construct for a conditional compare, if the false arm contains
- 0, then both conditions must be true, otherwise either condition
- must be true. Not all conditions are possible, so CCmode is
- returned if it can't be done. */
- if (GET_CODE (x) == IF_THEN_ELSE
- && (XEXP (x, 2) == const0_rtx
- || XEXP (x, 2) == const1_rtx)
- && GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) == '<'
- && GET_RTX_CLASS (GET_CODE (XEXP (x, 1))) == '<')
- return select_dominance_cc_mode (XEXP (x, 0), XEXP (x, 1),
- INTVAL (XEXP (x, 2)));
-
if (GET_MODE (x) == QImode && (op == EQ || op == NE))
return CC_Zmode;
if (GET_CODE (ref) == SUBREG)
{
- offset = SUBREG_WORD (ref) * UNITS_PER_WORD;
- if (BYTES_BIG_ENDIAN)
- offset -= (MIN (UNITS_PER_WORD, GET_MODE_SIZE (GET_MODE (ref)))
- - MIN (UNITS_PER_WORD,
- GET_MODE_SIZE (GET_MODE (SUBREG_REG (ref)))));
+ offset = SUBREG_BYTE (ref);
ref = SUBREG_REG (ref);
}
gen_rtx_MEM (QImode,
plus_constant (base,
offset + 1))));
- if (! BYTES_BIG_ENDIAN)
+ if (!BYTES_BIG_ENDIAN)
emit_insn (gen_rtx_SET (VOIDmode, gen_rtx_SUBREG (SImode, operands[0], 0),
gen_rtx_IOR (SImode,
gen_rtx_ASHIFT
if (GET_CODE (ref) == SUBREG)
{
- offset = SUBREG_WORD (ref) * UNITS_PER_WORD;
- if (BYTES_BIG_ENDIAN)
- offset -= (MIN (UNITS_PER_WORD, GET_MODE_SIZE (GET_MODE (ref)))
- - MIN (UNITS_PER_WORD,
- GET_MODE_SIZE (GET_MODE (SUBREG_REG (ref)))));
+ offset = SUBREG_BYTE (ref);
ref = SUBREG_REG (ref);
}
{
/* Updating base_plus might destroy outval, see if we can
swap the scratch and base_plus. */
- if (! reg_overlap_mentioned_p (scratch, outval))
+ if (!reg_overlap_mentioned_p (scratch, outval))
{
rtx tmp = scratch;
scratch = base_plus;
hi = ((((offset - lo) & HOST_INT (0xffffffff))
^ HOST_INT (0x80000000))
- - HOST_INT (0x80000000));
+ - HOST_INT (0x80000000));
if (hi + lo != offset)
abort ();
{
/* Updating base_plus might destroy outval, see if we
can swap the scratch and base_plus. */
- if (! reg_overlap_mentioned_p (scratch, outval))
+ if (!reg_overlap_mentioned_p (scratch, outval))
{
rtx tmp = scratch;
scratch = base_plus;
emit_label_after (label, barrier);
/* Create a minipool barrier entry for the new barrier. */
- new_fix = (Mfix *) oballoc (sizeof (* new_fix));
+ new_fix = (Mfix *) obstack_alloc (&minipool_obstack, sizeof (* new_fix));
new_fix->insn = barrier;
new_fix->address = selected_address;
new_fix->next = fix->next;
rtx insn;
HOST_WIDE_INT address;
{
- Mfix * fix = (Mfix *) oballoc (sizeof (* fix));
+ Mfix * fix = (Mfix *) obstack_alloc (&minipool_obstack, sizeof (* fix));
fix->insn = insn;
fix->address = address;
enum machine_mode mode;
rtx value;
{
- Mfix * fix = (Mfix *) oballoc (sizeof (* fix));
+ Mfix * fix = (Mfix *) obstack_alloc (&minipool_obstack, sizeof (* fix));
#ifdef AOF_ASSEMBLER
/* PIC symbol refereneces need to be converted into offsets into the
extract_insn (insn);
- if (! constrain_operands (1))
+ if (!constrain_operands (1))
fatal_insn_not_found (insn);
/* Fill in recog_op_alt with information about the constraints of this
this shouldn't be needed any more. */
#ifndef AOF_ASSEMBLER
/* XXX Is this still needed? */
- else if (GET_CODE (op) == UNSPEC && XINT (op, 1) == 3)
+ else if (GET_CODE (op) == UNSPEC && XINT (op, 1) == UNSPEC_PIC_SYM)
push_minipool_fix (insn, address, recog_data.operand_loc[opno],
recog_data.operand_mode[opno],
XVECEXP (op, 0, 0));
/* Scan all the insns and record the operands that will need fixing. */
for (insn = next_nonnote_insn (first); insn; insn = next_nonnote_insn (insn))
{
-
if (GET_CODE (insn) == BARRIER)
push_minipool_barrier (insn, address);
else if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN
directly. This can happen if the RTL gets split during final
instruction generation. */
after_arm_reorg = 1;
+
+ /* Free the minipool memory. */
+ obstack_free (&minipool_obstack, minipool_startobj);
}
\f
/* Routines to output assembly language. */
{
int i;
- if (! fpa_consts_inited)
+ if (!fpa_consts_inited)
init_fpa_table ();
for (i = 0; i < 8; i++)
/* Output the operands of a LDM/STM instruction to STREAM.
MASK is the ARM register set mask of which only bits 0-15 are important.
- INSTR is the possibly suffixed base register. HAT unequals zero if a hat
- must follow the register list. */
+ REG is the base register, either the frame pointer or the stack pointer,
+ INSTR is the possibly suffixed load or store instruction. */
static void
-print_multi_reg (stream, instr, reg, mask, hat)
+print_multi_reg (stream, instr, reg, mask)
FILE * stream;
const char * instr;
int reg;
int mask;
- int hat;
{
int i;
int not_first = FALSE;
not_first = TRUE;
}
- fprintf (stream, "}%s\n", hat ? "^" : "");
+ fprintf (stream, "}%s\n", TARGET_APCS_32 ? "" : "^");
}
/* Output a 'call' insn. */
long l[2];
union real_extract u;
- bcopy ((char *) &CONST_DOUBLE_LOW (operands[1]), (char *) &u,
- sizeof (u));
+ memcpy (&u, &CONST_DOUBLE_LOW (operands[1]), sizeof (u));
REAL_VALUE_TO_TARGET_DOUBLE (u.d, l);
otherops[1] = GEN_INT (l[1]);
operands[1] = GEN_INT (l[0]);
}
else
{
- otherops[1] = adj_offsettable_operand (operands[1], 4);
+ otherops[1] = adjust_address (operands[1], VOIDmode, 4);
/* Take care of overlapping base/data reg. */
if (reg_mentioned_p (operands[0], operands[1]))
{
/* Fall through */
default:
- otherops[0] = adj_offsettable_operand (operands[0], 4);
+ otherops[0] = adjust_address (operands[0], VOIDmode, 4);
otherops[1] = gen_rtx_REG (SImode, 1 + REGNO (operands[1]));
output_asm_insn ("str%?\t%1, %0", operands);
output_asm_insn ("str%?\t%1, %0", otherops);
case '\"':
case '\\':
putc ('\\', stream);
- len_so_far ++;
+ len_so_far++;
/* drop through. */
default:
if (c >= ' ' && c <= '~')
{
putc (c, stream);
- len_so_far ++;
+ len_so_far++;
}
else
{
fputs ("\"\n", stream);
}
\f
+/* Compute a bit mask of which registers need to be
+ saved on the stack for the current function. */
-const char *
-output_return_instruction (operand, really_return, reverse)
- rtx operand;
- int really_return;
- int reverse;
+static unsigned long
+arm_compute_save_reg_mask ()
{
- char instr[100];
- int reg, live_regs = 0;
- int volatile_func = arm_volatile_func ();
+ unsigned int save_reg_mask = 0;
+ unsigned int reg;
+ unsigned long func_type = arm_current_func_type ();
- /* If a function is naked, don't use the "return" insn. */
- if (arm_naked_function_p (current_function_decl))
- return "";
-
- return_used_this_function = 1;
-
- if (TARGET_ABORT_NORETURN && volatile_func)
+ if (IS_NAKED (func_type))
+ /* This should never really happen. */
+ return 0;
+
+ /* If we are creating a stack frame, then we must save the frame pointer,
+ IP (which will hold the old stack pointer), LR and the PC. */
+ if (frame_pointer_needed)
+ save_reg_mask |=
+ (1 << ARM_HARD_FRAME_POINTER_REGNUM)
+ | (1 << IP_REGNUM)
+ | (1 << LR_REGNUM)
+ | (1 << PC_REGNUM);
+
+ /* Volatile functions do not return, so there
+ is no need to save any other registers. */
+ if (IS_VOLATILE (func_type))
+ return save_reg_mask;
+
+ if (IS_INTERRUPT (func_type))
+ {
+ unsigned int max_reg;
+
+ /* Interrupt functions must not corrupt any registers,
+ even call clobbered ones. If this is a leaf function
+ we can just examine the registers used by the RTL, but
+ otherwise we have to assume that whatever function is
+ called might clobber anything, and so we have to save
+ all the call-clobbered registers as well. */
+ if (ARM_FUNC_TYPE (func_type) == ARM_FT_FIQ)
+ /* FIQ handlers have registers r8 - r12 banked, so
+ we only need to check r0 - r7, Normal ISRs only
+ bank r14 and r15, so ew must check up to r12.
+ r13 is the stack pointer which is always preserved,
+ so we do not need to consider it here. */
+ max_reg = 7;
+ else
+ max_reg = 12;
+
+ for (reg = 0; reg <= max_reg; reg++)
+ if (regs_ever_live[reg]
+ || (! current_function_is_leaf && call_used_regs [reg]))
+ save_reg_mask |= (1 << reg);
+ }
+ else
+ {
+ /* In the normal case we only need to save those registers
+ which are call saved and which are used by this function. */
+ for (reg = 0; reg <= 10; reg++)
+ if (regs_ever_live[reg] && ! call_used_regs [reg])
+ save_reg_mask |= (1 << reg);
+
+ /* Handle the frame pointer as a special case. */
+ if (! TARGET_APCS_FRAME
+ && ! frame_pointer_needed
+ && regs_ever_live[HARD_FRAME_POINTER_REGNUM]
+ && ! call_used_regs[HARD_FRAME_POINTER_REGNUM])
+ save_reg_mask |= 1 << HARD_FRAME_POINTER_REGNUM;
+
+ /* If we aren't loading the PIC register,
+ don't stack it even though it may be live. */
+ if (flag_pic
+ && ! TARGET_SINGLE_PIC_BASE
+ && regs_ever_live[PIC_OFFSET_TABLE_REGNUM])
+ save_reg_mask |= 1 << PIC_OFFSET_TABLE_REGNUM;
+ }
+
+ /* Decide if we need to save the link register.
+ Interrupt routines have their own banked link register,
+ so they never need to save it.
+ Otheriwse if we do not use the link register we do not need to save
+ it. If we are pushing other registers onto the stack however, we
+ can save an instruction in the epilogue by pushing the link register
+ now and then popping it back into the PC. This incurs extra memory
+ accesses though, so we only do it when optimising for size, and only
+ if we know that we will not need a fancy return sequence. */
+ if (! IS_INTERRUPT (func_type)
+ && (regs_ever_live [LR_REGNUM]
+ || (save_reg_mask
+ && optimize_size
+ && ARM_FUNC_TYPE (func_type) == ARM_FT_NORMAL)))
+ save_reg_mask |= 1 << LR_REGNUM;
+
+ if (cfun->machine->lr_save_eliminated)
+ save_reg_mask &= ~ (1 << LR_REGNUM);
+
+ return save_reg_mask;
+}
+
+/* Generate a function exit sequence. If REALLY_RETURN is true, then do
+ everything bar the final return instruction. */
+
+const char *
+output_return_instruction (operand, really_return, reverse)
+ rtx operand;
+ int really_return;
+ int reverse;
+{
+ char conditional[10];
+ char instr[100];
+ int reg;
+ unsigned long live_regs_mask;
+ unsigned long func_type;
+
+ func_type = arm_current_func_type ();
+
+ if (IS_NAKED (func_type))
+ return "";
+
+ if (IS_VOLATILE (func_type) && TARGET_ABORT_NORETURN)
{
/* If this function was declared non-returning, and we have found a tail
call, then we have to trust that the called function won't return. */
return "";
}
-
- if (current_function_calls_alloca && ! really_return)
- abort ();
-
- for (reg = 0; reg <= 10; reg++)
- if (regs_ever_live[reg] && ! call_used_regs[reg])
- live_regs++;
- if (! TARGET_APCS_FRAME
- && ! frame_pointer_needed
- && regs_ever_live[HARD_FRAME_POINTER_REGNUM]
- && ! call_used_regs[HARD_FRAME_POINTER_REGNUM])
- live_regs++;
+ if (current_function_calls_alloca && !really_return)
+ abort ();
- if (flag_pic && ! TARGET_SINGLE_PIC_BASE
- && regs_ever_live[PIC_OFFSET_TABLE_REGNUM])
- live_regs++;
+ /* Construct the conditional part of the instruction(s) to be emitted. */
+ sprintf (conditional, "%%?%%%c0", reverse ? 'D' : 'd');
- if (live_regs || regs_ever_live[LR_REGNUM])
- live_regs++;
+ return_used_this_function = 1;
- if (frame_pointer_needed)
- live_regs += 4;
+ live_regs_mask = arm_compute_save_reg_mask ();
/* On some ARM architectures it is faster to use LDR rather than LDM to
- load a single register. On other architectures, the cost is the same. */
- if (live_regs == 1
- && regs_ever_live[LR_REGNUM]
- && ! really_return)
- output_asm_insn (reverse ? "ldr%?%D0\t%|lr, [%|sp], #4"
- : "ldr%?%d0\t%|lr, [%|sp], #4", &operand);
- else if (live_regs == 1
- && regs_ever_live[LR_REGNUM]
- && TARGET_APCS_32)
- output_asm_insn (reverse ? "ldr%?%D0\t%|pc, [%|sp], #4"
- : "ldr%?%d0\t%|pc, [%|sp], #4", &operand);
- else if (live_regs)
- {
- if (! regs_ever_live[LR_REGNUM])
- live_regs++;
+ load a single register. On other architectures, the cost is the same.
+ In 26 bit mode we have to use LDM in order to be able to restore the CPSR. */
+ if ((live_regs_mask == (1 << LR_REGNUM))
+ && ! TARGET_INTERWORK
+ && ! IS_INTERRUPT (func_type)
+ && (! really_return || TARGET_APCS_32))
+ {
+ if (! really_return)
+ sprintf (instr, "ldr%s\t%%|lr, [%%|sp], #4", conditional);
+ else
+ sprintf (instr, "ldr%s\t%%|pc, [%%|sp], #4", conditional);
+ }
+ else if (live_regs_mask)
+ {
+ if ((live_regs_mask & (1 << IP_REGNUM)) == (1 << IP_REGNUM))
+ /* There are two possible reasons for the IP register being saved.
+ Either a stack frame was created, in which case IP contains the
+ old stack pointer, or an ISR routine corrupted it. If this in an
+ ISR routine then just restore IP, otherwise restore IP into SP. */
+ if (! IS_INTERRUPT (func_type))
+ {
+ live_regs_mask &= ~ (1 << IP_REGNUM);
+ live_regs_mask |= (1 << SP_REGNUM);
+ }
+ /* Generate the load multiple instruction to restore the registers. */
if (frame_pointer_needed)
- strcpy (instr,
- reverse ? "ldm%?%D0ea\t%|fp, {" : "ldm%?%d0ea\t%|fp, {");
+ sprintf (instr, "ldm%sea\t%%|fp, {", conditional);
else
- strcpy (instr,
- reverse ? "ldm%?%D0fd\t%|sp!, {" : "ldm%?%d0fd\t%|sp!, {");
+ sprintf (instr, "ldm%sfd\t%%|sp!, {", conditional);
- for (reg = 0; reg <= 10; reg++)
- if (regs_ever_live[reg]
- && (! call_used_regs[reg]
- || (flag_pic && ! TARGET_SINGLE_PIC_BASE
- && reg == PIC_OFFSET_TABLE_REGNUM)))
- {
+ for (reg = 0; reg <= SP_REGNUM; reg++)
+ if (live_regs_mask & (1 << reg))
+ {
strcat (instr, "%|");
- strcat (instr, reg_names[reg]);
- if (--live_regs)
- strcat (instr, ", ");
- }
+ strcat (instr, reg_names[reg]);
+ strcat (instr, ", ");
+ }
- if (frame_pointer_needed)
- {
- strcat (instr, "%|");
- strcat (instr, reg_names[11]);
- strcat (instr, ", ");
- strcat (instr, "%|");
- strcat (instr, reg_names[13]);
- strcat (instr, ", ");
- strcat (instr, "%|");
- strcat (instr, TARGET_INTERWORK || (! really_return)
- ? reg_names[LR_REGNUM] : reg_names[PC_REGNUM] );
- }
+ if ((live_regs_mask & (1 << LR_REGNUM)) == 0)
+ {
+ /* If we are not restoring the LR register then we will
+ have added one too many commas to the list above.
+ Replace it with a closing brace. */
+ instr [strlen (instr) - 2] = '}';
+ }
else
{
- if (! TARGET_APCS_FRAME
- && regs_ever_live[HARD_FRAME_POINTER_REGNUM]
- && ! call_used_regs[HARD_FRAME_POINTER_REGNUM])
- {
- strcat (instr, "%|");
- strcat (instr, reg_names[HARD_FRAME_POINTER_REGNUM]);
- strcat (instr, ", ");
- }
-
strcat (instr, "%|");
-
- if (TARGET_INTERWORK && really_return)
- strcat (instr, reg_names[IP_REGNUM]);
+
+ /* At this point there should only be one or two registers left in
+ live_regs_mask: always LR, and possibly PC if we created a stack
+ frame. LR contains the return address. If we do not have any
+ special requirements for function exit (eg interworking, or ISR)
+ then we can load this value directly into the PC and save an
+ instruction. */
+ if (! TARGET_INTERWORK
+ && ! IS_INTERRUPT (func_type)
+ && really_return)
+ strcat (instr, reg_names [PC_REGNUM]);
else
- strcat (instr, really_return ? reg_names[PC_REGNUM] : reg_names[LR_REGNUM]);
+ strcat (instr, reg_names [LR_REGNUM]);
+
+ strcat (instr, (TARGET_APCS_32 || !really_return) ? "}" : "}^");
}
-
- strcat (instr, (TARGET_APCS_32 || !really_return) ? "}" : "}^");
- output_asm_insn (instr, &operand);
- if (TARGET_INTERWORK && really_return)
+ if (really_return)
{
- strcpy (instr, "bx%?");
- strcat (instr, reverse ? "%D0" : "%d0");
- strcat (instr, "\t%|");
- strcat (instr, frame_pointer_needed ? "lr" : "ip");
+ /* See if we need to generate an extra instruction to
+ perform the actual function return. */
+ switch ((int) ARM_FUNC_TYPE (func_type))
+ {
+ case ARM_FT_ISR:
+ case ARM_FT_FIQ:
+ output_asm_insn (instr, & operand);
- output_asm_insn (instr, & operand);
+ strcpy (instr, "sub");
+ strcat (instr, conditional);
+ strcat (instr, "s\t%|pc, %|lr, #4");
+ break;
+
+ case ARM_FT_EXCEPTION:
+ output_asm_insn (instr, & operand);
+
+ strcpy (instr, "mov");
+ strcat (instr, conditional);
+ strcat (instr, "s\t%|pc, %|lr");
+ break;
+
+ case ARM_FT_INTERWORKED:
+ output_asm_insn (instr, & operand);
+
+ strcpy (instr, "bx");
+ strcat (instr, conditional);
+ strcat (instr, "\t%|lr");
+ break;
+
+ default:
+ /* The return has already been handled
+ by loading the LR into the PC. */
+ if ((live_regs_mask & (1 << LR_REGNUM)) == 0)
+ {
+ output_asm_insn (instr, & operand);
+
+ strcpy (instr, "mov");
+ strcat (instr, conditional);
+ if (! TARGET_APCS_32)
+ strcat (instr, "s");
+ strcat (instr, "\t%|pc, %|lr");
+ }
+ break;
+ }
}
}
else if (really_return)
{
- if (TARGET_INTERWORK)
- sprintf (instr, "bx%%?%%%s0\t%%|lr", reverse ? "D" : "d");
- else
- sprintf (instr, "mov%%?%%%s0%s\t%%|pc, %%|lr",
- reverse ? "D" : "d", TARGET_APCS_32 ? "" : "s");
-
- output_asm_insn (instr, & operand);
+ switch ((int) ARM_FUNC_TYPE (func_type))
+ {
+ case ARM_FT_ISR:
+ case ARM_FT_FIQ:
+ sprintf (instr, "sub%ss\t%%|pc, %%|lr, #4", conditional);
+ break;
+
+ case ARM_FT_INTERWORKED:
+ sprintf (instr, "bx%s\t%%|lr", conditional);
+ break;
+
+ case ARM_FT_EXCEPTION:
+ sprintf (instr, "mov%ss\t%%|pc, %%|lr", conditional);
+ break;
+
+ default:
+ sprintf (instr, "mov%s%s\t%%|pc, %%|lr",
+ conditional, TARGET_APCS_32 ? "" : "s");
+ break;
+ }
}
+ else
+ /* Nothing to load off the stack, and
+ no return instruction to generate. */
+ return "";
+ output_asm_insn (instr, & operand);
+
return "";
}
-/* Return nonzero if optimizing and the current function is volatile.
- Such functions never return, and many memory cycles can be saved
- by not storing register values that will never be needed again.
- This optimization was added to speed up context switching in a
- kernel application. */
-int
-arm_volatile_func ()
-{
- return (optimize > 0
- && current_function_nothrow
- && TREE_THIS_VOLATILE (current_function_decl));
-}
-
/* Write the function name into the code section, directly preceding
the function prologue.
ASM_OUTPUT_INT (stream, x);
}
-/* The amount of stack adjustment that happens here, in output_return and in
- output_epilogue must be exactly the same as was calculated during reload,
- or things will point to the wrong place. The only time we can safely
- ignore this constraint is when a function has no arguments on the stack,
- no stack frame requirement and no live registers execpt for `lr'. If we
- can guarantee that by making all function calls into tail calls and that
- lr is not clobbered in any other way, then there is no need to push lr
- onto the stack. */
+/* Place some comments into the assembler stream
+ describing the current function. */
+
void
output_arm_prologue (f, frame_size)
FILE * f;
int frame_size;
{
- int reg, live_regs_mask = 0;
- int volatile_func = arm_volatile_func ();
-
- /* Nonzero if we must stuff some register arguments onto the stack as if
- they were passed there. */
- int store_arg_regs = 0;
-
+ unsigned long func_type;
+
+ /* Sanity check. */
if (arm_ccfsm_state || arm_target_insn)
- abort (); /* Sanity check. */
-
- if (arm_naked_function_p (current_function_decl))
- return;
+ abort ();
- return_used_this_function = 0;
+ func_type = arm_current_func_type ();
+ switch ((int) ARM_FUNC_TYPE (func_type))
+ {
+ default:
+ case ARM_FT_NORMAL:
+ break;
+ case ARM_FT_INTERWORKED:
+ asm_fprintf (f, "\t%@ Function supports interworking.\n");
+ break;
+ case ARM_FT_EXCEPTION_HANDLER:
+ asm_fprintf (f, "\t%@ C++ Exception Handler.\n");
+ break;
+ case ARM_FT_ISR:
+ asm_fprintf (f, "\t%@ Interrupt Service Routine.\n");
+ break;
+ case ARM_FT_FIQ:
+ asm_fprintf (f, "\t%@ Fast Interrupt Service Routine.\n");
+ break;
+ case ARM_FT_EXCEPTION:
+ asm_fprintf (f, "\t%@ ARM Exception Handler.\n");
+ break;
+ }
+
+ if (IS_NAKED (func_type))
+ asm_fprintf (f, "\t%@ Naked Function: prologue and epilogue provided by programmer.\n");
+
+ if (IS_VOLATILE (func_type))
+ asm_fprintf (f, "\t%@ Volatile: function does not return.\n");
+
+ if (IS_NESTED (func_type))
+ asm_fprintf (f, "\t%@ Nested: function declared inside another function.\n");
+
asm_fprintf (f, "\t%@ args = %d, pretend = %d, frame = %d\n",
current_function_args_size,
current_function_pretend_args_size, frame_size);
+
asm_fprintf (f, "\t%@ frame_needed = %d, current_function_anonymous_args = %d\n",
frame_pointer_needed,
current_function_anonymous_args);
- if (volatile_func)
- asm_fprintf (f, "\t%@ Volatile function.\n");
-
- if (current_function_anonymous_args && current_function_pretend_args_size)
- store_arg_regs = 1;
-
- for (reg = 0; reg <= 10; reg++)
- if (regs_ever_live[reg] && ! call_used_regs[reg])
- live_regs_mask |= (1 << reg);
-
- if (! TARGET_APCS_FRAME
- && ! frame_pointer_needed
- && regs_ever_live[HARD_FRAME_POINTER_REGNUM]
- && ! call_used_regs[HARD_FRAME_POINTER_REGNUM])
- live_regs_mask |= (1 << HARD_FRAME_POINTER_REGNUM);
-
- if (flag_pic && ! TARGET_SINGLE_PIC_BASE
- && regs_ever_live[PIC_OFFSET_TABLE_REGNUM])
- live_regs_mask |= (1 << PIC_OFFSET_TABLE_REGNUM);
-
- if (frame_pointer_needed)
- live_regs_mask |= 0xD800;
- else if (regs_ever_live[LR_REGNUM])
- {
- live_regs_mask |= 1 << LR_REGNUM;
- }
-
- if (live_regs_mask)
- /* If a di mode load/store multiple is used, and the base register
- is r3, then r4 can become an ever live register without lr
- doing so, in this case we need to push lr as well, or we
- will fail to get a proper return. */
- live_regs_mask |= 1 << LR_REGNUM;
+ if (cfun->machine->lr_save_eliminated)
+ asm_fprintf (f, "\t%@ link register save eliminated.\n");
#ifdef AOF_ASSEMBLER
if (flag_pic)
asm_fprintf (f, "\tmov\t%r, %r\n", IP_REGNUM, PIC_OFFSET_TABLE_REGNUM);
#endif
+
+ return_used_this_function = 0;
}
const char *
int really_return;
{
int reg;
- int live_regs_mask = 0;
+ unsigned long saved_regs_mask;
+ unsigned long func_type;
/* If we need this, then it will always be at least this much. */
int floats_offset = 12;
rtx operands[3];
int frame_size = get_frame_size ();
- rtx eh_ofs = cfun->machine->eh_epilogue_sp_ofs;
FILE * f = asm_out_file;
- int volatile_func = arm_volatile_func ();
- int return_regnum;
+ rtx eh_ofs = cfun->machine->eh_epilogue_sp_ofs;
+ /* If we have already generated the return instruction
+ then it is futile to generate anything else. */
if (use_return_insn (FALSE) && return_used_this_function)
return "";
- /* Naked functions don't have epilogues. */
- if (arm_naked_function_p (current_function_decl))
- return "";
-
- /* If we are throwing an exception, the address we want to jump to is in
- R1; otherwise, it's in LR. */
- return_regnum = eh_ofs ? 2 : LR_REGNUM;
+ func_type = arm_current_func_type ();
- /* If we are throwing an exception, then we really must be doing a return,
- so we can't tail-call. */
- if (eh_ofs && ! really_return)
- abort();
+ if (IS_NAKED (func_type))
+ /* Naked functions don't have epilogues. */
+ return "";
- /* A volatile function should never return. Call abort. */
- if (TARGET_ABORT_NORETURN && volatile_func)
+ if (IS_VOLATILE (func_type) && TARGET_ABORT_NORETURN)
{
rtx op;
+
+ /* A volatile function should never return. Call abort. */
op = gen_rtx_SYMBOL_REF (Pmode, NEED_PLT_RELOC ? "abort(PLT)" : "abort");
assemble_external_libcall (op);
output_asm_insn ("bl\t%a0", &op);
+
return "";
}
- for (reg = 0; reg <= 10; reg++)
- if (regs_ever_live[reg] && ! call_used_regs[reg])
- {
- live_regs_mask |= (1 << reg);
- floats_offset += 4;
- }
-
- /* Handle the frame pointer as a special case. */
- if (! TARGET_APCS_FRAME
- && ! frame_pointer_needed
- && regs_ever_live[HARD_FRAME_POINTER_REGNUM]
- && ! call_used_regs[HARD_FRAME_POINTER_REGNUM])
- {
- live_regs_mask |= (1 << HARD_FRAME_POINTER_REGNUM);
- floats_offset += 4;
- }
-
- /* If we aren't loading the PIC register, don't stack it even though it may
- be live. */
- if (flag_pic && ! TARGET_SINGLE_PIC_BASE
- && regs_ever_live[PIC_OFFSET_TABLE_REGNUM])
- {
- live_regs_mask |= (1 << PIC_OFFSET_TABLE_REGNUM);
+ if (ARM_FUNC_TYPE (func_type) == ARM_FT_EXCEPTION_HANDLER
+ && ! really_return)
+ /* If we are throwing an exception, then we really must
+ be doing a return, so we can't tail-call. */
+ abort ();
+
+ saved_regs_mask = arm_compute_save_reg_mask ();
+
+ /* Compute how far away the floats will be. */
+ for (reg = 0; reg <= LAST_ARM_REGNUM; reg ++)
+ if (saved_regs_mask & (1 << reg))
floats_offset += 4;
- }
-
+
if (frame_pointer_needed)
{
if (arm_fpu_arch == FP_SOFT2)
{
for (reg = LAST_ARM_FP_REGNUM; reg >= FIRST_ARM_FP_REGNUM; reg--)
- if (regs_ever_live[reg] && ! call_used_regs[reg])
+ if (regs_ever_live[reg] && !call_used_regs[reg])
{
floats_offset += 12;
asm_fprintf (f, "\tldfe\t%r, [%r, #-%d]\n",
for (reg = LAST_ARM_FP_REGNUM; reg >= FIRST_ARM_FP_REGNUM; reg--)
{
- if (regs_ever_live[reg] && ! call_used_regs[reg])
+ if (regs_ever_live[reg] && !call_used_regs[reg])
{
floats_offset += 12;
reg + 1, start_reg - reg,
FP_REGNUM, floats_offset);
}
-
- if (TARGET_INTERWORK)
- {
- live_regs_mask |= 0x6800;
- print_multi_reg (f, "ldmea\t%r", FP_REGNUM, live_regs_mask, FALSE);
- if (eh_ofs)
- asm_fprintf (f, "\tadd\t%r, %r, %r\n", SP_REGNUM, SP_REGNUM,
- REGNO (eh_ofs));
- if (really_return)
- asm_fprintf (f, "\tbx\t%r\n", return_regnum);
- }
- else if (eh_ofs || ! really_return)
- {
- live_regs_mask |= 0x6800;
- print_multi_reg (f, "ldmea\t%r", FP_REGNUM, live_regs_mask, FALSE);
- if (eh_ofs)
- {
- asm_fprintf (f, "\tadd\t%r, %r, %r\n", SP_REGNUM, SP_REGNUM,
- REGNO (eh_ofs));
- /* Even in 26-bit mode we do a mov (rather than a movs)
- because we don't have the PSR bits set in the
- address. */
- asm_fprintf (f, "\tmov\t%r, %r\n", PC_REGNUM, return_regnum);
- }
- }
+
+ /* saved_regs_mask should contain the IP, which at the time of stack
+ frame generation actually contains the old stack pointer. So a
+ quick way to unwind the stack is just pop the IP register directly
+ into the stack pointer. */
+ if ((saved_regs_mask & (1 << IP_REGNUM)) == 0)
+ abort ();
+ saved_regs_mask &= ~ (1 << IP_REGNUM);
+ saved_regs_mask |= (1 << SP_REGNUM);
+
+ /* There are two registers left in saved_regs_mask - LR and PC. We
+ only need to restore the LR register (the return address), but to
+ save time we can load it directly into the PC, unless we need a
+ special function exit sequence, or we are not really returning. */
+ if (really_return && ARM_FUNC_TYPE (func_type) == ARM_FT_NORMAL)
+ /* Delete the LR from the register mask, so that the LR on
+ the stack is loaded into the PC in the register mask. */
+ saved_regs_mask &= ~ (1 << LR_REGNUM);
else
- {
- live_regs_mask |= 0xA800;
- print_multi_reg (f, "ldmea\t%r", FP_REGNUM, live_regs_mask,
- TARGET_APCS_32 ? FALSE : TRUE);
- }
+ saved_regs_mask &= ~ (1 << PC_REGNUM);
+
+ print_multi_reg (f, "ldmea\t%r", FP_REGNUM, saved_regs_mask);
+
+ if (IS_INTERRUPT (func_type))
+ /* Interrupt handlers will have pushed the
+ IP onto the stack, so restore it now. */
+ print_multi_reg (f, "ldmea\t%r", SP_REGNUM, 1 << IP_REGNUM);
}
else
{
if (arm_fpu_arch == FP_SOFT2)
{
for (reg = FIRST_ARM_FP_REGNUM; reg <= LAST_ARM_FP_REGNUM; reg++)
- if (regs_ever_live[reg] && ! call_used_regs[reg])
+ if (regs_ever_live[reg] && !call_used_regs[reg])
asm_fprintf (f, "\tldfe\t%r, [%r], #12\n",
reg, SP_REGNUM);
}
for (reg = FIRST_ARM_FP_REGNUM; reg <= LAST_ARM_FP_REGNUM; reg++)
{
- if (regs_ever_live[reg] && ! call_used_regs[reg])
+ if (regs_ever_live[reg] && !call_used_regs[reg])
{
if (reg - start_reg == 3)
{
start_reg, reg - start_reg, SP_REGNUM);
}
- if (current_function_pretend_args_size == 0 && regs_ever_live[LR_REGNUM])
+ /* If we can, restore the LR into the PC. */
+ if (ARM_FUNC_TYPE (func_type) == ARM_FT_NORMAL
+ && really_return
+ && current_function_pretend_args_size == 0
+ && saved_regs_mask & (1 << LR_REGNUM))
{
- if (TARGET_INTERWORK)
- {
- live_regs_mask |= 1 << LR_REGNUM;
-
- /* Handle LR on its own. */
- if (live_regs_mask == (1 << LR_REGNUM))
- {
- if (eh_ofs)
- asm_fprintf (f, "\tadd\t%r, %r, #4\n", SP_REGNUM,
- SP_REGNUM);
- else
- asm_fprintf (f, "\tldr\t%r, [%r], #4\n", LR_REGNUM,
- SP_REGNUM);
- }
- else if (live_regs_mask != 0)
- print_multi_reg (f, "ldmfd\t%r!", SP_REGNUM, live_regs_mask,
- FALSE);
-
- if (eh_ofs)
- asm_fprintf (f, "\tadd\t%r, %r, %r\n", SP_REGNUM, SP_REGNUM,
- REGNO (eh_ofs));
+ saved_regs_mask &= ~ (1 << LR_REGNUM);
+ saved_regs_mask |= (1 << PC_REGNUM);
+ }
- if (really_return)
- asm_fprintf (f, "\tbx\t%r\n", return_regnum);
- }
- else if (eh_ofs)
- {
- if (live_regs_mask == 0)
- asm_fprintf (f, "\tadd\t%r, %r, #4\n", SP_REGNUM, SP_REGNUM);
- else
- print_multi_reg (f, "\tldmfd\t%r!", SP_REGNUM,
- live_regs_mask | (1 << LR_REGNUM), FALSE);
-
- asm_fprintf (f, "\tadd\t%r, %r, %r\n", SP_REGNUM, SP_REGNUM,
- REGNO (eh_ofs));
- /* Jump to the target; even in 26-bit mode. */
- asm_fprintf (f, "\tmov\t%r, %r\n", PC_REGNUM, return_regnum);
- }
- else if (TARGET_APCS_32 && live_regs_mask == 0 && ! really_return)
- asm_fprintf (f, "\tldr\t%r, [%r], #4\n", LR_REGNUM, SP_REGNUM);
- else if (TARGET_APCS_32 && live_regs_mask == 0 && really_return)
- asm_fprintf (f, "\tldr\t%r, [%r], #4\n", PC_REGNUM, SP_REGNUM);
- else if (! really_return)
- print_multi_reg (f, "ldmfd\t%r!", SP_REGNUM,
- live_regs_mask | (1 << LR_REGNUM), FALSE);
+ /* Load the registers off the stack. If we only have one register
+ to load use the LDR instruction - it is faster. */
+ if (saved_regs_mask == (1 << LR_REGNUM))
+ {
+ /* The excpetion handler ignores the LR, so we do
+ not really need to load it off the stack. */
+ if (eh_ofs)
+ asm_fprintf (f, "\tadd\t%r, %r, #4\n", SP_REGNUM, SP_REGNUM);
else
- print_multi_reg (f, "ldmfd\t%r!", SP_REGNUM,
- live_regs_mask | (1 << PC_REGNUM),
- TARGET_APCS_32 ? FALSE : TRUE);
+ asm_fprintf (f, "\tldr\t%r, [%r], #4\n", LR_REGNUM, SP_REGNUM);
}
- else
+ else if (saved_regs_mask)
+ print_multi_reg (f, "ldmfd\t%r!", SP_REGNUM, saved_regs_mask);
+
+ if (current_function_pretend_args_size)
{
- if (live_regs_mask || regs_ever_live[LR_REGNUM])
- {
- /* Restore the integer regs, and the return address into lr. */
- live_regs_mask |= 1 << LR_REGNUM;
+ /* Unwind the pre-pushed regs. */
+ operands[0] = operands[1] = stack_pointer_rtx;
+ operands[2] = GEN_INT (current_function_pretend_args_size);
+ output_add_immediate (operands);
+ }
+ }
- if (live_regs_mask == (1 << LR_REGNUM))
- {
- if (eh_ofs)
- asm_fprintf (f, "\tadd\t%r, %r, #4\n", SP_REGNUM,
- SP_REGNUM);
- else
- asm_fprintf (f, "\tldr\t%r, [%r], #4\n", LR_REGNUM,
- SP_REGNUM);
- }
- else if (live_regs_mask != 0)
- print_multi_reg (f, "ldmfd\t%r!", SP_REGNUM, live_regs_mask,
- FALSE);
- }
+#if 0
+ if (ARM_FUNC_TYPE (func_type) == ARM_FT_EXCEPTION_HANDLER)
+ /* Adjust the stack to remove the exception handler stuff. */
+ asm_fprintf (f, "\tadd\t%r, %r, %r\n", SP_REGNUM, SP_REGNUM,
+ REGNO (eh_ofs));
+#endif
- if (current_function_pretend_args_size)
- {
- /* Unwind the pre-pushed regs. */
- operands[0] = operands[1] = stack_pointer_rtx;
- operands[2] = GEN_INT (current_function_pretend_args_size);
- output_add_immediate (operands);
- }
+ if (! really_return)
+ return "";
- if (eh_ofs)
- asm_fprintf (f, "\tadd\t%r, %r, %r\n", SP_REGNUM, SP_REGNUM,
- REGNO (eh_ofs));
+ /* Generate the return instruction. */
+ switch ((int) ARM_FUNC_TYPE (func_type))
+ {
+ case ARM_FT_EXCEPTION_HANDLER:
+ /* Even in 26-bit mode we do a mov (rather than a movs)
+ because we don't have the PSR bits set in the address. */
+ asm_fprintf (f, "\tmov\t%r, %r\n", PC_REGNUM, EXCEPTION_LR_REGNUM);
+ break;
- if (really_return)
- {
- /* And finally, go home. */
- if (TARGET_INTERWORK)
- asm_fprintf (f, "\tbx\t%r\n", return_regnum);
- else if (TARGET_APCS_32 || eh_ofs)
- asm_fprintf (f, "\tmov\t%r, %r\n", PC_REGNUM, return_regnum);
- else
- asm_fprintf (f, "\tmovs\t%r, %r\n", PC_REGNUM, return_regnum);
- }
- }
+ case ARM_FT_ISR:
+ case ARM_FT_FIQ:
+ asm_fprintf (f, "\tsubs\t%r, %r, #4\n", PC_REGNUM, LR_REGNUM);
+ break;
+
+ case ARM_FT_EXCEPTION:
+ asm_fprintf (f, "\tmovs\t%r, %r\n", PC_REGNUM, LR_REGNUM);
+ break;
+
+ case ARM_FT_INTERWORKED:
+ asm_fprintf (f, "\tbx\t%r\n", LR_REGNUM);
+ break;
+
+ default:
+ if (frame_pointer_needed)
+ /* If we used the frame pointer then the return adddress
+ will have been loaded off the stack directly into the
+ PC, so there is no need to issue a MOV instruction
+ here. */
+ ;
+ else if (current_function_pretend_args_size == 0
+ && (saved_regs_mask & (1 << LR_REGNUM)))
+ /* Similarly we may have been able to load LR into the PC
+ even if we did not create a stack frame. */
+ ;
+ else if (TARGET_APCS_32)
+ asm_fprintf (f, "\tmov\t%r, %r\n", PC_REGNUM, LR_REGNUM);
+ else
+ asm_fprintf (f, "\tmovs\t%r, %r\n", PC_REGNUM, LR_REGNUM);
+ break;
}
return "";
if (use_return_insn (FALSE)
&& return_used_this_function
&& (frame_size + current_function_outgoing_args_size) != 0
- && ! frame_pointer_needed)
+ && !frame_pointer_needed)
abort ();
/* Reset the ARM-specific per-function variables. */
Unfortunately, since this insn does not reflect very well the actual
semantics of the operation, we need to annotate the insn for the benefit
of DWARF2 frame unwind information. */
+
static rtx
emit_multi_reg_push (mask)
int mask;
{
int num_regs = 0;
+ int num_dwarf_regs;
int i, j;
rtx par;
rtx dwarf;
for (i = 0; i <= LAST_ARM_REGNUM; i++)
if (mask & (1 << i))
- num_regs ++;
+ num_regs++;
if (num_regs == 0 || num_regs > 16)
abort ();
+ /* We don't record the PC in the dwarf frame information. */
+ num_dwarf_regs = num_regs;
+ if (mask & (1 << PC_REGNUM))
+ num_dwarf_regs--;
+
/* For the body of the insn we are going to generate an UNSPEC in
parallel with several USEs. This allows the insn to be recognised
by the push_multi pattern in the arm.md file. The insn looks
something like this:
(parallel [
- (set (mem:BLK (pre_dec:BLK (reg:SI sp))) (unspec:BLK [(reg:SI r4)] 2))
+ (set (mem:BLK (pre_dec:BLK (reg:SI sp)))
+ (unspec:BLK [(reg:SI r4)] UNSPEC_PUSH_MULT))
(use (reg:SI 11 fp))
(use (reg:SI 12 ip))
(use (reg:SI 14 lr))
(set (mem:SI (plus:SI (reg:SI sp) (const_int 4))) (reg:SI fp))
(set (mem:SI (plus:SI (reg:SI sp) (const_int 8))) (reg:SI ip))
(set (mem:SI (plus:SI (reg:SI sp) (const_int 12))) (reg:SI lr))
- (set (mem:SI (plus:SI (reg:SI sp) (const_int 16))) (reg:SI pc))
])
This sequence is used both by the code to support stack unwinding for
exceptions handlers and the code to generate dwarf2 frame debugging. */
par = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (num_regs));
- dwarf = gen_rtx_SEQUENCE (VOIDmode, rtvec_alloc (num_regs + 1));
+ dwarf = gen_rtx_SEQUENCE (VOIDmode, rtvec_alloc (num_dwarf_regs + 1));
RTX_FRAME_RELATED_P (dwarf) = 1;
dwarf_par_index = 1;
stack_pointer_rtx)),
gen_rtx_UNSPEC (BLKmode,
gen_rtvec (1, reg),
- 2));
+ UNSPEC_PUSH_MULT));
- tmp = gen_rtx_SET (VOIDmode,
- gen_rtx_MEM (SImode, stack_pointer_rtx),
- reg);
- RTX_FRAME_RELATED_P (tmp) = 1;
- XVECEXP (dwarf, 0, dwarf_par_index) = tmp;
- dwarf_par_index ++;
+ if (i != PC_REGNUM)
+ {
+ tmp = gen_rtx_SET (VOIDmode,
+ gen_rtx_MEM (SImode, stack_pointer_rtx),
+ reg);
+ RTX_FRAME_RELATED_P (tmp) = 1;
+ XVECEXP (dwarf, 0, dwarf_par_index) = tmp;
+ dwarf_par_index++;
+ }
break;
}
XVECEXP (par, 0, j) = gen_rtx_USE (VOIDmode, reg);
- tmp = gen_rtx_SET (VOIDmode,
- gen_rtx_MEM (SImode,
- gen_rtx_PLUS (SImode,
- stack_pointer_rtx,
- GEN_INT (4 * j))),
- reg);
- RTX_FRAME_RELATED_P (tmp) = 1;
- XVECEXP (dwarf, 0, dwarf_par_index ++) = tmp;
-
+ if (i != PC_REGNUM)
+ {
+ tmp = gen_rtx_SET (VOIDmode,
+ gen_rtx_MEM (SImode,
+ plus_constant (stack_pointer_rtx,
+ 4 * j)),
+ reg);
+ RTX_FRAME_RELATED_P (tmp) = 1;
+ XVECEXP (dwarf, 0, dwarf_par_index++) = tmp;
+ }
+
j++;
}
}
gen_rtx_PRE_DEC (BLKmode, stack_pointer_rtx)),
gen_rtx_UNSPEC (BLKmode,
gen_rtvec (1, reg),
- 2));
+ UNSPEC_PUSH_MULT));
tmp
= gen_rtx_SET (VOIDmode,
gen_rtx_MEM (XFmode,
return par;
}
+/* Generate the prologue instructions for entry into an ARM function. */
+
void
arm_expand_prologue ()
{
int reg;
- rtx amount = GEN_INT (-(get_frame_size ()
- + current_function_outgoing_args_size));
- int live_regs_mask = 0;
- int store_arg_regs = 0;
- /* If this function doesn't return, then there is no need to push
- the call-saved regs. */
- int volatile_func = arm_volatile_func ();
+ rtx amount;
rtx insn;
+ rtx ip_rtx;
+ unsigned long live_regs_mask;
+ unsigned long func_type;
+ int fp_offset = 0;
+
+ func_type = arm_current_func_type ();
/* Naked functions don't have prologues. */
- if (arm_naked_function_p (current_function_decl))
+ if (IS_NAKED (func_type))
return;
- if (current_function_anonymous_args && current_function_pretend_args_size)
- store_arg_regs = 1;
+ /* Compute which register we will have to save onto the stack. */
+ live_regs_mask = arm_compute_save_reg_mask ();
- if (! volatile_func)
+ ip_rtx = gen_rtx_REG (SImode, IP_REGNUM);
+
+ if (frame_pointer_needed)
{
- for (reg = 0; reg <= 10; reg++)
- if (regs_ever_live[reg] && ! call_used_regs[reg])
- live_regs_mask |= 1 << reg;
+ if (IS_INTERRUPT (func_type))
+ {
+ /* Interrupt functions must not corrupt any registers.
+ Creating a frame pointer however, corrupts the IP
+ register, so we must push it first. */
+ insn = emit_multi_reg_push (1 << IP_REGNUM);
+ RTX_FRAME_RELATED_P (insn) = 1;
+ }
+ else if (IS_NESTED (func_type))
+ {
+ /* The Static chain register is the same as the IP register
+ used as a scratch register during stack frame creation.
+ To get around this need to find somewhere to store IP
+ whilst the frame is being created. We try the following
+ places in order:
+
+ 1. The last argument register.
+ 2. A slot on the stack above the frame. (This only
+ works if the function is not a varargs function).
+
+ If neither of these places is available, we abort (for now).
- if (! TARGET_APCS_FRAME
- && ! frame_pointer_needed
- && regs_ever_live[HARD_FRAME_POINTER_REGNUM]
- && ! call_used_regs[HARD_FRAME_POINTER_REGNUM])
- live_regs_mask |= 1 << HARD_FRAME_POINTER_REGNUM;
-
- if (flag_pic && regs_ever_live[PIC_OFFSET_TABLE_REGNUM])
- live_regs_mask |= 1 << PIC_OFFSET_TABLE_REGNUM;
+ Note - we only need to tell the dwarf2 backend about the SP
+ adjustment in the second variant; the static chain register
+ doesn't need to be unwound, as it doesn't contain a value
+ inherited from the caller. */
- if (regs_ever_live[LR_REGNUM])
- live_regs_mask |= 1 << LR_REGNUM;
- }
+ if (regs_ever_live[3] == 0)
+ {
+ insn = gen_rtx_REG (SImode, 3);
+ insn = gen_rtx_SET (SImode, insn, ip_rtx);
+ insn = emit_insn (insn);
+ }
+ else if (current_function_pretend_args_size == 0)
+ {
+ rtx dwarf;
+ insn = gen_rtx_PRE_DEC (SImode, stack_pointer_rtx);
+ insn = gen_rtx_MEM (SImode, insn);
+ insn = gen_rtx_SET (VOIDmode, insn, ip_rtx);
+ insn = emit_insn (insn);
+
+ fp_offset = 4;
+
+ /* Just tell the dwarf backend that we adjusted SP. */
+ dwarf = gen_rtx_SET (VOIDmode, stack_pointer_rtx,
+ gen_rtx_PLUS (SImode, stack_pointer_rtx,
+ GEN_INT (-fp_offset)));
+ RTX_FRAME_RELATED_P (insn) = 1;
+ REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
+ dwarf, REG_NOTES (insn));
+ }
+ else
+ /* FIXME - the way to handle this situation is to allow
+ the pretend args to be dumped onto the stack, then
+ reuse r3 to save IP. This would involve moving the
+ copying of SP into IP until after the pretend args
+ have been dumped, but this is not too hard. */
+ /* [See e.g. gcc.c-torture/execute/nest-stdar-1.c.] */
+ error ("Unable to find a temporary location for static chain register");
+ }
- if (frame_pointer_needed)
- {
- live_regs_mask |= 0xD800;
- insn = emit_insn (gen_movsi (gen_rtx_REG (SImode, IP_REGNUM),
- stack_pointer_rtx));
+ if (fp_offset)
+ {
+ insn = gen_rtx_PLUS (SImode, stack_pointer_rtx, GEN_INT (fp_offset));
+ insn = gen_rtx_SET (SImode, ip_rtx, insn);
+ }
+ else
+ insn = gen_movsi (ip_rtx, stack_pointer_rtx);
+
+ insn = emit_insn (insn);
RTX_FRAME_RELATED_P (insn) = 1;
}
if (current_function_pretend_args_size)
{
- if (store_arg_regs)
+ /* Push the argument registers, or reserve space for them. */
+ if (current_function_anonymous_args)
insn = emit_multi_reg_push
((0xf0 >> (current_function_pretend_args_size / 4)) & 0xf);
else
if (live_regs_mask)
{
- /* If we have to push any regs, then we must push lr as well, or
- we won't get a proper return. */
- live_regs_mask |= 1 << LR_REGNUM;
insn = emit_multi_reg_push (live_regs_mask);
RTX_FRAME_RELATED_P (insn) = 1;
}
-
- /* For now the integer regs are still pushed in output_arm_epilogue (). */
- if (! volatile_func)
+ if (! IS_VOLATILE (func_type))
{
+ /* Save any floating point call-saved registers used by this function. */
if (arm_fpu_arch == FP_SOFT2)
{
for (reg = LAST_ARM_FP_REGNUM; reg >= FIRST_ARM_FP_REGNUM; reg --)
- if (regs_ever_live[reg] && ! call_used_regs[reg])
+ if (regs_ever_live[reg] && !call_used_regs[reg])
{
insn = gen_rtx_PRE_DEC (XFmode, stack_pointer_rtx);
insn = gen_rtx_MEM (XFmode, insn);
for (reg = LAST_ARM_FP_REGNUM; reg >= FIRST_ARM_FP_REGNUM; reg --)
{
- if (regs_ever_live[reg] && ! call_used_regs[reg])
+ if (regs_ever_live[reg] && !call_used_regs[reg])
{
if (start_reg - reg == 3)
{
if (frame_pointer_needed)
{
- insn = GEN_INT (-(4 + current_function_pretend_args_size));
- insn = emit_insn (gen_addsi3 (hard_frame_pointer_rtx,
- gen_rtx_REG (SImode, IP_REGNUM),
- insn));
+ /* Create the new frame pointer. */
+ insn = GEN_INT (-(4 + current_function_pretend_args_size + fp_offset));
+ insn = emit_insn (gen_addsi3 (hard_frame_pointer_rtx, ip_rtx, insn));
RTX_FRAME_RELATED_P (insn) = 1;
+
+ if (IS_NESTED (func_type))
+ {
+ /* Recover the static chain register. */
+ if (regs_ever_live [3] == 0)
+ {
+ insn = gen_rtx_REG (SImode, 3);
+ insn = gen_rtx_SET (SImode, ip_rtx, insn);
+ insn = emit_insn (insn);
+ }
+ else /* if (current_function_pretend_args_size == 0) */
+ {
+ insn = gen_rtx_PLUS (SImode, hard_frame_pointer_rtx, GEN_INT (4));
+ insn = gen_rtx_MEM (SImode, insn);
+ insn = gen_rtx_SET (SImode, ip_rtx, insn);
+ insn = emit_insn (insn);
+ }
+ }
}
+ amount = GEN_INT (-(get_frame_size ()
+ + current_function_outgoing_args_size));
+
if (amount != const0_rtx)
{
+ /* This add can produce multiple insns for a large constant, so we
+ need to get tricky. */
+ rtx last = get_last_insn ();
insn = emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx,
amount));
- RTX_FRAME_RELATED_P (insn) = 1;
- emit_insn (gen_rtx_CLOBBER (VOIDmode,
- gen_rtx_MEM (BLKmode, stack_pointer_rtx)));
+ do
+ {
+ last = last ? NEXT_INSN (last) : get_insns ();
+ RTX_FRAME_RELATED_P (last) = 1;
+ }
+ while (last != insn);
+
+ /* If the frame pointer is needed, emit a special barrier that
+ will prevent the scheduler from moving stores to the frame
+ before the stack adjustment. */
+ if (frame_pointer_needed)
+ {
+ rtx unspec = gen_rtx_UNSPEC (SImode,
+ gen_rtvec (2, stack_pointer_rtx,
+ hard_frame_pointer_rtx),
+ UNSPEC_PRLG_STK);
+
+ insn = emit_insn (gen_rtx_CLOBBER (VOIDmode,
+ gen_rtx_MEM (BLKmode, unspec)));
+ }
}
/* If we are profiling, make sure no instructions are scheduled before
scheduling in the prolog. */
if (profile_flag || profile_block_flag || TARGET_NO_SCHED_PRO)
emit_insn (gen_blockage ());
+
+ /* If the link register is being kept alive, with the return address in it,
+ then make sure that it does not get reused by the ce2 pass. */
+ if ((live_regs_mask & (1 << LR_REGNUM)) == 0)
+ {
+ emit_insn (gen_rtx_USE (VOIDmode, gen_rtx_REG (SImode, LR_REGNUM)));
+ cfun->machine->lr_save_eliminated = 1;
+ }
}
\f
/* If CODE is 'd', then the X is a condition operand and the instruction
case '?':
if (arm_ccfsm_state == 3 || arm_ccfsm_state == 4)
- fputs (arm_condition_codes[arm_current_cc], stream);
+ {
+ if (TARGET_THUMB || current_insn_predicate != NULL)
+ abort ();
+
+ fputs (arm_condition_codes[arm_current_cc], stream);
+ }
+ else if (current_insn_predicate)
+ {
+ enum arm_cond_code code;
+
+ if (TARGET_THUMB)
+ abort ();
+
+ code = get_arm_condition_code (current_insn_predicate);
+ fputs (arm_condition_codes[code], stream);
+ }
return;
case 'N':
if (GET_CODE (x) == CONST_INT)
{
HOST_WIDE_INT val;
- val = ARM_SIGN_EXTEND (~ INTVAL (x));
+ val = ARM_SIGN_EXTEND (~INTVAL (x));
fprintf (stream, HOST_WIDE_INT_PRINT_DEC, val);
}
else
case 'S':
{
HOST_WIDE_INT val;
- const char * shift = shift_op (x, & val);
+ const char * shift = shift_op (x, &val);
if (shift)
{
- fprintf (stream, ", %s ", shift_op (x, & val));
+ fprintf (stream, ", %s ", shift_op (x, &val));
if (val == -1)
arm_print_operand (stream, XEXP (x, 1), 0);
else
return;
case 'd':
- if (! x)
+ if (!x)
return;
if (TARGET_ARM)
return;
case 'D':
- if (! x)
+ if (!x)
return;
if (TARGET_ARM)
/* Fail if a conditional return is undesirable (eg on a
StrongARM), but still allow this if optimizing for size. */
else if (GET_CODE (scanbody) == RETURN
- && ! use_return_insn (TRUE)
- && ! optimize_size)
+ && !use_return_insn (TRUE)
+ && !optimize_size)
fail = TRUE;
else if (GET_CODE (scanbody) == RETURN
&& seeking_return)
break;
}
}
+ else
+ fail = TRUE; /* Unrecognized jump (eg epilogue). */
+
break;
case INSN:
/* Instructions using or affecting the condition codes make it
fail. */
scanbody = PATTERN (this_insn);
- if (! (GET_CODE (scanbody) == SET
- || GET_CODE (scanbody) == PARALLEL)
+ if (!(GET_CODE (scanbody) == SET
+ || GET_CODE (scanbody) == PARALLEL)
|| get_attr_conds (this_insn) != CONDS_NOCOND)
fail = TRUE;
break;
if (!this_insn)
{
/* Oh, dear! we ran off the end.. give up */
- recog (PATTERN (insn), insn, NULL_PTR);
+ recog (PATTERN (insn), insn, NULL);
arm_ccfsm_state = 0;
arm_target_insn = NULL;
return;
destroy this array, but final.c assumes that it remains intact
across this call; since the insn has been recognized already we
call recog direct). */
- recog (PATTERN (insn), insn, NULL_PTR);
+ recog (PATTERN (insn), insn, NULL);
}
}
/* If we are using the stack pointer to point at the
argument, then an offset of 0 is correct. */
- if ((TARGET_THUMB || ! frame_pointer_needed)
+ if ((TARGET_THUMB || !frame_pointer_needed)
&& REGNO (addr) == SP_REGNUM)
return 0;
return value;
}
+#define def_builtin(NAME, TYPE, CODE) \
+ builtin_function ((NAME), (TYPE), (CODE), BUILT_IN_MD, NULL)
+
+void
+arm_init_builtins ()
+{
+ tree endlink = void_list_node;
+ tree int_endlink = tree_cons (NULL_TREE, integer_type_node, endlink);
+ tree pchar_type_node = build_pointer_type (char_type_node);
+
+ tree int_ftype_int, void_ftype_pchar;
+
+ /* void func (void *) */
+ void_ftype_pchar
+ = build_function_type (void_type_node,
+ tree_cons (NULL_TREE, pchar_type_node, endlink));
+
+ /* int func (int) */
+ int_ftype_int
+ = build_function_type (integer_type_node, int_endlink);
+
+ /* Initialize arm V5 builtins. */
+ if (arm_arch5)
+ def_builtin ("__builtin_clz", int_ftype_int, ARM_BUILTIN_CLZ);
+
+ /* Initialize arm V5E builtins. */
+ if (arm_arch5e)
+ def_builtin ("__builtin_prefetch", void_ftype_pchar,
+ ARM_BUILTIN_PREFETCH);
+}
+
+/* Expand an expression EXP that calls a built-in function,
+ with result going to TARGET if that's convenient
+ (and in mode MODE if that's convenient).
+ SUBTARGET may be used as the target for computing one of EXP's operands.
+ IGNORE is nonzero if the value is to be ignored. */
+
+rtx
+arm_expand_builtin (exp, target, subtarget, mode, ignore)
+ tree exp;
+ rtx target;
+ rtx subtarget ATTRIBUTE_UNUSED;
+ enum machine_mode mode ATTRIBUTE_UNUSED;
+ int ignore ATTRIBUTE_UNUSED;
+{
+ enum insn_code icode;
+ tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
+ tree arglist = TREE_OPERAND (exp, 1);
+ tree arg0;
+ rtx op0, pat;
+ enum machine_mode tmode, mode0;
+ int fcode = DECL_FUNCTION_CODE (fndecl);
+
+ switch (fcode)
+ {
+ default:
+ break;
+
+ case ARM_BUILTIN_CLZ:
+ icode = CODE_FOR_clz;
+ arg0 = TREE_VALUE (arglist);
+ op0 = expand_expr (arg0, NULL_RTX, VOIDmode, 0);
+ tmode = insn_data[icode].operand[0].mode;
+ mode0 = insn_data[icode].operand[1].mode;
+
+ if (! (*insn_data[icode].operand[1].predicate) (op0, mode0))
+ op0 = copy_to_mode_reg (mode0, op0);
+ if (target == 0
+ || GET_MODE (target) != tmode
+ || ! (*insn_data[icode].operand[0].predicate) (target, tmode))
+ target = gen_reg_rtx (tmode);
+ pat = GEN_FCN (icode) (target, op0);
+ if (! pat)
+ return 0;
+ emit_insn (pat);
+ return target;
+
+ case ARM_BUILTIN_PREFETCH:
+ icode = CODE_FOR_prefetch;
+ arg0 = TREE_VALUE (arglist);
+ op0 = expand_expr (arg0, NULL_RTX, VOIDmode, 0);
+
+ op0 = gen_rtx_MEM (SImode, copy_to_mode_reg (Pmode, op0));
+
+ pat = GEN_FCN (icode) (op0);
+ if (! pat)
+ return 0;
+ emit_insn (pat);
+ return target;
+ }
+
+ /* @@@ Should really do something sensible here. */
+ return NULL_RTX;
+}
\f
/* Recursively search through all of the blocks in a function
checking to see if any of the variables created in that
{
tree sym;
- if (! TREE_USED (block))
+ if (!TREE_USED (block))
continue;
for (sym = BLOCK_VARS (block); sym; sym = TREE_CHAIN (sym))
|| DECL_IGNORED_P (sym)
|| TREE_CODE (sym) != VAR_DECL
|| DECL_EXTERNAL (sym)
- || ! rtx_equal_p (DECL_RTL (sym), orig)
+ || !rtx_equal_p (DECL_RTL (sym), orig)
)
continue;
- DECL_RTL (sym) = new;
+ SET_DECL_RTL (sym, new);
}
replace_symbols_in_block (BLOCK_SUBBLOCKS (block), orig, new);
for (bit = 0;
(mask & (1 << bit)) == 0;
- ++ bit)
+ ++bit)
continue;
return bit;
abort ();
regs_to_pop |= 1 << LR_REGNUM;
- ++ pops_needed;
+ ++pops_needed;
}
if (TARGET_BACKTRACE)
/* Otherwise if we are not supporting interworking and we have not created
a backtrace structure and the function was not entered in ARM mode then
just pop the return address straight into the PC. */
- else if ( ! TARGET_INTERWORK
- && ! TARGET_BACKTRACE
- && ! is_called_in_ARM_mode (current_function_decl))
+ else if (!TARGET_INTERWORK
+ && !TARGET_BACKTRACE
+ && !is_called_in_ARM_mode (current_function_decl))
{
if (eh_ofs)
{
/* If we have any popping registers left over, remove them. */
if (available > 0)
- regs_available_for_popping &= ~ available;
+ regs_available_for_popping &= ~available;
/* Otherwise if we need another popping register we can use
the fourth argument register. */
/* The fourth argument register is available. */
regs_available_for_popping |= 1 << LAST_ARG_REGNUM;
- -- pops_needed;
+ --pops_needed;
}
}
if (reg_containing_return_addr == -1)
{
/* The return address was popped into the lowest numbered register. */
- regs_to_pop &= ~ (1 << LR_REGNUM);
+ regs_to_pop &= ~(1 << LR_REGNUM);
reg_containing_return_addr =
number_of_first_bit_set (regs_available_for_popping);
/* Remove this register for the mask of available registers, so that
the return address will not be corrupted by futher pops. */
- regs_available_for_popping &= ~ (1 << reg_containing_return_addr);
+ regs_available_for_popping &= ~(1 << reg_containing_return_addr);
}
/* If we popped other registers then handle them here. */
ARM_HARD_FRAME_POINTER_REGNUM, frame_pointer);
/* (Temporarily) remove it from the mask of popped registers. */
- regs_available_for_popping &= ~ (1 << frame_pointer);
- regs_to_pop &= ~ (1 << ARM_HARD_FRAME_POINTER_REGNUM);
+ regs_available_for_popping &= ~(1 << frame_pointer);
+ regs_to_pop &= ~(1 << ARM_HARD_FRAME_POINTER_REGNUM);
if (regs_available_for_popping)
{
asm_fprintf (f, "\tmov\t%r, %r\n", move_to, popped_into);
- regs_to_pop &= ~ (1 << move_to);
+ regs_to_pop &= ~(1 << move_to);
- -- pops_needed;
+ --pops_needed;
}
/* If we still have not popped everything then we must have only
int regno;
int lo_mask = mask & 0xFF;
- if (lo_mask == 0 && ! push && (mask & (1 << 15)))
+ if (lo_mask == 0 && !push && (mask & (1 << 15)))
{
/* Special case. Do not generate a POP PC statement here, do it in
thumb_exit() */
fprintf (f, "\t%s\t{", push ? "push" : "pop");
/* Look at the low registers first. */
- for (regno = 0; regno <= LAST_LO_REGNUM; regno ++, lo_mask >>= 1)
+ for (regno = 0; regno <= LAST_LO_REGNUM; regno++, lo_mask >>= 1)
{
if (lo_mask & 1)
{
rtx insn;
/* This test is only important for leaf functions. */
- /* assert (! leaf_function_p ()); */
+ /* assert (!leaf_function_p ()); */
/* If we have already decided that far jumps may be used,
do not bother checking again, and always return true even if
/* If this function is not being called from the prologue/epilogue
generation code then it must be being called from the
INITIAL_ELIMINATION_OFFSET macro. */
- if (! in_prologue)
+ if (!in_prologue)
{
/* In this case we know that we are being asked about the elimination
of the arg pointer register. If that register is not being used,
hope that this does not occur too often. */
if (regs_ever_live [ARG_POINTER_REGNUM])
cfun->machine->arg_pointer_live = 1;
- else if (! cfun->machine->arg_pointer_live)
+ else if (!cfun->machine->arg_pointer_live)
return 0;
}
}
/* The bits which aren't usefully expanded as rtl. */
+
const char *
thumb_unexpanded_epilogue ()
{
return "";
for (regno = 0; regno <= LAST_LO_REGNUM; regno++)
- if (regs_ever_live[regno] && ! call_used_regs[regno]
- && ! (TARGET_SINGLE_PIC_BASE && (regno == arm_pic_register)))
+ if (regs_ever_live[regno] && !call_used_regs[regno]
+ && !(TARGET_SINGLE_PIC_BASE && (regno == arm_pic_register)))
live_regs_mask |= 1 << regno;
for (regno = 8; regno < 13; regno++)
{
- if (regs_ever_live[regno] && ! call_used_regs[regno]
- && ! (TARGET_SINGLE_PIC_BASE && (regno == arm_pic_register)))
- high_regs_pushed ++;
+ if (regs_ever_live[regno] && !call_used_regs[regno]
+ && !(TARGET_SINGLE_PIC_BASE && (regno == arm_pic_register)))
+ high_regs_pushed++;
}
/* The prolog may have pushed some high registers to use as
if (mask == 0)
/* Oh dear! We have no low registers into which we can pop
high registers! */
- fatal ("No low registers available for popping high registers");
+ internal_error
+ ("no low registers available for popping high registers");
for (next_hi_reg = 8; next_hi_reg < 13; next_hi_reg++)
- if (regs_ever_live[next_hi_reg] && ! call_used_regs[next_hi_reg]
- && ! (TARGET_SINGLE_PIC_BASE && (next_hi_reg == arm_pic_register)))
+ if (regs_ever_live[next_hi_reg] && !call_used_regs[next_hi_reg]
+ && !(TARGET_SINGLE_PIC_BASE && (next_hi_reg == arm_pic_register)))
break;
while (high_regs_pushed)
regno);
for (next_hi_reg++; next_hi_reg < 13; next_hi_reg++)
- if (regs_ever_live[next_hi_reg] &&
- ! call_used_regs[next_hi_reg]
- && ! (TARGET_SINGLE_PIC_BASE
- && (next_hi_reg == arm_pic_register)))
+ if (regs_ever_live[next_hi_reg]
+ && !call_used_regs[next_hi_reg]
+ && !(TARGET_SINGLE_PIC_BASE
+ && (next_hi_reg == arm_pic_register)))
break;
}
}
}
}
- had_to_push_lr = (live_regs_mask || ! leaf_function
+ had_to_push_lr = (live_regs_mask || !leaf_function
|| thumb_far_jump_used_p (1));
if (TARGET_BACKTRACE
if (current_function_pretend_args_size == 0 || TARGET_BACKTRACE)
{
if (had_to_push_lr
- && ! is_called_in_ARM_mode (current_function_decl)
- && ! eh_ofs)
+ && !is_called_in_ARM_mode (current_function_decl)
+ && !eh_ofs)
live_regs_mask |= 1 << PC_REGNUM;
/* Either no argument registers were pushed or a backtrace
else
{
/* Pop everything but the return address. */
- live_regs_mask &= ~ (1 << PC_REGNUM);
+ live_regs_mask &= ~(1 << PC_REGNUM);
if (live_regs_mask)
thumb_pushpop (asm_out_file, live_regs_mask, FALSE);
arm_mark_machine_status (p)
struct function * p;
{
- struct machine_function *machine = p->machine;
+ machine_function *machine = p->machine;
- ggc_mark_rtx (machine->ra_rtx);
- ggc_mark_rtx (machine->eh_epilogue_sp_ofs);
+ if (machine)
+ ggc_mark_rtx (machine->eh_epilogue_sp_ofs);
}
static void
struct function * p;
{
p->machine =
- (struct machine_function *) xcalloc (1, sizeof (struct machine_function));
+ (machine_function *) xcalloc (1, sizeof (machine_function));
+
+#if ARM_FT_UNKNOWWN != 0
+ ((machine_function *) p->machine)->func_type = ARM_FT_UNKNOWN;
+#endif
+}
+
+static void
+arm_free_machine_status (p)
+ struct function * p;
+{
+ if (p->machine)
+ {
+ free (p->machine);
+ p->machine = NULL;
+ }
}
/* Return an RTX indicating where the return address to the
int count;
rtx frame ATTRIBUTE_UNUSED;
{
- rtx reg;
-
if (count != 0)
return NULL_RTX;
- reg = cfun->machine->ra_rtx;
-
- if (reg == NULL)
+ if (TARGET_APCS_32)
+ return get_hard_reg_initial_val (Pmode, LR_REGNUM);
+ else
{
- rtx init;
-
- /* No rtx yet. Invent one, and initialize it for r14 (lr) in
- the prologue. */
- reg = gen_reg_rtx (Pmode);
- cfun->machine->ra_rtx = reg;
-
- if (! TARGET_APCS_32)
- init = gen_rtx_AND (Pmode, gen_rtx_REG (Pmode, LR_REGNUM),
+ rtx lr = gen_rtx_AND (Pmode, gen_rtx_REG (Pmode, LR_REGNUM),
GEN_INT (RETURN_ADDR_MASK26));
- else
- init = gen_rtx_REG (Pmode, LR_REGNUM);
-
- init = gen_rtx_SET (VOIDmode, reg, init);
-
- /* Emit the insn to the prologue with the other argument copies. */
- push_topmost_sequence ();
- emit_insn_after (init, get_insns ());
- pop_topmost_sequence ();
+ return get_func_hard_reg_initial_val (cfun, lr);
}
-
- return reg;
}
/* Do anything needed before RTL is emitted for each function. */
/* Arrange to initialize and mark the machine per-function status. */
init_machine_status = arm_init_machine_status;
mark_machine_status = arm_mark_machine_status;
+ free_machine_status = arm_free_machine_status;
}
/* Generate the rest of a function's prologue. */
{
HOST_WIDE_INT amount = (get_frame_size ()
+ current_function_outgoing_args_size);
+ unsigned long func_type;
+
+ func_type = arm_current_func_type ();
/* Naked functions don't have prologues. */
- if (arm_naked_function_p (current_function_decl))
+ if (IS_NAKED (func_type))
return;
+ if (IS_INTERRUPT (func_type))
+ {
+ error ("Interrupt Service Routines cannot be coded in Thumb mode.");
+ return;
+ }
+
if (frame_pointer_needed)
emit_insn (gen_movsi (hard_frame_pointer_rtx, stack_pointer_rtx));
if (amount < 512)
emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx,
- GEN_INT (- amount)));
+ GEN_INT (-amount)));
else
{
int regno;
it now. */
for (regno = LAST_ARG_REGNUM + 1; regno <= LAST_LO_REGNUM; regno++)
if (regs_ever_live[regno]
- && ! call_used_regs[regno] /* Paranoia */
- && ! (TARGET_SINGLE_PIC_BASE && (regno == arm_pic_register))
- && ! (frame_pointer_needed
- && (regno == THUMB_HARD_FRAME_POINTER_REGNUM)))
+ && !call_used_regs[regno] /* Paranoia */
+ && !(TARGET_SINGLE_PIC_BASE && (regno == arm_pic_register))
+ && !(frame_pointer_needed
+ && (regno == THUMB_HARD_FRAME_POINTER_REGNUM)))
break;
if (regno > LAST_LO_REGNUM) /* Very unlikely */
emit_insn (gen_movsi (spare, reg));
/* Decrement the stack. */
- emit_insn (gen_movsi (reg, GEN_INT (- amount)));
+ emit_insn (gen_movsi (reg, GEN_INT (-amount)));
emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx,
reg));
{
reg = gen_rtx (REG, SImode, regno);
- emit_insn (gen_movsi (reg, GEN_INT (- amount)));
+ emit_insn (gen_movsi (reg, GEN_INT (-amount)));
emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx,
reg));
}
{
HOST_WIDE_INT amount = (get_frame_size ()
+ current_function_outgoing_args_size);
-
- /* Naked functions don't have epilogues. */
- if (arm_naked_function_p (current_function_decl))
+
+ /* Naked functions don't have prologues. */
+ if (IS_NAKED (arm_current_func_type ()))
return;
if (frame_pointer_needed)
{
int live_regs_mask = 0;
int high_regs_pushed = 0;
- int store_arg_regs = 0;
int regno;
- if (arm_naked_function_p (current_function_decl))
+ if (IS_NAKED (arm_current_func_type ()))
return;
if (is_called_in_ARM_mode (current_function_decl))
asm_fprintf (f, "%s%U%s:\n", STUB_NAME, name);
}
- if (current_function_anonymous_args && current_function_pretend_args_size)
- store_arg_regs = 1;
-
if (current_function_pretend_args_size)
{
- if (store_arg_regs)
+ if (current_function_anonymous_args)
{
int num_pushes;
for (regno = LAST_ARG_REGNUM + 1 - num_pushes;
regno <= LAST_ARG_REGNUM;
- regno ++)
+ regno++)
asm_fprintf (f, "%r%s", regno,
regno == LAST_ARG_REGNUM ? "" : ", ");
current_function_pretend_args_size);
}
- for (regno = 0; regno <= LAST_LO_REGNUM; regno ++)
- if (regs_ever_live[regno] && ! call_used_regs[regno]
- && ! (TARGET_SINGLE_PIC_BASE && (regno == arm_pic_register)))
+ for (regno = 0; regno <= LAST_LO_REGNUM; regno++)
+ if (regs_ever_live[regno] && !call_used_regs[regno]
+ && !(TARGET_SINGLE_PIC_BASE && (regno == arm_pic_register)))
live_regs_mask |= 1 << regno;
- if (live_regs_mask || ! leaf_function_p () || thumb_far_jump_used_p (1))
+ if (live_regs_mask || !leaf_function_p () || thumb_far_jump_used_p (1))
live_regs_mask |= 1 << LR_REGNUM;
if (TARGET_BACKTRACE)
for (regno = 8; regno < 13; regno++)
{
- if (regs_ever_live[regno] && ! call_used_regs[regno]
- && ! (TARGET_SINGLE_PIC_BASE && (regno == arm_pic_register)))
- high_regs_pushed ++;
+ if (regs_ever_live[regno] && !call_used_regs[regno]
+ && !(TARGET_SINGLE_PIC_BASE && (regno == arm_pic_register)))
+ high_regs_pushed++;
}
if (high_regs_pushed)
for (next_hi_reg = 12; next_hi_reg > LAST_LO_REGNUM; next_hi_reg--)
{
- if (regs_ever_live[next_hi_reg] && ! call_used_regs[next_hi_reg]
- && ! (TARGET_SINGLE_PIC_BASE
- && (next_hi_reg == arm_pic_register)))
+ if (regs_ever_live[next_hi_reg] && !call_used_regs[next_hi_reg]
+ && !(TARGET_SINGLE_PIC_BASE
+ && (next_hi_reg == arm_pic_register)))
break;
}
{
/* Desperation time -- this probably will never happen. */
if (regs_ever_live[LAST_ARG_REGNUM]
- || ! call_used_regs[LAST_ARG_REGNUM])
+ || !call_used_regs[LAST_ARG_REGNUM])
asm_fprintf (f, "\tmov\t%r, %r\n", IP_REGNUM, LAST_ARG_REGNUM);
mask = 1 << LAST_ARG_REGNUM;
}
{
asm_fprintf (f, "\tmov\t%r, %r\n", regno, next_hi_reg);
- high_regs_pushed --;
+ high_regs_pushed--;
if (high_regs_pushed)
for (next_hi_reg--; next_hi_reg > LAST_LO_REGNUM;
next_hi_reg--)
{
if (regs_ever_live[next_hi_reg]
- && ! call_used_regs[next_hi_reg]
- && ! (TARGET_SINGLE_PIC_BASE
- && (next_hi_reg == arm_pic_register)))
+ && !call_used_regs[next_hi_reg]
+ && !(TARGET_SINGLE_PIC_BASE
+ && (next_hi_reg == arm_pic_register)))
break;
}
else
{
- mask &= ~ ((1 << regno) - 1);
+ mask &= ~((1 << regno) - 1);
break;
}
}
if (pushable_regs == 0
&& (regs_ever_live[LAST_ARG_REGNUM]
- || ! call_used_regs[LAST_ARG_REGNUM]))
+ || !call_used_regs[LAST_ARG_REGNUM]))
asm_fprintf (f, "\tmov\t%r, %r\n", LAST_ARG_REGNUM, IP_REGNUM);
}
}
const char *
thumb_load_double_from_address (operands)
- rtx * operands;
+ rtx *operands;
{
rtx addr;
rtx base;
rtx arg2;
if (GET_CODE (operands[0]) != REG)
- fatal ("thumb_load_double_from_address: destination is not a register");
+ abort ();
if (GET_CODE (operands[1]) != MEM)
- {
- debug_rtx (operands[1]);
- fatal ("thumb_load_double_from_address: source is not a computed memory address");
- }
+ abort ();
/* Get the memory address. */
addr = XEXP (operands[1], 0);
base = arg1, offset = arg2;
if (GET_CODE (base) != REG)
- fatal ("thumb_load_double_from_address: base is not a register");
+ abort ();
/* Catch the case of <address> = <reg> + <reg> */
if (GET_CODE (offset) == REG)
break;
default:
- debug_rtx (operands[1]);
- fatal ("thumb_load_double_from_address: Unhandled address calculation");
+ abort ();
break;
}
switch (n)
{
case 2:
- if (REGNO (operands[2]) > REGNO (operands[3]))
+ if (REGNO (operands[4]) > REGNO (operands[5]))
{
- tmp = operands[2];
- operands[2] = operands[3];
- operands[3] = tmp;
+ tmp = operands[4];
+ operands[4] = operands[5];
+ operands[5] = tmp;
}
- output_asm_insn ("ldmia\t%1!, {%2, %3}", operands);
- output_asm_insn ("stmia\t%0!, {%2, %3}", operands);
+ output_asm_insn ("ldmia\t%1!, {%4, %5}", operands);
+ output_asm_insn ("stmia\t%0!, {%4, %5}", operands);
break;
case 3:
- if (REGNO (operands[2]) > REGNO (operands[3]))
+ if (REGNO (operands[4]) > REGNO (operands[5]))
{
- tmp = operands[2];
- operands[2] = operands[3];
- operands[3] = tmp;
+ tmp = operands[4];
+ operands[4] = operands[5];
+ operands[5] = tmp;
}
- if (REGNO (operands[3]) > REGNO (operands[4]))
+ if (REGNO (operands[5]) > REGNO (operands[6]))
{
- tmp = operands[3];
- operands[3] = operands[4];
- operands[4] = tmp;
+ tmp = operands[5];
+ operands[5] = operands[6];
+ operands[6] = tmp;
}
- if (REGNO (operands[2]) > REGNO (operands[3]))
+ if (REGNO (operands[4]) > REGNO (operands[5]))
{
- tmp = operands[2];
- operands[2] = operands[3];
- operands[3] = tmp;
+ tmp = operands[4];
+ operands[4] = operands[5];
+ operands[5] = tmp;
}
- output_asm_insn ("ldmia\t%1!, {%2, %3, %4}", operands);
- output_asm_insn ("stmia\t%0!, {%2, %3, %4}", operands);
+ output_asm_insn ("ldmia\t%1!, {%4, %5, %6}", operands);
+ output_asm_insn ("stmia\t%0!, {%4, %5, %6}", operands);
break;
default:
while (len >= 12)
{
- emit_insn (gen_movmem12b (out, in));
+ emit_insn (gen_movmem12b (out, in, out, in));
len -= 12;
}
if (len >= 8)
{
- emit_insn (gen_movmem8b (out, in));
+ emit_insn (gen_movmem8b (out, in, out, in));
len -= 8;
}
/* We mark this here and not in arm_add_gc_roots() to avoid
polluting even more code with ifdefs, and because it never
contains anything useful until we assign to it here. */
- ggc_add_rtx_root (& aof_pic_label, 1);
- /* This needs to persist throughout the compilation. */
- end_temporary_allocation ();
+ ggc_add_rtx_root (&aof_pic_label, 1);
aof_pic_label = gen_rtx_SYMBOL_REF (Pmode, "x$adcons");
- resume_temporary_allocation ();
}
for (offset = 0, chainp = &aof_pic_chain; *chainp;