#include "params.h"
#include "opts.h"
#include "dumpfile.h"
+#include "gimple-expr.h"
+#include "builtins.h"
/* Forward definitions of types. */
typedef struct minipool_node Mnode;
static reg_class_t arm_preferred_reload_class (rtx, reg_class_t);
static rtx thumb_legitimize_address (rtx, rtx, enum machine_mode);
inline static int thumb1_index_register_rtx_p (rtx, int);
-static bool arm_legitimate_address_p (enum machine_mode, rtx, bool);
static int thumb_far_jump_used_p (void);
static bool thumb_force_lr_save (void);
static unsigned arm_size_return_regs (void);
static bool arm_assemble_integer (rtx, unsigned int, int);
+static void arm_atomic_assign_expand_fenv (tree *hold, tree *clear, tree *update);
static void arm_print_operand (FILE *, rtx, int);
static void arm_print_operand_address (FILE *, rtx);
static bool arm_print_operand_punct_valid_p (unsigned char code);
static const char *shift_op (rtx, HOST_WIDE_INT *);
static struct machine_function *arm_init_machine_status (void);
static void thumb_exit (FILE *, int);
-static HOST_WIDE_INT get_jump_table_size (rtx);
+static HOST_WIDE_INT get_jump_table_size (rtx_jump_table_data *);
static Mnode *move_minipool_fix_forward_ref (Mnode *, Mnode *, HOST_WIDE_INT);
static Mnode *add_minipool_forward_ref (Mfix *);
static Mnode *move_minipool_fix_backward_ref (Mnode *, Mnode *, HOST_WIDE_INT);
static Mnode *add_minipool_backward_ref (Mfix *);
static void assign_minipool_offsets (Mfix *);
static void arm_print_value (FILE *, rtx);
-static void dump_minipool (rtx);
+static void dump_minipool (rtx_insn *);
static int arm_barrier_cost (rtx);
static Mfix *create_fix_barrier (Mfix *, HOST_WIDE_INT);
-static void push_minipool_barrier (rtx, HOST_WIDE_INT);
-static void push_minipool_fix (rtx, HOST_WIDE_INT, rtx *, enum machine_mode,
- rtx);
+static void push_minipool_barrier (rtx_insn *, HOST_WIDE_INT);
+static void push_minipool_fix (rtx_insn *, HOST_WIDE_INT, rtx *,
+ enum machine_mode, rtx);
static void arm_reorg (void);
-static void note_invalid_constants (rtx, HOST_WIDE_INT, int);
+static void note_invalid_constants (rtx_insn *, HOST_WIDE_INT, int);
static unsigned long arm_compute_save_reg0_reg12_mask (void);
static unsigned long arm_compute_save_reg_mask (void);
static unsigned long arm_isr_value (tree);
static void arm_output_function_prologue (FILE *, HOST_WIDE_INT);
static int arm_comp_type_attributes (const_tree, const_tree);
static void arm_set_default_type_attributes (tree);
-static int arm_adjust_cost (rtx, rtx, rtx, int);
-static int arm_sched_reorder (FILE *, int, rtx *, int *, int);
+static int arm_adjust_cost (rtx_insn *, rtx, rtx_insn *, int);
+static int arm_sched_reorder (FILE *, int, rtx_insn **, int *, int);
static int optimal_immediate_sequence (enum rtx_code code,
unsigned HOST_WIDE_INT val,
struct four_ints *return_sequence);
static rtx arm_expand_builtin (tree, rtx, rtx, enum machine_mode, int);
static tree arm_builtin_decl (unsigned, bool);
static void emit_constant_insn (rtx cond, rtx pattern);
-static rtx emit_set_insn (rtx, rtx);
+static rtx_insn *emit_set_insn (rtx, rtx);
static rtx emit_multi_reg_push (unsigned long, unsigned long);
static int arm_arg_partial_bytes (cumulative_args_t, enum machine_mode,
tree, bool);
static bool arm_must_pass_in_stack (enum machine_mode, const_tree);
static bool arm_return_in_memory (const_tree, const_tree);
#if ARM_UNWIND_INFO
-static void arm_unwind_emit (FILE *, rtx);
+static void arm_unwind_emit (FILE *, rtx_insn *);
static bool arm_output_ttype (rtx);
static void arm_asm_emit_except_personality (rtx);
static void arm_asm_init_sections (void);
static tree arm_gimplify_va_arg_expr (tree, tree, gimple_seq *, gimple_seq *);
static void arm_option_override (void);
static unsigned HOST_WIDE_INT arm_shift_truncation_mask (enum machine_mode);
-static bool arm_cannot_copy_insn_p (rtx);
+static bool arm_cannot_copy_insn_p (rtx_insn *);
static int arm_issue_rate (void);
static void arm_output_dwarf_dtprel (FILE *, int, rtx) ATTRIBUTE_UNUSED;
static bool arm_output_addr_const_extra (FILE *, rtx);
#undef TARGET_MANGLE_TYPE
#define TARGET_MANGLE_TYPE arm_mangle_type
+#undef TARGET_ATOMIC_ASSIGN_EXPAND_FENV
+#define TARGET_ATOMIC_ASSIGN_EXPAND_FENV arm_atomic_assign_expand_fenv
+
#undef TARGET_BUILD_BUILTIN_VA_LIST
#define TARGET_BUILD_BUILTIN_VA_LIST arm_build_builtin_va_list
#undef TARGET_EXPAND_BUILTIN_VA_START
#undef TARGET_CONST_NOT_OK_FOR_DEBUG_P
#define TARGET_CONST_NOT_OK_FOR_DEBUG_P arm_const_not_ok_for_debug_p
+#undef TARGET_CALL_FUSAGE_CONTAINS_NON_CALLEE_CLOBBERS
+#define TARGET_CALL_FUSAGE_CONTAINS_NON_CALLEE_CLOBBERS true
+
struct gcc_target targetm = TARGET_INITIALIZER;
\f
/* Obstack for minipool constant handling. */
}
};
+const struct cpu_cost_table cortexa5_extra_costs =
+{
+ /* ALU */
+ {
+ 0, /* arith. */
+ 0, /* logical. */
+ COSTS_N_INSNS (1), /* shift. */
+ COSTS_N_INSNS (1), /* shift_reg. */
+ COSTS_N_INSNS (1), /* arith_shift. */
+ COSTS_N_INSNS (1), /* arith_shift_reg. */
+ COSTS_N_INSNS (1), /* log_shift. */
+ COSTS_N_INSNS (1), /* log_shift_reg. */
+ COSTS_N_INSNS (1), /* extend. */
+ COSTS_N_INSNS (1), /* extend_arith. */
+ COSTS_N_INSNS (1), /* bfi. */
+ COSTS_N_INSNS (1), /* bfx. */
+ COSTS_N_INSNS (1), /* clz. */
+ COSTS_N_INSNS (1), /* rev. */
+ 0, /* non_exec. */
+ true /* non_exec_costs_exec. */
+ },
+
+ {
+ /* MULT SImode */
+ {
+ 0, /* simple. */
+ COSTS_N_INSNS (1), /* flag_setting. */
+ COSTS_N_INSNS (1), /* extend. */
+ COSTS_N_INSNS (1), /* add. */
+ COSTS_N_INSNS (1), /* extend_add. */
+ COSTS_N_INSNS (7) /* idiv. */
+ },
+ /* MULT DImode */
+ {
+ 0, /* simple (N/A). */
+ 0, /* flag_setting (N/A). */
+ COSTS_N_INSNS (1), /* extend. */
+ 0, /* add. */
+ COSTS_N_INSNS (2), /* extend_add. */
+ 0 /* idiv (N/A). */
+ }
+ },
+ /* LD/ST */
+ {
+ COSTS_N_INSNS (1), /* load. */
+ COSTS_N_INSNS (1), /* load_sign_extend. */
+ COSTS_N_INSNS (6), /* ldrd. */
+ COSTS_N_INSNS (1), /* ldm_1st. */
+ 1, /* ldm_regs_per_insn_1st. */
+ 2, /* ldm_regs_per_insn_subsequent. */
+ COSTS_N_INSNS (2), /* loadf. */
+ COSTS_N_INSNS (4), /* loadd. */
+ COSTS_N_INSNS (1), /* load_unaligned. */
+ COSTS_N_INSNS (1), /* store. */
+ COSTS_N_INSNS (3), /* strd. */
+ COSTS_N_INSNS (1), /* stm_1st. */
+ 1, /* stm_regs_per_insn_1st. */
+ 2, /* stm_regs_per_insn_subsequent. */
+ COSTS_N_INSNS (2), /* storef. */
+ COSTS_N_INSNS (2), /* stored. */
+ COSTS_N_INSNS (1) /* store_unaligned. */
+ },
+ {
+ /* FP SFmode */
+ {
+ COSTS_N_INSNS (15), /* div. */
+ COSTS_N_INSNS (3), /* mult. */
+ COSTS_N_INSNS (7), /* mult_addsub. */
+ COSTS_N_INSNS (7), /* fma. */
+ COSTS_N_INSNS (3), /* addsub. */
+ COSTS_N_INSNS (3), /* fpconst. */
+ COSTS_N_INSNS (3), /* neg. */
+ COSTS_N_INSNS (3), /* compare. */
+ COSTS_N_INSNS (3), /* widen. */
+ COSTS_N_INSNS (3), /* narrow. */
+ COSTS_N_INSNS (3), /* toint. */
+ COSTS_N_INSNS (3), /* fromint. */
+ COSTS_N_INSNS (3) /* roundint. */
+ },
+ /* FP DFmode */
+ {
+ COSTS_N_INSNS (30), /* div. */
+ COSTS_N_INSNS (6), /* mult. */
+ COSTS_N_INSNS (10), /* mult_addsub. */
+ COSTS_N_INSNS (7), /* fma. */
+ COSTS_N_INSNS (3), /* addsub. */
+ COSTS_N_INSNS (3), /* fpconst. */
+ COSTS_N_INSNS (3), /* neg. */
+ COSTS_N_INSNS (3), /* compare. */
+ COSTS_N_INSNS (3), /* widen. */
+ COSTS_N_INSNS (3), /* narrow. */
+ COSTS_N_INSNS (3), /* toint. */
+ COSTS_N_INSNS (3), /* fromint. */
+ COSTS_N_INSNS (3) /* roundint. */
+ }
+ },
+ /* Vector */
+ {
+ COSTS_N_INSNS (1) /* alu. */
+ }
+};
const struct cpu_cost_table cortexa7_extra_costs =
const struct tune_params arm_cortex_a5_tune =
{
arm_9e_rtx_costs,
- NULL,
+ &cortexa5_extra_costs,
NULL, /* Sched adj cost. */
1, /* Constant limit. */
1, /* Max cond insns. */
/* Emit an insn that's a simple single-set. Both the operands must be known
to be valid. */
-inline static rtx
+inline static rtx_insn *
emit_set_insn (rtx x, rtx y)
{
return emit_insn (gen_rtx_SET (VOIDmode, x, y));
if (TARGET_APCS_FLOAT)
warning (0, "passing floating point arguments in fp regs not yet supported");
- if (TARGET_LITTLE_WORDS)
- warning (OPT_Wdeprecated, "%<mwords-little-endian%> is deprecated and "
- "will be removed in a future release");
-
/* Initialize boolean versions of the flags, for use in the arm.md file. */
arm_arch3m = (insn_flags & FL_ARCH3M) != 0;
arm_arch4 = (insn_flags & FL_ARCH4) != 0;
/* If optimizing for size, bump the number of instructions that we
are prepared to conditionally execute (even on a StrongARM). */
max_insns_skipped = 6;
+
+ /* For THUMB2, we limit the conditional sequence to one IT block. */
+ if (TARGET_THUMB2)
+ max_insns_skipped = MAX_INSN_PER_IT_BLOCK;
}
else
max_insns_skipped = current_tune->max_insns_skipped;
return hash_rtx (p1, VOIDmode, NULL, NULL, FALSE);
}
-typedef hash_table <libcall_hasher> libcall_table_type;
+typedef hash_table<libcall_hasher> libcall_table_type;
static void
-add_libcall (libcall_table_type htab, rtx libcall)
+add_libcall (libcall_table_type *htab, rtx libcall)
{
- *htab.find_slot (libcall, INSERT) = libcall;
+ *htab->find_slot (libcall, INSERT) = libcall;
}
static bool
arm_libcall_uses_aapcs_base (const_rtx libcall)
{
static bool init_done = false;
- static libcall_table_type libcall_htab;
+ static libcall_table_type *libcall_htab = NULL;
if (!init_done)
{
init_done = true;
- libcall_htab.create (31);
+ libcall_htab = new libcall_table_type (31);
add_libcall (libcall_htab,
convert_optab_libfunc (sfloat_optab, SFmode, SImode));
add_libcall (libcall_htab,
DFmode));
}
- return libcall && libcall_htab.find (libcall) != NULL;
+ return libcall && libcall_htab->find (libcall) != NULL;
}
static rtx
so we are free to use whatever conventions are
appropriate. */
/* FIXME: remove CONST_CAST_TREE when cgraph is constified. */
- struct cgraph_local_info *i = cgraph_local_info (CONST_CAST_TREE(decl));
+ cgraph_local_info *i = cgraph_node::local_info (CONST_CAST_TREE(decl));
if (i && i->local)
return ARM_PCS_AAPCS_LOCAL;
}
}
else
{
- rtx seq, insn;
+ rtx_insn *seq, *insn;
if (!cfun->machine->pic_reg)
cfun->machine->pic_reg = gen_reg_rtx (Pmode);
that overlaps with SHIFTED_OPERAND, then we have increase the
cost of this dependency. */
extract_insn (dep);
- preprocess_constraints ();
+ preprocess_constraints (dep);
for (opno = 0; opno < recog_data.n_operands; opno++)
{
/* We can ignore strict inputs. */
switch (get_attr_type (insn))
{
- case TYPE_ALU_REG:
- case TYPE_ALUS_REG:
+ case TYPE_ALU_DSP_REG:
+ case TYPE_ALU_SREG:
+ case TYPE_ALUS_SREG:
case TYPE_LOGIC_REG:
case TYPE_LOGICS_REG:
case TYPE_ADC_REG:
instructions. This heuristic may affect dual issue opportunities
in the current cycle. */
static void
-cortexa7_sched_reorder (FILE *file, int verbose, rtx *ready, int *n_readyp,
- int clock)
+cortexa7_sched_reorder (FILE *file, int verbose, rtx_insn **ready,
+ int *n_readyp, int clock)
{
int i;
int first_older_only = -1, first_younger = -1;
older. */
for (i = *n_readyp - 1; i >= 0; i--)
{
- rtx insn = ready[i];
+ rtx_insn *insn = ready[i];
if (cortexa7_older_only (insn))
{
first_older_only = i;
fprintf (file, ";; cortexa7_sched_reorder insn %d before %d\n",
INSN_UID(ready [first_older_only]),
INSN_UID(ready [first_younger]));
- rtx first_older_only_insn = ready [first_older_only];
+ rtx_insn *first_older_only_insn = ready [first_older_only];
for (i = first_older_only; i < first_younger; i++)
{
ready[i] = ready[i+1];
/* Implement TARGET_SCHED_REORDER. */
static int
-arm_sched_reorder (FILE *file, int verbose, rtx *ready, int *n_readyp,
+arm_sched_reorder (FILE *file, int verbose, rtx_insn **ready, int *n_readyp,
int clock)
{
switch (arm_tune)
adjust_cost function. Only put bits of code into arm_adjust_cost that
are common across all cores. */
static int
-arm_adjust_cost (rtx insn, rtx link, rtx dep, int cost)
+arm_adjust_cost (rtx_insn *insn, rtx link, rtx_insn *dep, int cost)
{
rtx i_pat, d_pat;
|| (type == 0 && GET_CODE (ind) == PRE_DEC))
return arm_address_register_rtx_p (XEXP (ind, 0), 0);
- /* FIXME: vld1 allows register post-modify. */
+ /* Allow post-increment by register for VLDn */
+ if (type == 2 && GET_CODE (ind) == POST_MODIFY
+ && GET_CODE (XEXP (ind, 1)) == PLUS
+ && REG_P (XEXP (XEXP (ind, 1), 1)))
+ return true;
/* Match:
(plus (reg)
}
static bool
-arm_cannot_copy_insn_p (rtx insn)
+arm_cannot_copy_insn_p (rtx_insn *insn)
{
/* The tls call insn cannot be copied, as it is paired with a data
word. */
struct minipool_fixup
{
Mfix * next;
- rtx insn;
+ rtx_insn * insn;
HOST_WIDE_INT address;
rtx * loc;
enum machine_mode mode;
#endif
static HOST_WIDE_INT
-get_jump_table_size (rtx insn)
+get_jump_table_size (rtx_jump_table_data *insn)
{
/* ADDR_VECs only take room if read-only data does into the text
section. */
/* Output the literal table */
static void
-dump_minipool (rtx scan)
+dump_minipool (rtx_insn *scan)
{
Mnode * mp;
Mnode * nmp;
create_fix_barrier (Mfix *fix, HOST_WIDE_INT max_address)
{
HOST_WIDE_INT count = 0;
- rtx barrier;
- rtx from = fix->insn;
+ rtx_barrier *barrier;
+ rtx_insn *from = fix->insn;
/* The instruction after which we will insert the jump. */
- rtx selected = NULL;
+ rtx_insn *selected = NULL;
int selected_cost;
/* The address at which the jump instruction will be placed. */
HOST_WIDE_INT selected_address;
Mfix * new_fix;
HOST_WIDE_INT max_count = max_address - fix->address;
- rtx label = gen_label_rtx ();
+ rtx_code_label *label = gen_label_rtx ();
selected_cost = arm_barrier_cost (from);
selected_address = fix->address;
while (from && count < max_count)
{
- rtx tmp;
+ rtx_jump_table_data *tmp;
int new_cost;
/* This code shouldn't have been called if there was a natural barrier
CALL_ARG_LOCATION note. */
if (CALL_P (selected))
{
- rtx next = NEXT_INSN (selected);
+ rtx_insn *next = NEXT_INSN (selected);
if (next && NOTE_P (next)
&& NOTE_KIND (next) == NOTE_INSN_CALL_ARG_LOCATION)
selected = next;
/* Record that there is a natural barrier in the insn stream at
ADDRESS. */
static void
-push_minipool_barrier (rtx insn, HOST_WIDE_INT address)
+push_minipool_barrier (rtx_insn *insn, HOST_WIDE_INT address)
{
Mfix * fix = (Mfix *) obstack_alloc (&minipool_obstack, sizeof (* fix));
fixing; VALUE is the constant that must be loaded, which is of type
MODE. */
static void
-push_minipool_fix (rtx insn, HOST_WIDE_INT address, rtx *loc,
+push_minipool_fix (rtx_insn *insn, HOST_WIDE_INT address, rtx *loc,
enum machine_mode mode, rtx value)
{
Mfix * fix = (Mfix *) obstack_alloc (&minipool_obstack, sizeof (* fix));
If DO_PUSHES is false we do not actually push any of the fixups
needed. */
static void
-note_invalid_constants (rtx insn, HOST_WIDE_INT address, int do_pushes)
+note_invalid_constants (rtx_insn *insn, HOST_WIDE_INT address, int do_pushes)
{
int opno;
/* Fill in recog_op_alt with information about the constraints of
this insn. */
- preprocess_constraints ();
+ preprocess_constraints (insn);
+ const operand_alternative *op_alt = which_op_alt ();
for (opno = 0; opno < recog_data.n_operands; opno++)
{
/* Things we need to fix can only occur in inputs. */
of constants in this alternative is really to fool reload
into allowing us to accept one there. We need to fix them up
now so that we output the right code. */
- if (recog_op_alt[opno][which_alternative].memory_ok)
+ if (op_alt[opno].memory_ok)
{
rtx op = recog_data.operand[opno];
{
rtx dest, src;
rtx pat, op0, set = NULL;
- rtx prev, insn = BB_END (bb);
+ rtx_insn *prev, *insn = BB_END (bb);
bool insn_clobbered = false;
while (insn != BB_HEAD (bb) && !NONDEBUG_INSN_P (insn))
insn = PREV_INSN (insn);
/* Find the last cbranchsi4_insn in basic block BB. */
- if (INSN_CODE (insn) != CODE_FOR_cbranchsi4_insn)
+ if (insn == BB_HEAD (bb)
+ || INSN_CODE (insn) != CODE_FOR_cbranchsi4_insn)
continue;
/* Get the register with which we are comparing. */
&& optimize_bb_for_speed_p (bb))
continue;
- rtx insn;
+ rtx_insn *insn;
Convert_Action action = SKIP;
Convert_Action action_for_partial_flag_setting
= (current_tune->disparage_partial_flag_setting_t16_encodings
static void
arm_reorg (void)
{
- rtx insn;
+ rtx_insn *insn;
HOST_WIDE_INT address = 0;
Mfix * fix;
push_minipool_barrier (insn, address);
else if (INSN_P (insn))
{
- rtx table;
+ rtx_jump_table_data *table;
note_invalid_constants (insn, address, true);
address += get_attr_length (insn);
the call target. */
void
-arm_emit_call_insn (rtx pat, rtx addr)
+arm_emit_call_insn (rtx pat, rtx addr, bool sibcall)
{
rtx insn;
to the instruction's CALL_INSN_FUNCTION_USAGE. */
if (TARGET_VXWORKS_RTP
&& flag_pic
+ && !sibcall
&& GET_CODE (addr) == SYMBOL_REF
&& (SYMBOL_REF_DECL (addr)
? !targetm.binds_local_p (SYMBOL_REF_DECL (addr))
require_pic_register ();
use_reg (&CALL_INSN_FUNCTION_USAGE (insn), cfun->machine->pic_reg);
}
+
+ if (TARGET_AAPCS_BASED)
+ {
+ /* For AAPCS, IP and CC can be clobbered by veneers inserted by the
+ linker. We need to add an IP clobber to allow setting
+ TARGET_CALL_FUSAGE_CONTAINS_NON_CALLEE_CLOBBERS to true. A CC clobber
+ is not needed since it's a fixed register. */
+ rtx *fusage = &CALL_INSN_FUNCTION_USAGE (insn);
+ clobber_reg (fusage, gen_rtx_REG (word_mode, IP_REGNUM));
+ }
}
/* Output a 'call' insn. */
{
int reg = -1;
+ /* Register r3 is caller-saved. Normally it does not need to be
+ saved on entry by the prologue. However if we choose to save
+ it for padding then we may confuse the compiler into thinking
+ a prologue sequence is required when in fact it is not. This
+ will occur when shrink-wrapping if r3 is used as a scratch
+ register and there are no other callee-saved writes.
+
+ This situation can be avoided when other callee-saved registers
+ are available and r3 is not mandatory if we choose a callee-saved
+ register for padding. */
+ bool prefer_callee_reg_p = false;
+
/* If it is safe to use r3, then do so. This sometimes
generates better code on Thumb-2 by avoiding the need to
use 32-bit push/pop instructions. */
if (! any_sibcall_could_use_r3 ()
&& arm_size_return_regs () <= 12
&& (offsets->saved_regs_mask & (1 << 3)) == 0
- && (TARGET_THUMB2
+ && (TARGET_THUMB2
|| !(TARGET_LDRD && current_tune->prefer_ldrd_strd)))
{
reg = 3;
+ if (!TARGET_THUMB2)
+ prefer_callee_reg_p = true;
+ }
+ if (reg == -1
+ || prefer_callee_reg_p)
+ {
+ for (i = 4; i <= (TARGET_THUMB1 ? LAST_LO_REGNUM : 11); i++)
+ {
+ /* Avoid fixed registers; they may be changed at
+ arbitrary times so it's unsafe to restore them
+ during the epilogue. */
+ if (!fixed_regs[i]
+ && (offsets->saved_regs_mask & (1 << i)) == 0)
+ {
+ reg = i;
+ break;
+ }
+ }
}
- else
- for (i = 4; i <= (TARGET_THUMB1 ? LAST_LO_REGNUM : 11); i++)
- {
- /* Avoid fixed registers; they may be changed at
- arbitrary times so it's unsafe to restore them
- during the epilogue. */
- if (!fixed_regs[i]
- && (offsets->saved_regs_mask & (1 << i)) == 0)
- {
- reg = i;
- break;
- }
- }
if (reg != -1)
{
{
/* This add can produce multiple insns for a large constant, so we
need to get tricky. */
- rtx last = get_last_insn ();
+ rtx_insn *last = get_last_insn ();
amount = GEN_INT (offsets->saved_args + saved_regs
- offsets->outgoing_args);
{
rtx addr;
bool postinc = FALSE;
+ rtx postinc_reg = NULL;
unsigned align, memsize, align_bits;
gcc_assert (MEM_P (x));
postinc = 1;
addr = XEXP (addr, 0);
}
+ if (GET_CODE (addr) == POST_MODIFY)
+ {
+ postinc_reg = XEXP( XEXP (addr, 1), 1);
+ addr = XEXP (addr, 0);
+ }
asm_fprintf (stream, "[%r", REGNO (addr));
/* We know the alignment of this access, so we can emit a hint in the
if (postinc)
fputs("!", stream);
+ if (postinc_reg)
+ asm_fprintf (stream, ", %r", REGNO (postinc_reg));
}
return;
/* Tell arm_asm_output_opcode to output IT blocks for conditionally executed
instructions. */
void
-thumb2_final_prescan_insn (rtx insn)
+thumb2_final_prescan_insn (rtx_insn *insn)
{
- rtx first_insn = insn;
+ rtx_insn *first_insn = insn;
rtx body = PATTERN (insn);
rtx predicate;
enum arm_cond_code code;
}
void
-arm_final_prescan_insn (rtx insn)
+arm_final_prescan_insn (rtx_insn *insn)
{
/* BODY will hold the body of INSN. */
rtx body = PATTERN (insn);
/* START_INSN will hold the insn from where we start looking. This is the
first insn after the following code_label if REVERSE is true. */
- rtx start_insn = insn;
+ rtx_insn *start_insn = insn;
/* If in state 4, check if the target branch is reached, in order to
change back to state 0. */
int fail = FALSE, succeed = FALSE;
/* Flag which part of the IF_THEN_ELSE is the LABEL_REF. */
int then_not_else = TRUE;
- rtx this_insn = start_insn, label = 0;
+ rtx_insn *this_insn = start_insn;
+ rtx label = 0;
/* Register the insn jumped to. */
if (reverse)
|| (TARGET_HARD_FLOAT && TARGET_VFP
&& regno == VFPCC_REGNUM));
+ if (regno == CC_REGNUM && GET_MODE_CLASS (mode) != MODE_CC)
+ return false;
+
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
enum reg_class
arm_regno_class (int regno)
{
+ if (regno == PC_REGNUM)
+ return NO_REGS;
+
if (TARGET_THUMB1)
{
if (regno == STACK_POINTER_REGNUM)
int
arm_debugger_arg_offset (int value, rtx addr)
{
- rtx insn;
+ rtx_insn *insn;
/* We are only interested if dbxout_parms() failed to compute the offset. */
if (value != 0)
ARM_BUILTIN_CRC32CH,
ARM_BUILTIN_CRC32CW,
+ ARM_BUILTIN_GET_FPSCR,
+ ARM_BUILTIN_SET_FPSCR,
+
#undef CRYPTO1
#undef CRYPTO2
#undef CRYPTO3
IWMMXT_BUILTIN2 (iwmmxt_wmacuz, WMACUZ)
IWMMXT_BUILTIN2 (iwmmxt_wmacsz, WMACSZ)
+
+#define FP_BUILTIN(L, U) \
+ {0, CODE_FOR_##L, "__builtin_arm_"#L, ARM_BUILTIN_##U, \
+ UNKNOWN, 0},
+
+ FP_BUILTIN (get_fpscr, GET_FPSCR)
+ FP_BUILTIN (set_fpscr, SET_FPSCR)
+#undef FP_BUILTIN
+
#define CRC32_BUILTIN(L, U) \
{0, CODE_FOR_##L, "__builtin_arm_"#L, ARM_BUILTIN_##U, \
UNKNOWN, 0},
if (TARGET_CRC32)
arm_init_crc32_builtins ();
+
+ if (TARGET_VFP && TARGET_HARD_FLOAT)
+ {
+ tree ftype_set_fpscr
+ = build_function_type_list (void_type_node, unsigned_type_node, NULL);
+ tree ftype_get_fpscr
+ = build_function_type_list (unsigned_type_node, NULL);
+
+ arm_builtin_decls[ARM_BUILTIN_GET_FPSCR]
+ = add_builtin_function ("__builtin_arm_ldfscr", ftype_get_fpscr,
+ ARM_BUILTIN_GET_FPSCR, BUILT_IN_MD, NULL, NULL_TREE);
+ arm_builtin_decls[ARM_BUILTIN_SET_FPSCR]
+ = add_builtin_function ("__builtin_arm_stfscr", ftype_set_fpscr,
+ ARM_BUILTIN_SET_FPSCR, BUILT_IN_MD, NULL, NULL_TREE);
+ }
}
/* Return the ARM builtin for CODE. */
switch (fcode)
{
+ case ARM_BUILTIN_GET_FPSCR:
+ case ARM_BUILTIN_SET_FPSCR:
+ if (fcode == ARM_BUILTIN_GET_FPSCR)
+ {
+ icode = CODE_FOR_get_fpscr;
+ target = gen_reg_rtx (SImode);
+ pat = GEN_FCN (icode) (target);
+ }
+ else
+ {
+ target = NULL_RTX;
+ icode = CODE_FOR_set_fpscr;
+ arg0 = CALL_EXPR_ARG (exp, 0);
+ op0 = expand_normal (arg0);
+ pat = GEN_FCN (icode) (op0);
+ }
+ emit_insn (pat);
+ return target;
+
case ARM_BUILTIN_TEXTRMSB:
case ARM_BUILTIN_TEXTRMUB:
case ARM_BUILTIN_TEXTRMSH:
to be saved; REAL_REGS is the set of registers to be described as
saved. If REAL_REGS is 0, only describe the stack adjustment. */
-static rtx
+static rtx_insn *
thumb1_emit_multi_reg_push (unsigned long mask, unsigned long real_regs)
{
unsigned long regno;
- rtx par[10], tmp, reg, insn;
+ rtx par[10], tmp, reg;
+ rtx_insn *insn;
int i, j;
/* Build the parallel of the registers actually being stored. */
For Thumb-1, we track the status of the condition codes; this
information is used in the cbranchsi4_insn pattern. */
void
-thumb1_final_prescan_insn (rtx insn)
+thumb1_final_prescan_insn (rtx_insn *insn)
{
if (flag_print_asm_name)
asm_fprintf (asm_out_file, "%@ 0x%04x\n",
static int
thumb_far_jump_used_p (void)
{
- rtx insn;
+ rtx_insn *insn;
bool far_jump = false;
unsigned int func_size = 0;
void
thumb1_expand_prologue (void)
{
- rtx insn;
+ rtx_insn *insn;
HOST_WIDE_INT amount;
arm_stack_offsets *offsets;
int saved_size = arm_get_vfp_saved_size ();
if (saved_size > 0)
{
- rtx insn;
+ rtx_insn *insn;
floats_from_frame += saved_size;
insn = emit_insn (gen_addsi3 (ip_rtx,
hard_frame_pointer_rtx,
{
/* The frame pointer is guaranteed to be non-double-word aligned, as
it is set to double-word-aligned old_stack_pointer - 4. */
- rtx insn;
+ rtx_insn *insn;
int lrm_count = (num_regs % 2) ? (num_regs + 2) : (num_regs + 1);
for (i = LAST_IWMMXT_REGNUM; i >= FIRST_IWMMXT_REGNUM; i--)
num_regs = bit_count (saved_regs_mask);
if ((offsets->outgoing_args != (1 + num_regs)) || cfun->calls_alloca)
{
- rtx insn;
+ rtx_insn *insn;
emit_insn (gen_blockage ());
/* Unwind the stack to just below the saved registers. */
insn = emit_insn (gen_addsi3 (stack_pointer_rtx,
{
/* Interrupt handlers will have pushed the
IP onto the stack, so restore it now. */
- rtx insn;
+ rtx_insn *insn;
rtx addr = gen_rtx_MEM (SImode,
gen_rtx_POST_INC (SImode,
stack_pointer_rtx));
if (frame_pointer_needed)
{
- rtx insn;
+ rtx_insn *insn;
/* Restore stack pointer if necessary. */
if (TARGET_ARM)
{
amount = offsets->outgoing_args - offsets->saved_regs;
if (amount)
{
- rtx tmp;
+ rtx_insn *tmp;
/* Force out any pending memory operations that reference stacked data
before stack de-allocation occurs. */
emit_insn (gen_blockage ());
for (i = FIRST_IWMMXT_REGNUM; i <= LAST_IWMMXT_REGNUM; i++)
if (df_regs_ever_live_p (i) && !call_used_regs[i])
{
- rtx insn;
+ rtx_insn *insn;
rtx addr = gen_rtx_MEM (V2SImode,
gen_rtx_POST_INC (SImode,
stack_pointer_rtx));
{
int i, j;
rtx dwarf = NULL_RTX;
- rtx tmp = emit_insn (gen_addsi3 (stack_pointer_rtx,
- stack_pointer_rtx,
- GEN_INT (crtl->args.pretend_args_size)));
+ rtx_insn *tmp =
+ emit_insn (gen_addsi3 (stack_pointer_rtx,
+ stack_pointer_rtx,
+ GEN_INT (crtl->args.pretend_args_size)));
RTX_FRAME_RELATED_P (tmp) = 1;
fputs (":\n", file);
if (flag_pic)
{
- /* Output ".word .LTHUNKn-7-.LTHUNKPCn". */
+ /* Output ".word .LTHUNKn-[3,7]-.LTHUNKPCn". */
rtx tem = XEXP (DECL_RTL (function), 0);
- tem = plus_constant (GET_MODE (tem), tem, -7);
+ /* For TARGET_THUMB1_ONLY the thunk is in Thumb mode, so the PC
+ pipeline offset is four rather than eight. Adjust the offset
+ accordingly. */
+ tem = plus_constant (GET_MODE (tem), tem,
+ TARGET_THUMB1_ONLY ? -3 : -7);
tem = gen_rtx_MINUS (GET_MODE (tem),
tem,
gen_rtx_SYMBOL_REF (Pmode,
/* Emit unwind directives for the given insn. */
static void
-arm_unwind_emit (FILE * asm_out_file, rtx insn)
+arm_unwind_emit (FILE * asm_out_file, rtx_insn *insn)
{
rtx note, pat;
bool handled_one = false;
const char *
thumb1_output_casesi (rtx *operands)
{
- rtx diff_vec = PATTERN (NEXT_INSN (operands[0]));
+ rtx diff_vec = PATTERN (NEXT_INSN (as_a <rtx_insn *> (operands[0])));
gcc_assert (GET_CODE (diff_vec) == ADDR_DIFF_VEC);
const char *
thumb2_output_casesi (rtx *operands)
{
- rtx diff_vec = PATTERN (NEXT_INSN (operands[2]));
+ rtx diff_vec = PATTERN (NEXT_INSN (as_a <rtx_insn *> (operands[2])));
gcc_assert (GET_CODE (diff_vec) == ADDR_DIFF_VEC);
enum machine_mode mode;
enum memmodel mod_s, mod_f;
bool is_weak;
- rtx label1, label2, x, cond;
+ rtx_code_label *label1, *label2;
+ rtx x, cond;
rval = operands[0];
mem = operands[1];
if (!(use_acquire || use_release))
arm_pre_atomic_barrier (mod_s);
- label1 = NULL_RTX;
+ label1 = NULL;
if (!is_weak)
{
label1 = gen_label_rtx ();
enum memmodel model = (enum memmodel) INTVAL (model_rtx);
enum machine_mode mode = GET_MODE (mem);
enum machine_mode wmode = (mode == DImode ? DImode : SImode);
- rtx label, x;
+ rtx_code_label *label;
+ rtx x;
bool use_acquire = TARGET_HAVE_LDACQ
&& !(model == MEMMODEL_RELAXED
ORR (SHIFT (ASHIFT, in_up, scratch1), out_down)));
if (code == ASHIFTRT)
{
- rtx done_label = gen_label_rtx ();
+ rtx_code_label *done_label = gen_label_rtx ();
emit_jump_insn (BRANCH (LT, done_label));
emit_insn (SET (out_down, ORR (SHIFT (ASHIFTRT, in_up, scratch2),
out_down)));
if (code == ASHIFTRT)
{
- rtx done_label = gen_label_rtx ();
+ rtx_code_label *done_label = gen_label_rtx ();
emit_jump_insn (BRANCH (LT, done_label));
emit_insn (SET (scratch2, SHIFT (ASHIFTRT, in_up, scratch2)));
emit_insn (SET (out_down, ORR (out_down, scratch2)));
return false;
}
+static void
+arm_atomic_assign_expand_fenv (tree *hold, tree *clear, tree *update)
+{
+ const unsigned ARM_FE_INVALID = 1;
+ const unsigned ARM_FE_DIVBYZERO = 2;
+ const unsigned ARM_FE_OVERFLOW = 4;
+ const unsigned ARM_FE_UNDERFLOW = 8;
+ const unsigned ARM_FE_INEXACT = 16;
+ const unsigned HOST_WIDE_INT ARM_FE_ALL_EXCEPT = (ARM_FE_INVALID
+ | ARM_FE_DIVBYZERO
+ | ARM_FE_OVERFLOW
+ | ARM_FE_UNDERFLOW
+ | ARM_FE_INEXACT);
+ const unsigned HOST_WIDE_INT ARM_FE_EXCEPT_SHIFT = 8;
+ tree fenv_var, get_fpscr, set_fpscr, mask, ld_fenv, masked_fenv;
+ tree new_fenv_var, reload_fenv, restore_fnenv;
+ tree update_call, atomic_feraiseexcept, hold_fnclex;
+
+ if (!TARGET_VFP || !TARGET_HARD_FLOAT)
+ return;
+
+ /* Generate the equivalent of :
+ unsigned int fenv_var;
+ fenv_var = __builtin_arm_get_fpscr ();
+
+ unsigned int masked_fenv;
+ masked_fenv = fenv_var & mask;
+
+ __builtin_arm_set_fpscr (masked_fenv); */
+
+ fenv_var = create_tmp_var (unsigned_type_node, NULL);
+ get_fpscr = arm_builtin_decls[ARM_BUILTIN_GET_FPSCR];
+ set_fpscr = arm_builtin_decls[ARM_BUILTIN_SET_FPSCR];
+ mask = build_int_cst (unsigned_type_node,
+ ~((ARM_FE_ALL_EXCEPT << ARM_FE_EXCEPT_SHIFT)
+ | ARM_FE_ALL_EXCEPT));
+ ld_fenv = build2 (MODIFY_EXPR, unsigned_type_node,
+ fenv_var, build_call_expr (get_fpscr, 0));
+ masked_fenv = build2 (BIT_AND_EXPR, unsigned_type_node, fenv_var, mask);
+ hold_fnclex = build_call_expr (set_fpscr, 1, masked_fenv);
+ *hold = build2 (COMPOUND_EXPR, void_type_node,
+ build2 (COMPOUND_EXPR, void_type_node, masked_fenv, ld_fenv),
+ hold_fnclex);
+
+ /* Store the value of masked_fenv to clear the exceptions:
+ __builtin_arm_set_fpscr (masked_fenv); */
+
+ *clear = build_call_expr (set_fpscr, 1, masked_fenv);
+
+ /* Generate the equivalent of :
+ unsigned int new_fenv_var;
+ new_fenv_var = __builtin_arm_get_fpscr ();
+
+ __builtin_arm_set_fpscr (fenv_var);
+
+ __atomic_feraiseexcept (new_fenv_var); */
+
+ new_fenv_var = create_tmp_var (unsigned_type_node, NULL);
+ reload_fenv = build2 (MODIFY_EXPR, unsigned_type_node, new_fenv_var,
+ build_call_expr (get_fpscr, 0));
+ restore_fnenv = build_call_expr (set_fpscr, 1, fenv_var);
+ atomic_feraiseexcept = builtin_decl_implicit (BUILT_IN_ATOMIC_FERAISEEXCEPT);
+ update_call = build_call_expr (atomic_feraiseexcept, 1,
+ fold_convert (integer_type_node, new_fenv_var));
+ *update = build2 (COMPOUND_EXPR, void_type_node,
+ build2 (COMPOUND_EXPR, void_type_node,
+ reload_fenv, restore_fnenv), update_call);
+}
+
+/* return TRUE if x is a reference to a value in a constant pool */
+extern bool
+arm_is_constant_pool_ref (rtx x)
+{
+ return (MEM_P (x)
+ && GET_CODE (XEXP (x, 0)) == SYMBOL_REF
+ && CONSTANT_POOL_ADDRESS_P (XEXP (x, 0)));
+}
+
#include "gt-arm.h"