static void force_into (rtx, rtx);
static void print_slot (rtx);
static rtx add_constant (rtx, enum machine_mode, rtx);
-static void dump_table (rtx);
+static void dump_table (rtx, rtx);
static int hi_const (rtx);
static int broken_move (rtx);
static int mova_p (rtx);
static struct save_entry_s *sh5_schedule_saves (HARD_REG_SET *,
struct save_schedule_s *, int);
-static bool sh_promote_prototypes (tree);
static rtx sh_struct_value_rtx (tree, int);
static bool sh_return_in_memory (tree, tree);
static rtx sh_builtin_saveregs (void);
TARGET_SCHED_INIT_GLOBAL: Added a new target hook in the generic
scheduler; it is called inside the sched_init function just after
find_insn_reg_weights function call. It is used to calculate the SImode
- and SFmode weights of insns of basic blocks; much similiar to what
+ and SFmode weights of insns of basic blocks; much similar to what
find_insn_reg_weights does.
TARGET_SCHED_FINISH_GLOBAL: Corresponding cleanup hook.
{
case TLS_MODEL_GLOBAL_DYNAMIC:
tga_ret = gen_rtx_REG (Pmode, R0_REG);
- emit_insn (gen_tls_global_dynamic (tga_ret, op1));
+ emit_call_insn (gen_tls_global_dynamic (tga_ret, op1));
op1 = tga_ret;
break;
case TLS_MODEL_LOCAL_DYNAMIC:
tga_ret = gen_rtx_REG (Pmode, R0_REG);
- emit_insn (gen_tls_local_dynamic (tga_ret, op1));
+ emit_call_insn (gen_tls_local_dynamic (tga_ret, op1));
tmp = gen_reg_rtx (Pmode);
emit_move_insn (tmp, tga_ret);
return lab;
}
-/* Output the literal table. */
+/* Output the literal table. START, if nonzero, is the first instruction
+ this table is needed for, and also indicates that there is at least one
+ casesi_worker_2 instruction; We have to emit the operand3 labels from
+ these insns at a 4-byte aligned position. BARRIER is the barrier
+ after which we are to place the table. */
static void
-dump_table (rtx scan)
+dump_table (rtx start, rtx barrier)
{
+ rtx scan = barrier;
int i;
int need_align = 1;
rtx lab, ref;
need_align = 1;
+ if (start)
+ {
+ scan = emit_insn_after (gen_align_4 (), scan);
+ need_align = 0;
+ for (; start != barrier; start = NEXT_INSN (start))
+ if (GET_CODE (start) == INSN
+ && recog_memoized (start) == CODE_FOR_casesi_worker_2)
+ {
+ rtx src = SET_SRC (XVECEXP (PATTERN (start), 0, 0));
+ rtx lab = XEXP (XVECEXP (src, 0, 3), 0);
+
+ scan = emit_label_after (lab, scan);
+ }
+ }
if (TARGET_FMOVD && TARGET_ALIGN_DOUBLE && have_df)
{
rtx align_insn = NULL_RTX;
&& GET_CODE (XVECEXP (SET_SRC (PATTERN (insn)), 0, 0)) == LABEL_REF);
}
+/* Fix up a mova from a switch that went out of range. */
+static void
+fixup_mova (rtx mova)
+{
+ if (! flag_pic)
+ {
+ SET_SRC (PATTERN (mova)) = XVECEXP (SET_SRC (PATTERN (mova)), 0, 0);
+ INSN_CODE (mova) = -1;
+ }
+ else
+ {
+ rtx worker = mova;
+ rtx lab = gen_label_rtx ();
+ rtx wpat, wpat0, wpat1, wsrc, diff;
+
+ do
+ {
+ worker = NEXT_INSN (worker);
+ if (! worker
+ || GET_CODE (worker) == CODE_LABEL
+ || GET_CODE (worker) == JUMP_INSN)
+ abort ();
+ } while (recog_memoized (worker) != CODE_FOR_casesi_worker_1);
+ wpat = PATTERN (worker);
+ wpat0 = XVECEXP (wpat, 0, 0);
+ wpat1 = XVECEXP (wpat, 0, 1);
+ wsrc = SET_SRC (wpat0);
+ PATTERN (worker) = (gen_casesi_worker_2
+ (SET_DEST (wpat0), XVECEXP (wsrc, 0, 1),
+ XEXP (XVECEXP (wsrc, 0, 2), 0), lab,
+ XEXP (wpat1, 0)));
+ INSN_CODE (worker) = -1;
+ diff = gen_rtx_MINUS (Pmode, XVECEXP (SET_SRC (PATTERN (mova)), 0, 0),
+ gen_rtx_LABEL_REF (Pmode, lab));
+ diff = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, diff), UNSPEC_PIC);
+ SET_SRC (PATTERN (mova)) = gen_rtx_CONST (Pmode, diff);
+ INSN_CODE (mova) = -1;
+ }
+}
+
/* Find the last barrier from insn FROM which is close enough to hold the
constant pool. If we can't find one, then create one near the end of
the range. */
{
/* Try as we might, the leading mova is out of range. Change
it into a load (which will become a pcload) and retry. */
- SET_SRC (PATTERN (mova)) = XVECEXP (SET_SRC (PATTERN (mova)), 0, 0);
- INSN_CODE (mova) = -1;
+ fixup_mova (mova);
return find_barrier (0, 0, mova);
}
else
if (INSN_DELETED_P (scan))
continue;
code = GET_CODE (scan);
- if (GET_RTX_CLASS (code) == 'i')
+ if (INSN_P (scan))
{
used |= regs_used (PATTERN (scan), 0);
if (code == CALL_INSN)
{
if (mova_p (insn))
{
- if (! num_mova++)
+ /* ??? basic block reordering can move a switch table dispatch
+ below the switch table. Check if that has happened.
+ We only have the addresses available when optimizing; but then,
+ this check shouldn't be needed when not optimizing. */
+ rtx label_ref = XVECEXP (SET_SRC (PATTERN (insn)), 0, 0);
+ if (optimize
+ && (INSN_ADDRESSES (INSN_UID (insn))
+ > INSN_ADDRESSES (INSN_UID (XEXP (label_ref, 0)))))
+ {
+ /* Change the mova into a load.
+ broken_move will then return true for it. */
+ fixup_mova (insn);
+ }
+ else if (! num_mova++)
mova = insn;
}
else if (GET_CODE (insn) == JUMP_INSN
{
/* Change the mova into a load, and restart scanning
there. broken_move will then return true for mova. */
- SET_SRC (PATTERN (mova))
- = XVECEXP (SET_SRC (PATTERN (mova)), 0, 0);
- INSN_CODE (mova) = -1;
+ fixup_mova (mova);
insn = mova;
}
}
- if (broken_move (insn))
+ if (broken_move (insn)
+ || (GET_CODE (insn) == INSN
+ && recog_memoized (insn) == CODE_FOR_casesi_worker_2))
{
rtx scan;
/* Scan ahead looking for a barrier to stick the constant table
behind. */
rtx barrier = find_barrier (num_mova, mova, insn);
rtx last_float_move = NULL_RTX, last_float = 0, *last_float_addr = NULL;
+ int need_aligned_label = 0;
if (num_mova && ! mova_p (mova))
{
{
if (GET_CODE (scan) == CODE_LABEL)
last_float = 0;
+ if (GET_CODE (scan) == INSN
+ && recog_memoized (scan) == CODE_FOR_casesi_worker_2)
+ need_aligned_label = 1;
if (broken_move (scan))
{
rtx *patp = &PATTERN (scan), pat = *patp;
}
dst = gen_rtx_REG (HImode, REGNO (dst) + offset);
}
-
if (GET_CODE (dst) == REG && FP_ANY_REGISTER_P (REGNO (dst)))
{
/* This must be an insn that clobbers r0. */
INSN_CODE (scan) = -1;
}
}
- dump_table (barrier);
+ dump_table (need_aligned_label ? insn : 0, barrier);
insn = barrier;
}
}
/* Number of bytes pushed for anonymous args, used to pass information
between expand_prologue and expand_epilogue. */
-static int extra_push;
-
/* Adjust the stack by SIZE bytes. REG holds the rtl of the register to be
adjusted. If epilogue_p is zero, this is for a prologue; otherwise, it's
- for an epilogue. If LIVE_REGS_MASK is nonzero, it points to a HARD_REG_SET
- of all the registers that are about to be restored, and hence dead. */
+ for an epilogue and a negative value means that it's for a sibcall
+ epilogue. If LIVE_REGS_MASK is nonzero, it points to a HARD_REG_SET of
+ all the registers that are about to be restored, and hence dead. */
static void
output_stack_adjust (int size, rtx reg, int epilogue_p,
/* If TEMP is invalid, we could temporarily save a general
register to MACL. However, there is currently no need
to handle this case, so just abort when we see it. */
- if (current_function_interrupt
+ if (epilogue_p < 0
+ || current_function_interrupt
|| ! call_used_regs[temp] || fixed_regs[temp])
temp = -1;
- if (temp < 0 && ! current_function_interrupt)
+ if (temp < 0 && ! current_function_interrupt
+ && (TARGET_SHMEDIA || 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);
- if (epilogue_p)
+ if (epilogue_p > 0)
{
- for (i = 0; i < HARD_REGNO_NREGS (FIRST_RET_REG, DImode); i++)
+ int nreg = 0;
+ if (current_function_return_rtx)
+ {
+ enum machine_mode mode;
+ mode = GET_MODE (current_function_return_rtx);
+ if (BASE_RETURN_VALUE_REG (mode) == FIRST_RET_REG)
+ nreg = HARD_REGNO_NREGS (FIRST_RET_REG, mode);
+ }
+ for (i = 0; i < nreg; i++)
CLEAR_HARD_REG_BIT (temps, FIRST_RET_REG + i);
if (current_function_calls_eh_return)
{
CLEAR_HARD_REG_BIT (temps, EH_RETURN_DATA_REGNO (i));
}
}
- else
+ if (TARGET_SHMEDIA && epilogue_p < 0)
+ for (i = FIRST_TARGET_REG; i <= LAST_TARGET_REG; i++)
+ CLEAR_HARD_REG_BIT (temps, i);
+ if (epilogue_p <= 0)
{
for (i = FIRST_PARM_REG;
i < FIRST_PARM_REG + NPARM_REGS (SImode); i++)
CLEAR_HARD_REG_BIT (temps, i);
- if (current_function_needs_context)
+ if (cfun->static_chain_decl != NULL)
CLEAR_HARD_REG_BIT (temps, STATIC_CHAIN_REGNUM);
}
temp = scavenge_reg (&temps);
if (temp < 0 && live_regs_mask)
temp = scavenge_reg (live_regs_mask);
if (temp < 0)
- abort ();
+ {
+ /* If we reached here, the most likely case is the (sibcall)
+ epilogue for non SHmedia. Put a special push/pop sequence
+ for such case as the last resort. This looks lengthy but
+ would not be problem because it seems to be very rare. */
+ if (! TARGET_SHMEDIA && epilogue_p)
+ {
+ rtx adj_reg, tmp_reg, mem;
+
+ /* ??? There is still the slight possibility that r4 or r5
+ have been reserved as fixed registers or assigned as
+ global registers, and they change during an interrupt.
+ There are possible ways to handle this:
+ - If we are adjusting the frame pointer (r14), we can do
+ with a single temp register and an ordinary push / pop
+ on the stack.
+ - Grab any call-used or call-saved registers (i.e. not
+ fixed or globals) for the temps we need. We might
+ also grab r14 if we are adjusting the stack pointer.
+ If we can't find enough available registers, issue
+ a diagnostic and abort - the user must have reserved
+ way too many registers.
+ But since all this is rather unlikely to happen and
+ would require extra testing, we just abort if r4 / r5
+ are not available. */
+ if (fixed_regs[4] || fixed_regs[5]
+ || global_regs[4] || global_regs[5])
+ abort ();
+
+ adj_reg = gen_rtx_REG (GET_MODE (reg), 4);
+ tmp_reg = gen_rtx_REG (GET_MODE (reg), 5);
+ emit_move_insn (gen_rtx_MEM (Pmode, reg), adj_reg);
+ emit_insn (GEN_MOV (adj_reg, GEN_INT (size)));
+ emit_insn (GEN_ADD3 (adj_reg, adj_reg, reg));
+ mem = gen_rtx_MEM (Pmode, gen_rtx_PRE_DEC (Pmode, adj_reg));
+ emit_move_insn (mem, tmp_reg);
+ emit_move_insn (tmp_reg, gen_rtx_MEM (Pmode, reg));
+ mem = gen_rtx_MEM (Pmode, gen_rtx_PRE_DEC (Pmode, adj_reg));
+ emit_move_insn (mem, tmp_reg);
+ emit_move_insn (reg, adj_reg);
+ mem = gen_rtx_MEM (Pmode, gen_rtx_POST_INC (Pmode, reg));
+ emit_move_insn (adj_reg, mem);
+ mem = gen_rtx_MEM (Pmode, gen_rtx_POST_INC (Pmode, reg));
+ emit_move_insn (tmp_reg, mem);
+ return;
+ }
+ else
+ abort ();
+ }
const_reg = gen_rtx_REG (GET_MODE (reg), temp);
/* If SIZE is negative, subtract the positive value.
if (call_used_regs[i] && ! fixed_regs[i] && i != PR_MEDIA_REG
&& ! FUNCTION_ARG_REGNO_P (i)
&& i != FIRST_RET_REG
- && ! (current_function_needs_context && i == STATIC_CHAIN_REGNUM)
+ && ! (cfun->static_chain_decl != NULL && i == STATIC_CHAIN_REGNUM)
&& ! (current_function_calls_eh_return
&& (i == EH_RETURN_STACKADJ_REGNO
|| ((unsigned) i <= EH_RETURN_DATA_REGNO (0)
int d, i;
int d_rounding = 0;
int save_flags = target_flags;
+ int pretend_args;
current_function_interrupt = sh_cfun_interrupt_handler_p ();
/* We have pretend args if we had an object sent partially in registers
and partially on the stack, e.g. a large structure. */
- output_stack_adjust (-current_function_pretend_args_size
+ pretend_args = current_function_pretend_args_size;
+ if (TARGET_VARARGS_PRETEND_ARGS (current_function_decl)
+ && (NPARM_REGS(SImode)
+ > current_function_args_info.arg_count[(int) SH_ARG_INT]))
+ pretend_args = 0;
+ output_stack_adjust (-pretend_args
- current_function_args_info.stack_regs * 8,
stack_pointer_rtx, 0, NULL);
- extra_push = 0;
-
if (TARGET_SHCOMPACT && flag_pic && current_function_args_info.call_cookie)
/* We're going to use the PIC register to load the address of the
incoming-argument decoder and/or of the return trampoline from
/* Emit the code for SETUP_VARARGS. */
if (current_function_stdarg)
{
- /* This is not used by the SH2E calling convention */
- if (TARGET_SH1 && ! TARGET_SH2E && ! TARGET_SH5
- && ! (TARGET_HITACHI || sh_cfun_attr_renesas_p ()))
+ 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++)
break;
insn = push (rn);
RTX_FRAME_RELATED_P (insn) = 0;
- extra_push += 4;
}
}
}
}
void
-sh_expand_epilogue (void)
+sh_expand_epilogue (bool sibcall_p)
{
HARD_REG_SET live_regs_mask;
int d, i;
int save_flags = target_flags;
int frame_size, save_size;
int fpscr_deferred = 0;
+ int e = sibcall_p ? -1 : 1;
d = calc_live_regs (&live_regs_mask);
if (frame_pointer_needed)
{
- output_stack_adjust (frame_size, frame_pointer_rtx, 1, &live_regs_mask);
+ output_stack_adjust (frame_size, frame_pointer_rtx, e, &live_regs_mask);
/* We must avoid moving the stack pointer adjustment past code
which reads from the local frame, else an interrupt could
occur after the SP adjustment and clobber data in the local
frame. */
emit_insn (gen_blockage ());
- output_stack_adjust (frame_size, stack_pointer_rtx, 1, &live_regs_mask);
+ output_stack_adjust (frame_size, stack_pointer_rtx, e, &live_regs_mask);
}
if (SHMEDIA_REGS_STACK_ADJUST ())
emit_insn (gen_toggle_sz ());
target_flags = save_flags;
- output_stack_adjust (extra_push + current_function_pretend_args_size
+ output_stack_adjust (current_function_pretend_args_size
+ save_size + d_rounding
+ current_function_args_info.stack_regs * 8,
- stack_pointer_rtx, 1, NULL);
+ stack_pointer_rtx, e, NULL);
if (current_function_calls_eh_return)
emit_insn (GEN_ADD3 (stack_pointer_rtx, stack_pointer_rtx,
rtx epilogue;
start_sequence ();
- sh_expand_epilogue ();
+ sh_expand_epilogue (0);
epilogue = get_insns ();
end_sequence ();
sh_need_epilogue_known = (epilogue == NULL ? -1 : 1);
f_next_fp_limit = TREE_CHAIN (f_next_fp);
f_next_stack = TREE_CHAIN (f_next_fp_limit);
- next_o = build (COMPONENT_REF, TREE_TYPE (f_next_o), valist, f_next_o);
+ next_o = build (COMPONENT_REF, TREE_TYPE (f_next_o), valist, f_next_o,
+ NULL_TREE);
next_o_limit = build (COMPONENT_REF, TREE_TYPE (f_next_o_limit),
- valist, f_next_o_limit);
- next_fp = build (COMPONENT_REF, TREE_TYPE (f_next_fp), valist, f_next_fp);
+ valist, f_next_o_limit, NULL_TREE);
+ next_fp = build (COMPONENT_REF, TREE_TYPE (f_next_fp), valist, f_next_fp,
+ NULL_TREE);
next_fp_limit = build (COMPONENT_REF, TREE_TYPE (f_next_fp_limit),
- valist, f_next_fp_limit);
+ valist, f_next_fp_limit, NULL_TREE);
next_stack = build (COMPONENT_REF, TREE_TYPE (f_next_stack),
- valist, f_next_stack);
+ valist, f_next_stack, NULL_TREE);
/* Call __builtin_saveregs. */
u = make_tree (ptr_type_node, expand_builtin_saveregs ());
f_next_fp_limit = TREE_CHAIN (f_next_fp);
f_next_stack = TREE_CHAIN (f_next_fp_limit);
- next_o = build (COMPONENT_REF, TREE_TYPE (f_next_o), valist, f_next_o);
+ next_o = build (COMPONENT_REF, TREE_TYPE (f_next_o), valist, f_next_o,
+ NULL_TREE);
next_o_limit = build (COMPONENT_REF, TREE_TYPE (f_next_o_limit),
- valist, f_next_o_limit);
+ valist, f_next_o_limit, NULL_TREE);
next_fp = build (COMPONENT_REF, TREE_TYPE (f_next_fp),
- valist, f_next_fp);
+ valist, f_next_fp, NULL_TREE);
next_fp_limit = build (COMPONENT_REF, TREE_TYPE (f_next_fp_limit),
- valist, f_next_fp_limit);
+ valist, f_next_fp_limit, NULL_TREE);
next_stack = build (COMPONENT_REF, TREE_TYPE (f_next_stack),
- valist, f_next_stack);
+ valist, f_next_stack, NULL_TREE);
/* Structures with a single member with a distinct mode are passed
like their member. This is relevant if the latter has a REAL_TYPE
return result;
}
-static bool
+bool
sh_promote_prototypes (tree type)
{
if (TARGET_HITACHI)
later. Fortunately, we already have two flags that are part of struct
function that tell if a function uses varargs or stdarg. */
static void
-sh_setup_incoming_varargs (CUMULATIVE_ARGS *ca ATTRIBUTE_UNUSED,
- enum machine_mode mode ATTRIBUTE_UNUSED,
- tree type ATTRIBUTE_UNUSED,
- int *pretend_arg_size ATTRIBUTE_UNUSED,
+sh_setup_incoming_varargs (CUMULATIVE_ARGS *ca,
+ enum machine_mode mode,
+ tree type,
+ int *pretend_arg_size,
int second_time ATTRIBUTE_UNUSED)
{
if (! current_function_stdarg)
abort ();
+ if (TARGET_VARARGS_PRETEND_ARGS (current_function_decl))
+ {
+ int named_parm_regs, anon_parm_regs;
+
+ named_parm_regs = (ROUND_REG (*ca, mode)
+ + (mode == BLKmode
+ ? ROUND_ADVANCE (int_size_in_bytes (type))
+ : ROUND_ADVANCE (GET_MODE_SIZE (mode))));
+ anon_parm_regs = NPARM_REGS (SImode) - named_parm_regs;
+ if (anon_parm_regs > 0)
+ *pretend_arg_size = anon_parm_regs * 4;
+ }
}
static bool
while ((insn = NEXT_INSN (insn)))
{
+ rtx set;
+ if (!INSN_P (insn))
+ continue;
+
code = GET_CODE (insn);
#if 0
else if (code == JUMP_INSN)
return 0;
}
- else if (GET_RTX_CLASS (code) == 'i')
- {
- rtx set = single_set (insn);
- if (set && reg_overlap_mentioned_p (reg, SET_SRC (set)))
- return 0;
- if (set && reg_overlap_mentioned_p (reg, SET_DEST (set)))
- return GET_CODE (SET_DEST (set)) != MEM;
- if (set == 0 && reg_overlap_mentioned_p (reg, PATTERN (insn)))
- return 0;
- }
+ set = single_set (insn);
+ if (set && reg_overlap_mentioned_p (reg, SET_SRC (set)))
+ return 0;
+ if (set && reg_overlap_mentioned_p (reg, SET_DEST (set)))
+ return GET_CODE (SET_DEST (set)) != MEM;
+ if (set == 0 && reg_overlap_mentioned_p (reg, PATTERN (insn)))
+ return 0;
if (code == CALL_INSN && call_used_regs[REGNO (reg)])
return 1;
}
/* This Function returns nonzero if the DFA based scheduler interface
- is to be used. At present this is supported for the SH4 only. */
+ is to be used. At present this is only supported properly for the SH4.
+ For the SH1 the current DFA model is just the converted form of the old
+ pipeline model description. */
static int
sh_use_dfa_interface (void)
{
- if (TARGET_HARD_SH4)
+ if (TARGET_SH1)
return 1;
else
return 0;
functions that already have high pressure on r0. */
#define R0_MAX_LIFE_REGIONS 2
#define R0_MAX_LIVE_LENGTH 12
-/* Register Pressure threshols for SImode and SFmode registers. */
+/* Register Pressure thresholds for SImode and SFmode registers. */
#define SIMODE_MAX_WEIGHT 5
#define SFMODE_MAX_WEIGHT 10
epilogue_completed = 1;
no_new_pseudos = 1;
current_function_uses_only_leaf_regs = 1;
+ reset_block_changes ();
emit_note (NOTE_INSN_PROLOGUE_END);
if (optimize > 0 && flag_schedule_insns_after_reload)
{
find_basic_blocks (insns, max_reg_num (), dump_file);
- life_analysis (insns, dump_file, PROP_FINAL);
+ life_analysis (dump_file, PROP_FINAL);
split_all_insns (1);
if (optimize > 0 && flag_schedule_insns_after_reload)
{
/* Release all memory allocated by flow. */
- free_basic_block_vars (0);
+ free_basic_block_vars ();
/* Release all memory held by regsets now. */
regset_release_memory ();