aboutsummaryrefslogtreecommitdiff
path: root/gcc/config/arm/arm.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/config/arm/arm.c')
-rw-r--r--gcc/config/arm/arm.c1550
1 files changed, 1184 insertions, 366 deletions
diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c
index c7a1ec2..9558275 100644
--- a/gcc/config/arm/arm.c
+++ b/gcc/config/arm/arm.c
@@ -1,6 +1,6 @@
/* Output routines for GCC for ARM.
Copyright (C) 1991, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
- 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
+ 2002, 2003, 2004, 2005, 2006, 2007 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).
@@ -67,10 +67,12 @@ static int arm_gen_constant (enum rtx_code, enum machine_mode, rtx,
static unsigned bit_count (unsigned long);
static int arm_address_register_rtx_p (rtx, int);
static int arm_legitimate_index_p (enum machine_mode, rtx, RTX_CODE, int);
-static int thumb_base_register_rtx_p (rtx, enum machine_mode, int);
-inline static int thumb_index_register_rtx_p (rtx, int);
+static int thumb2_legitimate_index_p (enum machine_mode, rtx, int);
+static int thumb1_base_register_rtx_p (rtx, enum machine_mode, int);
+inline static int thumb1_index_register_rtx_p (rtx, int);
static int thumb_far_jump_used_p (void);
static bool thumb_force_lr_save (void);
+static unsigned long thumb1_compute_save_reg_mask (void);
static int const_ok_for_op (HOST_WIDE_INT, enum rtx_code);
static rtx emit_sfm (int, int);
static int arm_size_return_regs (void);
@@ -114,7 +116,7 @@ static tree arm_handle_notshared_attribute (tree *, tree, tree, int, bool *);
#endif
static void arm_output_function_epilogue (FILE *, HOST_WIDE_INT);
static void arm_output_function_prologue (FILE *, HOST_WIDE_INT);
-static void thumb_output_function_prologue (FILE *, HOST_WIDE_INT);
+static void thumb1_output_function_prologue (FILE *, HOST_WIDE_INT);
static int arm_comp_type_attributes (tree, tree);
static void arm_set_default_type_attributes (tree);
static int arm_adjust_cost (rtx, rtx, rtx, int);
@@ -177,6 +179,7 @@ static bool arm_must_pass_in_stack (enum machine_mode, tree);
static void arm_unwind_emit (FILE *, rtx);
static bool arm_output_ttype (rtx);
#endif
+static void arm_dwarf_handle_frame_unspec (const char *, rtx, int);
static tree arm_cxx_guard_type (void);
static bool arm_cxx_guard_mask_bit (void);
@@ -205,7 +208,6 @@ static bool arm_tls_symbol_p (rtx x);
#undef TARGET_ASM_FILE_START
#define TARGET_ASM_FILE_START arm_file_start
-
#undef TARGET_ASM_FILE_END
#define TARGET_ASM_FILE_END arm_file_end
@@ -361,6 +363,9 @@ static bool arm_tls_symbol_p (rtx x);
#define TARGET_ARM_EABI_UNWINDER true
#endif /* TARGET_UNWIND_INFO */
+#undef TARGET_DWARF_HANDLE_FRAME_UNSPEC
+#define TARGET_DWARF_HANDLE_FRAME_UNSPEC arm_dwarf_handle_frame_unspec
+
#undef TARGET_CANNOT_COPY_INSN_P
#define TARGET_CANNOT_COPY_INSN_P arm_cannot_copy_insn_p
@@ -441,11 +446,15 @@ static int thumb_call_reg_needed;
#define FL_WBUF (1 << 14) /* Schedule for write buffer ops.
Note: ARM6 & 7 derivatives only. */
#define FL_ARCH6K (1 << 15) /* Architecture rel 6 K extensions. */
+#define FL_THUMB2 (1 << 16) /* Thumb-2. */
+#define FL_NOTM (1 << 17) /* Instructions not present in the 'M'
+ profile. */
+#define FL_DIV (1 << 18) /* Hardware divde. */
#define FL_IWMMXT (1 << 29) /* XScale v2 or "Intel Wireless MMX technology". */
-#define FL_FOR_ARCH2 0
-#define FL_FOR_ARCH3 FL_MODE32
+#define FL_FOR_ARCH2 FL_NOTM
+#define FL_FOR_ARCH3 (FL_FOR_ARCH2 | FL_MODE32)
#define FL_FOR_ARCH3M (FL_FOR_ARCH3 | FL_ARCH3M)
#define FL_FOR_ARCH4 (FL_FOR_ARCH3M | FL_ARCH4)
#define FL_FOR_ARCH4T (FL_FOR_ARCH4 | FL_THUMB)
@@ -459,6 +468,11 @@ static int thumb_call_reg_needed;
#define FL_FOR_ARCH6K (FL_FOR_ARCH6 | FL_ARCH6K)
#define FL_FOR_ARCH6Z FL_FOR_ARCH6
#define FL_FOR_ARCH6ZK FL_FOR_ARCH6K
+#define FL_FOR_ARCH6T2 (FL_FOR_ARCH6 | FL_THUMB2)
+#define FL_FOR_ARCH7 (FL_FOR_ARCH6T2 &~ FL_NOTM)
+#define FL_FOR_ARCH7A (FL_FOR_ARCH7 | FL_NOTM)
+#define FL_FOR_ARCH7R (FL_FOR_ARCH7A | FL_DIV)
+#define FL_FOR_ARCH7M (FL_FOR_ARCH7 | FL_DIV)
/* The bits in this mask specify which
instructions we are allowed to generate. */
@@ -492,6 +506,9 @@ int arm_arch6 = 0;
/* Nonzero if this chip supports the ARM 6K extensions. */
int arm_arch6k = 0;
+/* Nonzero if instructions not present in the 'M' profile can be used. */
+int arm_arch_notm = 0;
+
/* Nonzero if this chip can benefit from load scheduling. */
int arm_ld_sched = 0;
@@ -524,6 +541,12 @@ int thumb_code = 0;
interworking clean. */
int arm_cpp_interwork = 0;
+/* Nonzero if chip supports Thumb 2. */
+int arm_arch_thumb2;
+
+/* Nonzero if chip supports integer division instruction. */
+int arm_arch_hwdiv;
+
/* In case of a PRE_INC, POST_INC, PRE_DEC, POST_DEC memory reference, we
must report the mode of the memory reference from PRINT_OPERAND to
PRINT_OPERAND_ADDRESS. */
@@ -545,9 +568,17 @@ static int arm_constant_limit = 3;
/* For an explanation of these variables, see final_prescan_insn below. */
int arm_ccfsm_state;
+/* arm_current_cc is also used for Thumb-2 cond_exec blocks. */
enum arm_cond_code arm_current_cc;
rtx arm_target_insn;
int arm_target_label;
+/* The number of conditionally executed insns, including the current insn. */
+int arm_condexec_count = 0;
+/* A bitmask specifying the patterns for the IT block.
+ Zero means do not output an IT block before this insn. */
+int arm_condexec_mask = 0;
+/* The number of bits used in arm_condexec_mask. */
+int arm_condexec_masklen = 0;
/* The condition codes of the ARM, and the inverse function. */
static const char * const arm_condition_codes[] =
@@ -556,7 +587,12 @@ static const char * const arm_condition_codes[] =
"hi", "ls", "ge", "lt", "gt", "le", "al", "nv"
};
+#define ARM_LSL_NAME (TARGET_UNIFIED_ASM ? "lsl" : "asl")
#define streq(string1, string2) (strcmp (string1, string2) == 0)
+
+#define THUMB2_WORK_REGS (0xff & ~( (1 << THUMB_HARD_FRAME_POINTER_REGNUM) \
+ | (1 << SP_REGNUM) | (1 << PC_REGNUM) \
+ | (1 << PIC_OFFSET_TABLE_REGNUM)))
/* Initialization code. */
@@ -604,6 +640,11 @@ static const struct processors all_architectures[] =
{"armv6k", mpcore, "6K", FL_CO_PROC | FL_FOR_ARCH6K, NULL},
{"armv6z", arm1176jzs, "6Z", FL_CO_PROC | FL_FOR_ARCH6Z, NULL},
{"armv6zk", arm1176jzs, "6ZK", FL_CO_PROC | FL_FOR_ARCH6ZK, NULL},
+ {"armv6t2", arm1156t2s, "6T2", FL_CO_PROC | FL_FOR_ARCH6T2, NULL},
+ {"armv7", cortexa8, "7", FL_CO_PROC | FL_FOR_ARCH7, NULL},
+ {"armv7-a", cortexa8, "7A", FL_CO_PROC | FL_FOR_ARCH7A, NULL},
+ {"armv7-r", cortexr4, "7R", FL_CO_PROC | FL_FOR_ARCH7R, NULL},
+ {"armv7-m", cortexm3, "7M", FL_CO_PROC | FL_FOR_ARCH7M, NULL},
{"ep9312", ep9312, "4T", FL_LDSCHED | FL_CIRRUS | FL_FOR_ARCH4, NULL},
{"iwmmxt", iwmmxt, "5TE", FL_LDSCHED | FL_STRONG | FL_FOR_ARCH5TE | FL_XSCALE | FL_IWMMXT , NULL},
{NULL, arm_none, NULL, 0 , NULL}
@@ -1044,6 +1085,9 @@ arm_override_options (void)
/* Make sure that the processor choice does not conflict with any of the
other command line choices. */
+ if (TARGET_ARM && !(insn_flags & FL_NOTM))
+ error ("target CPU does not support ARM mode");
+
if (TARGET_INTERWORK && !(insn_flags & FL_THUMB))
{
warning (0, "target CPU does not support interworking" );
@@ -1117,6 +1161,8 @@ arm_override_options (void)
arm_arch5e = (insn_flags & FL_ARCH5E) != 0;
arm_arch6 = (insn_flags & FL_ARCH6) != 0;
arm_arch6k = (insn_flags & FL_ARCH6K) != 0;
+ arm_arch_notm = (insn_flags & FL_NOTM) != 0;
+ arm_arch_thumb2 = (insn_flags & FL_THUMB2) != 0;
arm_arch_xscale = (insn_flags & FL_XSCALE) != 0;
arm_arch_cirrus = (insn_flags & FL_CIRRUS) != 0;
@@ -1126,6 +1172,7 @@ arm_override_options (void)
arm_tune_wbuf = (tune_flags & FL_WBUF) != 0;
arm_tune_xscale = (tune_flags & FL_XSCALE) != 0;
arm_arch_iwmmxt = (insn_flags & FL_IWMMXT) != 0;
+ arm_arch_hwdiv = (insn_flags & FL_DIV) != 0;
/* V5 code we generate is completely interworking capable, so we turn off
TARGET_INTERWORK here to avoid many tests later on. */
@@ -1240,6 +1287,10 @@ arm_override_options (void)
if (TARGET_IWMMXT && !TARGET_SOFT_FLOAT)
sorry ("iWMMXt and hardware floating point");
+ /* ??? iWMMXt insn patterns need auditing for Thumb-2. */
+ if (TARGET_THUMB2 && TARGET_IWMMXT)
+ sorry ("Thumb-2 iWMMXt");
+
/* If soft-float is specified then don't use FPU. */
if (TARGET_SOFT_FLOAT)
arm_fpu_arch = FPUTYPE_NONE;
@@ -1273,8 +1324,8 @@ arm_override_options (void)
target_thread_pointer = TP_SOFT;
}
- if (TARGET_HARD_TP && TARGET_THUMB)
- error ("can not use -mtp=cp15 with -mthumb");
+ if (TARGET_HARD_TP && TARGET_THUMB1)
+ error ("can not use -mtp=cp15 with 16-bit Thumb");
/* Override the default structure alignment for AAPCS ABI. */
if (TARGET_AAPCS_BASED)
@@ -1309,6 +1360,7 @@ arm_override_options (void)
arm_pic_register = pic_register;
}
+ /* ??? We might want scheduling for thumb2. */
if (TARGET_THUMB && flag_schedule_insns)
{
/* Don't warn since it's on by default in -O2. */
@@ -1390,6 +1442,9 @@ arm_isr_value (tree argument)
const isr_attribute_arg * ptr;
const char * arg;
+ if (!arm_arch_notm)
+ return ARM_FT_NORMAL | ARM_FT_STACKALIGN;
+
/* No argument - default to IRQ. */
if (argument == NULL_TREE)
return ARM_FT_ISR;
@@ -1483,9 +1538,9 @@ use_return_insn (int iscond, rtx sibling)
func_type = arm_current_func_type ();
- /* Naked functions and volatile functions need special
+ /* Naked, volatile and stack alignment functions need special
consideration. */
- if (func_type & (ARM_FT_VOLATILE | ARM_FT_NAKED))
+ if (func_type & (ARM_FT_VOLATILE | ARM_FT_NAKED | ARM_FT_STACKALIGN))
return 0;
/* So do interrupt functions that use the frame pointer. */
@@ -1522,7 +1577,7 @@ use_return_insn (int iscond, rtx sibling)
We test for !arm_arch5 here, because code for any architecture
less than this could potentially be run on one of the buggy
chips. */
- if (stack_adjust == 4 && !arm_arch5)
+ if (stack_adjust == 4 && !arm_arch5 && TARGET_ARM)
{
/* Validate that r3 is a call-clobbered register (always true in
the default abi) ... */
@@ -1616,17 +1671,36 @@ const_ok_for_arm (HOST_WIDE_INT i)
if ((i & ~(unsigned HOST_WIDE_INT) 0xff) == 0)
return TRUE;
- /* Get the number of trailing zeros, rounded down to the nearest even
- number. */
- lowbit = (ffs ((int) i) - 1) & ~1;
+ /* Get the number of trailing zeros. */
+ lowbit = ffs((int) i) - 1;
+
+ /* Only even shifts are allowed in ARM mode so round down to the
+ nearest even number. */
+ if (TARGET_ARM)
+ lowbit &= ~1;
if ((i & ~(((unsigned HOST_WIDE_INT) 0xff) << lowbit)) == 0)
return TRUE;
- else if (lowbit <= 4
+
+ if (TARGET_ARM)
+ {
+ /* Allow rotated constants in ARM mode. */
+ if (lowbit <= 4
&& ((i & ~0xc000003f) == 0
|| (i & ~0xf000000f) == 0
|| (i & ~0xfc000003) == 0))
- return TRUE;
+ return TRUE;
+ }
+ else
+ {
+ HOST_WIDE_INT v;
+
+ /* Allow repeated pattern. */
+ v = i & 0xff;
+ v |= v << 16;
+ if (i == v || i == (v | (v << 8)))
+ return TRUE;
+ }
return FALSE;
}
@@ -1666,6 +1740,7 @@ const_ok_for_op (HOST_WIDE_INT i, enum rtx_code code)
either produce a simpler sequence, or we will want to cse the values.
Return value is the number of insns emitted. */
+/* ??? Tweak this for thumb2. */
int
arm_split_constant (enum rtx_code code, enum machine_mode mode, rtx insn,
HOST_WIDE_INT val, rtx target, rtx source, int subtargets)
@@ -1724,6 +1799,8 @@ arm_split_constant (enum rtx_code code, enum machine_mode mode, rtx insn,
1);
}
+/* Return the number of ARM instructions required to synthesize the given
+ constant. */
static int
count_insns_for_constant (HOST_WIDE_INT remainder, int i)
{
@@ -1765,6 +1842,7 @@ emit_constant_insn (rtx cond, rtx pattern)
/* As above, but extra parameter GENERATE which, if clear, suppresses
RTL generation. */
+/* ??? This needs more work for thumb2. */
static int
arm_gen_constant (enum rtx_code code, enum machine_mode mode, rtx cond,
@@ -1941,6 +2019,15 @@ arm_gen_constant (enum rtx_code code, enum machine_mode mode, rtx cond,
switch (code)
{
case SET:
+ /* See if we can use movw. */
+ if (arm_arch_thumb2 && (remainder & 0xffff0000) == 0)
+ {
+ if (generate)
+ emit_constant_insn (cond, gen_rtx_SET (VOIDmode, target,
+ GEN_INT (val)));
+ return 1;
+ }
+
/* See if we can do this by sign_extending a constant that is known
to be negative. This is a good, way of doing it, since the shift
may well merge into a subsequent insn. */
@@ -2282,59 +2369,67 @@ arm_gen_constant (enum rtx_code code, enum machine_mode mode, rtx cond,
We start by looking for the largest block of zeros that are aligned on
a 2-bit boundary, we then fill up the temps, wrapping around to the
top of the word when we drop off the bottom.
- In the worst case this code should produce no more than four insns. */
+ In the worst case this code should produce no more than four insns.
+ Thumb-2 constants are shifted, not rotated, so the MSB is always the
+ best place to start. */
+
+ /* ??? Use thumb2 replicated constants when the high and low halfwords are
+ the same. */
{
int best_start = 0;
- int best_consecutive_zeros = 0;
-
- for (i = 0; i < 32; i += 2)
+ if (!TARGET_THUMB2)
{
- int consecutive_zeros = 0;
+ int best_consecutive_zeros = 0;
- if (!(remainder & (3 << i)))
+ for (i = 0; i < 32; i += 2)
{
- while ((i < 32) && !(remainder & (3 << i)))
- {
- consecutive_zeros += 2;
- i += 2;
- }
- if (consecutive_zeros > best_consecutive_zeros)
+ int consecutive_zeros = 0;
+
+ if (!(remainder & (3 << i)))
{
- best_consecutive_zeros = consecutive_zeros;
- best_start = i - consecutive_zeros;
+ while ((i < 32) && !(remainder & (3 << i)))
+ {
+ consecutive_zeros += 2;
+ i += 2;
+ }
+ if (consecutive_zeros > best_consecutive_zeros)
+ {
+ best_consecutive_zeros = consecutive_zeros;
+ best_start = i - consecutive_zeros;
+ }
+ i -= 2;
}
- i -= 2;
}
- }
-
- /* 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 (i.e. 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;
+ /* 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 (i.e. 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;
@@ -2400,9 +2495,17 @@ arm_gen_constant (enum rtx_code code, enum machine_mode mode, rtx cond,
code = PLUS;
insns++;
- i -= 6;
+ if (TARGET_ARM)
+ i -= 6;
+ else
+ i -= 7;
}
- i -= 2;
+ /* Arm allows rotates by a multiple of two. Thumb-2 allows arbitary
+ shifts. */
+ if (TARGET_ARM)
+ i -= 2;
+ else
+ i--;
}
while (remainder);
}
@@ -3149,6 +3252,7 @@ static bool
arm_function_ok_for_sibcall (tree decl, tree exp ATTRIBUTE_UNUSED)
{
int call_type = TARGET_LONG_CALLS ? CALL_LONG : CALL_NORMAL;
+ unsigned long func_type;
if (cfun->machine->sibcall_blocked)
return false;
@@ -3176,8 +3280,13 @@ arm_function_ok_for_sibcall (tree decl, tree exp ATTRIBUTE_UNUSED)
if (TARGET_INTERWORK && TREE_PUBLIC (decl) && !TREE_ASM_WRITTEN (decl))
return false;
+ func_type = arm_current_func_type ();
/* Never tailcall from an ISR routine - it needs a special exit sequence. */
- if (IS_INTERRUPT (arm_current_func_type ()))
+ if (IS_INTERRUPT (func_type))
+ return false;
+
+ /* Never tailcall if function may be called with a misaligned SP. */
+ if (IS_STACKALIGN (func_type))
return false;
/* Everything else is ok. */
@@ -3276,8 +3385,10 @@ legitimize_pic_address (rtx orig, enum machine_mode mode, rtx reg)
if (TARGET_ARM)
emit_insn (gen_pic_load_addr_arm (address, orig));
- else
- emit_insn (gen_pic_load_addr_thumb (address, orig));
+ else if (TARGET_THUMB2)
+ emit_insn (gen_pic_load_addr_thumb2 (address, orig));
+ else /* TARGET_THUMB1 */
+ emit_insn (gen_pic_load_addr_thumb1 (address, orig));
if ((GET_CODE (orig) == LABEL_REF
|| (GET_CODE (orig) == SYMBOL_REF &&
@@ -3352,7 +3463,7 @@ legitimize_pic_address (rtx orig, enum machine_mode mode, rtx reg)
}
-/* Find a spare low register to use during the prolog of a function. */
+/* Find a spare register to use during the prolog of a function. */
static int
thumb_find_work_register (unsigned long pushed_regs_mask)
@@ -3401,6 +3512,13 @@ thumb_find_work_register (unsigned long pushed_regs_mask)
if (pushed_regs_mask & (1 << reg))
return reg;
+ if (TARGET_THUMB2)
+ {
+ /* Thumb-2 can use high regs. */
+ for (reg = FIRST_HI_REGNUM; reg < 15; reg ++)
+ if (pushed_regs_mask & (1 << reg))
+ return reg;
+ }
/* Something went wrong - thumb_compute_save_reg_mask()
should have arranged for a suitable register to be pushed. */
gcc_unreachable ();
@@ -3448,7 +3566,27 @@ arm_load_pic_register (unsigned long saved_regs ATTRIBUTE_UNUSED)
emit_insn (gen_pic_add_dot_plus_eight (cfun->machine->pic_reg,
cfun->machine->pic_reg, labelno));
}
- else
+ else if (TARGET_THUMB2)
+ {
+ /* Thumb-2 only allows very limited access to the PC. Calculate the
+ address in a temporary register. */
+ if (arm_pic_register != INVALID_REGNUM)
+ {
+ pic_tmp = gen_rtx_REG (SImode,
+ thumb_find_work_register (saved_regs));
+ }
+ else
+ {
+ gcc_assert (!no_new_pseudos);
+ pic_tmp = gen_reg_rtx (Pmode);
+ }
+
+ emit_insn (gen_pic_load_addr_thumb2 (cfun->machine->pic_reg, pic_rtx));
+ emit_insn (gen_pic_load_dot_plus_four (pic_tmp, labelno));
+ emit_insn (gen_addsi3(cfun->machine->pic_reg, cfun->machine->pic_reg,
+ pic_tmp));
+ }
+ else /* TARGET_THUMB1 */
{
if (arm_pic_register != INVALID_REGNUM
&& REGNO (cfun->machine->pic_reg) > LAST_LO_REGNUM)
@@ -3457,11 +3595,11 @@ arm_load_pic_register (unsigned long saved_regs ATTRIBUTE_UNUSED)
able to find a work register. */
pic_tmp = gen_rtx_REG (SImode,
thumb_find_work_register (saved_regs));
- emit_insn (gen_pic_load_addr_thumb (pic_tmp, pic_rtx));
+ emit_insn (gen_pic_load_addr_thumb1 (pic_tmp, pic_rtx));
emit_insn (gen_movsi (pic_offset_table_rtx, pic_tmp));
}
else
- emit_insn (gen_pic_load_addr_thumb (cfun->machine->pic_reg, pic_rtx));
+ emit_insn (gen_pic_load_addr_thumb1 (cfun->machine->pic_reg, pic_rtx));
emit_insn (gen_pic_add_dot_plus_four (cfun->machine->pic_reg,
cfun->machine->pic_reg, labelno));
}
@@ -3591,6 +3729,80 @@ arm_legitimate_address_p (enum machine_mode mode, rtx x, RTX_CODE outer,
return 0;
}
+/* Return nonzero if X is a valid Thumb-2 address operand. */
+int
+thumb2_legitimate_address_p (enum machine_mode mode, rtx x, int strict_p)
+{
+ bool use_ldrd;
+ enum rtx_code code = GET_CODE (x);
+
+ if (arm_address_register_rtx_p (x, strict_p))
+ return 1;
+
+ use_ldrd = (TARGET_LDRD
+ && (mode == DImode
+ || (mode == DFmode && (TARGET_SOFT_FLOAT || TARGET_VFP))));
+
+ if (code == POST_INC || code == PRE_DEC
+ || ((code == PRE_INC || code == POST_DEC)
+ && (use_ldrd || GET_MODE_SIZE (mode) <= 4)))
+ return arm_address_register_rtx_p (XEXP (x, 0), strict_p);
+
+ else if ((code == POST_MODIFY || code == PRE_MODIFY)
+ && arm_address_register_rtx_p (XEXP (x, 0), strict_p)
+ && GET_CODE (XEXP (x, 1)) == PLUS
+ && rtx_equal_p (XEXP (XEXP (x, 1), 0), XEXP (x, 0)))
+ {
+ /* Thumb-2 only has autoincrement by constant. */
+ rtx addend = XEXP (XEXP (x, 1), 1);
+ HOST_WIDE_INT offset;
+
+ if (GET_CODE (addend) != CONST_INT)
+ return 0;
+
+ offset = INTVAL(addend);
+ if (GET_MODE_SIZE (mode) <= 4)
+ return (offset > -256 && offset < 256);
+
+ return (use_ldrd && offset > -1024 && offset < 1024
+ && (offset & 3) == 0);
+ }
+
+ /* After reload constants split into minipools will have addresses
+ from a LABEL_REF. */
+ else if (reload_completed
+ && (code == LABEL_REF
+ || (code == CONST
+ && GET_CODE (XEXP (x, 0)) == PLUS
+ && GET_CODE (XEXP (XEXP (x, 0), 0)) == LABEL_REF
+ && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT)))
+ return 1;
+
+ else if (mode == TImode)
+ return 0;
+
+ else if (code == PLUS)
+ {
+ rtx xop0 = XEXP (x, 0);
+ rtx xop1 = XEXP (x, 1);
+
+ return ((arm_address_register_rtx_p (xop0, strict_p)
+ && thumb2_legitimate_index_p (mode, xop1, strict_p))
+ || (arm_address_register_rtx_p (xop1, strict_p)
+ && thumb2_legitimate_index_p (mode, xop0, strict_p)));
+ }
+
+ else if (GET_MODE_CLASS (mode) != MODE_FLOAT
+ && code == SYMBOL_REF
+ && CONSTANT_POOL_ADDRESS_P (x)
+ && ! (flag_pic
+ && symbol_mentioned_p (get_pool_constant (x))
+ && ! pcrel_constant_p (get_pool_constant (x))))
+ return 1;
+
+ return 0;
+}
+
/* Return nonzero if INDEX is valid for an address index operand in
ARM state. */
static int
@@ -3678,9 +3890,87 @@ arm_legitimate_index_p (enum machine_mode mode, rtx index, RTX_CODE outer,
&& INTVAL (index) > -range);
}
-/* Return nonzero if X is valid as a Thumb state base register. */
+/* Return true if OP is a valid index scaling factor for Thumb-2 address
+ index operand. i.e. 1, 2, 4 or 8. */
+static bool
+thumb2_index_mul_operand (rtx op)
+{
+ HOST_WIDE_INT val;
+
+ if (GET_CODE(op) != CONST_INT)
+ return false;
+
+ val = INTVAL(op);
+ return (val == 1 || val == 2 || val == 4 || val == 8);
+}
+
+/* Return nonzero if INDEX is a valid Thumb-2 address index operand. */
+static int
+thumb2_legitimate_index_p (enum machine_mode mode, rtx index, int strict_p)
+{
+ enum rtx_code code = GET_CODE (index);
+
+ /* ??? Combine arm and thumb2 coprocessor addressing modes. */
+ /* Standard coprocessor addressing modes. */
+ if (TARGET_HARD_FLOAT
+ && (TARGET_FPA || TARGET_MAVERICK)
+ && (GET_MODE_CLASS (mode) == MODE_FLOAT
+ || (TARGET_MAVERICK && mode == DImode)))
+ return (code == CONST_INT && INTVAL (index) < 1024
+ && INTVAL (index) > -1024
+ && (INTVAL (index) & 3) == 0);
+
+ if (TARGET_REALLY_IWMMXT && VALID_IWMMXT_REG_MODE (mode))
+ return (code == CONST_INT
+ && INTVAL (index) < 1024
+ && INTVAL (index) > -1024
+ && (INTVAL (index) & 3) == 0);
+
+ if (arm_address_register_rtx_p (index, strict_p)
+ && (GET_MODE_SIZE (mode) <= 4))
+ return 1;
+
+ if (mode == DImode || mode == DFmode)
+ {
+ HOST_WIDE_INT val = INTVAL (index);
+ /* ??? Can we assume ldrd for thumb2? */
+ /* Thumb-2 ldrd only has reg+const addressing modes. */
+ if (code != CONST_INT)
+ return 0;
+
+ /* ldrd supports offsets of +-1020.
+ However the ldr fallback does not. */
+ return val > -256 && val < 256 && (val & 3) == 0;
+ }
+
+ if (code == MULT)
+ {
+ rtx xiop0 = XEXP (index, 0);
+ rtx xiop1 = XEXP (index, 1);
+
+ return ((arm_address_register_rtx_p (xiop0, strict_p)
+ && thumb2_index_mul_operand (xiop1))
+ || (arm_address_register_rtx_p (xiop1, strict_p)
+ && thumb2_index_mul_operand (xiop0)));
+ }
+ else if (code == ASHIFT)
+ {
+ rtx op = XEXP (index, 1);
+
+ return (arm_address_register_rtx_p (XEXP (index, 0), strict_p)
+ && GET_CODE (op) == CONST_INT
+ && INTVAL (op) > 0
+ && INTVAL (op) <= 3);
+ }
+
+ return (code == CONST_INT
+ && INTVAL (index) < 4096
+ && INTVAL (index) > -256);
+}
+
+/* Return nonzero if X is valid as a 16-bit Thumb state base register. */
static int
-thumb_base_register_rtx_p (rtx x, enum machine_mode mode, int strict_p)
+thumb1_base_register_rtx_p (rtx x, enum machine_mode mode, int strict_p)
{
int regno;
@@ -3690,7 +3980,7 @@ thumb_base_register_rtx_p (rtx x, enum machine_mode mode, int strict_p)
regno = REGNO (x);
if (strict_p)
- return THUMB_REGNO_MODE_OK_FOR_BASE_P (regno, mode);
+ return THUMB1_REGNO_MODE_OK_FOR_BASE_P (regno, mode);
return (regno <= LAST_LO_REGNUM
|| regno > LAST_VIRTUAL_REGISTER
@@ -3705,12 +3995,12 @@ thumb_base_register_rtx_p (rtx x, enum machine_mode mode, int strict_p)
/* Return nonzero if x is a legitimate index register. This is the case
for any base register that can access a QImode object. */
inline static int
-thumb_index_register_rtx_p (rtx x, int strict_p)
+thumb1_index_register_rtx_p (rtx x, int strict_p)
{
- return thumb_base_register_rtx_p (x, QImode, strict_p);
+ return thumb1_base_register_rtx_p (x, QImode, strict_p);
}
-/* Return nonzero if x is a legitimate Thumb-state address.
+/* Return nonzero if x is a legitimate 16-bit Thumb-state address.
The AP may be eliminated to either the SP or the FP, so we use the
least common denominator, e.g. SImode, and offsets from 0 to 64.
@@ -3728,7 +4018,7 @@ thumb_index_register_rtx_p (rtx x, int strict_p)
reload pass starts. This is so that eliminating such addresses
into stack based ones won't produce impossible code. */
int
-thumb_legitimate_address_p (enum machine_mode mode, rtx x, int strict_p)
+thumb1_legitimate_address_p (enum machine_mode mode, rtx x, int strict_p)
{
/* ??? Not clear if this is right. Experiment. */
if (GET_MODE_SIZE (mode) < 4
@@ -3742,7 +4032,7 @@ thumb_legitimate_address_p (enum machine_mode mode, rtx x, int strict_p)
return 0;
/* Accept any base register. SP only in SImode or larger. */
- else if (thumb_base_register_rtx_p (x, mode, strict_p))
+ else if (thumb1_base_register_rtx_p (x, mode, strict_p))
return 1;
/* This is PC relative data before arm_reorg runs. */
@@ -3762,7 +4052,7 @@ thumb_legitimate_address_p (enum machine_mode mode, rtx x, int strict_p)
/* Post-inc indexing only supported for SImode and larger. */
else if (GET_CODE (x) == POST_INC && GET_MODE_SIZE (mode) >= 4
- && thumb_index_register_rtx_p (XEXP (x, 0), strict_p))
+ && thumb1_index_register_rtx_p (XEXP (x, 0), strict_p))
return 1;
else if (GET_CODE (x) == PLUS)
@@ -3774,12 +4064,12 @@ thumb_legitimate_address_p (enum machine_mode mode, rtx x, int strict_p)
if (GET_MODE_SIZE (mode) <= 4
&& XEXP (x, 0) != frame_pointer_rtx
&& XEXP (x, 1) != frame_pointer_rtx
- && thumb_index_register_rtx_p (XEXP (x, 0), strict_p)
- && thumb_index_register_rtx_p (XEXP (x, 1), strict_p))
+ && thumb1_index_register_rtx_p (XEXP (x, 0), strict_p)
+ && thumb1_index_register_rtx_p (XEXP (x, 1), strict_p))
return 1;
/* REG+const has 5-7 bit offset for non-SP registers. */
- else if ((thumb_index_register_rtx_p (XEXP (x, 0), strict_p)
+ else if ((thumb1_index_register_rtx_p (XEXP (x, 0), strict_p)
|| XEXP (x, 0) == arg_pointer_rtx)
&& GET_CODE (XEXP (x, 1)) == CONST_INT
&& thumb_legitimate_offset_p (mode, INTVAL (XEXP (x, 1))))
@@ -3914,7 +4204,16 @@ arm_call_tls_get_addr (rtx x, rtx reg, rtx *valuep, int reloc)
if (TARGET_ARM)
emit_insn (gen_pic_add_dot_plus_eight (reg, reg, labelno));
- else
+ else if (TARGET_THUMB2)
+ {
+ rtx tmp;
+ /* Thumb-2 only allows very limited access to the PC. Calculate
+ the address in a temporary register. */
+ tmp = gen_reg_rtx (SImode);
+ emit_insn (gen_pic_load_dot_plus_four (tmp, labelno));
+ emit_insn (gen_addsi3(reg, reg, tmp));
+ }
+ else /* TARGET_THUMB1 */
emit_insn (gen_pic_add_dot_plus_four (reg, reg, labelno));
*valuep = emit_library_call_value (get_tls_get_addr (), NULL_RTX, LCT_PURE, /* LCT_CONST? */
@@ -3968,6 +4267,16 @@ legitimize_tls_address (rtx x, rtx reg)
if (TARGET_ARM)
emit_insn (gen_tls_load_dot_plus_eight (reg, reg, labelno));
+ else if (TARGET_THUMB2)
+ {
+ rtx tmp;
+ /* Thumb-2 only allows very limited access to the PC. Calculate
+ the address in a temporary register. */
+ tmp = gen_reg_rtx (SImode);
+ emit_insn (gen_pic_load_dot_plus_four (tmp, labelno));
+ emit_insn (gen_addsi3(reg, reg, tmp));
+ emit_move_insn (reg, gen_const_mem (SImode, reg));
+ }
else
{
emit_insn (gen_pic_add_dot_plus_four (reg, reg, labelno));
@@ -4275,7 +4584,7 @@ arm_tls_referenced_p (rtx x)
#define COSTS_N_INSNS(N) ((N) * 4 - 2)
#endif
static inline int
-thumb_rtx_costs (rtx x, enum rtx_code code, enum rtx_code outer)
+thumb1_rtx_costs (rtx x, enum rtx_code code, enum rtx_code outer)
{
enum machine_mode mode = GET_MODE (x);
@@ -4393,6 +4702,7 @@ thumb_rtx_costs (rtx x, enum rtx_code code, enum rtx_code outer)
/* Worker routine for arm_rtx_costs. */
+/* ??? This needs updating for thumb2. */
static inline int
arm_rtx_costs_1 (rtx x, enum rtx_code code, enum rtx_code outer)
{
@@ -4574,6 +4884,7 @@ arm_rtx_costs_1 (rtx x, enum rtx_code code, enum rtx_code outer)
return 4 + (mode == DImode ? 4 : 0);
case SIGN_EXTEND:
+ /* ??? value extensions are cheaper on armv6. */
if (GET_MODE (XEXP (x, 0)) == QImode)
return (4 + (mode == DImode ? 4 : 0)
+ (GET_CODE (XEXP (x, 0)) == MEM ? 10 : 0));
@@ -4644,7 +4955,7 @@ arm_size_rtx_costs (rtx x, int code, int outer_code, int *total)
if (TARGET_THUMB)
{
/* XXX TBD. For now, use the standard costs. */
- *total = thumb_rtx_costs (x, code, outer_code);
+ *total = thumb1_rtx_costs (x, code, outer_code);
return true;
}
@@ -4856,7 +5167,8 @@ arm_size_rtx_costs (rtx x, int code, int outer_code, int *total)
}
}
-/* RTX costs for cores with a slow MUL implementation. */
+/* RTX costs for cores with a slow MUL implementation. Thumb-2 is not
+ supported on any "slowmul" cores, so it can be ignored. */
static bool
arm_slowmul_rtx_costs (rtx x, int code, int outer_code, int *total)
@@ -4865,7 +5177,7 @@ arm_slowmul_rtx_costs (rtx x, int code, int outer_code, int *total)
if (TARGET_THUMB)
{
- *total = thumb_rtx_costs (x, code, outer_code);
+ *total = thumb1_rtx_costs (x, code, outer_code);
return true;
}
@@ -4917,12 +5229,13 @@ arm_fastmul_rtx_costs (rtx x, int code, int outer_code, int *total)
{
enum machine_mode mode = GET_MODE (x);
- if (TARGET_THUMB)
+ if (TARGET_THUMB1)
{
- *total = thumb_rtx_costs (x, code, outer_code);
+ *total = thumb1_rtx_costs (x, code, outer_code);
return true;
}
+ /* ??? should thumb2 use different costs? */
switch (code)
{
case MULT:
@@ -4976,7 +5289,8 @@ arm_fastmul_rtx_costs (rtx x, int code, int outer_code, int *total)
}
-/* RTX cost for XScale CPUs. */
+/* RTX cost for XScale CPUs. Thumb-2 is not supported on any xscale cores,
+ so it can be ignored. */
static bool
arm_xscale_rtx_costs (rtx x, int code, int outer_code, int *total)
@@ -4985,7 +5299,7 @@ arm_xscale_rtx_costs (rtx x, int code, int outer_code, int *total)
if (TARGET_THUMB)
{
- *total = thumb_rtx_costs (x, code, outer_code);
+ *total = thumb1_rtx_costs (x, code, outer_code);
return true;
}
@@ -5066,7 +5380,7 @@ arm_9e_rtx_costs (rtx x, int code, int outer_code, int *total)
int nonreg_cost;
int cost;
- if (TARGET_THUMB)
+ if (TARGET_THUMB1)
{
switch (code)
{
@@ -5075,7 +5389,7 @@ arm_9e_rtx_costs (rtx x, int code, int outer_code, int *total)
return true;
default:
- *total = thumb_rtx_costs (x, code, outer_code);
+ *total = thumb1_rtx_costs (x, code, outer_code);
return true;
}
}
@@ -5167,7 +5481,7 @@ arm_thumb_address_cost (rtx x)
static int
arm_address_cost (rtx x)
{
- return TARGET_ARM ? arm_arm_address_cost (x) : arm_thumb_address_cost (x);
+ return TARGET_32BIT ? arm_arm_address_cost (x) : arm_thumb_address_cost (x);
}
static int
@@ -5360,7 +5674,9 @@ cirrus_memory_offset (rtx op)
}
/* Return TRUE if OP is a valid coprocessor memory address pattern.
- WB if true if writeback address modes are allowed. */
+ WB is true if full writeback address modes are allowed and is false
+ if limited writeback address modes (POST_INC and PRE_DEC) are
+ allowed. */
int
arm_coproc_mem_operand (rtx op, bool wb)
@@ -5395,12 +5711,15 @@ arm_coproc_mem_operand (rtx op, bool wb)
if (GET_CODE (ind) == REG)
return arm_address_register_rtx_p (ind, 0);
- /* Autoincremment addressing modes. */
- if (wb
- && (GET_CODE (ind) == PRE_INC
- || GET_CODE (ind) == POST_INC
- || GET_CODE (ind) == PRE_DEC
- || GET_CODE (ind) == POST_DEC))
+ /* Autoincremment addressing modes. POST_INC and PRE_DEC are
+ acceptable in any case (subject to verification by
+ arm_address_register_rtx_p). We need WB to be true to accept
+ PRE_INC and POST_DEC. */
+ if (GET_CODE (ind) == POST_INC
+ || GET_CODE (ind) == PRE_DEC
+ || (wb
+ && (GET_CODE (ind) == PRE_INC
+ || GET_CODE (ind) == POST_DEC)))
return arm_address_register_rtx_p (XEXP (ind, 0), 0);
if (wb
@@ -5948,10 +6267,10 @@ load_multiple_sequence (rtx *operands, int nops, int *regs, int *base,
if (unsorted_offsets[order[0]] == 0)
return 1; /* ldmia */
- if (unsorted_offsets[order[0]] == 4)
+ if (TARGET_ARM && unsorted_offsets[order[0]] == 4)
return 2; /* ldmib */
- if (unsorted_offsets[order[nops - 1]] == 0)
+ if (TARGET_ARM && unsorted_offsets[order[nops - 1]] == 0)
return 3; /* ldmda */
if (unsorted_offsets[order[nops - 1]] == -4)
@@ -6007,19 +6326,19 @@ emit_ldm_seq (rtx *operands, int nops)
switch (load_multiple_sequence (operands, nops, regs, &base_reg, &offset))
{
case 1:
- strcpy (buf, "ldm%?ia\t");
+ strcpy (buf, "ldm%(ia%)\t");
break;
case 2:
- strcpy (buf, "ldm%?ib\t");
+ strcpy (buf, "ldm%(ib%)\t");
break;
case 3:
- strcpy (buf, "ldm%?da\t");
+ strcpy (buf, "ldm%(da%)\t");
break;
case 4:
- strcpy (buf, "ldm%?db\t");
+ strcpy (buf, "ldm%(db%)\t");
break;
case 5:
@@ -6033,7 +6352,7 @@ emit_ldm_seq (rtx *operands, int nops)
(long) -offset);
output_asm_insn (buf, operands);
base_reg = regs[0];
- strcpy (buf, "ldm%?ia\t");
+ strcpy (buf, "ldm%(ia%)\t");
break;
default:
@@ -6196,19 +6515,19 @@ emit_stm_seq (rtx *operands, int nops)
switch (store_multiple_sequence (operands, nops, regs, &base_reg, &offset))
{
case 1:
- strcpy (buf, "stm%?ia\t");
+ strcpy (buf, "stm%(ia%)\t");
break;
case 2:
- strcpy (buf, "stm%?ib\t");
+ strcpy (buf, "stm%(ib%)\t");
break;
case 3:
- strcpy (buf, "stm%?da\t");
+ strcpy (buf, "stm%(da%)\t");
break;
case 4:
- strcpy (buf, "stm%?db\t");
+ strcpy (buf, "stm%(db%)\t");
break;
default:
@@ -6769,7 +7088,7 @@ arm_select_cc_mode (enum rtx_code op, rtx x, rtx y)
/* An operation (on Thumb) where we want to test for a single bit.
This is done by shifting that bit up into the top bit of a
scratch register; we can then branch on the sign bit. */
- if (TARGET_THUMB
+ if (TARGET_THUMB1
&& GET_MODE (x) == SImode
&& (op == EQ || op == NE)
&& GET_CODE (x) == ZERO_EXTRACT
@@ -6780,6 +7099,7 @@ arm_select_cc_mode (enum rtx_code op, rtx x, rtx y)
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"
instead.) */
+ /* ??? Does the ZERO_EXTRACT case really apply to thumb2? */
if (GET_MODE (x) == SImode
&& y == const0_rtx
&& (op == EQ || op == NE || op == LT || op == GE)
@@ -6790,7 +7110,7 @@ arm_select_cc_mode (enum rtx_code op, rtx x, rtx y)
|| GET_CODE (x) == LSHIFTRT
|| GET_CODE (x) == ASHIFT || GET_CODE (x) == ASHIFTRT
|| GET_CODE (x) == ROTATERT
- || (TARGET_ARM && GET_CODE (x) == ZERO_EXTRACT)))
+ || (TARGET_32BIT && GET_CODE (x) == ZERO_EXTRACT)))
return CC_NOOVmode;
if (GET_MODE (x) == QImode && (op == EQ || op == NE))
@@ -7373,8 +7693,29 @@ get_jump_table_size (rtx insn)
{
rtx body = PATTERN (insn);
int elt = GET_CODE (body) == ADDR_DIFF_VEC ? 1 : 0;
+ HOST_WIDE_INT size;
+ HOST_WIDE_INT modesize;
- return GET_MODE_SIZE (GET_MODE (body)) * XVECLEN (body, elt);
+ modesize = GET_MODE_SIZE (GET_MODE (body));
+ size = modesize * XVECLEN (body, elt);
+ switch (modesize)
+ {
+ case 1:
+ /* Round up size of TBB table to a hafword boundary. */
+ size = (size + 1) & ~(HOST_WIDE_INT)1;
+ break;
+ case 2:
+ /* No padding neccessary for TBH. */
+ break;
+ case 4:
+ /* Add two bytes for alignment on Thumb. */
+ if (TARGET_THUMB)
+ size += 2;
+ break;
+ default:
+ gcc_unreachable ();
+ }
+ return size;
}
return 0;
@@ -8409,7 +8750,7 @@ print_multi_reg (FILE *stream, const char *instr, unsigned reg,
fputc ('\t', stream);
asm_fprintf (stream, instr, reg);
- fputs (", {", stream);
+ fputc ('{', stream);
for (i = 0; i <= LAST_ARM_REGNUM; i++)
if (mask & (1 << i))
@@ -8634,7 +8975,7 @@ output_mov_long_double_fpa_from_arm (rtx *operands)
ops[1] = gen_rtx_REG (SImode, 1 + arm_reg0);
ops[2] = gen_rtx_REG (SImode, 2 + arm_reg0);
- output_asm_insn ("stm%?fd\t%|sp!, {%0, %1, %2}", ops);
+ output_asm_insn ("stm%(fd%)\t%|sp!, {%0, %1, %2}", ops);
output_asm_insn ("ldf%?e\t%0, [%|sp], #12", operands);
return "";
@@ -8656,7 +8997,7 @@ output_mov_long_double_arm_from_fpa (rtx *operands)
ops[2] = gen_rtx_REG (SImode, 2 + arm_reg0);
output_asm_insn ("stf%?e\t%1, [%|sp, #-12]!", operands);
- output_asm_insn ("ldm%?fd\t%|sp!, {%0, %1, %2}", ops);
+ output_asm_insn ("ldm%(fd%)\t%|sp!, {%0, %1, %2}", ops);
return "";
}
@@ -8708,7 +9049,7 @@ output_mov_double_fpa_from_arm (rtx *operands)
ops[0] = gen_rtx_REG (SImode, arm_reg0);
ops[1] = gen_rtx_REG (SImode, 1 + arm_reg0);
- output_asm_insn ("stm%?fd\t%|sp!, {%0, %1}", ops);
+ output_asm_insn ("stm%(fd%)\t%|sp!, {%0, %1}", ops);
output_asm_insn ("ldf%?d\t%0, [%|sp], #8", operands);
return "";
}
@@ -8727,7 +9068,7 @@ output_mov_double_arm_from_fpa (rtx *operands)
ops[0] = gen_rtx_REG (SImode, arm_reg0);
ops[1] = gen_rtx_REG (SImode, 1 + arm_reg0);
output_asm_insn ("stf%?d\t%1, [%|sp, #-8]!", operands);
- output_asm_insn ("ldm%?fd\t%|sp!, {%0, %1}", ops);
+ output_asm_insn ("ldm%(fd%)\t%|sp!, {%0, %1}", ops);
return "";
}
@@ -8752,25 +9093,28 @@ output_move_double (rtx *operands)
switch (GET_CODE (XEXP (operands[1], 0)))
{
case REG:
- output_asm_insn ("ldm%?ia\t%m1, %M0", operands);
+ output_asm_insn ("ldm%(ia%)\t%m1, %M0", operands);
break;
case PRE_INC:
gcc_assert (TARGET_LDRD);
- output_asm_insn ("ldr%?d\t%0, [%m1, #8]!", operands);
+ output_asm_insn ("ldr%(d%)\t%0, [%m1, #8]!", operands);
break;
case PRE_DEC:
- output_asm_insn ("ldm%?db\t%m1!, %M0", operands);
+ if (TARGET_LDRD)
+ output_asm_insn ("ldr%(d%)\t%0, [%m1, #-8]!", operands);
+ else
+ output_asm_insn ("ldm%(db%)\t%m1!, %M0", operands);
break;
case POST_INC:
- output_asm_insn ("ldm%?ia\t%m1!, %M0", operands);
+ output_asm_insn ("ldm%(ia%)\t%m1!, %M0", operands);
break;
case POST_DEC:
gcc_assert (TARGET_LDRD);
- output_asm_insn ("ldr%?d\t%0, [%m1], #-8", operands);
+ output_asm_insn ("ldr%(d%)\t%0, [%m1], #-8", operands);
break;
case PRE_MODIFY:
@@ -8785,24 +9129,25 @@ output_move_double (rtx *operands)
{
/* Registers overlap so split out the increment. */
output_asm_insn ("add%?\t%1, %1, %2", otherops);
- output_asm_insn ("ldr%?d\t%0, [%1] @split", otherops);
+ output_asm_insn ("ldr%(d%)\t%0, [%1] @split", otherops);
}
else
- output_asm_insn ("ldr%?d\t%0, [%1, %2]!", otherops);
+ output_asm_insn ("ldr%(d%)\t%0, [%1, %2]!", otherops);
}
else
{
/* We only allow constant increments, so this is safe. */
- output_asm_insn ("ldr%?d\t%0, [%1], %2", otherops);
+ output_asm_insn ("ldr%(d%)\t%0, [%1], %2", otherops);
}
break;
case LABEL_REF:
case CONST:
output_asm_insn ("adr%?\t%0, %1", operands);
- output_asm_insn ("ldm%?ia\t%0, %M0", operands);
+ output_asm_insn ("ldm%(ia%)\t%0, %M0", operands);
break;
+ /* ??? This needs checking for thumb2. */
default:
if (arm_add_operand (XEXP (XEXP (operands[1], 0), 1),
GET_MODE (XEXP (XEXP (operands[1], 0), 1))))
@@ -8818,13 +9163,17 @@ output_move_double (rtx *operands)
switch ((int) INTVAL (otherops[2]))
{
case -8:
- output_asm_insn ("ldm%?db\t%1, %M0", otherops);
+ output_asm_insn ("ldm%(db%)\t%1, %M0", otherops);
return "";
case -4:
- output_asm_insn ("ldm%?da\t%1, %M0", otherops);
+ if (TARGET_THUMB2)
+ break;
+ output_asm_insn ("ldm%(da%)\t%1, %M0", otherops);
return "";
case 4:
- output_asm_insn ("ldm%?ib\t%1, %M0", otherops);
+ if (TARGET_THUMB2)
+ break;
+ output_asm_insn ("ldm%(ib%)\t%1, %M0", otherops);
return "";
}
}
@@ -8847,11 +9196,11 @@ output_move_double (rtx *operands)
if (reg_overlap_mentioned_p (otherops[0], otherops[2]))
{
output_asm_insn ("add%?\t%1, %1, %2", otherops);
- output_asm_insn ("ldr%?d\t%0, [%1]",
+ output_asm_insn ("ldr%(d%)\t%0, [%1]",
otherops);
}
else
- output_asm_insn ("ldr%?d\t%0, [%1, %2]", otherops);
+ output_asm_insn ("ldr%(d%)\t%0, [%1, %2]", otherops);
return "";
}
@@ -8868,7 +9217,7 @@ output_move_double (rtx *operands)
else
output_asm_insn ("sub%?\t%0, %1, %2", otherops);
- return "ldm%?ia\t%0, %M0";
+ return "ldm%(ia%)\t%0, %M0";
}
else
{
@@ -8896,25 +9245,28 @@ output_move_double (rtx *operands)
switch (GET_CODE (XEXP (operands[0], 0)))
{
case REG:
- output_asm_insn ("stm%?ia\t%m0, %M1", operands);
+ output_asm_insn ("stm%(ia%)\t%m0, %M1", operands);
break;
case PRE_INC:
gcc_assert (TARGET_LDRD);
- output_asm_insn ("str%?d\t%1, [%m0, #8]!", operands);
+ output_asm_insn ("str%(d%)\t%1, [%m0, #8]!", operands);
break;
case PRE_DEC:
- output_asm_insn ("stm%?db\t%m0!, %M1", operands);
+ if (TARGET_LDRD)
+ output_asm_insn ("str%(d%)\t%1, [%m0, #-8]!", operands);
+ else
+ output_asm_insn ("stm%(db%)\t%m0!, %M1", operands);
break;
case POST_INC:
- output_asm_insn ("stm%?ia\t%m0!, %M1", operands);
+ output_asm_insn ("stm%(ia%)\t%m0!, %M1", operands);
break;
case POST_DEC:
gcc_assert (TARGET_LDRD);
- output_asm_insn ("str%?d\t%1, [%m0], #-8", operands);
+ output_asm_insn ("str%(d%)\t%1, [%m0], #-8", operands);
break;
case PRE_MODIFY:
@@ -8924,9 +9276,9 @@ output_move_double (rtx *operands)
otherops[2] = XEXP (XEXP (XEXP (operands[0], 0), 1), 1);
if (GET_CODE (XEXP (operands[0], 0)) == PRE_MODIFY)
- output_asm_insn ("str%?d\t%0, [%1, %2]!", otherops);
+ output_asm_insn ("str%(d%)\t%0, [%1, %2]!", otherops);
else
- output_asm_insn ("str%?d\t%0, [%1], %2", otherops);
+ output_asm_insn ("str%(d%)\t%0, [%1], %2", otherops);
break;
case PLUS:
@@ -8936,15 +9288,19 @@ output_move_double (rtx *operands)
switch ((int) INTVAL (XEXP (XEXP (operands[0], 0), 1)))
{
case -8:
- output_asm_insn ("stm%?db\t%m0, %M1", operands);
+ output_asm_insn ("stm%(db%)\t%m0, %M1", operands);
return "";
case -4:
- output_asm_insn ("stm%?da\t%m0, %M1", operands);
+ if (TARGET_THUMB2)
+ break;
+ output_asm_insn ("stm%(da%)\t%m0, %M1", operands);
return "";
case 4:
- output_asm_insn ("stm%?ib\t%m0, %M1", operands);
+ if (TARGET_THUMB2)
+ break;
+ output_asm_insn ("stm%(ib%)\t%m0, %M1", operands);
return "";
}
}
@@ -8956,7 +9312,7 @@ output_move_double (rtx *operands)
{
otherops[0] = operands[1];
otherops[1] = XEXP (XEXP (operands[0], 0), 0);
- output_asm_insn ("str%?d\t%0, [%1, %2]", otherops);
+ output_asm_insn ("str%(d%)\t%0, [%1, %2]", otherops);
return "";
}
/* Fall through */
@@ -8972,6 +9328,62 @@ output_move_double (rtx *operands)
return "";
}
+/* Output a VFP load or store instruction. */
+
+const char *
+output_move_vfp (rtx *operands)
+{
+ rtx reg, mem, addr, ops[2];
+ int load = REG_P (operands[0]);
+ int dp = GET_MODE_SIZE (GET_MODE (operands[0])) == 8;
+ int integer_p = GET_MODE_CLASS (GET_MODE (operands[0])) == MODE_INT;
+ const char *template;
+ char buff[50];
+
+ reg = operands[!load];
+ mem = operands[load];
+
+ gcc_assert (REG_P (reg));
+ gcc_assert (IS_VFP_REGNUM (REGNO (reg)));
+ gcc_assert (GET_MODE (reg) == SFmode
+ || GET_MODE (reg) == DFmode
+ || GET_MODE (reg) == SImode
+ || GET_MODE (reg) == DImode);
+ gcc_assert (MEM_P (mem));
+
+ addr = XEXP (mem, 0);
+
+ switch (GET_CODE (addr))
+ {
+ case PRE_DEC:
+ template = "f%smdb%c%%?\t%%0!, {%%%s1}%s";
+ ops[0] = XEXP (addr, 0);
+ ops[1] = reg;
+ break;
+
+ case POST_INC:
+ template = "f%smia%c%%?\t%%0!, {%%%s1}%s";
+ ops[0] = XEXP (addr, 0);
+ ops[1] = reg;
+ break;
+
+ default:
+ template = "f%s%c%%?\t%%%s0, %%1%s";
+ ops[0] = reg;
+ ops[1] = mem;
+ break;
+ }
+
+ sprintf (buff, template,
+ load ? "ld" : "st",
+ dp ? 'd' : 's',
+ dp ? "P" : "",
+ integer_p ? "\t%@ int" : "");
+ output_asm_insn (buff, ops);
+
+ return "";
+}
+
/* Output an ADD r, s, #n where n may be too big for one instruction.
If adding zero to one register, output nothing. */
const char *
@@ -9035,6 +9447,29 @@ output_multi_immediate (rtx *operands, const char *instr1, const char *instr2,
return "";
}
+/* Return the name of a shifter operation. */
+static const char *
+arm_shift_nmem(enum rtx_code code)
+{
+ switch (code)
+ {
+ case ASHIFT:
+ return ARM_LSL_NAME;
+
+ case ASHIFTRT:
+ return "asr";
+
+ case LSHIFTRT:
+ return "lsr";
+
+ case ROTATERT:
+ return "ror";
+
+ default:
+ abort();
+ }
+}
+
/* Return the appropriate ARM instruction for the operation code.
The returned result should not be overwritten. OP is the rtx of the
operation. SHIFT_FIRST_ARG is TRUE if the first argument of the operator
@@ -9059,6 +9494,12 @@ arithmetic_instr (rtx op, int shift_first_arg)
case AND:
return "and";
+ case ASHIFT:
+ case ASHIFTRT:
+ case LSHIFTRT:
+ case ROTATERT:
+ return arm_shift_nmem(GET_CODE(op));
+
default:
gcc_unreachable ();
}
@@ -9092,26 +9533,18 @@ shift_op (rtx op, HOST_WIDE_INT *amountp)
switch (code)
{
- case ASHIFT:
- mnem = "asl";
- break;
-
- case ASHIFTRT:
- mnem = "asr";
- break;
-
- case LSHIFTRT:
- mnem = "lsr";
- break;
-
case ROTATE:
gcc_assert (*amountp != -1);
*amountp = 32 - *amountp;
+ code = ROTATERT;
/* Fall through. */
+ case ASHIFT:
+ case ASHIFTRT:
+ case LSHIFTRT:
case ROTATERT:
- mnem = "ror";
+ mnem = arm_shift_nmem(code);
break;
case MULT:
@@ -9119,7 +9552,7 @@ shift_op (rtx op, HOST_WIDE_INT *amountp)
power of 2, since this case can never be reloaded from a reg. */
gcc_assert (*amountp != -1);
*amountp = int_log2 (*amountp);
- return "asl";
+ return ARM_LSL_NAME;
default:
gcc_unreachable ();
@@ -9129,7 +9562,7 @@ shift_op (rtx op, HOST_WIDE_INT *amountp)
{
/* This is not 100% correct, but follows from the desire to merge
multiplication by a power of 2 with the recognizer for a
- shift. >=32 is not a valid shift for "asl", so we must try and
+ shift. >=32 is not a valid shift for "lsl", so we must try and
output a shift that produces the correct arithmetical result.
Using lsr #32 is identical except for the fact that the carry bit
is not set correctly if we set the flags; but we never use the
@@ -9259,17 +9692,22 @@ arm_compute_save_reg0_reg12_mask (void)
}
else
{
+ /* In arm mode we handle r11 (FP) as a special case. */
+ unsigned last_reg = TARGET_ARM ? 10 : 11;
+
/* 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++)
+ for (reg = 0; reg <= last_reg; 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])
+ if (TARGET_THUMB2 && frame_pointer_needed)
+ save_reg_mask |= 1 << HARD_FRAME_POINTER_REGNUM;
+ else 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,
@@ -9280,6 +9718,10 @@ arm_compute_save_reg0_reg12_mask (void)
&& (regs_ever_live[PIC_OFFSET_TABLE_REGNUM]
|| current_function_uses_pic_offset_table))
save_reg_mask |= 1 << PIC_OFFSET_TABLE_REGNUM;
+
+ /* The prologue will copy SP into R0, so save it. */
+ if (IS_STACKALIGN (func_type))
+ save_reg_mask |= 1;
}
/* Save registers so the exception handler can modify them. */
@@ -9299,6 +9741,7 @@ arm_compute_save_reg0_reg12_mask (void)
return save_reg_mask;
}
+
/* Compute a bit mask of which registers need to be
saved on the stack for the current function. */
@@ -9307,6 +9750,7 @@ arm_compute_save_reg_mask (void)
{
unsigned int save_reg_mask = 0;
unsigned long func_type = arm_current_func_type ();
+ unsigned int reg;
if (IS_NAKED (func_type))
/* This should never really happen. */
@@ -9314,7 +9758,7 @@ arm_compute_save_reg_mask (void)
/* 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)
+ if (frame_pointer_needed && TARGET_ARM)
save_reg_mask |=
(1 << ARM_HARD_FRAME_POINTER_REGNUM)
| (1 << IP_REGNUM)
@@ -9351,8 +9795,6 @@ arm_compute_save_reg_mask (void)
&& ((bit_count (save_reg_mask)
+ ARM_NUM_INTS (current_function_pretend_args_size)) % 2) != 0)
{
- unsigned int reg;
-
/* The total number of registers that are going to be pushed
onto the stack is odd. We need to ensure that the stack
is 64-bit aligned before we start to save iWMMXt registers,
@@ -9375,6 +9817,16 @@ arm_compute_save_reg_mask (void)
}
}
+ /* We may need to push an additional register for use initializing the
+ PIC base register. */
+ if (TARGET_THUMB2 && IS_NESTED (func_type) && flag_pic
+ && (save_reg_mask & THUMB2_WORK_REGS) == 0)
+ {
+ reg = thumb_find_work_register (1 << 4);
+ if (!call_used_regs[reg])
+ save_reg_mask |= (1 << reg);
+ }
+
return save_reg_mask;
}
@@ -9382,7 +9834,7 @@ arm_compute_save_reg_mask (void)
/* Compute a bit mask of which registers need to be
saved on the stack for the current function. */
static unsigned long
-thumb_compute_save_reg_mask (void)
+thumb1_compute_save_reg_mask (void)
{
unsigned long mask;
unsigned reg;
@@ -9578,7 +10030,7 @@ output_return_instruction (rtx operand, int really_return, int reverse)
stack_adjust = offsets->outgoing_args - offsets->saved_regs;
gcc_assert (stack_adjust == 0 || stack_adjust == 4);
- if (stack_adjust && arm_arch5)
+ if (stack_adjust && arm_arch5 && TARGET_ARM)
sprintf (instr, "ldm%sib\t%%|sp, {", conditional);
else
{
@@ -9643,6 +10095,7 @@ output_return_instruction (rtx operand, int really_return, int reverse)
{
case ARM_FT_ISR:
case ARM_FT_FIQ:
+ /* ??? This is wrong for unified assembly syntax. */
sprintf (instr, "sub%ss\t%%|pc, %%|lr, #4", conditional);
break;
@@ -9651,6 +10104,7 @@ output_return_instruction (rtx operand, int really_return, int reverse)
break;
case ARM_FT_EXCEPTION:
+ /* ??? This is wrong for unified assembly syntax. */
sprintf (instr, "mov%ss\t%%|pc, %%|lr", conditional);
break;
@@ -9718,9 +10172,9 @@ arm_output_function_prologue (FILE *f, HOST_WIDE_INT frame_size)
{
unsigned long func_type;
- if (!TARGET_ARM)
+ if (TARGET_THUMB1)
{
- thumb_output_function_prologue (f, frame_size);
+ thumb1_output_function_prologue (f, frame_size);
return;
}
@@ -9756,6 +10210,8 @@ arm_output_function_prologue (FILE *f, HOST_WIDE_INT frame_size)
if (IS_NESTED (func_type))
asm_fprintf (f, "\t%@ Nested: function declared inside another function.\n");
+ if (IS_STACKALIGN (func_type))
+ asm_fprintf (f, "\t%@ Stack Align: May be called with mis-aligned SP.\n");
asm_fprintf (f, "\t%@ args = %d, pretend = %d, frame = %wd\n",
current_function_args_size,
@@ -9834,7 +10290,7 @@ arm_output_epilogue (rtx sibling)
if (saved_regs_mask & (1 << reg))
floats_offset += 4;
- if (frame_pointer_needed)
+ if (frame_pointer_needed && TARGET_ARM)
{
/* This variable is for the Virtual Frame Pointer, not VFP regs. */
int vfp_offset = offsets->frame;
@@ -9971,22 +10427,40 @@ arm_output_epilogue (rtx sibling)
|| current_function_calls_alloca)
asm_fprintf (f, "\tsub\t%r, %r, #%d\n", SP_REGNUM, FP_REGNUM,
4 * bit_count (saved_regs_mask));
- print_multi_reg (f, "ldmfd\t%r", SP_REGNUM, saved_regs_mask);
+ print_multi_reg (f, "ldmfd\t%r, ", SP_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, "ldmfd\t%r!", SP_REGNUM, 1 << IP_REGNUM);
+ print_multi_reg (f, "ldmfd\t%r!, ", SP_REGNUM, 1 << IP_REGNUM);
}
else
{
+ HOST_WIDE_INT amount;
/* Restore stack pointer if necessary. */
- if (offsets->outgoing_args != offsets->saved_regs)
+ if (frame_pointer_needed)
{
- operands[0] = operands[1] = stack_pointer_rtx;
- operands[2] = GEN_INT (offsets->outgoing_args - offsets->saved_regs);
+ /* For Thumb-2 restore sp from the frame pointer.
+ Operand restrictions mean we have to incrememnt FP, then copy
+ to SP. */
+ amount = offsets->locals_base - offsets->saved_regs;
+ operands[0] = hard_frame_pointer_rtx;
+ }
+ else
+ {
+ operands[0] = stack_pointer_rtx;
+ amount = offsets->outgoing_args - offsets->saved_regs;
+ }
+
+ if (amount)
+ {
+ operands[1] = operands[0];
+ operands[2] = GEN_INT (amount);
output_add_immediate (operands);
}
+ if (frame_pointer_needed)
+ asm_fprintf (f, "\tmov\t%r, %r\n",
+ SP_REGNUM, HARD_FRAME_POINTER_REGNUM);
if (arm_fpu_arch == FPUTYPE_FPA_EMU2)
{
@@ -10054,6 +10528,7 @@ arm_output_epilogue (rtx sibling)
/* If we can, restore the LR into the PC. */
if (ARM_FUNC_TYPE (func_type) == ARM_FT_NORMAL
+ && !IS_STACKALIGN (func_type)
&& really_return
&& current_function_pretend_args_size == 0
&& saved_regs_mask & (1 << LR_REGNUM)
@@ -10064,8 +10539,9 @@ arm_output_epilogue (rtx sibling)
}
/* 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))
+ to load use the LDR instruction - it is faster. For Thumb-2
+ always use pop and the assembler will pick the best instruction.*/
+ if (TARGET_ARM && saved_regs_mask == (1 << LR_REGNUM))
{
asm_fprintf (f, "\tldr\t%r, [%r], #4\n", LR_REGNUM, SP_REGNUM);
}
@@ -10076,9 +10552,11 @@ arm_output_epilogue (rtx sibling)
(i.e. "ldmfd sp!..."). We know that the stack pointer is
in the list of registers and if we add writeback the
instruction becomes UNPREDICTABLE. */
- print_multi_reg (f, "ldmfd\t%r", SP_REGNUM, saved_regs_mask);
+ print_multi_reg (f, "ldmfd\t%r, ", SP_REGNUM, saved_regs_mask);
+ else if (TARGET_ARM)
+ print_multi_reg (f, "ldmfd\t%r!, ", SP_REGNUM, saved_regs_mask);
else
- print_multi_reg (f, "ldmfd\t%r!", SP_REGNUM, saved_regs_mask);
+ print_multi_reg (f, "pop\t", SP_REGNUM, saved_regs_mask);
}
if (current_function_pretend_args_size)
@@ -10116,6 +10594,11 @@ arm_output_epilogue (rtx sibling)
break;
default:
+ if (IS_STACKALIGN (func_type))
+ {
+ /* See comment in arm_expand_prologue. */
+ asm_fprintf (f, "\tmov\t%r, %r\n", SP_REGNUM, 0);
+ }
if (arm_arch5 || arm_arch4t)
asm_fprintf (f, "\tbx\t%r\n", LR_REGNUM);
else
@@ -10132,7 +10615,7 @@ arm_output_function_epilogue (FILE *file ATTRIBUTE_UNUSED,
{
arm_stack_offsets *offsets;
- if (TARGET_THUMB)
+ if (TARGET_THUMB1)
{
int regno;
@@ -10156,7 +10639,7 @@ arm_output_function_epilogue (FILE *file ATTRIBUTE_UNUSED,
RTL for it. This does not happen for inline functions. */
return_used_this_function = 0;
}
- else
+ else /* TARGET_32BIT */
{
/* We need to take into account any stack-frame rounding. */
offsets = arm_get_frame_offsets ();
@@ -10464,9 +10947,10 @@ arm_get_frame_offsets (void)
/* Space for variadic functions. */
offsets->saved_args = current_function_pretend_args_size;
+ /* In Thumb mode this is incorrect, but never used. */
offsets->frame = offsets->saved_args + (frame_pointer_needed ? 4 : 0);
- if (TARGET_ARM)
+ if (TARGET_32BIT)
{
unsigned int regno;
@@ -10499,9 +10983,9 @@ arm_get_frame_offsets (void)
saved += arm_get_vfp_saved_size ();
}
}
- else /* TARGET_THUMB */
+ else /* TARGET_THUMB1 */
{
- saved = bit_count (thumb_compute_save_reg_mask ()) * 4;
+ saved = bit_count (thumb1_compute_save_reg_mask ()) * 4;
if (TARGET_BACKTRACE)
saved += 16;
}
@@ -10618,11 +11102,132 @@ arm_compute_initial_elimination_offset (unsigned int from, unsigned int to)
}
-/* Generate the prologue instructions for entry into an ARM function. */
+/* Emit RTL to save coprocessor registers on funciton entry. Returns the
+ number of bytes pushed. */
+
+static int
+arm_save_coproc_regs(void)
+{
+ int saved_size = 0;
+ unsigned reg;
+ unsigned start_reg;
+ rtx insn;
+
+ for (reg = LAST_IWMMXT_REGNUM; reg >= FIRST_IWMMXT_REGNUM; reg--)
+ if (regs_ever_live[reg] && ! call_used_regs [reg])
+ {
+ insn = gen_rtx_PRE_DEC (V2SImode, stack_pointer_rtx);
+ insn = gen_rtx_MEM (V2SImode, insn);
+ insn = emit_set_insn (insn, gen_rtx_REG (V2SImode, reg));
+ RTX_FRAME_RELATED_P (insn) = 1;
+ saved_size += 8;
+ }
+
+ /* Save any floating point call-saved registers used by this
+ function. */
+ if (arm_fpu_arch == FPUTYPE_FPA_EMU2)
+ {
+ for (reg = LAST_FPA_REGNUM; reg >= FIRST_FPA_REGNUM; 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);
+ insn = emit_set_insn (insn, gen_rtx_REG (XFmode, reg));
+ RTX_FRAME_RELATED_P (insn) = 1;
+ saved_size += 12;
+ }
+ }
+ else
+ {
+ start_reg = LAST_FPA_REGNUM;
+
+ for (reg = LAST_FPA_REGNUM; reg >= FIRST_FPA_REGNUM; reg--)
+ {
+ if (regs_ever_live[reg] && !call_used_regs[reg])
+ {
+ if (start_reg - reg == 3)
+ {
+ insn = emit_sfm (reg, 4);
+ RTX_FRAME_RELATED_P (insn) = 1;
+ saved_size += 48;
+ start_reg = reg - 1;
+ }
+ }
+ else
+ {
+ if (start_reg != reg)
+ {
+ insn = emit_sfm (reg + 1, start_reg - reg);
+ RTX_FRAME_RELATED_P (insn) = 1;
+ saved_size += (start_reg - reg) * 12;
+ }
+ start_reg = reg - 1;
+ }
+ }
+
+ if (start_reg != reg)
+ {
+ insn = emit_sfm (reg + 1, start_reg - reg);
+ saved_size += (start_reg - reg) * 12;
+ RTX_FRAME_RELATED_P (insn) = 1;
+ }
+ }
+ if (TARGET_HARD_FLOAT && TARGET_VFP)
+ {
+ start_reg = FIRST_VFP_REGNUM;
+
+ for (reg = FIRST_VFP_REGNUM; reg < LAST_VFP_REGNUM; reg += 2)
+ {
+ if ((!regs_ever_live[reg] || call_used_regs[reg])
+ && (!regs_ever_live[reg + 1] || call_used_regs[reg + 1]))
+ {
+ if (start_reg != reg)
+ saved_size += vfp_emit_fstmd (start_reg,
+ (reg - start_reg) / 2);
+ start_reg = reg + 2;
+ }
+ }
+ if (start_reg != reg)
+ saved_size += vfp_emit_fstmd (start_reg,
+ (reg - start_reg) / 2);
+ }
+ return saved_size;
+}
+
+
+/* Set the Thumb frame pointer from the stack pointer. */
+
+static void
+thumb_set_frame_pointer (arm_stack_offsets *offsets)
+{
+ HOST_WIDE_INT amount;
+ rtx insn, dwarf;
+
+ amount = offsets->outgoing_args - offsets->locals_base;
+ if (amount < 1024)
+ insn = emit_insn (gen_addsi3 (hard_frame_pointer_rtx,
+ stack_pointer_rtx, GEN_INT (amount)));
+ else
+ {
+ emit_insn (gen_movsi (hard_frame_pointer_rtx, GEN_INT (amount)));
+ insn = emit_insn (gen_addsi3 (hard_frame_pointer_rtx,
+ hard_frame_pointer_rtx,
+ stack_pointer_rtx));
+ dwarf = gen_rtx_SET (VOIDmode, hard_frame_pointer_rtx,
+ plus_constant (stack_pointer_rtx, amount));
+ RTX_FRAME_RELATED_P (dwarf) = 1;
+ REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR, dwarf,
+ REG_NOTES (insn));
+ }
+
+ RTX_FRAME_RELATED_P (insn) = 1;
+}
+
+/* Generate the prologue instructions for entry into an ARM or Thumb-2
+ function. */
void
arm_expand_prologue (void)
{
- int reg;
rtx amount;
rtx insn;
rtx ip_rtx;
@@ -10648,7 +11253,38 @@ arm_expand_prologue (void)
ip_rtx = gen_rtx_REG (SImode, IP_REGNUM);
- if (frame_pointer_needed)
+ if (IS_STACKALIGN (func_type))
+ {
+ rtx dwarf;
+ rtx r0;
+ rtx r1;
+ /* Handle a word-aligned stack pointer. We generate the following:
+
+ mov r0, sp
+ bic r1, r0, #7
+ mov sp, r1
+ <save and restore r0 in normal prologue/epilogue>
+ mov sp, r0
+ bx lr
+
+ The unwinder doesn't need to know about the stack realignment.
+ Just tell it we saved SP in r0. */
+ gcc_assert (TARGET_THUMB2 && !arm_arch_notm && args_to_push == 0);
+
+ r0 = gen_rtx_REG (SImode, 0);
+ r1 = gen_rtx_REG (SImode, 1);
+ dwarf = gen_rtx_UNSPEC (SImode, NULL_RTVEC, UNSPEC_STACK_ALIGN);
+ dwarf = gen_rtx_SET (VOIDmode, r0, dwarf);
+ insn = gen_movsi (r0, stack_pointer_rtx);
+ RTX_FRAME_RELATED_P (insn) = 1;
+ REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
+ dwarf, REG_NOTES (insn));
+ emit_insn (insn);
+ emit_insn (gen_andsi3 (r1, r0, GEN_INT (~(HOST_WIDE_INT)7)));
+ emit_insn (gen_movsi (stack_pointer_rtx, r1));
+ }
+
+ if (frame_pointer_needed && TARGET_ARM)
{
if (IS_INTERRUPT (func_type))
{
@@ -10767,113 +11403,32 @@ arm_expand_prologue (void)
RTX_FRAME_RELATED_P (insn) = 1;
}
- if (TARGET_IWMMXT)
- for (reg = LAST_IWMMXT_REGNUM; reg >= FIRST_IWMMXT_REGNUM; reg--)
- if (regs_ever_live[reg] && ! call_used_regs [reg])
- {
- insn = gen_rtx_PRE_DEC (V2SImode, stack_pointer_rtx);
- insn = gen_frame_mem (V2SImode, insn);
- insn = emit_set_insn (insn, gen_rtx_REG (V2SImode, reg));
- RTX_FRAME_RELATED_P (insn) = 1;
- saved_regs += 8;
- }
-
if (! IS_VOLATILE (func_type))
- {
- int start_reg;
-
- /* Save any floating point call-saved registers used by this
- function. */
- if (arm_fpu_arch == FPUTYPE_FPA_EMU2)
- {
- for (reg = LAST_FPA_REGNUM; reg >= FIRST_FPA_REGNUM; reg--)
- if (regs_ever_live[reg] && !call_used_regs[reg])
- {
- insn = gen_rtx_PRE_DEC (XFmode, stack_pointer_rtx);
- insn = gen_frame_mem (XFmode, insn);
- insn = emit_set_insn (insn, gen_rtx_REG (XFmode, reg));
- RTX_FRAME_RELATED_P (insn) = 1;
- saved_regs += 12;
- }
- }
- else
- {
- start_reg = LAST_FPA_REGNUM;
+ saved_regs += arm_save_coproc_regs ();
- for (reg = LAST_FPA_REGNUM; reg >= FIRST_FPA_REGNUM; reg--)
- {
- if (regs_ever_live[reg] && !call_used_regs[reg])
- {
- if (start_reg - reg == 3)
- {
- insn = emit_sfm (reg, 4);
- RTX_FRAME_RELATED_P (insn) = 1;
- saved_regs += 48;
- start_reg = reg - 1;
- }
- }
- else
- {
- if (start_reg != reg)
- {
- insn = emit_sfm (reg + 1, start_reg - reg);
- RTX_FRAME_RELATED_P (insn) = 1;
- saved_regs += (start_reg - reg) * 12;
- }
- start_reg = reg - 1;
- }
- }
-
- if (start_reg != reg)
- {
- insn = emit_sfm (reg + 1, start_reg - reg);
- saved_regs += (start_reg - reg) * 12;
- RTX_FRAME_RELATED_P (insn) = 1;
- }
- }
- if (TARGET_HARD_FLOAT && TARGET_VFP)
+ if (frame_pointer_needed && TARGET_ARM)
+ {
+ /* Create the new frame pointer. */
{
- start_reg = FIRST_VFP_REGNUM;
+ insn = GEN_INT (-(4 + args_to_push + fp_offset));
+ insn = emit_insn (gen_addsi3 (hard_frame_pointer_rtx, ip_rtx, insn));
+ RTX_FRAME_RELATED_P (insn) = 1;
- for (reg = FIRST_VFP_REGNUM; reg < LAST_VFP_REGNUM; reg += 2)
+ if (IS_NESTED (func_type))
{
- if ((!regs_ever_live[reg] || call_used_regs[reg])
- && (!regs_ever_live[reg + 1] || call_used_regs[reg + 1]))
+ /* Recover the static chain register. */
+ if (regs_ever_live [3] == 0
+ || saved_pretend_args)
+ insn = gen_rtx_REG (SImode, 3);
+ else /* if (current_function_pretend_args_size == 0) */
{
- if (start_reg != reg)
- saved_regs += vfp_emit_fstmd (start_reg,
- (reg - start_reg) / 2);
- start_reg = reg + 2;
+ insn = plus_constant (hard_frame_pointer_rtx, 4);
+ insn = gen_frame_mem (SImode, insn);
}
+ emit_set_insn (ip_rtx, insn);
+ /* Add a USE to stop propagate_one_insn() from barfing. */
+ emit_insn (gen_prologue_use (ip_rtx));
}
- if (start_reg != reg)
- saved_regs += vfp_emit_fstmd (start_reg,
- (reg - start_reg) / 2);
- }
- }
-
- if (frame_pointer_needed)
- {
- /* Create the new frame pointer. */
- insn = GEN_INT (-(4 + args_to_push + 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
- || saved_pretend_args)
- insn = gen_rtx_REG (SImode, 3);
- else /* if (current_function_pretend_args_size == 0) */
- {
- insn = plus_constant (hard_frame_pointer_rtx, 4);
- insn = gen_frame_mem (SImode, insn);
- }
-
- emit_set_insn (ip_rtx, insn);
- /* Add a USE to stop propagate_one_insn() from barfing. */
- emit_insn (gen_prologue_use (ip_rtx));
}
}
@@ -10905,8 +11460,19 @@ arm_expand_prologue (void)
}
+ if (frame_pointer_needed && TARGET_THUMB2)
+ thumb_set_frame_pointer (offsets);
+
if (flag_pic && arm_pic_register != INVALID_REGNUM)
- arm_load_pic_register (0UL);
+ {
+ unsigned long mask;
+
+ mask = live_regs_mask;
+ mask &= THUMB2_WORK_REGS;
+ if (!IS_NESTED (func_type))
+ mask |= (1 << IP_REGNUM);
+ arm_load_pic_register (mask);
+ }
/* If we are profiling, make sure no instructions are scheduled before
the call to mcount. Similarly if the user has requested no
@@ -10926,6 +11492,43 @@ arm_expand_prologue (void)
}
}
+/* Print condition code to STREAM. Helper function for arm_print_operand. */
+static void
+arm_print_condition (FILE *stream)
+{
+ if (arm_ccfsm_state == 3 || arm_ccfsm_state == 4)
+ {
+ /* Branch conversion is not implemented for Thumb-2. */
+ if (TARGET_THUMB)
+ {
+ output_operand_lossage ("predicated Thumb instruction");
+ return;
+ }
+ if (current_insn_predicate != NULL)
+ {
+ output_operand_lossage
+ ("predicated instruction in conditional sequence");
+ return;
+ }
+
+ fputs (arm_condition_codes[arm_current_cc], stream);
+ }
+ else if (current_insn_predicate)
+ {
+ enum arm_cond_code code;
+
+ if (TARGET_THUMB1)
+ {
+ output_operand_lossage ("predicated Thumb instruction");
+ return;
+ }
+
+ code = get_arm_condition_code (current_insn_predicate);
+ fputs (arm_condition_codes[code], stream);
+ }
+}
+
+
/* If CODE is 'd', then the X is a condition operand and the instruction
should only be executed if the condition is true.
if CODE is 'D', then the X is a condition operand and the instruction
@@ -10957,37 +11560,46 @@ arm_print_operand (FILE *stream, rtx x, int code)
return;
case '?':
- if (arm_ccfsm_state == 3 || arm_ccfsm_state == 4)
- {
- if (TARGET_THUMB)
- {
- output_operand_lossage ("predicated Thumb instruction");
- break;
- }
- if (current_insn_predicate != NULL)
- {
- output_operand_lossage
- ("predicated instruction in conditional sequence");
- break;
- }
+ arm_print_condition (stream);
+ return;
+
+ case '(':
+ /* Nothing in unified syntax, otherwise the current condition code. */
+ if (!TARGET_UNIFIED_ASM)
+ arm_print_condition (stream);
+ break;
- fputs (arm_condition_codes[arm_current_cc], stream);
+ case ')':
+ /* The current condition code in unified syntax, otherwise nothing. */
+ if (TARGET_UNIFIED_ASM)
+ arm_print_condition (stream);
+ break;
+
+ case '.':
+ /* The current condition code for a condition code setting instruction.
+ Preceeded by 's' in unified syntax, otherwise followed by 's'. */
+ if (TARGET_UNIFIED_ASM)
+ {
+ fputc('s', stream);
+ arm_print_condition (stream);
}
- else if (current_insn_predicate)
+ else
{
- enum arm_cond_code code;
-
- if (TARGET_THUMB)
- {
- output_operand_lossage ("predicated Thumb instruction");
- break;
- }
-
- code = get_arm_condition_code (current_insn_predicate);
- fputs (arm_condition_codes[code], stream);
+ arm_print_condition (stream);
+ fputc('s', stream);
}
return;
+ case '!':
+ /* If the instruction is conditionally executed then print
+ the current condition code, otherwise print 's'. */
+ gcc_assert (TARGET_THUMB2 && TARGET_UNIFIED_ASM);
+ if (current_insn_predicate)
+ arm_print_condition (stream);
+ else
+ fputc('s', stream);
+ break;
+
case 'N':
{
REAL_VALUE_TYPE r;
@@ -11011,6 +11623,11 @@ arm_print_operand (FILE *stream, rtx x, int code)
}
return;
+ case 'L':
+ /* The low 16 bits of an immediate constant. */
+ fprintf (stream, HOST_WIDE_INT_PRINT_DEC, INTVAL(x) & 0xffff);
+ return;
+
case 'i':
fprintf (stream, "%s", arithmetic_instr (x, 1));
return;
@@ -11419,6 +12036,13 @@ arm_elf_asm_constructor (rtx symbol, int priority)
time. But then, I want to reduce the code size to somewhere near what
/bin/cc produces. */
+/* In addition to this, state is maintained for Thumb-2 COND_EXEC
+ instructions. When a COND_EXEC instruction is seen the subsequent
+ instructions are scanned so that multiple conditional instructions can be
+ combined into a single IT block. arm_condexec_count and arm_condexec_mask
+ specify the length and true/false mask for the IT block. These will be
+ decremented/zeroed by arm_asm_output_opcode as the insns are output. */
+
/* Returns the index of the ARM condition code string in
`arm_condition_codes'. COMPARISON should be an rtx like
`(eq (...) (...))'. */
@@ -11548,6 +12172,88 @@ get_arm_condition_code (rtx comparison)
}
}
+/* Tell arm_asm_ouput_opcode to output IT blocks for conditionally executed
+ instructions. */
+void
+thumb2_final_prescan_insn (rtx insn)
+{
+ rtx first_insn = insn;
+ rtx body = PATTERN (insn);
+ rtx predicate;
+ enum arm_cond_code code;
+ int n;
+ int mask;
+
+ /* Remove the previous insn from the count of insns to be output. */
+ if (arm_condexec_count)
+ arm_condexec_count--;
+
+ /* Nothing to do if we are already inside a conditional block. */
+ if (arm_condexec_count)
+ return;
+
+ if (GET_CODE (body) != COND_EXEC)
+ return;
+
+ /* Conditional jumps are implemented directly. */
+ if (GET_CODE (insn) == JUMP_INSN)
+ return;
+
+ predicate = COND_EXEC_TEST (body);
+ arm_current_cc = get_arm_condition_code (predicate);
+
+ n = get_attr_ce_count (insn);
+ arm_condexec_count = 1;
+ arm_condexec_mask = (1 << n) - 1;
+ arm_condexec_masklen = n;
+ /* See if subsequent instructions can be combined into the same block. */
+ for (;;)
+ {
+ insn = next_nonnote_insn (insn);
+
+ /* Jumping into the middle of an IT block is illegal, so a label or
+ barrier terminates the block. */
+ if (GET_CODE (insn) != INSN && GET_CODE(insn) != JUMP_INSN)
+ break;
+
+ body = PATTERN (insn);
+ /* USE and CLOBBER aren't really insns, so just skip them. */
+ if (GET_CODE (body) == USE
+ || GET_CODE (body) == CLOBBER)
+ {
+ arm_condexec_count++;
+ continue;
+ }
+
+ /* ??? Recognise conditional jumps, and combine them with IT blocks. */
+ if (GET_CODE (body) != COND_EXEC)
+ break;
+ /* Allow up to 4 conditionally executed instructions in a block. */
+ n = get_attr_ce_count (insn);
+ if (arm_condexec_masklen + n > 4)
+ break;
+
+ predicate = COND_EXEC_TEST (body);
+ code = get_arm_condition_code (predicate);
+ mask = (1 << n) - 1;
+ if (arm_current_cc == code)
+ arm_condexec_mask |= (mask << arm_condexec_masklen);
+ else if (arm_current_cc != ARM_INVERSE_CONDITION_CODE(code))
+ break;
+
+ arm_condexec_count++;
+ arm_condexec_masklen += n;
+
+ /* A jump must be the last instruction in a conditional block. */
+ if (GET_CODE(insn) == JUMP_INSN)
+ break;
+ }
+ /* Restore recog_data (getting the attributes of other insns can
+ destroy this array, but final.c assumes that it remains intact
+ across this call). */
+ extract_constrain_insn_cached (first_insn);
+}
+
void
arm_final_prescan_insn (rtx insn)
{
@@ -11852,7 +12558,7 @@ arm_final_prescan_insn (rtx insn)
if (!this_insn)
{
/* Oh, dear! we ran off the end.. give up. */
- recog (PATTERN (insn), insn, NULL);
+ extract_constrain_insn_cached (insn);
arm_ccfsm_state = 0;
arm_target_insn = NULL;
return;
@@ -11885,9 +12591,26 @@ arm_final_prescan_insn (rtx insn)
/* Restore recog_data (getting the attributes of other insns can
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);
+ across this call. */
+ extract_constrain_insn_cached (insn);
+ }
+}
+
+/* Output IT instructions. */
+void
+thumb2_asm_output_opcode (FILE * stream)
+{
+ char buff[5];
+ int n;
+
+ if (arm_condexec_mask)
+ {
+ for (n = 0; n < arm_condexec_masklen; n++)
+ buff[n] = (arm_condexec_mask & (1 << n)) ? 't' : 'e';
+ buff[n] = 0;
+ asm_fprintf(stream, "i%s\t%s\n\t", buff,
+ arm_condition_codes[arm_current_cc]);
+ arm_condexec_mask = 0;
}
}
@@ -11901,7 +12624,7 @@ arm_hard_regno_mode_ok (unsigned int regno, enum machine_mode mode)
|| (TARGET_HARD_FLOAT && TARGET_VFP
&& regno == VFPCC_REGNUM));
- if (TARGET_THUMB)
+ if (TARGET_THUMB1)
/* For the Thumb we only allow values bigger than SImode in
registers 0 - 6, so that there is always a second low
register available to hold the upper part of the value.
@@ -11958,10 +12681,12 @@ arm_hard_regno_mode_ok (unsigned int regno, enum machine_mode mode)
&& regno <= LAST_FPA_REGNUM);
}
+/* For efficiency and historical reasons LO_REGS, HI_REGS and CC_REGS are
+ not used in arm mode. */
int
arm_regno_class (int regno)
{
- if (TARGET_THUMB)
+ if (TARGET_THUMB1)
{
if (regno == STACK_POINTER_REGNUM)
return STACK_REG;
@@ -11972,13 +12697,16 @@ arm_regno_class (int regno)
return HI_REGS;
}
+ if (TARGET_THUMB2 && regno < 8)
+ return LO_REGS;
+
if ( regno <= LAST_ARM_REGNUM
|| regno == FRAME_POINTER_REGNUM
|| regno == ARG_POINTER_REGNUM)
- return GENERAL_REGS;
+ return TARGET_THUMB2 ? HI_REGS : GENERAL_REGS;
if (regno == CC_REGNUM || regno == VFPCC_REGNUM)
- return NO_REGS;
+ return TARGET_THUMB2 ? CC_REG : NO_REGS;
if (IS_CIRRUS_REGNUM (regno))
return CIRRUS_REGS;
@@ -12017,6 +12745,7 @@ arm_debugger_arg_offset (int value, rtx addr)
/* If we are using the stack pointer to point at the
argument, then an offset of 0 is correct. */
+ /* ??? Check this is consistent with thumb2 frame layout. */
if ((TARGET_THUMB || !frame_pointer_needed)
&& REGNO (addr) == SP_REGNUM)
return 0;
@@ -13293,7 +14022,7 @@ thumb_exit (FILE *f, int reg_containing_return_addr)
void
-thumb_final_prescan_insn (rtx insn)
+thumb1_final_prescan_insn (rtx insn)
{
if (flag_print_asm_name)
asm_fprintf (asm_out_file, "%@ 0x%04x\n",
@@ -13420,7 +14149,7 @@ thumb_unexpanded_epilogue (void)
if (IS_NAKED (arm_current_func_type ()))
return "";
- live_regs_mask = thumb_compute_save_reg_mask ();
+ live_regs_mask = thumb1_compute_save_reg_mask ();
high_regs_pushed = bit_count (live_regs_mask & 0x0f00);
/* If we can deduce the registers used from the function's return value.
@@ -13658,10 +14387,9 @@ thumb_compute_initial_elimination_offset (unsigned int from, unsigned int to)
}
}
-
/* Generate the rest of a function's prologue. */
void
-thumb_expand_prologue (void)
+thumb1_expand_prologue (void)
{
rtx insn, dwarf;
@@ -13683,7 +14411,7 @@ thumb_expand_prologue (void)
return;
}
- live_regs_mask = thumb_compute_save_reg_mask ();
+ live_regs_mask = thumb1_compute_save_reg_mask ();
/* Load the pic register before setting the frame pointer,
so we can use r7 as a temporary work register. */
if (flag_pic && arm_pic_register != INVALID_REGNUM)
@@ -13782,27 +14510,7 @@ thumb_expand_prologue (void)
}
if (frame_pointer_needed)
- {
- amount = offsets->outgoing_args - offsets->locals_base;
-
- if (amount < 1024)
- insn = emit_insn (gen_addsi3 (hard_frame_pointer_rtx,
- stack_pointer_rtx, GEN_INT (amount)));
- else
- {
- emit_insn (gen_movsi (hard_frame_pointer_rtx, GEN_INT (amount)));
- insn = emit_insn (gen_addsi3 (hard_frame_pointer_rtx,
- hard_frame_pointer_rtx,
- stack_pointer_rtx));
- dwarf = gen_rtx_SET (VOIDmode, hard_frame_pointer_rtx,
- plus_constant (stack_pointer_rtx, amount));
- RTX_FRAME_RELATED_P (dwarf) = 1;
- REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR, dwarf,
- REG_NOTES (insn));
- }
-
- RTX_FRAME_RELATED_P (insn) = 1;
- }
+ thumb_set_frame_pointer (offsets);
/* If we are profiling, make sure no instructions are scheduled before
the call to mcount. Similarly if the user has requested no
@@ -13825,7 +14533,7 @@ thumb_expand_prologue (void)
void
-thumb_expand_epilogue (void)
+thumb1_expand_epilogue (void)
{
HOST_WIDE_INT amount;
arm_stack_offsets *offsets;
@@ -13877,7 +14585,7 @@ thumb_expand_epilogue (void)
}
static void
-thumb_output_function_prologue (FILE *f, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
+thumb1_output_function_prologue (FILE *f, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
{
unsigned long live_regs_mask = 0;
unsigned long l_mask;
@@ -13963,7 +14671,7 @@ thumb_output_function_prologue (FILE *f, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
}
/* Get the registers we are going to push. */
- live_regs_mask = thumb_compute_save_reg_mask ();
+ live_regs_mask = thumb1_compute_save_reg_mask ();
/* Extract a mask of the ones we can give to the Thumb's push instruction. */
l_mask = live_regs_mask & 0x40ff;
/* Then count how many other high registers will need to be pushed. */
@@ -14428,6 +15136,9 @@ arm_file_start (void)
{
int val;
+ if (TARGET_UNIFIED_ASM)
+ asm_fprintf (asm_out_file, "\t.syntax unified\n");
+
if (TARGET_BPABI)
{
const char *fpu_name;
@@ -14846,7 +15557,8 @@ arm_output_mi_thunk (FILE *file, tree thunk ATTRIBUTE_UNUSED,
? 1 : 0);
if (mi_delta < 0)
mi_delta = - mi_delta;
- if (TARGET_THUMB)
+ /* When generating 16-bit thumb code, thunks are entered in arm mode. */
+ if (TARGET_THUMB1)
{
int labelno = thunk_label++;
ASM_GENERATE_INTERNAL_LABEL (label, "LTHUMBFUNC", labelno);
@@ -14871,6 +15583,7 @@ arm_output_mi_thunk (FILE *file, tree thunk ATTRIBUTE_UNUSED,
fputs ("\tadd\tr12, pc, r12\n", file);
}
}
+ /* TODO: Use movw/movt for large constants when available. */
while (mi_delta != 0)
{
if ((mi_delta & (3 << shift)) == 0)
@@ -14884,7 +15597,7 @@ arm_output_mi_thunk (FILE *file, tree thunk ATTRIBUTE_UNUSED,
shift += 8;
}
}
- if (TARGET_THUMB)
+ if (TARGET_THUMB1)
{
fprintf (file, "\tbx\tr12\n");
ASM_OUTPUT_ALIGN (file, 2);
@@ -15273,22 +15986,26 @@ thumb_set_return_address (rtx source, rtx scratch)
{
arm_stack_offsets *offsets;
HOST_WIDE_INT delta;
+ HOST_WIDE_INT limit;
int reg;
rtx addr;
unsigned long mask;
emit_insn (gen_rtx_USE (VOIDmode, source));
- mask = thumb_compute_save_reg_mask ();
+ mask = thumb1_compute_save_reg_mask ();
if (mask & (1 << LR_REGNUM))
{
offsets = arm_get_frame_offsets ();
+ limit = 1024;
/* Find the saved regs. */
if (frame_pointer_needed)
{
delta = offsets->soft_frame - offsets->saved_args;
reg = THUMB_HARD_FRAME_POINTER_REGNUM;
+ if (TARGET_THUMB1)
+ limit = 128;
}
else
{
@@ -15296,15 +16013,14 @@ thumb_set_return_address (rtx source, rtx scratch)
reg = SP_REGNUM;
}
/* Allow for the stack frame. */
- if (TARGET_BACKTRACE)
+ if (TARGET_THUMB1 && TARGET_BACKTRACE)
delta -= 16;
/* The link register is always the first saved register. */
delta -= 4;
/* Construct the address. */
addr = gen_rtx_REG (SImode, reg);
- if ((reg != SP_REGNUM && delta >= 128)
- || delta >= 1024)
+ if (delta > limit)
{
emit_insn (gen_movsi (scratch, GEN_INT (delta)));
emit_insn (gen_addsi3 (scratch, scratch, stack_pointer_rtx));
@@ -15370,12 +16086,13 @@ arm_dbx_register_number (unsigned int regno)
#ifdef TARGET_UNWIND_INFO
-/* Emit unwind directives for a store-multiple instruction. This should
- only ever be generated by the function prologue code, so we expect it
- to have a particular form. */
+/* Emit unwind directives for a store-multiple instruction or stack pointer
+ push during alignment.
+ These should only ever be generated by the function prologue code, so
+ expect them to have a particular form. */
static void
-arm_unwind_emit_stm (FILE * asm_out_file, rtx p)
+arm_unwind_emit_sequence (FILE * asm_out_file, rtx p)
{
int i;
HOST_WIDE_INT offset;
@@ -15385,8 +16102,11 @@ arm_unwind_emit_stm (FILE * asm_out_file, rtx p)
unsigned lastreg;
rtx e;
- /* First insn will adjust the stack pointer. */
e = XVECEXP (p, 0, 0);
+ if (GET_CODE (e) != SET)
+ abort ();
+
+ /* First insn will adjust the stack pointer. */
if (GET_CODE (e) != SET
|| GET_CODE (XEXP (e, 0)) != REG
|| REGNO (XEXP (e, 0)) != SP_REGNUM
@@ -15483,6 +16203,7 @@ arm_unwind_emit_set (FILE * asm_out_file, rtx p)
{
rtx e0;
rtx e1;
+ unsigned reg;
e0 = XEXP (p, 0);
e1 = XEXP (p, 1);
@@ -15519,7 +16240,6 @@ arm_unwind_emit_set (FILE * asm_out_file, rtx p)
else if (REGNO (e0) == HARD_FRAME_POINTER_REGNUM)
{
HOST_WIDE_INT offset;
- unsigned reg;
if (GET_CODE (e1) == PLUS)
{
@@ -15555,6 +16275,13 @@ arm_unwind_emit_set (FILE * asm_out_file, rtx p)
asm_fprintf (asm_out_file, "\t.movsp %r, #%d\n",
REGNO (e0), (int)INTVAL(XEXP (e1, 1)));
}
+ else if (GET_CODE (e1) == UNSPEC && XINT (e1, 1) == UNSPEC_STACK_ALIGN)
+ {
+ /* Stack pointer save before alignment. */
+ reg = REGNO (e0);
+ asm_fprintf (asm_out_file, "\t.unwind_raw 0, 0x%x @ vsp = r%d\n",
+ reg + 0x90, reg);
+ }
else
abort ();
break;
@@ -15592,7 +16319,7 @@ arm_unwind_emit (FILE * asm_out_file, rtx insn)
case SEQUENCE:
/* Store multiple. */
- arm_unwind_emit_stm (asm_out_file, pat);
+ arm_unwind_emit_sequence (asm_out_file, pat);
break;
default:
@@ -15620,6 +16347,30 @@ arm_output_ttype (rtx x)
#endif /* TARGET_UNWIND_INFO */
+/* Handle UNSPEC DWARF call frame instructions. These are needed for dynamic
+ stack alignment. */
+
+static void
+arm_dwarf_handle_frame_unspec (const char *label, rtx pattern, int index)
+{
+ rtx unspec = SET_SRC (pattern);
+ gcc_assert (GET_CODE (unspec) == UNSPEC);
+
+ switch (index)
+ {
+ case UNSPEC_STACK_ALIGN:
+ /* ??? We should set the CFA = (SP & ~7). At this point we haven't
+ put anything on the stack, so hopefully it won't matter.
+ CFA = SP will be correct after alignment. */
+ dwarf2out_reg_save_reg (label, stack_pointer_rtx,
+ SET_DEST (pattern));
+ break;
+ default:
+ gcc_unreachable ();
+ }
+}
+
+
/* Output unwind directives for the start/end of a function. */
void
@@ -15705,4 +16456,71 @@ arm_output_addr_const_extra (FILE *fp, rtx x)
return FALSE;
}
+/* Output assembly for a shift instruction.
+ SET_FLAGS determines how the instruction modifies the condition codes.
+ 0 - Do not set conditiona codes.
+ 1 - Set condition codes.
+ 2 - Use smallest instruction. */
+const char *
+arm_output_shift(rtx * operands, int set_flags)
+{
+ char pattern[100];
+ static const char flag_chars[3] = {'?', '.', '!'};
+ const char *shift;
+ HOST_WIDE_INT val;
+ char c;
+
+ c = flag_chars[set_flags];
+ if (TARGET_UNIFIED_ASM)
+ {
+ shift = shift_op(operands[3], &val);
+ if (shift)
+ {
+ if (val != -1)
+ operands[2] = GEN_INT(val);
+ sprintf (pattern, "%s%%%c\t%%0, %%1, %%2", shift, c);
+ }
+ else
+ sprintf (pattern, "mov%%%c\t%%0, %%1", c);
+ }
+ else
+ sprintf (pattern, "mov%%%c\t%%0, %%1%%S3", c);
+ output_asm_insn (pattern, operands);
+ return "";
+}
+
+/* Output a Thumb-2 casesi instruction. */
+const char *
+thumb2_output_casesi (rtx *operands)
+{
+ rtx diff_vec = PATTERN (next_real_insn (operands[2]));
+
+ gcc_assert (GET_CODE (diff_vec) == ADDR_DIFF_VEC);
+
+ output_asm_insn ("cmp\t%0, %1", operands);
+ output_asm_insn ("bhi\t%l3", operands);
+ switch (GET_MODE(diff_vec))
+ {
+ case QImode:
+ return "tbb\t[%|pc, %0]";
+ case HImode:
+ return "tbh\t[%|pc, %0, lsl #1]";
+ case SImode:
+ if (flag_pic)
+ {
+ output_asm_insn ("adr\t%4, %l2", operands);
+ output_asm_insn ("ldr\t%5, [%4, %0, lsl #2]", operands);
+ output_asm_insn ("add\t%4, %4, %5", operands);
+ return "bx\t%4";
+ }
+ else
+ {
+ output_asm_insn ("adr\t%4, %l2", operands);
+ return "ldr\t%|pc, [%4, %0, lsl #2]";
+ }
+ default:
+ gcc_unreachable ();
+ }
+}
+
#include "gt-arm.h"