/* Output routines for GCC for Renesas / SuperH SH.
- Copyright (C) 1993-2016 Free Software Foundation, Inc.
+ Copyright (C) 1993-2021 Free Software Foundation, Inc.
Contributed by Steve Chamberlain (sac@cygnus.com).
Improved by Jim Wilson (wilson@cygnus.com).
#include <sstream>
+#define IN_TARGET_CODE 1
+
#include "config.h"
#define INCLUDE_VECTOR
#include "system.h"
#include "gimple.h"
#include "cfghooks.h"
#include "df.h"
+#include "memmodel.h"
#include "tm_p.h"
#include "stringpool.h"
+#include "attribs.h"
#include "optabs.h"
#include "emit-rtl.h"
#include "recog.h"
#include "context.h"
#include "builtins.h"
#include "rtl-iter.h"
+#include "regs.h"
+#include "toplev.h"
/* This file should be included last. */
#include "target-def.h"
? (DECL_ATTRIBUTES (decl)) \
: TYPE_ATTRIBUTES (TREE_TYPE (decl))
-/* Set to 1 by expand_prologue() when the function is an interrupt handler. */
-int current_function_interrupt;
+/* Set to true by expand_prologue() when the function is an
+ interrupt handler. */
+bool current_function_interrupt;
tree sh_deferred_function_attributes;
tree *sh_deferred_function_attributes_tail = &sh_deferred_function_attributes;
static void sh_option_override (void);
static void sh_override_options_after_change (void);
static void output_stack_adjust (int, rtx, int, HARD_REG_SET *, bool);
-static rtx_insn *frame_insn (rtx);
+static rtx_insn* emit_frame_insn (rtx);
static rtx push (int);
static void pop (int);
-static void push_regs (HARD_REG_SET *, int);
+static void push_regs (HARD_REG_SET* mask, bool interrupt_handler);
static int calc_live_regs (HARD_REG_SET *);
static HOST_WIDE_INT rounded_frame_size (int);
static bool sh_frame_pointer_required (void);
static void sh_print_operand_address (FILE *, machine_mode, rtx);
static bool sh_print_operand_punct_valid_p (unsigned char code);
static bool sh_asm_output_addr_const_extra (FILE *file, rtx x);
-static void sh_output_function_epilogue (FILE *, HOST_WIDE_INT);
+static void sh_output_function_epilogue (FILE *);
static void sh_insert_attributes (tree, tree *);
static const char *sh_check_pch_target_flags (int);
static int sh_register_move_cost (machine_mode, reg_class_t, reg_class_t);
-static int sh_adjust_cost (rtx_insn *, rtx, rtx_insn *, int);
+static int sh_adjust_cost (rtx_insn *, int, rtx_insn *, int, unsigned int);
static int sh_issue_rate (void);
static int sh_dfa_new_cycle (FILE *, int, rtx_insn *, int, int, int *sort_p);
static short find_set_regmode_weight (rtx, machine_mode);
static bool sh_function_ok_for_sibcall (tree, tree);
static bool sh_can_follow_jump (const rtx_insn *, const rtx_insn *);
-static reg_class_t sh_target_reg_class (void);
-static bool sh_optimize_target_register_callee_saved (bool);
static bool sh_ms_bitfield_layout_p (const_tree);
static void sh_init_builtins (void);
HOST_WIDE_INT, tree);
static void sh_file_start (void);
static bool sh_assemble_integer (rtx, unsigned int, int);
-static bool flow_dependent_p (rtx, rtx);
+static bool flow_dependent_p (rtx_insn *, rtx_insn *);
static void flow_dependent_p_1 (rtx, const_rtx, void *);
static int shiftcosts (rtx);
static int and_xor_ior_costs (rtx, int);
static rtx sh_legitimize_address (rtx, rtx, machine_mode);
static rtx sh_delegitimize_address (rtx);
static bool sh_cannot_substitute_mem_equiv_p (rtx);
-static bool sh_legitimize_address_displacement (rtx *, rtx *, machine_mode);
+static bool sh_legitimize_address_displacement (rtx *, rtx *,
+ poly_int64, machine_mode);
static int scavenge_reg (HARD_REG_SET *s);
-struct save_schedule_s;
static rtx sh_struct_value_rtx (tree, int);
static rtx sh_function_value (const_tree, const_tree, bool);
static rtx sh_libcall_value (machine_mode, const_rtx);
static bool sh_return_in_memory (const_tree, const_tree);
static rtx sh_builtin_saveregs (void);
-static void sh_setup_incoming_varargs (cumulative_args_t, machine_mode,
- tree, int *, int);
+static void sh_setup_incoming_varargs (cumulative_args_t,
+ const function_arg_info &, int *, int);
static bool sh_strict_argument_naming (cumulative_args_t);
static bool sh_pretend_outgoing_varargs_named (cumulative_args_t);
static void sh_atomic_assign_expand_fenv (tree *, tree *, tree *);
int *punsignedp,
const_tree funtype,
int for_return);
-static bool sh_pass_by_reference (cumulative_args_t, machine_mode,
- const_tree, bool);
-static bool sh_callee_copies (cumulative_args_t, machine_mode,
- const_tree, bool);
-static int sh_arg_partial_bytes (cumulative_args_t, machine_mode,
- tree, bool);
-static void sh_function_arg_advance (cumulative_args_t, machine_mode,
- const_tree, bool);
-static rtx sh_function_arg (cumulative_args_t, machine_mode,
- const_tree, bool);
+static bool sh_pass_by_reference (cumulative_args_t,
+ const function_arg_info &);
+static bool sh_callee_copies (cumulative_args_t, const function_arg_info &);
+static int sh_arg_partial_bytes (cumulative_args_t, const function_arg_info &);
+static void sh_function_arg_advance (cumulative_args_t,
+ const function_arg_info &);
+static rtx sh_function_arg (cumulative_args_t, const function_arg_info &);
static int sh_dwarf_calling_convention (const_tree);
static void sh_encode_section_info (tree, rtx, int);
static bool sh2a_function_vector_p (tree);
static bool sh_fixed_condition_code_regs (unsigned int* p1, unsigned int* p2);
static void sh_init_sync_libfuncs (void) ATTRIBUTE_UNUSED;
+static unsigned int sh_hard_regno_nregs (unsigned int, machine_mode);
+static bool sh_hard_regno_mode_ok (unsigned int, machine_mode);
+static bool sh_modes_tieable_p (machine_mode, machine_mode);
+static bool sh_can_change_mode_class (machine_mode, machine_mode, reg_class_t);
\f
static const struct attribute_spec sh_attribute_table[] =
{
- /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler,
- affects_type_identity } */
- { "interrupt_handler", 0, 0, true, false, false,
- sh_handle_interrupt_handler_attribute, false },
- { "sp_switch", 1, 1, true, false, false,
- sh_handle_sp_switch_attribute, false },
- { "trap_exit", 1, 1, true, false, false,
- sh_handle_trap_exit_attribute, false },
- { "renesas", 0, 0, false, true, false,
- sh_handle_renesas_attribute, false },
- { "trapa_handler", 0, 0, true, false, false,
- sh_handle_interrupt_handler_attribute, false },
- { "nosave_low_regs", 0, 0, true, false, false,
- sh_handle_interrupt_handler_attribute, false },
- { "resbank", 0, 0, true, false, false,
- sh_handle_resbank_handler_attribute, false },
- { "function_vector", 1, 1, true, false, false,
- sh2a_handle_function_vector_handler_attribute, false },
- { NULL, 0, 0, false, false, false, NULL, false }
+ /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
+ affects_type_identity, handler, exclude } */
+ { "interrupt_handler", 0, 0, true, false, false, false,
+ sh_handle_interrupt_handler_attribute, NULL },
+ { "sp_switch", 1, 1, true, false, false, false,
+ sh_handle_sp_switch_attribute, NULL },
+ { "trap_exit", 1, 1, true, false, false, false,
+ sh_handle_trap_exit_attribute, NULL },
+ { "renesas", 0, 0, false, true, false, false,
+ sh_handle_renesas_attribute, NULL },
+ { "trapa_handler", 0, 0, true, false, false, false,
+ sh_handle_interrupt_handler_attribute, NULL },
+ { "nosave_low_regs", 0, 0, true, false, false, false,
+ sh_handle_interrupt_handler_attribute, NULL },
+ { "resbank", 0, 0, true, false, false, false,
+ sh_handle_resbank_handler_attribute, NULL },
+ { "function_vector", 1, 1, true, false, false, false,
+ sh2a_handle_function_vector_handler_attribute, NULL },
+ { NULL, 0, 0, false, false, false, false, NULL, NULL }
};
\f
/* Initialize the GCC target structure. */
#undef TARGET_ASM_UNALIGNED_SI_OP
#define TARGET_ASM_UNALIGNED_SI_OP "\t.ualong\t"
-/* These are NULLed out on non-SH5 in TARGET_OPTION_OVERRIDE. */
-#undef TARGET_ASM_UNALIGNED_DI_OP
-#define TARGET_ASM_UNALIGNED_DI_OP "\t.uaquad\t"
-#undef TARGET_ASM_ALIGNED_DI_OP
-#define TARGET_ASM_ALIGNED_DI_OP "\t.quad\t"
-
#undef TARGET_OPTION_OVERRIDE
#define TARGET_OPTION_OVERRIDE sh_option_override
#undef TARGET_CAN_FOLLOW_JUMP
#define TARGET_CAN_FOLLOW_JUMP sh_can_follow_jump
-#undef TARGET_BRANCH_TARGET_REGISTER_CLASS
-#define TARGET_BRANCH_TARGET_REGISTER_CLASS sh_target_reg_class
-#undef TARGET_BRANCH_TARGET_REGISTER_CALLEE_SAVED
-#define TARGET_BRANCH_TARGET_REGISTER_CALLEE_SAVED \
- sh_optimize_target_register_callee_saved
#undef TARGET_MS_BITFIELD_LAYOUT_P
#define TARGET_MS_BITFIELD_LAYOUT_P sh_ms_bitfield_layout_p
#undef TARGET_CANNOT_FORCE_CONST_MEM
#define TARGET_CANNOT_FORCE_CONST_MEM sh_cannot_force_const_mem_p
+#undef TARGET_HARD_REGNO_NREGS
+#define TARGET_HARD_REGNO_NREGS sh_hard_regno_nregs
+#undef TARGET_HARD_REGNO_MODE_OK
+#define TARGET_HARD_REGNO_MODE_OK sh_hard_regno_mode_ok
+
+#undef TARGET_MODES_TIEABLE_P
+#define TARGET_MODES_TIEABLE_P sh_modes_tieable_p
+
+#undef TARGET_CAN_CHANGE_MODE_CLASS
+#define TARGET_CAN_CHANGE_MODE_CLASS sh_can_change_mode_class
+
+#undef TARGET_CONSTANT_ALIGNMENT
+#define TARGET_CONSTANT_ALIGNMENT constant_alignment_word_strings
+
+#undef TARGET_HAVE_SPECULATION_SAFE_VALUE
+#define TARGET_HAVE_SPECULATION_SAFE_VALUE speculation_safe_value_not_needed
+
struct gcc_target targetm = TARGET_INITIALIZER;
\f
{
if (tokens[i] == "strict")
ret.strict = true;
- else if (tokens[i].find ("gbr-offset=") == 0)
+ else if (!tokens[i].compare (0, strlen ("gbr-offset="), "gbr-offset="))
{
std::string offset_str = tokens[i].substr (strlen ("gbr-offset="));
ret.tcb_gbr_offset = integral_argument (offset_str.c_str ());
/* Run sh_treg_combine pass after register allocation and basic block
reordering as this sometimes creates new opportunities. */
register_pass (make_pass_sh_treg_combine (g, true, "sh_treg_combine3"),
- PASS_POS_INSERT_AFTER, "split4", 1);
+ PASS_POS_INSERT_AFTER, "split3", 1);
/* Optimize sett and clrt insns, by e.g. removing them if the T bit value
is known after a conditional branch.
int regno;
SUBTARGET_OVERRIDE_OPTIONS;
- if (optimize > 1 && !optimize_size)
- target_flags |= MASK_SAVE_ALL_TARGET_REGS;
-
- /* Set default values of TARGET_CBRANCHDI4 and TARGET_CMPEQDI_T. */
- TARGET_CBRANCHDI4 = 1;
- TARGET_CMPEQDI_T = 0;
sh_cpu = PROCESSOR_SH1;
assembler_dialect = 0;
sh_cpu = PROCESSOR_SH4A;
}
- /* Only the sh64-elf assembler fully supports .quad properly. */
- targetm.asm_out.aligned_op.di = NULL;
- targetm.asm_out.unaligned_op.di = NULL;
-
/* User/priviledged mode is supported only on SH3* and SH4*.
Disable it for everything else. */
if (!TARGET_SH3 && TARGET_USERMODE)
to the pressure on R0. */
/* Enable sched1 for SH4 if the user explicitly requests.
When sched1 is enabled, the ready queue will be reordered by
- the target hooks if pressure is high. We can not do this for
+ the target hooks if pressure is high. We cannot do this for
PIC, SH3 and lower as they give spill failures for R0. */
if (!TARGET_HARD_SH4 || flag_pic)
flag_schedule_insns = 0;
else if (flag_exceptions)
{
if (flag_schedule_insns && global_options_set.x_flag_schedule_insns)
- warning (0, "ignoring -fschedule-insns because of exception "
+ warning (0, "ignoring %<-fschedule-insns%> because of exception "
"handling bug");
flag_schedule_insns = 0;
}
&& flag_omit_frame_pointer && !TARGET_ACCUMULATE_OUTGOING_ARGS)
{
warning (0, "unwind tables currently require either a frame pointer "
- "or -maccumulate-outgoing-args for correctness");
+ "or %<-maccumulate-outgoing-args%> for correctness");
TARGET_ACCUMULATE_OUTGOING_ARGS = 1;
}
if (flag_unsafe_math_optimizations)
{
/* Enable fsca insn for SH4A if not otherwise specified by the user. */
- if (global_options_set.x_TARGET_FSCA == 0 && TARGET_SH4A_FP)
+ if (global_options_set.x_TARGET_FSCA == 0
+ && (TARGET_SH4A_FP || TARGET_FPU_SH4_300))
TARGET_FSCA = 1;
/* Enable fsrra insn for SH4A if not otherwise specified by the user. */
- if (global_options_set.x_TARGET_FSRRA == 0 && TARGET_SH4A_FP)
+ if (global_options_set.x_TARGET_FSRRA == 0
+ && (TARGET_SH4A_FP || TARGET_FPU_SH4_300))
TARGET_FSRRA = 1;
}
Aligning all jumps increases the code size, even if it might
result in slightly faster code. Thus, it is set to the smallest
alignment possible if not specified by the user. */
- if (align_loops == 0)
- align_loops = optimize_size ? 2 : 4;
+ if (flag_align_loops && !str_align_loops)
+ str_align_loops = optimize_size ? "2" : "4";
- if (align_jumps == 0)
- align_jumps = 2;
- else if (align_jumps < 2)
- align_jumps = 2;
+ /* Parse values so that we can compare for current value. */
+ parse_alignment_opts ();
+ if (flag_align_jumps && !str_align_jumps)
+ str_align_jumps = "2";
+ else if (align_jumps.levels[0].get_value () < 2)
+ str_align_jumps = "2";
- if (align_functions == 0)
- align_functions = optimize_size ? 2 : 4;
+ if (flag_align_functions && !str_align_functions)
+ str_align_functions = optimize_size ? "2" : "4";
/* The linker relaxation code breaks when a function contains
alignments that are larger than that at the start of a
compilation unit. */
if (TARGET_RELAX)
{
- int min_align = align_loops > align_jumps ? align_loops : align_jumps;
+ /* Parse values so that we can compare for current value. */
+ parse_alignment_opts ();
+ int min_align = MAX (align_loops.levels[0].get_value (),
+ align_jumps.levels[0].get_value ());
/* Also take possible .long constants / mova tables into account. */
if (min_align < 4)
min_align = 4;
- if (align_functions < min_align)
- align_functions = min_align;
+ if (align_functions.levels[0].get_value () < min_align)
+ {
+ char *r = XNEWVEC (char, 16);
+ sprintf (r, "%d", min_align);
+ str_align_functions = r;
+ }
}
}
\f
int base_num = true_regnum (base);
int index_num = true_regnum (index);
- fprintf (stream, "@(r0,%s)",
- reg_names[MAX (base_num, index_num)]);
+ /* If base or index is R0, make sure that it comes first.
+ Usually one of them will be R0, but the order might be wrong.
+ If neither base nor index are R0 it's an error and we just
+ pass it on to the assembler. This avoids silent wrong code
+ bugs. */
+ if (base_num == 0 && index_num != 0)
+ std::swap (base_num, index_num);
+
+ fprintf (stream, "@(%s,%s)", reg_names[index_num],
+ reg_names[base_num]);
break;
}
{
rtx note = find_reg_note (current_output_insn, REG_BR_PROB, 0);
- if (note && XINT (note, 0) * 2 < REG_BR_PROB_BASE)
+ if (note
+ && profile_probability::from_reg_br_prob_note (XINT (note, 0))
+ < profile_probability::even ())
fputs ("/u", stream);
break;
}
{
switch (GET_MODE (x))
{
- case QImode: fputs (".b", stream); break;
- case HImode: fputs (".w", stream); break;
- case SImode: fputs (".l", stream); break;
- case SFmode: fputs (".s", stream); break;
- case DFmode: fputs (".d", stream); break;
+ case E_QImode: fputs (".b", stream); break;
+ case E_HImode: fputs (".w", stream); break;
+ case E_SImode: fputs (".l", stream); break;
+ case E_SFmode: fputs (".s", stream); break;
+ case E_DFmode: fputs (".d", stream); break;
default: gcc_unreachable ();
}
}
/* Floating point register pairs are always big endian;
general purpose registers are 64 bit wide. */
regno = REGNO (inner);
- regno = (HARD_REGNO_NREGS (regno, inner_mode)
- - HARD_REGNO_NREGS (regno, mode))
+ regno = (hard_regno_nregs (regno, inner_mode)
+ - hard_regno_nregs (regno, mode))
+ offset;
x = inner;
goto reg;
if (mode == Pmode || mode == ptr_mode)
{
- rtx op0, op1, opc;
- enum tls_model tls_kind;
-
- op0 = operands[0];
- op1 = operands[1];
+ rtx op0 = operands[0];
+ rtx op1 = operands[1];
+ rtx opc;
if (GET_CODE (op1) == CONST
&& GET_CODE (XEXP (op1, 0)) == PLUS
&& (tls_symbolic_operand (XEXP (XEXP (op1, 0), 0), Pmode)
else
opc = NULL_RTX;
+ enum tls_model tls_kind;
+
if (! reload_in_progress && ! reload_completed
&& (tls_kind = tls_symbolic_operand (op1, Pmode)) != TLS_MODEL_NONE)
{
emit_use (gen_rtx_REG (SImode, PIC_REG));
if (flag_schedule_insns)
emit_insn (gen_blockage ());
- }
+ }
switch (tls_kind)
{
return true;
}
+/* Try to calculate the branch distance of a conditional branch in bytes.
+
+ FIXME: Because of PR 59189 we can't use the CFG here. Instead just
+ walk from this insn into the next (fall-through) basic block and see if
+ we hit the label. */
+unsigned int
+sh_cbranch_distance (rtx_insn* _cbranch_insn, unsigned int max_dist)
+{
+ rtx_jump_insn* cbranch_insn = safe_as_a<rtx_jump_insn*> (_cbranch_insn);
+
+ if (dump_file)
+ {
+ fprintf (dump_file, "sh_cbranch_distance insn = \n");
+ print_rtl_single (dump_file, cbranch_insn);
+ }
+
+ unsigned int dist = 0;
+
+ for (rtx_insn* i = next_nonnote_insn (cbranch_insn);
+ i != NULL && dist < max_dist; i = next_nonnote_insn (i))
+ {
+ const unsigned int i_len = get_attr_length (i);
+ dist += i_len;
+
+ if (dump_file)
+ fprintf (dump_file, " insn %d length = %u dist = %u\n",
+ INSN_UID (i), i_len, dist);
+
+ if (rtx_code_label* l = dyn_cast<rtx_code_label*> (i))
+ {
+ if (l == cbranch_insn->jump_target ())
+ {
+ if (dump_file)
+ fprintf (dump_file, " cbranch dist = %u\n", dist);
+ return dist;
+ }
+ break;
+ }
+ }
+
+ if (dump_file)
+ fprintf (dump_file, " cbranch dist = unknown\n");
+
+ return unknown_cbranch_distance;
+}
+
enum rtx_code
prepare_cbranch_operands (rtx *operands, machine_mode mode,
enum rtx_code comparison)
{
- /* The scratch reg is only available when this is invoked from within
- the cbranchdi4_i splitter, through expand_cbranchdi4. */
- rtx scratch = NULL_RTX;
+ gcc_assert (can_create_pseudo_p ());
if (comparison == LAST_AND_UNUSED_RTX_CODE)
comparison = GET_CODE (operands[0]);
- else
- scratch = operands[4];
sh_canonicalize_comparison (comparison, operands[1], operands[2],
mode, false);
- /* Notice that this function is also invoked after reload by
- the cbranchdi4_i pattern, through expand_cbranchdi4. */
rtx op1 = operands[1];
+ operands[1] = force_reg (mode, op1);
- if (can_create_pseudo_p ())
- operands[1] = force_reg (mode, op1);
/* When we are handling DImode comparisons, we want to keep constants so
that we can optimize the component comparisons; however, memory loads
are better issued as a whole so that they can be scheduled well.
&& ((comparison != EQ && comparison != NE)
|| (REG_P (op1) && REGNO (op1) != R0_REG)
|| !satisfies_constraint_I08 (operands[2])))))
- {
- if (scratch && GET_MODE (scratch) == mode)
- {
- emit_move_insn (scratch, operands[2]);
- operands[2] = scratch;
- }
- else if (can_create_pseudo_p ())
- operands[2] = force_reg (mode, operands[2]);
- }
+ operands[2] = force_reg (mode, operands[2]);
+
return comparison;
}
-void
-expand_cbranchsi4 (rtx *operands, enum rtx_code comparison, int probability)
+static void
+expand_cbranchsi4 (rtx *operands, enum rtx_code comparison,
+ profile_probability probability)
{
rtx (*branch_expander) (rtx) = gen_branch_true;
comparison = prepare_cbranch_operands (operands, SImode, comparison);
gen_rtx_fmt_ee (comparison, SImode,
operands[1], operands[2])));
rtx_insn *jump = emit_jump_insn (branch_expander (operands[3]));
- if (probability >= 0)
- add_int_reg_note (jump, REG_BR_PROB, probability);
+ if (probability.initialized_p ())
+ add_reg_br_prob_note (jump, probability);
+}
+
+void
+expand_cbranchsi4 (rtx *operands, enum rtx_code comparison)
+{
+ expand_cbranchsi4 (operands, comparison,
+ profile_probability::uninitialized ());
}
/* ??? How should we distribute probabilities when more than one branch
rtx_code_label *skip_label = NULL;
rtx op1h, op1l, op2h, op2l;
int num_branches;
- int prob, rev_prob;
- int msw_taken_prob = -1, msw_skip_prob = -1, lsw_taken_prob = -1;
- rtx scratch = operands[4];
+ profile_probability prob, rev_prob;
+ profile_probability msw_taken_prob = profile_probability::uninitialized (),
+ msw_skip_prob = profile_probability::uninitialized (),
+ lsw_taken_prob = profile_probability::uninitialized ();
comparison = prepare_cbranch_operands (operands, DImode, comparison);
op1h = gen_highpart_mode (SImode, DImode, operands[1]);
op2l = gen_lowpart (SImode, operands[2]);
msw_taken = msw_skip = lsw_taken = LAST_AND_UNUSED_RTX_CODE;
prob = split_branch_probability;
- rev_prob = REG_BR_PROB_BASE - prob;
+ rev_prob = prob.invert ();
switch (comparison)
{
- /* ??? Should we use the cmpeqdi_t pattern for equality comparisons?
- That costs 1 cycle more when the first branch can be predicted taken,
- but saves us mispredicts because only one branch needs prediction.
- It also enables generating the cmpeqdi_t-1 pattern. */
case EQ:
- if (TARGET_CMPEQDI_T)
- {
- emit_insn (gen_cmpeqdi_t (operands[1], operands[2]));
- emit_jump_insn (gen_branch_true (operands[3]));
- return true;
- }
msw_skip = NE;
lsw_taken = EQ;
- if (prob >= 0)
+ if (prob.initialized_p ())
{
- // If we had more precision, we'd use rev_prob - (rev_prob >> 32) .
+ /* FIXME: This is not optimal. We do not really know the probability
+ that values differ by MCW only, but we should probably distribute
+ probabilities more evenly. */
msw_skip_prob = rev_prob;
- if (REG_BR_PROB_BASE <= 65535)
- lsw_taken_prob = prob ? REG_BR_PROB_BASE : 0;
- else
- {
- lsw_taken_prob
- = (prob
- ? (REG_BR_PROB_BASE
- - ((gcov_type) REG_BR_PROB_BASE * rev_prob
- / ((gcov_type) prob << 32)))
- : 0);
- }
+ lsw_taken_prob = prob > profile_probability::never ()
+ ? profile_probability::guessed_always ()
+ : profile_probability::guessed_never ();
}
break;
case NE:
- if (TARGET_CMPEQDI_T)
- {
- emit_insn (gen_cmpeqdi_t (operands[1], operands[2]));
- emit_jump_insn (gen_branch_false (operands[3]));
- return true;
- }
msw_taken = NE;
msw_taken_prob = prob;
lsw_taken = NE;
- lsw_taken_prob = 0;
+ lsw_taken_prob = profile_probability::guessed_never ();
break;
case GTU: case GT:
msw_taken = comparison;
if (comparison != EQ && comparison != NE && num_branches > 1)
{
if (!CONSTANT_P (operands[2])
- && prob >= (int) (REG_BR_PROB_BASE * 3 / 8U)
- && prob <= (int) (REG_BR_PROB_BASE * 5 / 8U))
- {
- msw_taken_prob = prob / 2U;
- msw_skip_prob
- = REG_BR_PROB_BASE * rev_prob / (REG_BR_PROB_BASE + rev_prob);
+ && prob.initialized_p ()
+ && prob.to_reg_br_prob_base () >= (int) (REG_BR_PROB_BASE * 3 / 8U)
+ && prob.to_reg_br_prob_base () <= (int) (REG_BR_PROB_BASE * 5 / 8U))
+ {
+ msw_taken_prob = prob.apply_scale (1, 2);
+ msw_skip_prob = rev_prob.apply_scale (REG_BR_PROB_BASE,
+ rev_prob.to_reg_br_prob_base ()
+ + REG_BR_PROB_BASE);
lsw_taken_prob = prob;
}
else
{
msw_taken_prob = prob;
- msw_skip_prob = REG_BR_PROB_BASE;
+ msw_skip_prob = profile_probability::guessed_always ();
/* ??? If we have a constant op2h, should we use that when
calculating lsw_taken_prob? */
lsw_taken_prob = prob;
}
operands[1] = op1h;
operands[2] = op2h;
- operands[4] = NULL_RTX;
- if (reload_completed
- && ! arith_reg_or_0_operand (op2h, SImode)
- && (true_regnum (op1h) || (comparison != EQ && comparison != NE))
- && (msw_taken != LAST_AND_UNUSED_RTX_CODE
- || msw_skip != LAST_AND_UNUSED_RTX_CODE))
- {
- emit_move_insn (scratch, operands[2]);
- operands[2] = scratch;
- }
+
if (msw_taken != LAST_AND_UNUSED_RTX_CODE)
expand_cbranchsi4 (operands, msw_taken, msw_taken_prob);
if (msw_skip != LAST_AND_UNUSED_RTX_CODE)
{
operands[1] = op1h;
operands[2] = op2h;
- if (reload_completed
- && ! arith_reg_or_0_operand (op2h, SImode)
- && (true_regnum (op1h) || (comparison != EQ && comparison != NE)))
- {
- emit_move_insn (scratch, operands[2]);
- operands[2] = scratch;
- }
}
operands[3] = skip_label = gen_label_rtx ();
operands[1] = op1l;
operands[2] = op2l;
if (lsw_taken != LAST_AND_UNUSED_RTX_CODE)
- {
- if (reload_completed
- && ! arith_reg_or_0_operand (op2l, SImode)
- && (true_regnum (op1l) || (lsw_taken != EQ && lsw_taken != NE)))
- {
- emit_move_insn (scratch, operands[2]);
- operands[2] = scratch;
- }
- expand_cbranchsi4 (operands, lsw_taken, lsw_taken_prob);
- }
+ expand_cbranchsi4 (operands, lsw_taken, lsw_taken_prob);
if (msw_skip != LAST_AND_UNUSED_RTX_CODE)
emit_label (skip_label);
return true;
{
rtx t_reg = get_t_reg_rtx ();
enum rtx_code oldcode = code;
- machine_mode mode;
/* First need a compare insn. */
switch (code)
if (code != oldcode)
std::swap (op0, op1);
- mode = GET_MODE (op0);
+ machine_mode mode = GET_MODE (op0);
if (mode == VOIDmode)
mode = GET_MODE (op1);
return "";
}
+ /* FALLTHRU */
/* When relaxing, handle this like a short branch. The linker
will fix it up if it still doesn't fit after relaxation. */
case 2:
return "";
}
- /* When relaxing, fall through. */
+ /* FALLTHRU */
case 4:
{
char buffer[10];
unspec_caller_rtx_p (rtx pat)
{
rtx base, offset;
- int i;
-
split_const (pat, &base, &offset);
+
if (GET_CODE (base) == UNSPEC)
{
if (XINT (base, 1) == UNSPEC_CALLER)
return true;
- for (i = 0; i < XVECLEN (base, 0); i++)
+ for (int i = 0; i < XVECLEN (base, 0); i++)
if (unspec_caller_rtx_p (XVECEXP (base, 0, i)))
return true;
}
static bool
sh_cannot_copy_insn_p (rtx_insn *insn)
{
- rtx pat;
-
if (!reload_completed || !flag_pic)
return false;
if (asm_noperands (insn) >= 0)
return false;
- pat = PATTERN (insn);
+ rtx pat = PATTERN (insn);
if (GET_CODE (pat) == CLOBBER || GET_CODE (pat) == USE)
return false;
vector-move, so we have to provide the correct cost in the number
of move insns to load/store the reg of the mode in question. */
case SET:
+ if (sh_movt_set_dest (x) != NULL || sh_movrt_set_dest (x) != NULL)
+ {
+ *total = COSTS_N_INSNS (1);
+ return true;
+ }
+
if (register_operand (SET_DEST (x), VOIDmode)
&& (register_operand (SET_SRC (x), VOIDmode)
|| satisfies_constraint_Z (SET_SRC (x))))
*total = 1; //COSTS_N_INSNS (1);
return true;
}
+
+ /* div0s variant. */
+ if (GET_CODE (XEXP (x, 0)) == XOR
+ && GET_CODE (XEXP (XEXP (x, 0), 0)) == XOR
+ && CONST_INT_P (XEXP (XEXP (x, 0), 1)))
+ {
+ *total = 1;
+ return true;
+ }
return false;
/* The cost of a sign or zero extend depends on whether the source is a
- reg or a mem. In case of a mem take the address into acount. */
+ reg or a mem. In case of a mem take the address into account. */
case SIGN_EXTEND:
if (arith_reg_operand (XEXP (x, 0), GET_MODE (XEXP (x, 0))))
{
*total = COSTS_N_INSNS (1);
return true;
}
- /* Fall through to shiftcosts. */
+ /* FALLTHRU */
case ASHIFT:
case ASHIFTRT:
{
}
case 4:
shift_gen_fun = gen_shifty_op;
+ /* FALLTHRU */
case 3:
/* If the topmost bit that matters is set, set the topmost bits
that don't matter. This way, we might be able to get a shorter
signed constant. */
if (mask & ((HOST_WIDE_INT) 1 << (31 - total_shift)))
mask |= (HOST_WIDE_INT) ((HOST_WIDE_INT_M1U) << (31 - total_shift));
+ /* FALLTHRU */
case 2:
/* Don't expand fine-grained when combining, because that will
make the pattern fail. */
static rtx_code_label *
add_constant (rtx x, machine_mode mode, rtx last_value)
{
- int i;
rtx_code_label *lab, *new_rtx;
label_ref_list_t ref, newref;
/* First see if we've already got it. */
- for (i = 0; i < pool_size; i++)
+ for (int i = 0; i < pool_size; i++)
{
if (x->code == pool_vector[i].value->code
&& mode == pool_vector[i].mode)
dump_table (rtx_insn *start, rtx_insn *barrier)
{
rtx_insn *scan = barrier;
- int i;
bool need_align = true;
- rtx lab;
+ rtx_code_label *lab;
label_ref_list_t ref;
bool have_df = false;
/* Do two passes, first time dump out the HI sized constants. */
- for (i = 0; i < pool_size; i++)
+ for (int i = 0; i < pool_size; i++)
{
pool_node *p = &pool_vector[i];
scan = emit_insn_after (gen_align_2 (), scan);
need_align = false;
}
- for (lab = p->label; lab; lab = LABEL_REFS (lab))
+ for (lab = p->label; lab;
+ lab = safe_as_a <rtx_code_label *> (LABEL_REFS (lab)))
scan = emit_label_after (lab, scan);
scan = emit_insn_after (gen_consttable_2 (p->value, const0_rtx),
scan);
rtx src = SET_SRC (XVECEXP (PATTERN (start), 0, 0));
rtx lab = XEXP (XVECEXP (src, 0, 3), 0);
- scan = emit_label_after (lab, scan);
+ scan = emit_label_after (as_a <rtx_insn *> (lab), scan);
}
}
if (TARGET_FMOVD && TARGET_ALIGN_DOUBLE && have_df)
scan = emit_insn_after (gen_align_log (GEN_INT (3)), scan);
need_align = false;
- for (i = 0; i < pool_size; i++)
+ for (int i = 0; i < pool_size; i++)
{
pool_node *p = &pool_vector[i];
switch (p->mode)
{
- case HImode:
+ case E_HImode:
break;
- case SImode:
- case SFmode:
+ case E_SImode:
+ case E_SFmode:
if (align_insn && !p->part_of_sequence_p)
{
- for (lab = p->label; lab; lab = LABEL_REFS (lab))
+ for (lab = p->label; lab;
+ lab = safe_as_a <rtx_code_label *> (LABEL_REFS (lab)))
emit_label_before (lab, align_insn);
emit_insn_before (gen_consttable_4 (p->value, const0_rtx),
align_insn);
}
else
{
- for (lab = p->label; lab; lab = LABEL_REFS (lab))
+ for (lab = p->label; lab;
+ lab = safe_as_a <rtx_code_label *> (LABEL_REFS (lab)))
scan = emit_label_after (lab, scan);
scan = emit_insn_after (gen_consttable_4 (p->value,
const0_rtx), scan);
need_align = ! need_align;
}
break;
- case DFmode:
+ case E_DFmode:
if (need_align)
{
scan = emit_insn_after (gen_align_log (GEN_INT (3)), scan);
align_insn = scan;
need_align = false;
}
- case DImode:
- for (lab = p->label; lab; lab = LABEL_REFS (lab))
+ /* FALLTHRU */
+ case E_DImode:
+ for (lab = p->label; lab;
+ lab = safe_as_a <rtx_code_label *> (LABEL_REFS (lab)))
scan = emit_label_after (lab, scan);
scan = emit_insn_after (gen_consttable_8 (p->value, const0_rtx),
scan);
pool_size = 0;
}
- for (i = 0; i < pool_size; i++)
+ for (int i = 0; i < pool_size; i++)
{
pool_node *p = &pool_vector[i];
switch (p->mode)
{
- case HImode:
+ case E_HImode:
break;
- case SImode:
- case SFmode:
+ case E_SImode:
+ case E_SFmode:
if (need_align)
{
need_align = false;
scan = emit_label_after (gen_label_rtx (), scan);
scan = emit_insn_after (gen_align_4 (), scan);
}
- for (lab = p->label; lab; lab = LABEL_REFS (lab))
+ for (lab = p->label; lab;
+ lab = safe_as_a <rtx_code_label *> (LABEL_REFS (lab)))
scan = emit_label_after (lab, scan);
scan = emit_insn_after (gen_consttable_4 (p->value, const0_rtx),
scan);
break;
- case DFmode:
- case DImode:
+ case E_DFmode:
+ case E_DImode:
if (need_align)
{
need_align = false;
scan = emit_label_after (gen_label_rtx (), scan);
scan = emit_insn_after (gen_align_4 (), scan);
}
- for (lab = p->label; lab; lab = LABEL_REFS (lab))
+ for (lab = p->label; lab;
+ lab = safe_as_a <rtx_code_label *> (LABEL_REFS (lab)))
scan = emit_label_after (lab, scan);
scan = emit_insn_after (gen_consttable_8 (p->value, const0_rtx),
scan);
&& CODE_LABEL_NUMBER (from) <= max_labelno_before_reorg)
{
if (optimize)
- new_align = 1 << label_to_alignment (from);
+ new_align = 1 << label_to_alignment (from).levels[0].log;
else if (BARRIER_P (prev_nonnote_insn (from)))
new_align = 1 << barrier_align (from);
else
&& (prev_nonnote_insn (from)
== XEXP (MOVA_LABELREF (mova), 0))))
num_mova--;
- if (barrier_align (next_real_insn (from)) == align_jumps_log)
+ if (barrier_align (next_real_insn (from)) == align_jumps.levels[0].log)
{
/* We have just passed the barrier in front of the
ADDR_DIFF_VEC, which is stored in found_barrier. Since
around the constant pool table will be hit. Putting it before
a jump makes it more likely that the bra delay slot will be
filled. */
- while (NOTE_P (from) || JUMP_P (from)
- || LABEL_P (from))
+ while (NOTE_P (from) || JUMP_P (from) || LABEL_P (from))
from = PREV_INSN (from);
- /* Make sure we do not split between a call and its corresponding
- CALL_ARG_LOCATION note. */
if (CALL_P (from))
{
- rtx_insn *next = NEXT_INSN (from);
- if (next && NOTE_P (next)
- && NOTE_KIND (next) == NOTE_INSN_CALL_ARG_LOCATION)
- from = next;
+ bool sibcall_p = SIBLING_CALL_P (from);
+
+ /* If FROM was a sibling call, then we know that control
+ will not return. In fact, we were guaranteed to hit
+ a barrier before another real insn.
+
+ The jump around the constant pool is unnecessary. It
+ costs space, but more importantly it confuses dwarf2cfi
+ generation. */
+ if (sibcall_p)
+ return emit_barrier_after (from);
}
from = emit_jump_insn_after (gen_jump (label), from);
if (! reg_part)
return NULL_RTX;
reg = XEXP (reg_part, 0);
- for (i = XVECLEN (pattern, 0) - 1; i >= 0; i--)
+ for (int i = XVECLEN (pattern, 0) - 1; i >= 0; i--)
{
part = XVECEXP (pattern, 0, i);
if (part == reg_part || GET_CODE (part) == CLOBBER)
static bool
noncall_uses_reg (rtx reg, rtx_insn *insn, rtx *set)
{
- rtx pattern, reg2;
-
*set = NULL_RTX;
- reg2 = sfunc_uses_reg (insn);
+ rtx reg2 = sfunc_uses_reg (insn);
if (reg2 && REGNO (reg2) == REGNO (reg))
{
- pattern = single_set (insn);
+ rtx pattern = single_set (insn);
if (pattern
&& REG_P (SET_DEST (pattern))
&& REGNO (reg) == REGNO (SET_DEST (pattern)))
{
/* We don't use rtx_equal_p because we don't care if the mode is
different. */
- pattern = single_set (insn);
+ rtx pattern = single_set (insn);
if (pattern
&& REG_P (SET_DEST (pattern))
&& REGNO (reg) == REGNO (SET_DEST (pattern)))
return true;
}
- pattern = PATTERN (insn);
+ rtx pattern = PATTERN (insn);
if (GET_CODE (pattern) == PARALLEL)
{
- int i;
-
- for (i = XVECLEN (pattern, 0) - 1; i >= 1; i--)
+ for (int i = XVECLEN (pattern, 0) - 1; i >= 1; i--)
if (reg_mentioned_p (reg, XVECEXP (pattern, 0, i)))
return true;
pattern = XVECEXP (pattern, 0, 0);
{
enum rtx_code code;
const char *fmt;
- int i, used = 0;
+ int used = 0;
if (! x)
return used;
{
case REG:
if (REGNO (x) < 16)
- return (((1 << HARD_REGNO_NREGS (0, GET_MODE (x))) - 1)
+ return (((1 << hard_regno_nregs (0, GET_MODE (x))) - 1)
<< (REGNO (x) + is_dest));
return 0;
case SUBREG:
if (!REG_P (y))
break;
if (REGNO (y) < 16)
- return (((1 << HARD_REGNO_NREGS (0, GET_MODE (x))) - 1)
+ return (((1 << hard_regno_nregs (0, GET_MODE (x))) - 1)
<< (REGNO (y) +
subreg_regno_offset (REGNO (y),
GET_MODE (y),
fmt = GET_RTX_FORMAT (code);
- for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ for (int i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'E')
{
- int j;
- for (j = XVECLEN (x, i) - 1; j >= 0; j--)
+ for (int j = XVECLEN (x, i) - 1; j >= 0; j--)
used |= regs_used (XVECEXP (x, i, j), is_dest);
}
else if (fmt[i] == 'e')
{
int dead = 0;
rtx_insn *prev = prev_nonnote_insn (jump);
- rtx dest;
/* First, check if we already have an instruction that satisfies our need. */
if (prev && NONJUMP_INSN_P (prev) && ! prev->deleted ())
}
/* We can't use JUMP_LABEL here because it might be undefined
when not optimizing. */
- dest = XEXP (SET_SRC (PATTERN (jump)), 0);
+ rtx dest = XEXP (SET_SRC (PATTERN (jump)), 0);
/* If the branch is out of range, try to find a scratch register for it. */
if (optimize
&& (INSN_ADDRESSES (INSN_UID (dest)) - addr + (unsigned) 4092
for (scan = jump; (scan = PREV_INSN (scan)); )
{
- enum rtx_code code;
-
if (scan->deleted ())
continue;
- code = GET_CODE (scan);
+ rtx_code code = GET_CODE (scan);
if (code == CODE_LABEL || code == JUMP_INSN)
break;
if (code == INSN
for (used = dead = 0, scan = JUMP_LABEL_AS_INSN (jump);
(scan = NEXT_INSN (scan)); )
{
- enum rtx_code code;
-
if (scan->deleted ())
continue;
- code = GET_CODE (scan);
+ rtx_code code = GET_CODE (scan);
if (INSN_P (scan))
{
used |= regs_used (PATTERN (scan), 0);
else if (optimize && need_block >= 0)
{
- rtx_insn *next = next_active_insn (next_active_insn (dest));
+ rtx_insn *next = next_active_insn (as_a<rtx_insn *> (dest));
+ next = next_active_insn (next);
if (next && JUMP_P (next)
&& GET_CODE (PATTERN (next)) == SET
&& recog_memoized (next) == CODE_FOR_jump_compact)
int address;
};
-static void gen_far_branch (struct far_branch *);
enum mdep_reorg_phase_e mdep_reorg_phase;
+
static void
gen_far_branch (struct far_branch *bp)
{
rtx_insn *insn = bp->insert_place;
rtx_jump_insn *jump;
rtx_code_label *label = gen_label_rtx ();
- int ok;
emit_label_after (label, insn);
if (bp->far_label)
JUMP_LABEL (jump) = pat;
}
- ok = invert_jump (as_a <rtx_jump_insn *> (insn), label, 1);
+ bool ok = invert_jump (as_a <rtx_jump_insn *> (insn), label, 1);
gcc_assert (ok);
/* If we are branching around a jump (rather than a return), prevent
/* Emit the reference label of the braf where it belongs, right after
the casesi_jump_2 (i.e. braf). */
braf_label = XEXP (XEXP (SET_SRC (XVECEXP (prevpat, 0, 0)), 1), 0);
- emit_label_after (braf_label, prev);
+ emit_label_after (as_a <rtx_insn *> (braf_label), prev);
/* Fix up the ADDR_DIF_VEC to be relative
to the reference address of the braf. */
int
barrier_align (rtx_insn *barrier_or_label)
{
- rtx next, pat;
-
if (! barrier_or_label)
return 0;
&& PREV_INSN (barrier_or_label)
&& JUMP_TABLE_DATA_P (PREV_INSN (barrier_or_label)))
{
- pat = PATTERN (PREV_INSN (barrier_or_label));
+ rtx pat = PATTERN (PREV_INSN (barrier_or_label));
/* If this is a very small table, we want to keep the alignment after
the table to the minimum for proper code alignment. */
return ((optimize_size
|| ((unsigned) XVECLEN (pat, 1) * GET_MODE_SIZE (GET_MODE (pat))
<= (unsigned) 1 << (CACHE_LOG - 2)))
- ? 1 : align_jumps_log);
+ ? 1 : align_jumps.levels[0].log);
}
- next = next_active_insn (barrier_or_label);
+ rtx_insn *next = next_active_insn (barrier_or_label);
if (! next)
return 0;
- pat = PATTERN (next);
+ rtx pat = PATTERN (next);
if (GET_CODE (pat) == UNSPEC_VOLATILE && XINT (pat, 1) == UNSPECV_ALIGN)
/* This is a barrier in front of a constant table. */
return 0;
if (! TARGET_SH2 || ! optimize)
- return align_jumps_log;
+ return align_jumps.levels[0].log;
/* When fixing up pcloads, a constant table might be inserted just before
the basic block that ends with the barrier. Thus, we can't trust the
{
rtx_insn *x;
if (jump_to_next
- || next_real_insn (JUMP_LABEL (prev)) == next
+ || next_real_insn (JUMP_LABEL_AS_INSN (prev)) == next
/* If relax_delay_slots() decides NEXT was redundant
with some previous instruction, it will have
redirected PREV's jump to the following insn. */
}
}
- return align_jumps_log;
+ return align_jumps.levels[0].log;
}
/* If we are inside a phony loop, almost any kind of label can turn up as the
|| recog_memoized (next) == CODE_FOR_consttable_2)
return 0;
- return align_loops_log;
+ return align_loops.levels[0].log;
}
/* Do a final pass over the function, just before delayed branch
/* Return the UID of the insn that follows the specified label. */
int
-get_dest_uid (rtx label, int max_uid)
+get_dest_uid (rtx_insn *label, int max_uid)
{
rtx_insn *dest = next_real_insn (label);
- int dest_uid;
+
if (! dest)
/* This can happen for an undefined label. */
return 0;
- dest_uid = INSN_UID (dest);
+ int dest_uid = INSN_UID (dest);
/* If this is a newly created branch redirection blocking instruction,
we cannot index the branch_uid or insn_addresses arrays with its
uid. But then, we won't need to, because the actual destination is
if (get_attr_length (insn) > 4)
{
rtx src = SET_SRC (PATTERN (insn));
- rtx olabel = XEXP (XEXP (src, 1), 0);
+ rtx_insn *olabel = safe_as_a <rtx_insn *> (XEXP (XEXP (src, 1), 0));
int addr = INSN_ADDRESSES (INSN_UID (insn));
rtx_insn *label = 0;
int dest_uid = get_dest_uid (olabel, max_uid);
/* We can't use JUMP_LABEL here because it might be undefined
when not optimizing. */
/* A syntax error might cause beyond to be NULL_RTX. */
- beyond
- = next_active_insn (XEXP (XEXP (SET_SRC (PATTERN (insn)), 1),
- 0));
+ rtx temp = XEXP (XEXP (SET_SRC (PATTERN (insn)), 1), 0);
+ beyond = next_active_insn (as_a<rtx_insn *> (temp));
if (beyond
&& (JUMP_P (beyond)
if (TARGET_RELAX)
{
- rtx note;
-
- note = find_reg_note (insn, REG_LABEL_OPERAND, NULL_RTX);
- if (note)
+ if (rtx note = find_reg_note (insn, REG_LABEL_OPERAND, NULL_RTX))
{
- rtx pattern;
-
- pattern = PATTERN (insn);
+ rtx pattern = PATTERN (insn);
if (GET_CODE (pattern) == PARALLEL)
pattern = XVECEXP (pattern, 0, 0);
switch (GET_CODE (pattern))
(asm_out_file, "L", CODE_LABEL_NUMBER (XEXP (note, 0)));
break;
}
- /* else FALLTHROUGH */
+ /* FALLTHROUGH */
case CALL:
asm_fprintf (asm_out_file, "\t.uses %LL%d\n",
CODE_LABEL_NUMBER (XEXP (note, 0)));
const char *
output_jump_label_table (void)
{
- int i;
-
if (pool_size)
{
fprintf (asm_out_file, "\t.align 2\n");
- for (i = 0; i < pool_size; i++)
+ for (int i = 0; i < pool_size; i++)
{
pool_node *p = &pool_vector[i];
output_stack_adjust (int size, rtx reg, int epilogue_p,
HARD_REG_SET *live_regs_mask, bool frame_p)
{
- rtx_insn *(*emit_fn) (rtx) = frame_p ? &frame_insn : &emit_insn;
+ rtx_insn *(*emit_fn) (rtx) = frame_p ? &emit_frame_insn : &emit_insn;
if (size)
{
HOST_WIDE_INT align = STACK_BOUNDARY / BITS_PER_UNIT;
to handle this case, so just die when we see it. */
if (epilogue_p < 0
|| current_function_interrupt
- || ! call_really_used_regs[temp] || fixed_regs[temp])
+ || ! call_used_regs[temp] || fixed_regs[temp])
temp = -1;
if (temp < 0 && ! current_function_interrupt && epilogue_p >= 0)
{
- HARD_REG_SET temps;
- COPY_HARD_REG_SET (temps, call_used_reg_set);
- AND_COMPL_HARD_REG_SET (temps, call_fixed_reg_set);
+ HARD_REG_SET temps = (regs_invalidated_by_call
+ & ~fixed_reg_set
+ & savable_regs);
if (epilogue_p > 0)
{
int nreg = 0;
machine_mode mode;
mode = GET_MODE (crtl->return_rtx);
if (BASE_RETURN_VALUE_REG (mode) == FIRST_RET_REG)
- nreg = HARD_REGNO_NREGS (FIRST_RET_REG, mode);
+ nreg = hard_regno_nregs (FIRST_RET_REG, mode);
}
for (i = 0; i < nreg; i++)
CLEAR_HARD_REG_BIT (temps, FIRST_RET_REG + i);
{
HARD_REG_SET temps;
- COPY_HARD_REG_SET (temps, *live_regs_mask);
+ temps = *live_regs_mask;
CLEAR_HARD_REG_BIT (temps, REGNO (reg));
temp = scavenge_reg (&temps);
}
}
}
-/* Emit the specified insn and mark it as frame related.
- FIXME: Rename this to emit_frame_insn. */
+/* Emit the specified insn and mark it as frame related. */
static rtx_insn *
-frame_insn (rtx x)
+emit_frame_insn (rtx x)
{
rtx_insn *insn = emit_insn (x);
RTX_FRAME_RELATED_P (insn) = 1;
else
x = gen_push (gen_rtx_REG (SImode, rn));
- x = frame_insn (x);
+ x = emit_frame_insn (x);
add_reg_note (x, REG_INC, gen_rtx_REG (SImode, STACK_POINTER_REGNUM));
return x;
}
/* Generate code to push the regs specified in the mask. */
static void
-push_regs (HARD_REG_SET *mask, int interrupt_handler)
+push_regs (HARD_REG_SET *mask, bool interrupt_handler)
{
- int i = interrupt_handler ? LAST_BANKED_REG + 1 : 0;
- int skip_fpscr = 0;
+ bool skip_fpscr = false;
/* Push PR last; this gives better latencies after the prologue, and
candidates for the return delay slot when there are no general
registers pushed. */
- for (; i < FIRST_PSEUDO_REGISTER; i++)
+ for (int i = interrupt_handler ? LAST_BANKED_REG + 1 : 0;
+ i < FIRST_PSEUDO_REGISTER; i++)
{
/* If this is an interrupt handler, and the SZ bit varies,
and we have to push any floating point register, we need
if (i == FIRST_FP_REG && interrupt_handler && TARGET_FMOVD
&& hard_reg_set_intersect_p (*mask, reg_class_contents[DF_REGS]))
{
- HARD_REG_SET unsaved;
-
push (FPSCR_REG);
- COMPL_HARD_REG_SET (unsaved, *mask);
- fpscr_set_from_mem (NORMAL_MODE (FP_MODE), unsaved);
- skip_fpscr = 1;
+ fpscr_set_from_mem (NORMAL_MODE (FP_MODE), ~*mask);
+ skip_fpscr = true;
}
if (i != PR_REG
&& (i != FPSCR_REG || ! skip_fpscr)
{
unsigned int count = 0;
- for (i = FIRST_BANKED_REG; i <= LAST_BANKED_REG; i++)
+ for (int i = FIRST_BANKED_REG; i <= LAST_BANKED_REG; i++)
if (TEST_HARD_REG_BIT (*mask, i))
count++;
else
insns. */
emit_insn (gen_blockage ());
x = gen_movml_push_banked (sp_reg);
- x = frame_insn (x);
- for (i = FIRST_BANKED_REG; i <= LAST_BANKED_REG; i++)
+ x = emit_frame_insn (x);
+ for (int i = FIRST_BANKED_REG; i <= LAST_BANKED_REG; i++)
{
mem = gen_rtx_MEM (SImode, plus_constant (Pmode, sp_reg, i * 4));
reg = gen_rtx_REG (SImode, i);
emit_insn (gen_blockage ());
}
else
- for (i = FIRST_BANKED_REG; i <= LAST_BANKED_REG; i++)
+ for (int i = FIRST_BANKED_REG; i <= LAST_BANKED_REG; i++)
if (TEST_HARD_REG_BIT (*mask, i))
push (i);
}
calc_live_regs (HARD_REG_SET *live_regs_mask)
{
unsigned int reg;
- int count;
tree attrs;
bool interrupt_or_trapa_handler, trapa_handler, interrupt_handler;
bool nosave_low_regs;
- int pr_live, has_call;
attrs = DECL_ATTRIBUTES (current_function_decl);
interrupt_or_trapa_handler = sh_cfun_interrupt_handler_p ();
target_flags &= ~MASK_FPU_SINGLE;
/* If we can save a lot of saves by switching to double mode, do that. */
else if (TARGET_FPU_DOUBLE && TARGET_FMOVD && TARGET_FPU_SINGLE)
- for (count = 0, reg = FIRST_FP_REG; reg <= LAST_FP_REG; reg += 2)
+ for (int count = 0, reg = FIRST_FP_REG; reg <= LAST_FP_REG; reg += 2)
if (df_regs_ever_live_p (reg) && df_regs_ever_live_p (reg+1)
- && (! call_really_used_regs[reg]
+ && (! call_used_regs[reg]
|| interrupt_handler)
&& ++count > 2)
{
break;
}
- {
- rtx pr_initial = has_hard_reg_initial_val (Pmode, PR_REG);
- pr_live = (pr_initial
+
+ rtx pr_initial = has_hard_reg_initial_val (Pmode, PR_REG);
+ bool pr_live = (pr_initial
? (!REG_P (pr_initial)
|| REGNO (pr_initial) != (PR_REG))
: df_regs_ever_live_p (PR_REG));
- /* For Shcompact, if not optimizing, we end up with a memory reference
- using the return address pointer for __builtin_return_address even
- though there is no actual need to put the PR register on the stack. */
- pr_live |= df_regs_ever_live_p (RETURN_ADDRESS_POINTER_REGNUM);
- }
+ /* For Shcompact, if not optimizing, we end up with a memory reference
+ using the return address pointer for __builtin_return_address even
+ though there is no actual need to put the PR register on the stack. */
+ pr_live |= df_regs_ever_live_p (RETURN_ADDRESS_POINTER_REGNUM);
+
/* Force PR to be live if the prologue has to call the SHmedia
argument decoder or register saver. */
- has_call = pr_live;
+ bool has_call = pr_live;
+
+ int count;
for (count = 0, reg = FIRST_PSEUDO_REGISTER; reg-- != 0; )
{
if (reg == PR_REG
: interrupt_handler
? (/* Need to save all the regs ever live. */
(df_regs_ever_live_p (reg)
- || (call_really_used_regs[reg]
+ || (call_used_regs[reg]
&& (! fixed_regs[reg] || reg == MACH_REG || reg == MACL_REG
|| reg == PIC_OFFSET_TABLE_REGNUM)
&& has_call))
: (/* Only push those regs which are used and need to be saved. */
(false)
|| (df_regs_ever_live_p (reg)
- && ((!call_really_used_regs[reg]
+ && ((!call_used_regs[reg]
&& !(reg != PIC_OFFSET_TABLE_REGNUM
- && fixed_regs[reg] && call_used_regs[reg]))
+ && fixed_regs[reg]
+ && call_used_or_fixed_reg_p (reg)))
|| (trapa_handler && reg == FPSCR_REG && TARGET_FPU_ANY)))
|| (crtl->calls_eh_return
&& (reg == EH_RETURN_DATA_REGNO (0)
if (nosave_low_regs && reg == R8_REG)
break;
}
- /* If we have a target register optimization pass after prologue / epilogue
- threading, we need to assume all target registers will be live even if
- they aren't now. */
- if (flag_branch_target_load_optimize2 && TARGET_SAVE_ALL_TARGET_REGS)
- for (reg = LAST_TARGET_REG; reg >= FIRST_TARGET_REG; reg--)
- if ((! call_really_used_regs[reg] || interrupt_handler)
- && ! TEST_HARD_REG_BIT (*live_regs_mask, reg))
- {
- SET_HARD_REG_BIT (*live_regs_mask, reg);
- count += GET_MODE_SIZE (REGISTER_NATURAL_MODE (reg));
- }
- /* If this is an interrupt handler, we don't have any call-clobbered
- registers we can conveniently use for target register save/restore.
- Make sure we save at least one general purpose register when we need
- to save target registers. */
- if (interrupt_handler
- && hard_reg_set_intersect_p (*live_regs_mask,
- reg_class_contents[TARGET_REGS])
- && ! hard_reg_set_intersect_p (*live_regs_mask,
- reg_class_contents[GENERAL_REGS]))
- {
- SET_HARD_REG_BIT (*live_regs_mask, R0_REG);
- count += GET_MODE_SIZE (REGISTER_NATURAL_MODE (R0_REG));
- }
return count;
}
return ((size + pushed + align - 1) & -align) - pushed;
}
-/* Choose a call-clobbered target-branch register that remains
- unchanged along the whole function. We set it up as the return
- value in the prologue. */
-int
-sh_media_register_for_return (void)
-{
- int regno;
- int tr0_used;
-
- if (! crtl->is_leaf)
- return -1;
- if (lookup_attribute ("interrupt_handler",
- DECL_ATTRIBUTES (current_function_decl)))
- return -1;
- if (sh_cfun_interrupt_handler_p ())
- return -1;
-
- tr0_used = flag_pic && df_regs_ever_live_p (PIC_OFFSET_TABLE_REGNUM);
-
- for (regno = FIRST_TARGET_REG + tr0_used; regno <= LAST_TARGET_REG; regno++)
- if (call_really_used_regs[regno] && ! df_regs_ever_live_p (regno))
- return regno;
-
- return -1;
-}
-
-/* The maximum registers we need to save are:
- - 62 general purpose registers (r15 is stack pointer, r63 is zero)
- - 32 floating point registers (for each pair, we save none,
- one single precision value, or a double precision value).
- - 8 target registers
- - add 1 entry for a delimiter. */
-#define MAX_SAVED_REGS (62+32+8)
-
-typedef struct save_entry_s
-{
- unsigned char reg;
- unsigned char mode;
- short offset;
-} save_entry;
-
-#define MAX_TEMPS 4
-
-/* There will be a delimiter entry with VOIDmode both at the start and the
- end of a filled in schedule. The end delimiter has the offset of the
- save with the smallest (i.e. most negative) offset. */
-typedef struct save_schedule_s
-{
- save_entry entries[MAX_SAVED_REGS + 2];
- int temps[MAX_TEMPS+1];
-} save_schedule;
-
/* Expand code for the function prologue. */
void
sh_expand_prologue (void)
{
- HARD_REG_SET live_regs_mask;
- int d, i;
- int d_rounding = 0;
int save_flags = target_flags;
- int pretend_args;
- int stack_usage;
tree sp_switch_attr
= lookup_attribute ("sp_switch", DECL_ATTRIBUTES (current_function_decl));
/* We have pretend args if we had an object sent partially in registers
and partially on the stack, e.g. a large structure. */
- pretend_args = crtl->args.pretend_args_size;
+ int pretend_args = crtl->args.pretend_args_size;
if (TARGET_VARARGS_PRETEND_ARGS (current_function_decl)
&& (NPARM_REGS(SImode)
> crtl->args.info.arg_count[(int) SH_ARG_INT]))
pretend_args = 0;
- output_stack_adjust (-pretend_args
- - crtl->args.info.stack_regs * 8,
- stack_pointer_rtx, 0, NULL, true);
- stack_usage = pretend_args + crtl->args.info.stack_regs * 8;
+ output_stack_adjust (-pretend_args, stack_pointer_rtx, 0, NULL, true);
+ int stack_usage = pretend_args;
/* Emit the code for SETUP_VARARGS. */
if (cfun->stdarg)
if (TARGET_VARARGS_PRETEND_ARGS (current_function_decl))
{
/* Push arg regs as if they'd been provided by caller in stack. */
- for (i = 0; i < NPARM_REGS(SImode); i++)
+ for (int i = 0; i < NPARM_REGS(SImode); i++)
{
int rn = NPARM_REGS(SImode) + FIRST_PARM_REG - i - 1;
/* The argument specifies a variable holding the address of the
stack the interrupt function should switch to/from at entry/exit. */
tree arg = TREE_VALUE ( TREE_VALUE (sp_switch_attr));
- const char *s
- = ggc_strdup (TREE_STRING_POINTER (arg));
+ const char* s = ggc_strdup (TREE_STRING_POINTER (arg));
rtx sp_switch = gen_rtx_SYMBOL_REF (Pmode, s);
lab = add_constant (sp_switch, SImode, 0);
emit_insn (gen_sp_switch_1 (newsrc));
}
- d = calc_live_regs (&live_regs_mask);
+ HARD_REG_SET live_regs_mask;
+ int d = calc_live_regs (&live_regs_mask);
/* ??? Maybe we could save some switching if we can move a mode switch
that already happens to be at the function start into the prologue. */
if (target_flags != save_flags && ! current_function_interrupt)
target_flags = save_flags;
- output_stack_adjust (-rounded_frame_size (d) + d_rounding,
+ output_stack_adjust (-rounded_frame_size (d),
stack_pointer_rtx, 0, NULL, true);
- stack_usage += rounded_frame_size (d) - d_rounding;
+ stack_usage += rounded_frame_size (d);
if (frame_pointer_needed)
- frame_insn (GEN_MOV (hard_frame_pointer_rtx, stack_pointer_rtx));
+ emit_frame_insn (GEN_MOV (hard_frame_pointer_rtx, stack_pointer_rtx));
/* If we are profiling, make sure no instructions are scheduled before
the call to mcount. Similarly if some call instructions are swapped
void
sh_expand_epilogue (bool sibcall_p)
{
- HARD_REG_SET live_regs_mask;
- int d, i;
- int d_rounding = 0;
-
int save_flags = target_flags;
- int frame_size, save_size;
- int fpscr_deferred = 0;
+ bool fpscr_deferred = false;
int e = sibcall_p ? -1 : 1;
- d = calc_live_regs (&live_regs_mask);
+ HARD_REG_SET live_regs_mask;
+ int d = calc_live_regs (&live_regs_mask);
- save_size = d;
- frame_size = rounded_frame_size (d);
+ int save_size = d;
+ int frame_size = rounded_frame_size (d);
if (frame_pointer_needed)
{
occur after the SP adjustment and clobber data in the local
frame. */
emit_insn (gen_blockage ());
- frame_insn (GEN_MOV (stack_pointer_rtx, hard_frame_pointer_rtx));
+ emit_frame_insn (GEN_MOV (stack_pointer_rtx, hard_frame_pointer_rtx));
}
else if (frame_size)
{
{
unsigned int count = 0;
- for (i = FIRST_BANKED_REG; i <= LAST_BANKED_REG; i++)
+ for (int i = FIRST_BANKED_REG; i <= LAST_BANKED_REG; i++)
if (TEST_HARD_REG_BIT (live_regs_mask, i))
count++;
else
emit_insn (gen_blockage ());
}
else
- for (i = LAST_BANKED_REG; i >= FIRST_BANKED_REG; i--)
+ for (int i = LAST_BANKED_REG; i >= FIRST_BANKED_REG; i--)
if (TEST_HARD_REG_BIT (live_regs_mask, i))
pop (i);
else
last_reg = FIRST_PSEUDO_REGISTER;
- for (i = 0; i < last_reg; i++)
+ for (int i = 0; i < last_reg; i++)
{
int j = (FIRST_PSEUDO_REGISTER - 1) - i;
if (j == FPSCR_REG && current_function_interrupt && TARGET_FMOVD
&& hard_reg_set_intersect_p (live_regs_mask,
reg_class_contents[DF_REGS]))
- fpscr_deferred = 1;
+ fpscr_deferred = true;
/* For an ISR with RESBANK attribute assigned, don't pop
following registers, R0-R14, MACH, MACL and GBR. */
else if (j != PR_REG && TEST_HARD_REG_BIT (live_regs_mask, j)
emit_insn (gen_toggle_sz ());
target_flags = save_flags;
- output_stack_adjust (crtl->args.pretend_args_size
- + save_size + d_rounding
- + crtl->args.info.stack_regs * 8,
+ output_stack_adjust (crtl->args.pretend_args_size + save_size,
stack_pointer_rtx, e, NULL, true);
if (crtl->calls_eh_return)
emit_insn (gen_sp_switch_2 ());
/* Tell flow the insn that pops PR isn't dead. */
- /* PR_REG will never be live in SHmedia mode, and we don't need to
- USE PR_MEDIA_REG, since it will be explicitly copied to TR0_REG
- by the return pattern. */
if (TEST_HARD_REG_BIT (live_regs_mask, PR_REG))
emit_use (gen_rtx_REG (SImode, PR_REG));
}
HARD_REG_SET live_regs_mask;
int d = calc_live_regs (&live_regs_mask);
- /* If pr_reg isn't life, we can set it (or the register given in
- sh_media_register_for_return) directly. */
+ /* If pr_reg isn't life, we can set it directly. */
if (! TEST_HARD_REG_BIT (live_regs_mask, PR_REG))
{
rtx rr = gen_rtx_REG (SImode, PR_REG);
/* Clear variables at function end. */
static void
-sh_output_function_epilogue (FILE *file ATTRIBUTE_UNUSED,
- HOST_WIDE_INT size ATTRIBUTE_UNUSED)
+sh_output_function_epilogue (FILE *)
{
}
int bufsize, regno;
alias_set_type alias_set;
- if (! TARGET_SH2E && ! TARGET_SH4)
+ if (!TARGET_FPU_ANY)
{
- error ("__builtin_saveregs not supported by this subtarget");
+ error ("%<__builtin_saveregs%> not supported by this subtarget");
return const0_rtx;
}
sh_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
gimple_seq *post_p ATTRIBUTE_UNUSED)
{
- HOST_WIDE_INT size, rsize;
- tree tmp, pptr_type_node;
+ tree tmp;
tree addr, lab_over = NULL, result = NULL;
- bool pass_by_ref;
tree eff_type;
- if (!VOID_TYPE_P (type))
- pass_by_ref = targetm.calls.must_pass_in_stack (TYPE_MODE (type), type);
- else
- pass_by_ref = false;
+ const bool pass_by_ref
+ = !VOID_TYPE_P (type) && must_pass_va_arg_in_stack (type);
if (pass_by_ref)
type = build_pointer_type (type);
- size = int_size_in_bytes (type);
- rsize = (size + UNITS_PER_WORD - 1) & -UNITS_PER_WORD;
- pptr_type_node = build_pointer_type (ptr_type_node);
+ HOST_WIDE_INT size = int_size_in_bytes (type);
+ HOST_WIDE_INT rsize = (size + UNITS_PER_WORD - 1) & -UNITS_PER_WORD;
+ tree pptr_type_node = build_pointer_type (ptr_type_node);
if ((TARGET_SH2E || TARGET_SH4)
&& ! (TARGET_HITACHI || sh_cfun_attr_renesas_p ()))
{
tree f_next_o, f_next_o_limit, f_next_fp, f_next_fp_limit, f_next_stack;
tree next_o, next_o_limit, next_fp, next_fp_limit, next_stack;
- int pass_as_float;
tree lab_false;
tree member;
}
}
+ bool pass_as_float;
if (TARGET_FPU_DOUBLE)
{
pass_as_float = ((TREE_CODE (eff_type) == REAL_TYPE && size <= 8)
}
static bool
-sh_pass_by_reference (cumulative_args_t cum_v, machine_mode mode,
- const_tree type, bool named ATTRIBUTE_UNUSED)
+sh_pass_by_reference (cumulative_args_t cum_v, const function_arg_info &arg)
{
CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v);
- if (targetm.calls.must_pass_in_stack (mode, type))
+ if (targetm.calls.must_pass_in_stack (arg))
return true;
/* ??? std_gimplify_va_arg_expr passes NULL for cum. That function
}
static bool
-sh_callee_copies (cumulative_args_t cum, machine_mode mode,
- const_tree type, bool named ATTRIBUTE_UNUSED)
+sh_callee_copies (cumulative_args_t cum, const function_arg_info &arg)
{
/* ??? How can it possibly be correct to return true only on the
caller side of the equation? Is there someplace else in the
sh backend that's magically producing the copies? */
return (get_cumulative_args (cum)->outgoing
- && ((mode == BLKmode ? TYPE_ALIGN (type) : GET_MODE_ALIGNMENT (mode))
+ && ((arg.mode == BLKmode
+ ? TYPE_ALIGN (arg.type)
+ : GET_MODE_ALIGNMENT (arg.mode))
% SH_MIN_ALIGN_FOR_CALLEE_COPY == 0));
}
+static sh_arg_class
+get_sh_arg_class (machine_mode mode)
+{
+ if (TARGET_FPU_ANY && mode == SFmode)
+ return SH_ARG_FLOAT;
+
+ if (TARGET_FPU_DOUBLE
+ && (GET_MODE_CLASS (mode) == MODE_FLOAT
+ || GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT))
+ return SH_ARG_FLOAT;
+
+ return SH_ARG_INT;
+}
+
/* Round a register number up to a proper boundary for an arg of mode
MODE.
The SH doesn't care about double alignment, so we only
&& (mode == DFmode || mode == DCmode)
&& cum.arg_count[(int) SH_ARG_FLOAT] < NPARM_REGS (mode)))
&& GET_MODE_UNIT_SIZE (mode) > UNITS_PER_WORD)
- ? (cum.arg_count[(int) GET_SH_ARG_CLASS (mode)]
- + (cum.arg_count[(int) GET_SH_ARG_CLASS (mode)] & 1))
- : cum.arg_count[(int) GET_SH_ARG_CLASS (mode)]);
+ ? (cum.arg_count[(int) get_sh_arg_class (mode)]
+ + (cum.arg_count[(int) get_sh_arg_class (mode)] & 1))
+ : cum.arg_count[(int) get_sh_arg_class (mode)]);
}
/* Return true if arg of the specified mode should be passed in a register
+ int_size_in_bytes (type))
<= NPARM_REGS (SImode) * UNITS_PER_WORD)
: ((sh_round_reg (cum, mode)
- + HARD_REGNO_NREGS (BASE_ARG_REG (mode), mode))
+ + sh_hard_regno_nregs (BASE_ARG_REG (mode), mode))
<= NPARM_REGS (mode)))
: sh_round_reg (cum, mode) < NPARM_REGS (mode)));
}
static int
-sh_arg_partial_bytes (cumulative_args_t cum_v, machine_mode mode,
- tree type, bool named ATTRIBUTE_UNUSED)
+sh_arg_partial_bytes (cumulative_args_t cum_v, const function_arg_info &arg)
{
CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v);
int words = 0;
- if (sh_pass_in_reg_p (*cum, mode, type)
+ if (sh_pass_in_reg_p (*cum, arg.mode, arg.type)
&& !TARGET_FPU_DOUBLE
- && (sh_round_reg (*cum, mode)
- + (mode != BLKmode
- ? CEIL (GET_MODE_SIZE (mode), UNITS_PER_WORD)
- : CEIL (int_size_in_bytes (type), UNITS_PER_WORD))
- > NPARM_REGS (mode)))
- words = NPARM_REGS (mode) - sh_round_reg (*cum, mode);
+ && (sh_round_reg (*cum, arg.mode)
+ + CEIL (arg.promoted_size_in_bytes (), UNITS_PER_WORD)
+ > NPARM_REGS (arg.mode)))
+ words = NPARM_REGS (arg.mode) - sh_round_reg (*cum, arg.mode);
return words * UNITS_PER_WORD;
}
Value is zero to push the argument on the stack,
or a hard register in which to store the argument.
- MODE is the argument's machine mode.
- TYPE is the data type of the argument (as a tree).
- This is null for libcalls where that information may
- not be available.
CUM is a variable of type CUMULATIVE_ARGS which gives info about
the preceding args and about the function being called.
- NAMED is nonzero if this argument is a named parameter
- (otherwise it is an extra parameter matching an ellipsis).
+ ARG is a description of the argument.
On SH the first args are normally in registers
and the rest are pushed. Any arg that starts within the first
NPARM_REGS words is at least partially passed in a register unless
its data type forbids. */
static rtx
-sh_function_arg (cumulative_args_t ca_v, machine_mode mode,
- const_tree type, bool named)
+sh_function_arg (cumulative_args_t ca_v, const function_arg_info &arg)
{
CUMULATIVE_ARGS *ca = get_cumulative_args (ca_v);
+ machine_mode mode = arg.mode;
- if (mode == VOIDmode)
+ if (arg.end_marker_p ())
return ca->renesas_abi ? const1_rtx : const0_rtx;
- if (sh_pass_in_reg_p (*ca, mode, type)
- && (named || ! (TARGET_HITACHI || ca->renesas_abi)))
+ if (sh_pass_in_reg_p (*ca, mode, arg.type)
+ && (arg.named || ! (TARGET_HITACHI || ca->renesas_abi)))
{
int regno;
return NULL_RTX;
}
-/* Update the data in CUM to advance over an argument
- of mode MODE and data type TYPE.
- (TYPE is null for libcalls where that information may not be
- available.) */
+/* Update the data in CUM to advance over argument ARG. */
static void
-sh_function_arg_advance (cumulative_args_t ca_v, machine_mode mode,
- const_tree type, bool named ATTRIBUTE_UNUSED)
+sh_function_arg_advance (cumulative_args_t ca_v,
+ const function_arg_info &arg)
{
CUMULATIVE_ARGS *ca = get_cumulative_args (ca_v);
if (ca->force_mem)
- ca->force_mem = 0;
+ ca->force_mem = false;
if ((TARGET_HITACHI || ca->renesas_abi) && TARGET_FPU_DOUBLE)
{
/* Note that we've used the skipped register. */
- if (mode == SFmode && ca->free_single_fp_reg)
+ if (arg.mode == SFmode && ca->free_single_fp_reg)
{
ca->free_single_fp_reg = 0;
return;
skipped in order to align the DF value. We note this skipped
register, because the next SF value will use it, and not the
SF that follows the DF. */
- if (mode == DFmode
+ if (arg.mode == DFmode
&& sh_round_reg (*ca, DFmode) != sh_round_reg (*ca, SFmode))
{
ca->free_single_fp_reg = (sh_round_reg (*ca, SFmode)
- + BASE_ARG_REG (mode));
+ + BASE_ARG_REG (arg.mode));
}
}
if (! ((TARGET_SH4 || TARGET_SH2A) || ca->renesas_abi)
- || sh_pass_in_reg_p (*ca, mode, type))
- (ca->arg_count[(int) GET_SH_ARG_CLASS (mode)]
- = (sh_round_reg (*ca, mode)
- + (mode == BLKmode
- ? CEIL (int_size_in_bytes (type), UNITS_PER_WORD)
- : CEIL (GET_MODE_SIZE (mode), UNITS_PER_WORD))));
+ || sh_pass_in_reg_p (*ca, arg.mode, arg.type))
+ (ca->arg_count[(int) get_sh_arg_class (arg.mode)]
+ = (sh_round_reg (*ca, arg.mode)
+ + CEIL (arg.promoted_size_in_bytes (), UNITS_PER_WORD)));
}
/* The Renesas calling convention doesn't quite fit into this scheme since
function that tell if a function uses varargs or stdarg. */
static void
sh_setup_incoming_varargs (cumulative_args_t ca,
- machine_mode mode,
- tree type,
+ const function_arg_info &arg,
int *pretend_arg_size,
int second_time ATTRIBUTE_UNUSED)
{
{
int named_parm_regs, anon_parm_regs;
- named_parm_regs = (sh_round_reg (*get_cumulative_args (ca), mode)
- + (mode == BLKmode
- ? CEIL (int_size_in_bytes (type), UNITS_PER_WORD)
- : CEIL (GET_MODE_SIZE (mode), UNITS_PER_WORD)));
+ named_parm_regs = (sh_round_reg (*get_cumulative_args (ca), arg.mode)
+ + CEIL (arg.promoted_size_in_bytes (),
+ UNITS_PER_WORD));
anon_parm_regs = NPARM_REGS (SImode) - named_parm_regs;
if (anon_parm_regs > 0)
*pretend_arg_size = anon_parm_regs * 4;
int
initial_elimination_offset (int from, int to)
{
- int regs_saved;
- int regs_saved_rounding = 0;
- int total_saved_regs_space;
- int total_auto_space;
+ const int regs_saved_rounding = 0;
int save_flags = target_flags;
HARD_REG_SET live_regs_mask;
- regs_saved = calc_live_regs (&live_regs_mask);
+ int regs_saved = calc_live_regs (&live_regs_mask);
- total_auto_space = rounded_frame_size (regs_saved) - regs_saved_rounding;
+ int total_auto_space = rounded_frame_size (regs_saved) - regs_saved_rounding;
target_flags = save_flags;
- total_saved_regs_space = regs_saved + regs_saved_rounding;
+ int total_saved_regs_space = regs_saved + regs_saved_rounding;
if (from == ARG_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM)
- return total_saved_regs_space + total_auto_space
- + crtl->args.info.byref_regs * 8;
+ return total_saved_regs_space + total_auto_space;
if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
- return total_saved_regs_space + total_auto_space
- + crtl->args.info.byref_regs * 8;
+ return total_saved_regs_space + total_auto_space;
/* Initial gap between fp and sp is 0. */
if (from == HARD_FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
void
sh_fix_range (const char *const_str)
{
- int i, first, last;
- char *str, *dash, *comma;
-
/* str must be of the form REG1'-'REG2{,REG1'-'REG} where REG1 and
REG2 are either register names or register numbers. The effect
of this option is to mark the registers in the range from REG1 to
REG2 as ``fixed'' so they won't be used by the compiler. */
- i = strlen (const_str);
- str = (char *) alloca (i + 1);
- memcpy (str, const_str, i + 1);
+ char* str = strcpy ((char*)alloca (strlen (const_str) + 1), const_str);
while (1)
{
- dash = strchr (str, '-');
+ char* dash = strchr (str, '-');
if (!dash)
{
- warning (0, "value of -mfixed-range must have form REG1-REG2");
+ warning (0, "value of %<-mfixed-range%> must have form REG1-REG2");
return;
}
*dash = '\0';
- comma = strchr (dash + 1, ',');
+ char* comma = strchr (dash + 1, ',');
if (comma)
*comma = '\0';
- first = decode_reg_name (str);
+ int first = decode_reg_name (str);
if (first < 0)
{
warning (0, "unknown register name: %s", str);
return;
}
- last = decode_reg_name (dash + 1);
+ int last = decode_reg_name (dash + 1);
if (last < 0)
{
warning (0, "unknown register name: %s", dash + 1);
return;
}
- for (i = first; i <= last; ++i)
- fixed_regs[i] = call_used_regs[i] = 1;
+ for (int i = first; i <= last; ++i)
+ fixed_regs[i] = 1;
if (!comma)
break;
static void
sh_insert_attributes (tree node, tree *attributes)
{
- tree attrs;
-
if (TREE_CODE (node) != FUNCTION_DECL)
return;
/* Append the attributes to the deferred attributes. */
*sh_deferred_function_attributes_tail = *attributes;
- attrs = sh_deferred_function_attributes;
+ tree attrs = sh_deferred_function_attributes;
if (!attrs)
return;
int
sh2a_get_function_vector_number (rtx x)
{
- int num;
- tree list, t;
-
if ((GET_CODE (x) == SYMBOL_REF)
&& (SYMBOL_REF_FLAGS (x) & SYMBOL_FLAG_FUNCVEC_FUNCTION))
{
- t = SYMBOL_REF_DECL (x);
+ tree t = SYMBOL_REF_DECL (x);
if (TREE_CODE (t) != FUNCTION_DECL)
return 0;
- list = SH_ATTRIBUTES (t);
- while (list)
- {
- if (is_attribute_p ("function_vector", TREE_PURPOSE (list)))
- {
- num = TREE_INT_CST_LOW (TREE_VALUE (TREE_VALUE (list)));
- return num;
- }
-
- list = TREE_CHAIN (list);
- }
+ for (tree list = SH_ATTRIBUTES (t); list; list = TREE_CHAIN (list))
+ if (is_attribute_p ("function_vector", TREE_PURPOSE (list)))
+ return TREE_INT_CST_LOW (TREE_VALUE (TREE_VALUE (list)));
return 0;
}
td = TREE_TYPE (td);
if (td == error_mark_node)
return false;
- return (lookup_attribute ("renesas", TYPE_ATTRIBUTES (td))
- != NULL_TREE);
+ return lookup_attribute ("renesas", TYPE_ATTRIBUTES (td)) != NULL_TREE;
}
/* True if __attribute__((renesas)) or -mrenesas, for the current
bool
sh2a_function_vector_p (tree func)
{
- tree list;
if (TREE_CODE (func) != FUNCTION_DECL)
return false;
- list = SH_ATTRIBUTES (func);
- while (list)
- {
- if (is_attribute_p ("function_vector", TREE_PURPOSE (list)))
- return true;
+ for (tree list = SH_ATTRIBUTES (func); list; list = TREE_CHAIN (list))
+ if (is_attribute_p ("function_vector", get_attribute_name (list)))
+ return true;
- list = TREE_CHAIN (list);
- }
return false;
}
bool
fp_zero_operand (rtx op)
{
- const REAL_VALUE_TYPE *r;
-
if (GET_MODE (op) != SFmode)
return false;
- r = CONST_DOUBLE_REAL_VALUE (op);
+ const REAL_VALUE_TYPE* r = CONST_DOUBLE_REAL_VALUE (op);
return real_equal (r, &dconst0) && ! REAL_VALUE_MINUS_ZERO (*r);
}
branch_dest (rtx branch)
{
rtx dest = SET_SRC (PATTERN (branch));
- int dest_uid;
if (GET_CODE (dest) == IF_THEN_ELSE)
dest = XEXP (dest, 1);
- dest = XEXP (dest, 0);
- dest_uid = INSN_UID (dest);
- return INSN_ADDRESSES (dest_uid);
+
+ return INSN_ADDRESSES (INSN_UID (XEXP (dest, 0)));
}
\f
/* Return nonzero if REG is not used after INSN.
bool
reg_unused_after (rtx reg, rtx_insn *insn)
{
- enum rtx_code code;
- rtx set;
-
/* If the reg is set by this instruction, then it is safe for our
case. Disregard the case where this is a store to memory, since
we are checking a register used in the store address. */
- set = single_set (insn);
+ rtx set = single_set (insn);
if (set && !MEM_P (SET_DEST (set))
&& reg_overlap_mentioned_p (reg, SET_DEST (set)))
return true;
while ((insn = NEXT_INSN (insn)))
{
- rtx set;
if (!INSN_P (insn))
continue;
- code = GET_CODE (insn);
+ rtx_code code = GET_CODE (insn);
#if 0
/* If this is a label that existed before reload, then the register
else if (code == INSN && GET_CODE (PATTERN (insn)) == SEQUENCE)
{
rtx_sequence *seq = as_a <rtx_sequence *> (PATTERN (insn));
- int i;
- int retval = 0;
+ bool retval = false;
- for (i = 0; i < seq->len (); i++)
+ for (int i = 0; i < seq->len (); i++)
{
rtx_insn *this_insn = seq->insn (i);
rtx set = single_set (this_insn);
&& reg_overlap_mentioned_p (reg, PATTERN (this_insn)))
return false;
}
- if (retval == 1)
+ if (retval)
return true;
else if (code == JUMP_INSN)
return false;
}
- set = single_set (insn);
+ rtx set = single_set (insn);
if (set && reg_overlap_mentioned_p (reg, SET_SRC (set)))
return false;
if (set && reg_overlap_mentioned_p (reg, SET_DEST (set)))
return !MEM_P (SET_DEST (set));
- if (set == 0 && reg_overlap_mentioned_p (reg, PATTERN (insn)))
+ if (set == NULL && reg_overlap_mentioned_p (reg, PATTERN (insn)))
return false;
- if (code == CALL_INSN && call_really_used_regs[REGNO (reg)])
+ if (code == CALL_INSN && call_used_regs[REGNO (reg)])
return true;
}
return true;
static void
emit_fpu_switch (rtx scratch, int index)
{
- rtx src;
-
if (fpscr_values == NULL)
{
- tree t;
-
- t = build_index_type (integer_one_node);
+ tree t = build_index_type (integer_one_node);
t = build_array_type (integer_type_node, t);
t = build_decl (BUILTINS_LOCATION,
VAR_DECL, get_identifier ("__fpscr_values"), t);
fpscr_values = t;
}
- src = DECL_RTL (fpscr_values);
+ rtx src = DECL_RTL (fpscr_values);
if (!can_create_pseudo_p ())
{
emit_move_insn (scratch, XEXP (src, 0));
{
enum attr_fp_mode fp_mode = (enum attr_fp_mode) mode;
enum attr_fp_mode norm_mode = ACTUAL_NORMAL_MODE (FP_MODE);
- rtx addr_reg;
- addr_reg = !can_create_pseudo_p () ? get_free_reg (regs_live) : NULL_RTX;
+ rtx addr_reg = !can_create_pseudo_p () ? get_free_reg (regs_live) : NULL_RTX;
emit_fpu_switch (addr_reg, fp_mode == norm_mode);
}
static bool
sequence_insn_p (rtx_insn *insn)
{
- rtx_insn *prev, *next;
-
- prev = PREV_INSN (insn);
+ rtx_insn* prev = PREV_INSN (insn);
if (prev == NULL)
return false;
- next = NEXT_INSN (prev);
+ rtx_insn* next = NEXT_INSN (prev);
if (next == NULL)
return false;
bool
nonpic_symbol_mentioned_p (rtx x)
{
- const char *fmt;
- int i;
-
if (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == LABEL_REF
|| GET_CODE (x) == PC)
return true;
|| XINT (x, 1) == UNSPEC_GOTOFFFUNCDESC))
return false;
- fmt = GET_RTX_FORMAT (GET_CODE (x));
- for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
+ const char* fmt = GET_RTX_FORMAT (GET_CODE (x));
+ for (int i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
{
if (fmt[i] == 'E')
{
- int j;
- for (j = XVECLEN (x, i) - 1; j >= 0; j--)
+ for (int j = XVECLEN (x, i) - 1; j >= 0; j--)
if (nonpic_symbol_mentioned_p (XVECEXP (x, i, j)))
return true;
}
/* Convert a non-PIC address in `orig' to a PIC address using @GOT or
@GOTOFF in `reg'. */
rtx
-legitimize_pic_address (rtx orig, machine_mode mode ATTRIBUTE_UNUSED,
- rtx reg)
+legitimize_pic_address (rtx orig, machine_mode mode ATTRIBUTE_UNUSED, rtx reg)
{
if (tls_symbolic_operand (orig, Pmode) != TLS_MODEL_NONE)
return orig;
static rtx
sh_delegitimize_address (rtx orig_x)
{
- rtx x, y;
-
orig_x = delegitimize_mem_from_attrs (orig_x);
- x = orig_x;
+ rtx x = orig_x;
if (MEM_P (x))
x = XEXP (x, 0);
if (GET_CODE (x) == CONST)
{
- y = XEXP (x, 0);
+ rtx y = XEXP (x, 0);
if (GET_CODE (y) == UNSPEC)
{
if (XINT (y, 1) == UNSPEC_GOT
static rtx
mark_constant_pool_use (rtx x)
{
- rtx_insn *insn, *lab;
- rtx pattern;
-
if (x == NULL_RTX)
return x;
/* Get the first label in the list of labels for the same constant
and delete another labels in the list. */
- lab = as_a <rtx_insn *> (x);
- for (insn = PREV_INSN (lab); insn; insn = PREV_INSN (insn))
+ rtx_insn* lab = as_a <rtx_insn*> (x);
+ for (rtx_insn* insn = PREV_INSN (lab); insn; insn = PREV_INSN (insn))
{
if (!LABEL_P (insn)
|| LABEL_REFS (insn) != NEXT_INSN (insn))
as_a<rtx_insn *> (insn)->set_deleted ();
/* Mark constants in a window. */
- for (insn = NEXT_INSN (as_a <rtx_insn *> (x)); insn; insn = NEXT_INSN (insn))
+ for (rtx_insn* insn = NEXT_INSN (as_a <rtx_insn *> (x)); insn;
+ insn = NEXT_INSN (insn))
{
if (!NONJUMP_INSN_P (insn))
continue;
- pattern = PATTERN (insn);
+ rtx pattern = PATTERN (insn);
if (GET_CODE (pattern) != UNSPEC_VOLATILE)
continue;
the same cost as a data-dependence. The return value should be
the new value for COST. */
static int
-sh_adjust_cost (rtx_insn *insn, rtx link ATTRIBUTE_UNUSED,
- rtx_insn *dep_insn, int cost)
+sh_adjust_cost (rtx_insn *insn, int dep_type, rtx_insn *dep_insn, int cost,
+ unsigned int)
{
rtx reg, use_pat;
- if (REG_NOTE_KIND (link) == 0)
+ if (dep_type == 0)
{
- enum attr_type type;
- rtx dep_set;
-
if (recog_memoized (insn) < 0
|| recog_memoized (dep_insn) < 0)
return cost;
- dep_set = single_set (dep_insn);
+ rtx dep_set = single_set (dep_insn);
/* The latency that we specify in the scheduling description refers
to the actual output, not to an auto-increment register; for that,
}
if (TARGET_HARD_SH4 && !TARGET_SH4_300)
{
- enum attr_type dep_type = get_attr_type (dep_insn);
-
+ attr_type dep_type = get_attr_type (dep_insn);
+ attr_type type;
if (dep_type == TYPE_FLOAD || dep_type == TYPE_PCFLOAD)
cost--;
else if ((dep_type == TYPE_LOAD_SI || dep_type == TYPE_PCLOAD_SI)
else if (TARGET_SH4_300)
{
/* Stores need their input register two cycles later. */
+ attr_type type;
if (dep_set && cost >= 1
&& ((type = get_attr_type (insn)) == TYPE_STORE
|| type == TYPE_PSTORE
/* An anti-dependence penalty of two applies if the first insn is a double
precision fadd / fsub / fmul. */
else if (!TARGET_SH4_300
- && REG_NOTE_KIND (link) == REG_DEP_ANTI
+ && dep_type == REG_DEP_ANTI
&& recog_memoized (dep_insn) >= 0
&& (get_attr_type (dep_insn) == TYPE_DFP_ARITH
|| get_attr_type (dep_insn) == TYPE_DFP_MUL)
/* Check if INSN is flow-dependent on DEP_INSN. Can also be used to check
if DEP_INSN is anti-flow dependent on INSN. */
static bool
-flow_dependent_p (rtx insn, rtx dep_insn)
+flow_dependent_p (rtx_insn *insn, rtx_insn *dep_insn)
{
rtx tmp = PATTERN (insn);
- note_stores (PATTERN (dep_insn), flow_dependent_p_1, &tmp);
+ note_stores (dep_insn, flow_dependent_p_1, &tmp);
return tmp == NULL_RTX;
}
static short
find_insn_regmode_weight (rtx insn, machine_mode mode)
{
- short reg_weight = 0;
- rtx x;
-
/* Increment weight for each register born here. */
- x = PATTERN (insn);
- reg_weight += find_set_regmode_weight (x, mode);
+ rtx x = PATTERN (insn);
+ short reg_weight = find_set_regmode_weight (x, mode);
if (GET_CODE (x) == PARALLEL)
{
int j;
static int
find_r0_life_regions (basic_block b)
{
- rtx_insn *end, *insn;
- rtx pset;
- rtx r0_reg;
- int live;
+ bool live;
int set;
int death = 0;
if (REGNO_REG_SET_P (df_get_live_in (b), R0_REG))
{
set = 1;
- live = 1;
+ live = true;
}
else
{
set = 0;
- live = 0;
+ live = false;
}
- insn = BB_HEAD (b);
- end = BB_END (b);
- r0_reg = gen_rtx_REG (SImode, R0_REG);
+ rtx_insn* insn = BB_HEAD (b);
+ rtx_insn* end = BB_END (b);
+ rtx r0_reg = gen_rtx_REG (SImode, R0_REG);
while (1)
{
if (INSN_P (insn))
if (find_regno_note (insn, REG_DEAD, R0_REG))
{
death++;
- live = 0;
+ live = false;
}
+
+ rtx pset;
if (!live
&& (pset = single_set (insn))
&& reg_overlap_mentioned_p (r0_reg, SET_DEST (pset))
&& !find_regno_note (insn, REG_UNUSED, R0_REG))
{
set++;
- live = 1;
+ live = true;
}
}
if (insn == end)
return 0;
}
-static reg_class_t
-sh_target_reg_class (void)
-{
- return NO_REGS;
-}
-
-static bool
-sh_optimize_target_register_callee_saved (bool after_prologue_epilogue_gen
- ATTRIBUTE_UNUSED)
-{
- return false;
-}
-
static bool
sh_ms_bitfield_layout_p (const_tree record_type ATTRIBUTE_UNUSED)
{
|| (!(TARGET_SH4A || TARGET_SH4_300) && TARGET_USERMODE))
emit_library_call (function_symbol (NULL, "__ic_invalidate",
FUNCTION_ORDINARY).sym,
- LCT_NORMAL, VOIDmode, 1, tramp, SImode);
+ LCT_NORMAL, VOIDmode, tramp, SImode);
else
emit_insn (gen_ic_invalidate_line (tramp));
}
return tramp;
}
-/* FIXME: This is overly conservative. A SHcompact function that
- receives arguments ``by reference'' will have them stored in its
- own stack frame, so it must not pass pointers or references to
- these arguments to other functions by means of sibling calls. */
/* If PIC, we cannot make sibling calls to global functions
because the PLT requires r12 to be live. */
static bool
machine_mode mode ATTRIBUTE_UNUSED, int ignore)
{
tree fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0);
- unsigned int fcode = DECL_FUNCTION_CODE (fndecl);
+ unsigned int fcode = DECL_MD_FUNCTION_CODE (fndecl);
const struct builtin_description *d = &bdesc[fcode];
enum insn_code icode = d->icode;
int signature = d->signature;
for (int i = 1; i <= 3; i++, nop++)
{
- tree arg;
- machine_mode opmode, argmode;
- tree optype;
-
if (! signature_args[signature][i])
break;
- arg = CALL_EXPR_ARG (exp, i - 1);
+ tree arg = CALL_EXPR_ARG (exp, i - 1);
if (arg == error_mark_node)
return const0_rtx;
+
+ machine_mode opmode;
+ tree optype;
if (signature_args[signature][i] & 8)
{
opmode = ptr_mode;
opmode = insn_data[icode].operand[nop].mode;
optype = (*lang_hooks.types.type_for_mode) (opmode, 0);
}
- argmode = TYPE_MODE (TREE_TYPE (arg));
+
+ machine_mode argmode = TYPE_MODE (TREE_TYPE (arg));
if (argmode != opmode)
arg = build1 (NOP_EXPR, optype, arg);
op[nop] = expand_expr (arg, NULL_RTX, opmode, EXPAND_NORMAL);
return target;
}
-/* Return true if hard register REGNO can hold a value of machine-mode MODE.
+/* Implement TARGET_HARD_REGNO_NREGS. On the SH all but the XD regs are
+ UNITS_PER_WORD bits wide. */
+
+static unsigned int
+sh_hard_regno_nregs (unsigned int regno, machine_mode mode)
+{
+ if (XD_REGISTER_P (regno))
+ return CEIL (GET_MODE_SIZE (mode), 2 * UNITS_PER_WORD);
+ return CEIL (GET_MODE_SIZE (mode), UNITS_PER_WORD);
+}
+
+/* Implement TARGET_HARD_REGNO_MODE_OK.
+
We can allow any mode in any general register. The special registers
only allow SImode. Don't allow any mode in the PR.
We want to allow TImode FP regs so that when V4SFmode is loaded as TImode,
it won't be ferried through GP registers first. */
-bool
+static bool
sh_hard_regno_mode_ok (unsigned int regno, machine_mode mode)
{
if (SPECIAL_REGISTER_P (regno))
if (XD_REGISTER_P (regno))
return mode == DFmode;
- if (TARGET_REGISTER_P (regno))
- return (mode == DImode || mode == SImode || mode == PDImode);
-
if (regno == PR_REG)
return mode == SImode;
return true;
}
+/* Implement TARGET_MODES_TIEABLE_P.
+
+ If TARGET_HARD_REGNO_MODE_OK could produce different values for MODE1
+ and MODE2, for any hard reg, then this must be false for correct output.
+ That's the case for xd registers: we don't hold SFmode values in
+ them, so we can't tie an SFmode pseudos with one in another
+ floating-point mode. */
+
+static bool
+sh_modes_tieable_p (machine_mode mode1, machine_mode mode2)
+{
+ return (mode1 == mode2
+ || (GET_MODE_CLASS (mode1) == GET_MODE_CLASS (mode2)
+ && (mode1 != SFmode && mode2 != SFmode)));
+}
+
/* Specify the modes required to caller save a given hard regno.
- choose_hard_reg_mode chooses mode based on HARD_REGNO_MODE_OK
+ choose_hard_reg_mode chooses mode based on TARGET_HARD_REGNO_MODE_OK
and returns ?Imode for float regs when sh_hard_regno_mode_ok
permits integer modes on them. That makes LRA's split process
unhappy. See PR55212.
&& ((regno - FIRST_FP_REG) & 1) == 0)))
return mode;
- return choose_hard_reg_mode (regno, nregs, false);
+ return choose_hard_reg_mode (regno, nregs, NULL);
}
-/* Return the class of registers for which a mode change from FROM to TO
- is invalid. */
-bool
-sh_cannot_change_mode_class (machine_mode from, machine_mode to,
- enum reg_class rclass)
+/* Implement TARGET_CAN_CHANGE_MODE_CLASS. */
+static bool
+sh_can_change_mode_class (machine_mode from, machine_mode to,
+ reg_class_t rclass)
{
/* We want to enable the use of SUBREGs as a means to
VEC_SELECT a single element of a vector. */
on the stack with displacement addressing, as it happens with -O0.
Thus we disallow the mode change for -O0. */
if (to == SFmode && VECTOR_MODE_P (from) && GET_MODE_INNER (from) == SFmode)
- return optimize ? (reg_classes_intersect_p (GENERAL_REGS, rclass)) : false;
+ return optimize ? !reg_classes_intersect_p (GENERAL_REGS, rclass) : true;
if (GET_MODE_SIZE (from) != GET_MODE_SIZE (to))
{
if (TARGET_LITTLE_ENDIAN)
{
if (GET_MODE_SIZE (to) < 8 || GET_MODE_SIZE (from) < 8)
- return reg_classes_intersect_p (DF_REGS, rclass);
+ return !reg_classes_intersect_p (DF_REGS, rclass);
}
else
{
if (GET_MODE_SIZE (from) < 8)
- return reg_classes_intersect_p (DF_REGS, rclass);
+ return !reg_classes_intersect_p (DF_REGS, rclass);
}
}
- return false;
+ return true;
}
/* Return true if registers in machine mode MODE will likely be
&& (dstclass == PR_REGS || dstclass == MAC_REGS)))
return 7;
- if ((srcclass == TARGET_REGS && ! REGCLASS_HAS_GENERAL_REG (dstclass))
- || ((dstclass) == TARGET_REGS && ! REGCLASS_HAS_GENERAL_REG (srcclass)))
- return 20;
-
if ((srcclass == FPSCR_REGS && ! REGCLASS_HAS_GENERAL_REG (dstclass))
|| (dstclass == FPSCR_REGS && ! REGCLASS_HAS_GENERAL_REG (srcclass)))
return 4;
HOST_WIDE_INT delta, HOST_WIDE_INT vcall_offset,
tree function)
{
+ const char *fnname = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (thunk_fndecl));
CUMULATIVE_ARGS cum;
int structure_value_byref = 0;
rtx this_rtx, this_value, sibcall, funexp;
{
tree ptype = build_pointer_type (TREE_TYPE (funtype));
- sh_function_arg_advance (pack_cumulative_args (&cum), Pmode, ptype, true);
+ function_arg_info ptr_arg (ptype, Pmode, /*named=*/true);
+ sh_function_arg_advance (pack_cumulative_args (&cum), ptr_arg);
}
- this_rtx
- = sh_function_arg (pack_cumulative_args (&cum), Pmode, ptr_type_node, true);
+ function_arg_info ptr_arg (ptr_type_node, Pmode, /*named=*/true);
+ this_rtx = sh_function_arg (pack_cumulative_args (&cum), ptr_arg);
/* For SHcompact, we only have r0 for a scratch register: r1 is the
static chain pointer (even if you can't have nested virtual functions
registers are used for argument passing, are callee-saved, or reserved. */
/* We need to check call_used_regs / fixed_regs in case -fcall_saved-reg /
-ffixed-reg has been used. */
- if (! call_used_regs[0] || fixed_regs[0])
+ if (! call_used_or_fixed_reg_p (0) || fixed_regs[0])
error ("r0 needs to be available as a call-clobbered register");
scratch0 = scratch1 = scratch2 = gen_rtx_REG (Pmode, 0);
{
- if (call_used_regs[1] && ! fixed_regs[1])
+ if (call_used_or_fixed_reg_p (1) && ! fixed_regs[1])
scratch1 = gen_rtx_REG (ptr_mode, 1);
/* N.B., if not TARGET_HITACHI, register 2 is used to pass the pointer
pointing where to return struct values. */
- if (call_used_regs[3] && ! fixed_regs[3])
+ if (call_used_or_fixed_reg_p (3) && ! fixed_regs[3])
scratch2 = gen_rtx_REG (Pmode, 3);
}
emit_insn (gen_add2_insn (scratch0, GEN_INT (vcall_offset)));
offset_addr = scratch0;
}
- else if (scratch0 != scratch1)
- {
- emit_move_insn (scratch1, GEN_INT (vcall_offset));
- emit_insn (gen_add2_insn (scratch0, scratch1));
- offset_addr = scratch0;
- }
else
gcc_unreachable (); /* FIXME */
emit_load_ptr (scratch0, offset_addr);
emit_barrier ();
/* Run just enough of rest_of_compilation to do scheduling and get
- the insns emitted. Note that use_thunk calls
- assemble_start_function and assemble_end_function. */
+ the insns emitted. */
insns = get_insns ();
sh_reorg ();
shorten_branches (insns);
+ assemble_start_function (thunk_fndecl, fnname);
final_start_function (insns, file, 1);
final (insns, file, 1);
final_end_function ();
+ assemble_end_function (thunk_fndecl, fnname);
reload_completed = 0;
epilogue_completed = 0;
return function_symbol_result (sym, lab);
}
-/* Find the number of a general purpose register in S. */
+/* Find the number of the first general purpose register in S that
+ is not set. */
static int
scavenge_reg (HARD_REG_SET *s)
{
- int r;
- for (r = FIRST_GENERAL_REG; r <= LAST_GENERAL_REG; r++)
+ for (int r = FIRST_GENERAL_REG; r <= LAST_GENERAL_REG; r++)
if (TEST_HARD_REG_BIT (*s, r))
return r;
return -1;
rtx op0 = operands[2];
rtx op1 = operands[3];
rtx result = target;
- HOST_WIDE_INT val;
if (!REG_P (op0) || REGNO (op0) != T_REG
|| !CONST_INT_P (op1))
return false;
if (!REG_P (result))
result = gen_reg_rtx (SImode);
- val = INTVAL (op1);
+ HOST_WIDE_INT val = INTVAL (op1);
if ((code == EQ && val == 1) || (code == NE && val == 0))
emit_insn (gen_movt (result, get_t_reg_rtx ()));
else if ((code == EQ && val == 0) || (code == NE && val == 1))
static rtx
extract_sfunc_addr (rtx insn)
{
- rtx pattern, part = NULL_RTX;
- int len, i;
-
- pattern = PATTERN (insn);
- len = XVECLEN (pattern, 0);
- for (i = 0; i < len; i++)
+ rtx pattern = PATTERN (insn);
+ const int len = XVECLEN (pattern, 0);
+ for (int i = 0; i < len; i++)
{
- part = XVECEXP (pattern, 0, i);
+ rtx part = XVECEXP (pattern, 0, i);
if (GET_CODE (part) == USE && GET_MODE (XEXP (part, 0)) == Pmode
&& GENERAL_REGISTER_P (true_regnum (XEXP (part, 0))))
return XEXP (part, 0);
{
pcum->arg_count [(int) SH_ARG_FLOAT] = 0;
pcum->free_single_fp_reg = 0;
- pcum->stack_regs = 0;
- pcum->byref_regs = 0;
- pcum->byref = 0;
- pcum->outgoing = (n_named_args == -1) ? 0 : 1;
+ pcum->outgoing = n_named_args != -1;
- /* XXX - Should we check TARGET_HITACHI here ??? */
- pcum->renesas_abi = sh_attr_renesas_p (fntype) ? 1 : 0;
+ /* FIXME: Should we check TARGET_HITACHI here ??? */
+ pcum->renesas_abi = sh_attr_renesas_p (fntype);
if (fntype)
{
else
{
pcum->arg_count [(int) SH_ARG_INT] = 0;
- pcum->prototype_p = FALSE;
+ pcum->prototype_p = false;
if (mode != VOIDmode)
{
/* If the default ABI is the Renesas ABI then all library
&& TARGET_FPU_DOUBLE)));
}
else
- pcum->force_mem = FALSE;
+ pcum->force_mem = false;
}
}
&& ! ((fp_zero_operand (x) || fp_one_operand (x)) && mode == SFmode))
switch (mode)
{
- case SFmode:
+ case E_SFmode:
sri->icode = CODE_FOR_reload_insf__frn;
return NO_REGS;
- case DFmode:
+ case E_DFmode:
sri->icode = CODE_FOR_reload_indf__frn;
return NO_REGS;
- case SImode:
+ case E_SImode:
/* ??? If we knew that we are in the appropriate mode -
single precision - we could use a reload pattern directly. */
return FPUL_REGS;
return GENERAL_REGS;
return NO_REGS; // LRA wants NO_REGS here, it used to be FPUL_REGS;
}
- if (rclass == TARGET_REGS
- && !satisfies_constraint_Csy (x)
- && (!REG_P (x) || ! GENERAL_REGISTER_P (REGNO (x))))
- return GENERAL_REGS;
+
if ((rclass == MAC_REGS || rclass == PR_REGS)
&& REG_P (x) && ! GENERAL_REGISTER_P (REGNO (x))
&& rclass != REGNO_REG_CLASS (REGNO (x)))
return GENERAL_REGS;
- if (rclass != GENERAL_REGS && REG_P (x)
- && TARGET_REGISTER_P (REGNO (x)))
- return GENERAL_REGS;
/* If here fall back to loading FPUL register through general registers.
This case can happen when movsi_ie insn is picked initially to
return true;
}
-/* Return true if DISP can be legitimized. */
+/* Implement TARGET_LEGITIMIZE_ADDRESS_DISPLACEMENT. */
static bool
-sh_legitimize_address_displacement (rtx *disp, rtx *offs,
+sh_legitimize_address_displacement (rtx *offset1, rtx *offset2,
+ poly_int64 orig_offset,
machine_mode mode)
{
if ((TARGET_FPU_DOUBLE && mode == DFmode)
|| (TARGET_SH2E && mode == SFmode))
return false;
- struct disp_adjust adj = sh_find_mov_disp_adjust (mode, INTVAL (*disp));
+ struct disp_adjust adj = sh_find_mov_disp_adjust (mode, orig_offset);
if (adj.offset_adjust != NULL_RTX && adj.mov_disp != NULL_RTX)
{
- *disp = adj.mov_disp;
- *offs = adj.offset_adjust;
+ *offset1 = adj.offset_adjust;
+ *offset2 = adj.mov_disp;
return true;
}
static void
sh_conditional_register_usage (void)
{
- int regno;
- for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno ++)
+ for (int regno = 0; regno < FIRST_PSEUDO_REGISTER; regno ++)
if (! VALID_REGISTER_P (regno))
- fixed_regs[regno] = call_used_regs[regno] = 1;
+ fixed_regs[regno] = 1;
/* R8 and R9 are call-clobbered on SH5, but not on earlier SH ABIs. */
if (flag_pic)
- {
- fixed_regs[PIC_OFFSET_TABLE_REGNUM] = 1;
- call_used_regs[PIC_OFFSET_TABLE_REGNUM] = 1;
- }
+ fixed_regs[PIC_OFFSET_TABLE_REGNUM] = 1;
if (TARGET_FDPIC)
{
fixed_regs[PIC_REG] = 1;
call_used_regs[PIC_REG] = 1;
- call_really_used_regs[PIC_REG] = 1;
}
/* Renesas saves and restores mac registers on call. */
if (TARGET_HITACHI && ! TARGET_NOMACSAVE)
{
- call_really_used_regs[MACH_REG] = 0;
- call_really_used_regs[MACL_REG] = 0;
+ call_used_regs[MACH_REG] = 0;
+ call_used_regs[MACL_REG] = 0;
}
- for (regno = FIRST_GENERAL_REG; regno <= LAST_GENERAL_REG; regno++)
- if (! fixed_regs[regno] && call_really_used_regs[regno])
+ for (int regno = FIRST_GENERAL_REG; regno <= LAST_GENERAL_REG; regno++)
+ if (! fixed_regs[regno] && call_used_regs[regno])
SET_HARD_REG_BIT (reg_class_contents[SIBCALL_REGS], regno);
- call_really_used_regs[FPSCR_MODES_REG] = 0;
- call_really_used_regs[FPSCR_STAT_REG] = 0;
+ call_used_regs[FPSCR_MODES_REG] = 0;
+ call_used_regs[FPSCR_STAT_REG] = 0;
}
/* Implement TARGET_LEGITIMATE_CONSTANT_P
bool
sh_can_use_simple_return_p (void)
{
- HARD_REG_SET live_regs_mask;
- int d;
-
if (! reload_completed || frame_pointer_needed)
return false;
return false;
/* Finally, allow for pr save. */
- d = calc_live_regs (&live_regs_mask);
+ HARD_REG_SET live_regs_mask;
+ int d = calc_live_regs (&live_regs_mask);
if (rounded_frame_size (d) > 4)
return false;
{
if (CALL_P (DF_REF_INSN (d)))
{
- if (REGNO_REG_SET_P (regs_invalidated_by_call_regset, GBR_REG))
+ if (TEST_HARD_REG_BIT (regs_invalidated_by_call, GBR_REG))
return NULL_RTX;
else
continue;
rtx
sh_movt_set_dest (const rtx_insn* i)
{
- if (i == NULL)
- return NULL;
+ return i == NULL ? NULL : sh_movt_set_dest (PATTERN (i));
+}
- const_rtx p = PATTERN (i);
- return GET_CODE (p) == SET
- && arith_reg_dest (XEXP (p, 0), SImode)
- && t_reg_operand (XEXP (p, 1), VOIDmode) ? XEXP (p, 0) : NULL;
+rtx
+sh_movt_set_dest (const_rtx pat)
+{
+ return GET_CODE (pat) == SET
+ && arith_reg_dest (XEXP (pat, 0), SImode)
+ && t_reg_operand (XEXP (pat, 1), VOIDmode) ? XEXP (pat, 0) : NULL;
}
/* Given an insn, check whether it's a 'movrt' kind of insn, i.e. an insn
rtx
sh_movrt_set_dest (const rtx_insn* i)
{
- if (i == NULL)
- return NULL;
-
- const_rtx p = PATTERN (i);
+ return i == NULL ? NULL : sh_movrt_set_dest (PATTERN (i));
+}
+rtx
+sh_movrt_set_dest (const_rtx pat)
+{
/* The negc movrt replacement is inside a parallel. */
- if (GET_CODE (p) == PARALLEL)
- p = XVECEXP (p, 0, 0);
+ if (GET_CODE (pat) == PARALLEL)
+ pat = XVECEXP (pat, 0, 0);
+
+ return GET_CODE (pat) == SET
+ && arith_reg_dest (XEXP (pat, 0), SImode)
+ && negt_reg_operand (XEXP (pat, 1), VOIDmode) ? XEXP (pat, 0) : NULL;
- return GET_CODE (p) == SET
- && arith_reg_dest (XEXP (p, 0), SImode)
- && negt_reg_operand (XEXP (p, 1), VOIDmode) ? XEXP (p, 0) : NULL;
}
/* Given an insn and a reg number, tell whether the reg dies or is unused
else
{
- set_of_reg op_set = sh_find_set_of_reg (ops[i], insn,
- prev_nonnote_insn_bb);
+ set_of_reg op_set = sh_find_set_of_reg
+ (ops[i], insn, prev_nonnote_nondebug_insn_bb);
if (op_set.set_src == NULL_RTX)
continue;
if (GET_MODE (extended_op) != SImode)
return NULL_RTX;
- set_of_reg s = sh_find_set_of_reg (extended_op, insn, prev_nonnote_insn_bb);
+ set_of_reg s = sh_find_set_of_reg (extended_op, insn,
+ prev_nonnote_nondebug_insn_bb);
if (s.set_src == NULL_RTX)
return NULL_RTX;
if (!can_create_pseudo_p ())
return false;
- set_of_reg t_before_negc = sh_find_set_of_reg (get_t_reg_rtx (), curr_insn,
- prev_nonnote_insn_bb);
- set_of_reg t_after_negc = sh_find_set_of_reg (get_t_reg_rtx (), curr_insn,
- next_nonnote_insn_bb);
+ set_of_reg t_before_negc = sh_find_set_of_reg
+ (get_t_reg_rtx (), curr_insn, prev_nonnote_nondebug_insn_bb);
+ set_of_reg t_after_negc = sh_find_set_of_reg
+ (get_t_reg_rtx (), curr_insn, next_nonnote_nondebug_insn_bb);
if (t_before_negc.set_rtx != NULL_RTX && t_after_negc.set_rtx != NULL_RTX
&& rtx_equal_p (t_before_negc.set_rtx, t_after_negc.set_rtx)
Also try to look through the first extension that we hit. There are some
cases, where a zero_extend is followed an (implicit) sign_extend, and it
fails to see the sign_extend. */
- sh_extending_set_of_reg result =
- sh_find_set_of_reg (reg, curr_insn, prev_nonnote_insn_bb, true);
+ sh_extending_set_of_reg result = sh_find_set_of_reg
+ (reg, curr_insn, prev_nonnote_nondebug_insn_bb, true);
if (result.set_src != NULL)
{
rtx r = gen_reg_rtx (SImode);
rtx_insn* i0;
if (from_mode == QImode)
- i0 = emit_insn_after (gen_extendqisi2 (r, set_src), insn);
+ i0 = sh_check_add_incdec_notes (
+ emit_insn_after (gen_extendqisi2 (r, set_src), insn));
else if (from_mode == HImode)
- i0 = emit_insn_after (gen_extendhisi2 (r, set_src), insn);
+ i0 = sh_check_add_incdec_notes (
+ emit_insn_after (gen_extendhisi2 (r, set_src), insn));
else
gcc_unreachable ();
sh_emit_mode_set (int entity ATTRIBUTE_UNUSED, int mode,
int prev_mode, HARD_REG_SET regs_live ATTRIBUTE_UNUSED)
{
- if ((TARGET_SH4A_FP || TARGET_SH4_300)
+ if ((TARGET_SH4A_FP || TARGET_FPU_SH4_300)
&& prev_mode != FP_MODE_NONE && prev_mode != mode)
{
emit_insn (gen_toggle_pr ());
switch (op)
{
case MOVE_BY_PIECES:
- return move_by_pieces_ninsns (size, align, MOVE_MAX_PIECES + 1)
+ return by_pieces_ninsns (size, align, MOVE_MAX_PIECES + 1, op)
< (!speed_p ? 2 : (align >= 32) ? 16 : 2);
case STORE_BY_PIECES:
case SET_BY_PIECES:
- return move_by_pieces_ninsns (size, align, STORE_MAX_PIECES + 1)
+ return by_pieces_ninsns (size, align, STORE_MAX_PIECES + 1, op)
< (!speed_p ? 2 : (align >= 32) ? 16 : 2);
default:
return default_use_by_pieces_infrastructure_p (size, align,