+ return 0;
+}
+\f
+/* Return TRUE if X references a SYMBOL_REF or LABEL_REF whose symbol
+ isn't protected by a PIC unspec. */
+int
+nonpic_symbol_mentioned_p (rtx x)
+{
+ register const char *fmt;
+ register int i;
+
+ if (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == LABEL_REF
+ || GET_CODE (x) == PC)
+ return 1;
+
+ /* We don't want to look into the possible MEM location of a
+ CONST_DOUBLE, since we're not going to use it, in general. */
+ if (GET_CODE (x) == CONST_DOUBLE)
+ return 0;
+
+ if (GET_CODE (x) == UNSPEC
+ && (XINT (x, 1) == UNSPEC_PIC
+ || XINT (x, 1) == UNSPEC_GOT
+ || XINT (x, 1) == UNSPEC_GOTOFF
+ || XINT (x, 1) == UNSPEC_GOTPLT
+ || XINT (x, 1) == UNSPEC_GOTTPOFF
+ || XINT (x, 1) == UNSPEC_DTPOFF
+ || XINT (x, 1) == UNSPEC_PLT
+ || XINT (x, 1) == UNSPEC_SYMOFF
+ || XINT (x, 1) == UNSPEC_PCREL_SYMOFF))
+ return 0;
+
+ fmt = GET_RTX_FORMAT (GET_CODE (x));
+ for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'E')
+ {
+ register int j;
+
+ for (j = XVECLEN (x, i) - 1; j >= 0; j--)
+ if (nonpic_symbol_mentioned_p (XVECEXP (x, i, j)))
+ return 1;
+ }
+ else if (fmt[i] == 'e' && nonpic_symbol_mentioned_p (XEXP (x, i)))
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Convert a non-PIC address in `orig' to a PIC address using @GOT or
+ @GOTOFF in `reg'. */
+rtx
+legitimize_pic_address (rtx orig, enum machine_mode mode ATTRIBUTE_UNUSED,
+ rtx reg)
+{
+ if (tls_symbolic_operand (orig, Pmode))
+ return orig;
+
+ if (GET_CODE (orig) == LABEL_REF
+ || (GET_CODE (orig) == SYMBOL_REF && SYMBOL_REF_LOCAL_P (orig)))
+ {
+ if (reg == 0)
+ reg = gen_reg_rtx (Pmode);
+
+ emit_insn (gen_symGOTOFF2reg (reg, orig));
+ return reg;
+ }
+ else if (GET_CODE (orig) == SYMBOL_REF)
+ {
+ if (reg == 0)
+ reg = gen_reg_rtx (Pmode);
+
+ emit_insn (gen_symGOT2reg (reg, orig));
+ return reg;
+ }
+ return orig;
+}
+
+/* Mark the use of a constant in the literal table. If the constant
+ has multiple labels, make it unique. */
+static rtx
+mark_constant_pool_use (rtx x)
+{
+ rtx insn, lab, pattern;
+
+ if (x == NULL)
+ return x;
+
+ switch (GET_CODE (x))
+ {
+ case LABEL_REF:
+ x = XEXP (x, 0);
+ case CODE_LABEL:
+ break;
+ default:
+ return x;
+ }
+
+ /* Get the first label in the list of labels for the same constant
+ and delete another labels in the list. */
+ lab = x;
+ for (insn = PREV_INSN (x); insn; insn = PREV_INSN (insn))
+ {
+ if (GET_CODE (insn) != CODE_LABEL
+ || LABEL_REFS (insn) != NEXT_INSN (insn))
+ break;
+ lab = insn;
+ }
+
+ for (insn = LABEL_REFS (lab); insn; insn = LABEL_REFS (insn))
+ INSN_DELETED_P (insn) = 1;
+
+ /* Mark constants in a window. */
+ for (insn = NEXT_INSN (x); insn; insn = NEXT_INSN (insn))
+ {
+ if (GET_CODE (insn) != INSN)
+ continue;
+
+ pattern = PATTERN (insn);
+ if (GET_CODE (pattern) != UNSPEC_VOLATILE)
+ continue;
+
+ switch (XINT (pattern, 1))
+ {
+ case UNSPECV_CONST2:
+ case UNSPECV_CONST4:
+ case UNSPECV_CONST8:
+ XVECEXP (pattern, 0, 1) = const1_rtx;
+ break;
+ case UNSPECV_WINDOW_END:
+ if (XVECEXP (pattern, 0, 0) == x)
+ return lab;
+ break;
+ case UNSPECV_CONST_END:
+ return lab;
+ default:
+ break;
+ }
+ }
+
+ return lab;
+}
+\f
+/* Return true if it's possible to redirect BRANCH1 to the destination
+ of an unconditional jump BRANCH2. We only want to do this if the
+ resulting branch will have a short displacement. */
+int
+sh_can_redirect_branch (rtx branch1, rtx branch2)
+{
+ if (flag_expensive_optimizations && simplejump_p (branch2))
+ {
+ rtx dest = XEXP (SET_SRC (single_set (branch2)), 0);
+ rtx insn;
+ int distance;
+
+ for (distance = 0, insn = NEXT_INSN (branch1);
+ insn && distance < 256;
+ insn = PREV_INSN (insn))
+ {
+ if (insn == dest)
+ return 1;
+ else
+ distance += get_attr_length (insn);
+ }
+ for (distance = 0, insn = NEXT_INSN (branch1);
+ insn && distance < 256;
+ insn = NEXT_INSN (insn))
+ {
+ if (insn == dest)
+ return 1;
+ else
+ distance += get_attr_length (insn);
+ }
+ }
+ return 0;
+}
+
+/* Return nonzero if register old_reg can be renamed to register new_reg. */
+int
+sh_hard_regno_rename_ok (unsigned int old_reg ATTRIBUTE_UNUSED,
+ unsigned int new_reg)
+{
+ /* Interrupt functions can only use registers that have already been
+ saved by the prologue, even if they would normally be
+ call-clobbered. */
+
+ if (sh_cfun_interrupt_handler_p () && !df_regs_ever_live_p (new_reg))
+ return 0;
+
+ return 1;
+}
+
+/* Function to update the integer COST
+ based on the relationship between INSN that is dependent on
+ DEP_INSN through the dependence LINK. The default is to make no
+ adjustment to COST. This can be used for example to specify to
+ the scheduler that an output- or anti-dependence does not incur
+ the same cost as a data-dependence. The return value should be
+ the new value for COST. */
+static int
+sh_adjust_cost (rtx insn, rtx link ATTRIBUTE_UNUSED, rtx dep_insn, int cost)
+{
+ rtx reg, use_pat;
+
+ if (TARGET_SHMEDIA)
+ {
+ /* On SHmedia, if the dependence is an anti-dependence or
+ output-dependence, there is no cost. */
+ if (REG_NOTE_KIND (link) != 0)
+ {
+ /* However, dependencies between target register loads and
+ uses of the register in a subsequent block that are separated
+ by a conditional branch are not modelled - we have to do with
+ the anti-dependency between the target register load and the
+ conditional branch that ends the current block. */
+ if (REG_NOTE_KIND (link) == REG_DEP_ANTI
+ && GET_CODE (PATTERN (dep_insn)) == SET
+ && (get_attr_type (dep_insn) == TYPE_PT_MEDIA
+ || get_attr_type (dep_insn) == TYPE_PTABS_MEDIA)
+ && get_attr_type (insn) == TYPE_CBRANCH_MEDIA)
+ {
+ int orig_cost = cost;
+ rtx note = find_reg_note (insn, REG_BR_PROB, 0);
+ rtx target = ((! note
+ || INTVAL (XEXP (note, 0)) * 2 < REG_BR_PROB_BASE)
+ ? insn : JUMP_LABEL (insn));
+ /* On the likely path, the branch costs 1, on the unlikely path,
+ it costs 3. */
+ cost--;
+ do
+ target = next_active_insn (target);
+ while (target && ! flow_dependent_p (target, dep_insn)
+ && --cost > 0);
+ /* If two branches are executed in immediate succession, with the
+ first branch properly predicted, this causes a stall at the
+ second branch, hence we won't need the target for the
+ second branch for two cycles after the launch of the first
+ branch. */
+ if (cost > orig_cost - 2)
+ cost = orig_cost - 2;
+ }
+ else
+ cost = 0;
+ }
+
+ else if (get_attr_is_mac_media (insn)
+ && get_attr_is_mac_media (dep_insn))
+ cost = 1;
+
+ else if (! reload_completed
+ && GET_CODE (PATTERN (insn)) == SET
+ && GET_CODE (SET_SRC (PATTERN (insn))) == FLOAT
+ && GET_CODE (PATTERN (dep_insn)) == SET
+ && fp_arith_reg_operand (SET_SRC (PATTERN (dep_insn)), VOIDmode)
+ && cost < 4)
+ cost = 4;
+ /* Schedule the ptabs for a casesi_jump_media in preference to stuff
+ that is needed at the target. */
+ else if (get_attr_type (insn) == TYPE_JUMP_MEDIA
+ && ! flow_dependent_p (insn, dep_insn))
+ cost--;
+ }
+ else if (REG_NOTE_KIND (link) == 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);
+
+ /* The latency that we specify in the scheduling description refers
+ to the actual output, not to an auto-increment register; for that,
+ the latency is one. */
+ if (dep_set && MEM_P (SET_SRC (dep_set)) && cost > 1)
+ {
+ rtx set = single_set (insn);
+
+ if (set
+ && !reg_mentioned_p (SET_DEST (dep_set), SET_SRC (set))
+ && (!MEM_P (SET_DEST (set))
+ || !reg_mentioned_p (SET_DEST (dep_set),
+ XEXP (SET_DEST (set), 0))))
+ cost = 1;
+ }
+ /* The only input for a call that is timing-critical is the
+ function's address. */
+ if (GET_CODE (insn) == CALL_INSN)
+ {
+ rtx call = PATTERN (insn);
+
+ if (GET_CODE (call) == PARALLEL)
+ call = XVECEXP (call, 0 ,0);
+ if (GET_CODE (call) == SET)
+ call = SET_SRC (call);
+ if (GET_CODE (call) == CALL && GET_CODE (XEXP (call, 0)) == MEM
+ /* sibcalli_thunk uses a symbol_ref in an unspec. */
+ && (GET_CODE (XEXP (XEXP (call, 0), 0)) == UNSPEC
+ || ! reg_set_p (XEXP (XEXP (call, 0), 0), dep_insn)))
+ cost -= TARGET_SH4_300 ? 3 : 6;
+ }
+ /* Likewise, the most timing critical input for an sfuncs call
+ is the function address. However, sfuncs typically start
+ using their arguments pretty quickly.
+ Assume a four cycle delay for SH4 before they are needed.
+ Cached ST40-300 calls are quicker, so assume only a one
+ cycle delay there.
+ ??? Maybe we should encode the delays till input registers
+ are needed by sfuncs into the sfunc call insn. */
+ /* All sfunc calls are parallels with at least four components.
+ Exploit this to avoid unnecessary calls to sfunc_uses_reg. */
+ else if (GET_CODE (PATTERN (insn)) == PARALLEL
+ && XVECLEN (PATTERN (insn), 0) >= 4
+ && (reg = sfunc_uses_reg (insn)))
+ {
+ if (! reg_set_p (reg, dep_insn))
+ cost -= TARGET_SH4_300 ? 1 : 4;
+ }
+ if (TARGET_HARD_SH4 && !TARGET_SH4_300)
+ {
+ enum attr_type dep_type = get_attr_type (dep_insn);
+
+ if (dep_type == TYPE_FLOAD || dep_type == TYPE_PCFLOAD)
+ cost--;
+ else if ((dep_type == TYPE_LOAD_SI || dep_type == TYPE_PCLOAD_SI)
+ && (type = get_attr_type (insn)) != TYPE_CALL
+ && type != TYPE_SFUNC)
+ cost--;
+ /* When the preceding instruction loads the shift amount of
+ the following SHAD/SHLD, the latency of the load is increased
+ by 1 cycle. */
+ if (get_attr_type (insn) == TYPE_DYN_SHIFT
+ && get_attr_any_int_load (dep_insn) == ANY_INT_LOAD_YES
+ && reg_overlap_mentioned_p (SET_DEST (dep_set),
+ XEXP (SET_SRC (single_set (insn)),
+ 1)))
+ cost++;
+ /* When an LS group instruction with a latency of less than
+ 3 cycles is followed by a double-precision floating-point
+ instruction, FIPR, or FTRV, the latency of the first
+ instruction is increased to 3 cycles. */
+ else if (cost < 3
+ && get_attr_insn_class (dep_insn) == INSN_CLASS_LS_GROUP
+ && get_attr_dfp_comp (insn) == DFP_COMP_YES)
+ cost = 3;
+ /* The lsw register of a double-precision computation is ready one
+ cycle earlier. */
+ else if (reload_completed
+ && get_attr_dfp_comp (dep_insn) == DFP_COMP_YES
+ && (use_pat = single_set (insn))
+ && ! regno_use_in (REGNO (SET_DEST (single_set (dep_insn))),
+ SET_SRC (use_pat)))
+ cost -= 1;
+
+ if (get_attr_any_fp_comp (dep_insn) == ANY_FP_COMP_YES
+ && get_attr_late_fp_use (insn) == LATE_FP_USE_YES)
+ cost -= 1;
+ }
+ else if (TARGET_SH4_300)
+ {
+ /* Stores need their input register two cycles later. */
+ if (dep_set && cost >= 1
+ && ((type = get_attr_type (insn)) == TYPE_STORE
+ || type == TYPE_PSTORE
+ || type == TYPE_FSTORE || type == TYPE_MAC_MEM))
+ {
+ rtx set = single_set (insn);
+
+ if (!reg_mentioned_p (SET_SRC (set), XEXP (SET_DEST (set), 0))
+ && rtx_equal_p (SET_SRC (set), SET_DEST (dep_set)))
+ {
+ cost -= 2;
+ /* But don't reduce the cost below 1 if the address depends
+ on a side effect of dep_insn. */
+ if (cost < 1
+ && modified_in_p (XEXP (SET_DEST (set), 0), dep_insn))
+ cost = 1;
+ }
+ }
+ }
+ }
+ /* 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
+ && recog_memoized (dep_insn) >= 0
+ && (get_attr_type (dep_insn) == TYPE_DFP_ARITH
+ || get_attr_type (dep_insn) == TYPE_DFP_MUL)
+ /* A lot of alleged anti-flow dependences are fake,
+ so check this one is real. */
+ && flow_dependent_p (dep_insn, insn))
+ cost = 2;
+
+ return cost;
+}
+
+/* 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 int
+flow_dependent_p (rtx insn, rtx dep_insn)
+{
+ rtx tmp = PATTERN (insn);
+
+ note_stores (PATTERN (dep_insn), flow_dependent_p_1, &tmp);
+ return tmp == NULL_RTX;
+}
+
+/* A helper function for flow_dependent_p called through note_stores. */
+static void
+flow_dependent_p_1 (rtx x, const_rtx pat ATTRIBUTE_UNUSED, void *data)
+{
+ rtx * pinsn = (rtx *) data;
+
+ if (*pinsn && reg_referenced_p (x, *pinsn))
+ *pinsn = NULL_RTX;
+}
+
+/* For use by sh_allocate_initial_value. Note that sh.md contains some
+ 'special function' patterns (type sfunc) that clobber pr, but that
+ do not look like function calls to leaf_function_p. Hence we must
+ do this extra check. */
+static int
+sh_pr_n_sets (void)
+{
+ return DF_REG_DEF_COUNT (TARGET_SHMEDIA ? PR_MEDIA_REG : PR_REG);
+}
+
+/* Return where to allocate pseudo for a given hard register initial
+ value. */
+static rtx
+sh_allocate_initial_value (rtx hard_reg)
+{
+ rtx x;
+
+ if (REGNO (hard_reg) == (TARGET_SHMEDIA ? PR_MEDIA_REG : PR_REG))
+ {
+ if (current_function_is_leaf
+ && ! sh_pr_n_sets ()
+ && ! (TARGET_SHCOMPACT
+ && ((crtl->args.info.call_cookie
+ & ~ CALL_COOKIE_RET_TRAMP (1))
+ || crtl->saves_all_registers)))
+ x = hard_reg;
+ else
+ x = gen_frame_mem (Pmode, return_address_pointer_rtx);
+ }
+ else
+ x = NULL_RTX;
+
+ return x;
+}
+
+/* This function returns "2" to indicate dual issue for the SH4
+ processor. To be used by the DFA pipeline description. */
+static int
+sh_issue_rate (void)
+{
+ if (TARGET_SUPERSCALAR)
+ return 2;
+ else
+ return 1;
+}
+
+/* Functions for ready queue reordering for sched1. */
+
+/* Get weight for mode for a set x. */
+static short
+find_set_regmode_weight (rtx x, enum machine_mode mode)
+{
+ if (GET_CODE (x) == CLOBBER && register_operand (SET_DEST (x), mode))
+ return 1;
+ if (GET_CODE (x) == SET && register_operand (SET_DEST (x), mode))
+ {
+ if (GET_CODE (SET_DEST (x)) == REG)
+ {
+ if (!reg_mentioned_p (SET_DEST (x), SET_SRC (x)))
+ return 1;
+ else
+ return 0;
+ }
+ return 1;
+ }
+ return 0;
+}
+
+/* Get regmode weight for insn. */
+static short
+find_insn_regmode_weight (rtx insn, enum 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);
+ if (GET_CODE (x) == PARALLEL)
+ {
+ int j;
+ for (j = XVECLEN (x, 0) - 1; j >= 0; j--)
+ {
+ x = XVECEXP (PATTERN (insn), 0, j);
+ reg_weight += find_set_regmode_weight (x, mode);
+ }
+ }
+ /* Decrement weight for each register that dies here. */
+ for (x = REG_NOTES (insn); x; x = XEXP (x, 1))
+ {
+ if (REG_NOTE_KIND (x) == REG_DEAD || REG_NOTE_KIND (x) == REG_UNUSED)
+ {
+ rtx note = XEXP (x, 0);
+ if (GET_CODE (note) == REG && GET_MODE (note) == mode)
+ reg_weight--;
+ }
+ }
+ return reg_weight;
+}
+
+/* Calculate regmode weights for all insns of a basic block. */
+static void
+find_regmode_weight (basic_block b, enum machine_mode mode)
+{
+ rtx insn, next_tail, head, tail;
+
+ get_ebb_head_tail (b, b, &head, &tail);
+ next_tail = NEXT_INSN (tail);
+
+ for (insn = head; insn != next_tail; insn = NEXT_INSN (insn))
+ {
+ /* Handle register life information. */
+ if (!INSN_P (insn))
+ continue;
+
+ if (mode == SFmode)
+ INSN_REGMODE_WEIGHT (insn, mode) =
+ find_insn_regmode_weight (insn, mode) + 2 * find_insn_regmode_weight (insn, DFmode);
+ else if (mode == SImode)
+ INSN_REGMODE_WEIGHT (insn, mode) =
+ find_insn_regmode_weight (insn, mode) + 2 * find_insn_regmode_weight (insn, DImode);
+ }
+}
+
+/* Comparison function for ready queue sorting. */
+static int
+rank_for_reorder (const void *x, const void *y)
+{
+ rtx tmp = *(const rtx *) y;
+ rtx tmp2 = *(const rtx *) x;
+
+ /* The insn in a schedule group should be issued the first. */
+ if (SCHED_GROUP_P (tmp) != SCHED_GROUP_P (tmp2))
+ return SCHED_GROUP_P (tmp2) ? 1 : -1;
+
+ /* If insns are equally good, sort by INSN_LUID (original insn order), This
+ minimizes instruction movement, thus minimizing sched's effect on
+ register pressure. */
+ return INSN_LUID (tmp) - INSN_LUID (tmp2);
+}
+
+/* Resort the array A in which only element at index N may be out of order. */
+static void
+swap_reorder (rtx *a, int n)
+{
+ rtx insn = a[n - 1];
+ int i = n - 2;
+
+ while (i >= 0 && rank_for_reorder (a + i, &insn) >= 0)
+ {
+ a[i + 1] = a[i];
+ i -= 1;
+ }
+ a[i + 1] = insn;
+}
+
+#define SCHED_REORDER(READY, N_READY) \
+ do \
+ { \
+ if ((N_READY) == 2) \
+ swap_reorder (READY, N_READY); \
+ else if ((N_READY) > 2) \
+ qsort (READY, N_READY, sizeof (rtx), rank_for_reorder); \
+ } \
+ while (0)
+
+/* Sort the ready list READY by ascending priority, using the SCHED_REORDER
+ macro. */
+static void
+ready_reorder (rtx *ready, int nready)
+{
+ SCHED_REORDER (ready, nready);
+}
+
+/* Count life regions of r0 for a block. */
+static int
+find_r0_life_regions (basic_block b)
+{
+ rtx end, insn;
+ rtx pset;
+ rtx r0_reg;
+ int live;
+ int set;
+ int death = 0;
+
+ if (REGNO_REG_SET_P (df_get_live_in (b), R0_REG))
+ {
+ set = 1;
+ live = 1;
+ }
+ else
+ {
+ set = 0;
+ live = 0;
+ }
+
+ insn = BB_HEAD (b);
+ end = BB_END (b);
+ 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;
+ }
+ 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;
+ }
+ }
+ if (insn == end)
+ break;
+ insn = NEXT_INSN (insn);
+ }
+ return set - death;
+}
+
+/* Calculate regmode weights for all insns of all basic block. */
+static void
+sh_md_init_global (FILE *dump ATTRIBUTE_UNUSED,
+ int verbose ATTRIBUTE_UNUSED,
+ int old_max_uid)
+{
+ basic_block b;
+
+ regmode_weight[0] = (short *) xcalloc (old_max_uid, sizeof (short));
+ regmode_weight[1] = (short *) xcalloc (old_max_uid, sizeof (short));
+ r0_life_regions = 0;
+
+ FOR_EACH_BB_REVERSE (b)
+ {
+ find_regmode_weight (b, SImode);
+ find_regmode_weight (b, SFmode);
+ if (!reload_completed)
+ r0_life_regions += find_r0_life_regions (b);
+ }
+
+ CURR_REGMODE_PRESSURE (SImode) = 0;
+ CURR_REGMODE_PRESSURE (SFmode) = 0;
+
+}
+
+/* Cleanup. */
+static void
+sh_md_finish_global (FILE *dump ATTRIBUTE_UNUSED,
+ int verbose ATTRIBUTE_UNUSED)
+{
+ if (regmode_weight[0])
+ {
+ free (regmode_weight[0]);
+ regmode_weight[0] = NULL;
+ }
+ if (regmode_weight[1])
+ {
+ free (regmode_weight[1]);
+ regmode_weight[1] = NULL;
+ }
+}
+
+/* The scalar modes supported differs from the default version in TImode
+ for 32-bit SHMEDIA. */
+static bool
+sh_scalar_mode_supported_p (enum machine_mode mode)
+{
+ if (TARGET_SHMEDIA32 && mode == TImode)
+ return false;
+
+ return default_scalar_mode_supported_p (mode);
+}
+
+/* Cache the can_issue_more so that we can return it from reorder2. Also,
+ keep count of register pressures on SImode and SFmode. */
+static int
+sh_variable_issue (FILE *dump ATTRIBUTE_UNUSED,
+ int sched_verbose ATTRIBUTE_UNUSED,
+ rtx insn,
+ int can_issue_more)
+{
+ if (GET_CODE (PATTERN (insn)) != USE
+ && GET_CODE (PATTERN (insn)) != CLOBBER)
+ cached_can_issue_more = can_issue_more - 1;
+ else
+ cached_can_issue_more = can_issue_more;
+
+ if (reload_completed)
+ return cached_can_issue_more;
+
+ CURR_REGMODE_PRESSURE (SImode) += INSN_REGMODE_WEIGHT (insn, SImode);
+ CURR_REGMODE_PRESSURE (SFmode) += INSN_REGMODE_WEIGHT (insn, SFmode);
+
+ return cached_can_issue_more;
+}
+
+static void
+sh_md_init (FILE *dump ATTRIBUTE_UNUSED,
+ int verbose ATTRIBUTE_UNUSED,
+ int veclen ATTRIBUTE_UNUSED)
+{
+ CURR_REGMODE_PRESSURE (SImode) = 0;
+ CURR_REGMODE_PRESSURE (SFmode) = 0;
+}
+
+/* Some magic numbers. */
+/* Pressure on register r0 can lead to spill failures. so avoid sched1 for
+ functions that already have high pressure on r0. */
+#define R0_MAX_LIFE_REGIONS 2
+/* Register Pressure thresholds for SImode and SFmode registers. */
+#define SIMODE_MAX_WEIGHT 5
+#define SFMODE_MAX_WEIGHT 10
+
+/* Return true if the pressure is high for MODE. */
+static short
+high_pressure (enum machine_mode mode)
+{
+ /* Pressure on register r0 can lead to spill failures. so avoid sched1 for
+ functions that already have high pressure on r0. */
+ if (r0_life_regions >= R0_MAX_LIFE_REGIONS)
+ return 1;
+
+ if (mode == SFmode)
+ return (CURR_REGMODE_PRESSURE (SFmode) > SFMODE_MAX_WEIGHT);
+ else
+ return (CURR_REGMODE_PRESSURE (SImode) > SIMODE_MAX_WEIGHT);
+}
+
+/* Reorder ready queue if register pressure is high. */
+static int
+sh_reorder (FILE *dump ATTRIBUTE_UNUSED,
+ int sched_verbose ATTRIBUTE_UNUSED,
+ rtx *ready,
+ int *n_readyp,
+ int clock_var ATTRIBUTE_UNUSED)
+{
+ if (reload_completed)
+ return sh_issue_rate ();
+
+ if (high_pressure (SFmode) || high_pressure (SImode))
+ {
+ ready_reorder (ready, *n_readyp);
+ }
+
+ return sh_issue_rate ();
+}
+
+/* Skip cycles if the current register pressure is high. */
+static int
+sh_reorder2 (FILE *dump ATTRIBUTE_UNUSED,
+ int sched_verbose ATTRIBUTE_UNUSED,
+ rtx *ready ATTRIBUTE_UNUSED,
+ int *n_readyp ATTRIBUTE_UNUSED,
+ int clock_var ATTRIBUTE_UNUSED)
+{
+ if (reload_completed)
+ return cached_can_issue_more;
+
+ if (high_pressure(SFmode) || high_pressure (SImode))
+ skip_cycles = 1;
+
+ return cached_can_issue_more;
+}
+
+/* Skip cycles without sorting the ready queue. This will move insn from
+ Q->R. If this is the last cycle we are skipping; allow sorting of ready
+ queue by sh_reorder. */
+
+/* Generally, skipping these many cycles are sufficient for all insns to move
+ from Q -> R. */
+#define MAX_SKIPS 8
+
+static int
+sh_dfa_new_cycle (FILE *sched_dump ATTRIBUTE_UNUSED,
+ int sched_verbose ATTRIBUTE_UNUSED,
+ rtx insn ATTRIBUTE_UNUSED,
+ int last_clock_var,
+ int clock_var,
+ int *sort_p)
+{
+ if (reload_completed)
+ return 0;
+
+ if (skip_cycles)
+ {
+ if ((clock_var - last_clock_var) < MAX_SKIPS)
+ {
+ *sort_p = 0;
+ return 1;
+ }
+ /* If this is the last cycle we are skipping, allow reordering of R. */
+ if ((clock_var - last_clock_var) == MAX_SKIPS)
+ {
+ *sort_p = 1;
+ return 1;
+ }
+ }
+
+ skip_cycles = 0;
+
+ return 0;
+}
+
+/* SHmedia requires registers for branches, so we can't generate new
+ branches past reload. */
+static bool
+sh_cannot_modify_jumps_p (void)
+{
+ return (TARGET_SHMEDIA && (reload_in_progress || reload_completed));
+}
+
+static int
+sh_target_reg_class (void)
+{
+ return TARGET_SHMEDIA ? TARGET_REGS : NO_REGS;
+}
+
+static bool
+sh_optimize_target_register_callee_saved (bool after_prologue_epilogue_gen)
+{
+ HARD_REG_SET dummy;
+#if 0
+ rtx insn;
+#endif
+
+ if (! shmedia_space_reserved_for_target_registers)
+ return 0;
+ if (after_prologue_epilogue_gen && ! TARGET_SAVE_ALL_TARGET_REGS)
+ return 0;
+ if (calc_live_regs (&dummy) >= 6 * 8)
+ return 1;
+ return 0;
+}
+
+static bool
+sh_ms_bitfield_layout_p (const_tree record_type ATTRIBUTE_UNUSED)
+{
+ return (TARGET_SH5 || TARGET_HITACHI || sh_attr_renesas_p (record_type));
+}
+\f
+/*
+ On the SH1..SH4, the trampoline looks like
+ 2 0002 D202 mov.l l2,r2
+ 1 0000 D301 mov.l l1,r3
+ 3 0004 422B jmp @r2
+ 4 0006 0009 nop
+ 5 0008 00000000 l1: .long area
+ 6 000c 00000000 l2: .long function
+
+ SH5 (compact) uses r1 instead of r3 for the static chain. */
+
+
+/* Emit RTL insns to initialize the variable parts of a trampoline.
+ FNADDR is an RTX for the address of the function's pure code.
+ CXT is an RTX for the static chain value for the function. */
+
+void
+sh_initialize_trampoline (rtx tramp, rtx fnaddr, rtx cxt)
+{
+ rtx tramp_mem = gen_frame_mem (BLKmode, tramp);
+
+ if (TARGET_SHMEDIA64)
+ {
+ rtx tramp_templ;
+ int fixed_len;
+
+ rtx movi1 = GEN_INT (0xcc000010);
+ rtx shori1 = GEN_INT (0xc8000010);
+ rtx src, dst;
+
+ /* The following trampoline works within a +- 128 KB range for cxt:
+ ptb/u cxt,tr1; movi fnaddr >> 48,r0; shori fnaddr >> 32,r0;
+ shori fnaddr >> 16,r0; shori fnaddr,r0; ptabs/l r0,tr0
+ gettr tr1,r1; blink tr0,r63 */
+ /* Address rounding makes it hard to compute the exact bounds of the
+ offset for this trampoline, but we have a rather generous offset
+ range, so frame_offset should do fine as an upper bound. */
+ if (cxt == virtual_stack_vars_rtx && frame_offset < 0x20000)
+ {
+ /* ??? could optimize this trampoline initialization
+ by writing DImode words with two insns each. */
+ rtx mask = force_reg (DImode, GEN_INT (0x3fffc00));
+ rtx insn = gen_rtx_MINUS (DImode, cxt, tramp);
+ insn = gen_rtx_ASHIFT (DImode, insn, GEN_INT (10-2));
+ insn = gen_rtx_AND (DImode, insn, mask);
+ /* Or in ptb/u .,tr1 pattern */
+ insn = gen_rtx_IOR (DImode, insn, gen_int_mode (0xec000010, SImode));
+ insn = force_operand (insn, NULL_RTX);
+ insn = gen_lowpart (SImode, insn);
+ emit_move_insn (change_address (tramp_mem, SImode, NULL_RTX), insn);
+ insn = gen_rtx_LSHIFTRT (DImode, fnaddr, GEN_INT (38));
+ insn = gen_rtx_AND (DImode, insn, mask);
+ insn = force_operand (gen_rtx_IOR (DImode, movi1, insn), NULL_RTX);
+ insn = gen_lowpart (SImode, insn);
+ emit_move_insn (adjust_address (tramp_mem, SImode, 4), insn);
+ insn = gen_rtx_LSHIFTRT (DImode, fnaddr, GEN_INT (22));
+ insn = gen_rtx_AND (DImode, insn, mask);
+ insn = force_operand (gen_rtx_IOR (DImode, shori1, insn), NULL_RTX);
+ insn = gen_lowpart (SImode, insn);
+ emit_move_insn (adjust_address (tramp_mem, SImode, 8), insn);
+ insn = gen_rtx_LSHIFTRT (DImode, fnaddr, GEN_INT (6));
+ insn = gen_rtx_AND (DImode, insn, mask);
+ insn = force_operand (gen_rtx_IOR (DImode, shori1, insn), NULL_RTX);
+ insn = gen_lowpart (SImode, insn);
+ emit_move_insn (adjust_address (tramp_mem, SImode, 12), insn);
+ insn = gen_rtx_ASHIFT (DImode, fnaddr, GEN_INT (10));
+ insn = gen_rtx_AND (DImode, insn, mask);
+ insn = force_operand (gen_rtx_IOR (DImode, shori1, insn), NULL_RTX);
+ insn = gen_lowpart (SImode, insn);
+ emit_move_insn (adjust_address (tramp_mem, SImode, 16), insn);
+ emit_move_insn (adjust_address (tramp_mem, SImode, 20),
+ GEN_INT (0x6bf10600));
+ emit_move_insn (adjust_address (tramp_mem, SImode, 24),
+ GEN_INT (0x4415fc10));
+ emit_move_insn (adjust_address (tramp_mem, SImode, 28),
+ GEN_INT (0x4401fff0));
+ emit_insn (gen_ic_invalidate_line (tramp));
+ return;
+ }
+ tramp_templ = gen_rtx_SYMBOL_REF (Pmode,"__GCC_nested_trampoline");
+ fixed_len = TRAMPOLINE_SIZE - 2 * GET_MODE_SIZE (Pmode);
+
+ tramp_templ = gen_datalabel_ref (tramp_templ);
+ dst = tramp_mem;
+ src = gen_const_mem (BLKmode, tramp_templ);
+ set_mem_align (dst, 256);
+ set_mem_align (src, 64);
+ emit_block_move (dst, src, GEN_INT (fixed_len), BLOCK_OP_NORMAL);
+
+ emit_move_insn (adjust_address (tramp_mem, Pmode, fixed_len), fnaddr);
+ emit_move_insn (adjust_address (tramp_mem, Pmode,
+ fixed_len + GET_MODE_SIZE (Pmode)),
+ cxt);
+ emit_insn (gen_ic_invalidate_line (tramp));
+ return;
+ }
+ else if (TARGET_SHMEDIA)
+ {
+ /* movi fnaddr >> 16,r1; shori fnaddr,r1; ptabs/l r1,tr0
+ movi cxt >> 16,r1; shori cxt,r1; blink tr0,r63 */
+ rtx quad0 = gen_reg_rtx (DImode), cxtload = gen_reg_rtx (DImode);
+ rtx quad1 = gen_reg_rtx (DImode), quad2 = gen_reg_rtx (DImode);
+ /* movi 0,r1: 0xcc000010 shori 0,r1: c8000010 concatenated,
+ rotated 10 right, and higher 16 bit of every 32 selected. */
+ rtx movishori
+ = force_reg (V2HImode, (simplify_gen_subreg
+ (V2HImode, GEN_INT (0x4330432), SImode, 0)));
+ rtx ptabs = force_reg (DImode, GEN_INT (0x6bf10600));
+ rtx blink = force_reg (DImode, GEN_INT (0x4401fff0));
+
+ tramp = force_reg (Pmode, tramp);
+ fnaddr = force_reg (SImode, fnaddr);
+ cxt = force_reg (SImode, cxt);
+ emit_insn (gen_mshflo_w_x (gen_rtx_SUBREG (V4HImode, quad0, 0),
+ gen_rtx_SUBREG (V2HImode, fnaddr, 0),
+ movishori));
+ emit_insn (gen_rotrdi3_mextr (quad0, quad0,
+ GEN_INT (TARGET_LITTLE_ENDIAN ? 24 : 56)));
+ emit_insn (gen_ashldi3_media (quad0, quad0, const2_rtx));
+ emit_move_insn (change_address (tramp_mem, DImode, NULL_RTX), quad0);
+ emit_insn (gen_mshflo_w_x (gen_rtx_SUBREG (V4HImode, cxtload, 0),
+ gen_rtx_SUBREG (V2HImode, cxt, 0),
+ movishori));
+ emit_insn (gen_rotrdi3_mextr (cxtload, cxtload,
+ GEN_INT (TARGET_LITTLE_ENDIAN ? 24 : 56)));
+ emit_insn (gen_ashldi3_media (cxtload, cxtload, const2_rtx));
+ if (TARGET_LITTLE_ENDIAN)
+ {
+ emit_insn (gen_mshflo_l_di (quad1, ptabs, cxtload));
+ emit_insn (gen_mextr4 (quad2, cxtload, blink));
+ }
+ else
+ {
+ emit_insn (gen_mextr4 (quad1, cxtload, ptabs));
+ emit_insn (gen_mshflo_l_di (quad2, blink, cxtload));
+ }
+ emit_move_insn (adjust_address (tramp_mem, DImode, 8), quad1);
+ emit_move_insn (adjust_address (tramp_mem, DImode, 16), quad2);
+ emit_insn (gen_ic_invalidate_line (tramp));
+ return;
+ }
+ else if (TARGET_SHCOMPACT)
+ {
+ emit_insn (gen_initialize_trampoline (tramp, cxt, fnaddr));
+ return;
+ }
+ emit_move_insn (change_address (tramp_mem, SImode, NULL_RTX),
+ gen_int_mode (TARGET_LITTLE_ENDIAN ? 0xd301d202 : 0xd202d301,
+ SImode));
+ emit_move_insn (adjust_address (tramp_mem, SImode, 4),
+ gen_int_mode (TARGET_LITTLE_ENDIAN ? 0x0009422b : 0x422b0009,
+ SImode));
+ emit_move_insn (adjust_address (tramp_mem, SImode, 8), cxt);
+ emit_move_insn (adjust_address (tramp_mem, SImode, 12), fnaddr);
+ if (TARGET_HARVARD)
+ {
+ if (!TARGET_INLINE_IC_INVALIDATE
+ || (!(TARGET_SH4A_ARCH || TARGET_SH4_300) && TARGET_USERMODE))
+ emit_library_call (function_symbol (NULL, "__ic_invalidate",
+ FUNCTION_ORDINARY),
+ 0, VOIDmode, 1, tramp, SImode);
+ else
+ emit_insn (gen_ic_invalidate_line (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
+sh_function_ok_for_sibcall (tree decl, tree exp ATTRIBUTE_UNUSED)
+{
+ return (1
+ && (! TARGET_SHCOMPACT
+ || crtl->args.info.stack_regs == 0)
+ && ! sh_cfun_interrupt_handler_p ()
+ && (! flag_pic
+ || (decl && ! TREE_PUBLIC (decl))
+ || (decl && DECL_VISIBILITY (decl) != VISIBILITY_DEFAULT)));
+}
+\f
+/* Machine specific built-in functions. */
+
+struct builtin_description
+{
+ const enum insn_code icode;
+ const char *const name;
+ int signature;
+};
+
+/* describe number and signedness of arguments; arg[0] == result
+ (1: unsigned, 2: signed, 4: don't care, 8: pointer 0: no argument */
+/* 9: 64-bit pointer, 10: 32-bit pointer */
+static const char signature_args[][4] =
+{
+#define SH_BLTIN_V2SI2 0
+ { 4, 4 },
+#define SH_BLTIN_V4HI2 1
+ { 4, 4 },
+#define SH_BLTIN_V2SI3 2
+ { 4, 4, 4 },
+#define SH_BLTIN_V4HI3 3
+ { 4, 4, 4 },
+#define SH_BLTIN_V8QI3 4
+ { 4, 4, 4 },
+#define SH_BLTIN_MAC_HISI 5
+ { 1, 4, 4, 1 },
+#define SH_BLTIN_SH_HI 6
+ { 4, 4, 1 },
+#define SH_BLTIN_SH_SI 7
+ { 4, 4, 1 },
+#define SH_BLTIN_V4HI2V2SI 8
+ { 4, 4, 4 },
+#define SH_BLTIN_V4HI2V8QI 9
+ { 4, 4, 4 },
+#define SH_BLTIN_SISF 10
+ { 4, 2 },
+#define SH_BLTIN_LDUA_L 11
+ { 2, 10 },
+#define SH_BLTIN_LDUA_Q 12
+ { 1, 10 },
+#define SH_BLTIN_STUA_L 13
+ { 0, 10, 2 },
+#define SH_BLTIN_STUA_Q 14
+ { 0, 10, 1 },
+#define SH_BLTIN_LDUA_L64 15
+ { 2, 9 },
+#define SH_BLTIN_LDUA_Q64 16
+ { 1, 9 },
+#define SH_BLTIN_STUA_L64 17
+ { 0, 9, 2 },
+#define SH_BLTIN_STUA_Q64 18
+ { 0, 9, 1 },
+#define SH_BLTIN_NUM_SHARED_SIGNATURES 19
+#define SH_BLTIN_2 19
+#define SH_BLTIN_SU 19
+ { 1, 2 },
+#define SH_BLTIN_3 20
+#define SH_BLTIN_SUS 20
+ { 2, 2, 1 },
+#define SH_BLTIN_PSSV 21
+ { 0, 8, 2, 2 },
+#define SH_BLTIN_XXUU 22
+#define SH_BLTIN_UUUU 22
+ { 1, 1, 1, 1 },
+#define SH_BLTIN_PV 23
+ { 0, 8 },
+};
+/* mcmv: operands considered unsigned. */
+/* mmulsum_wq, msad_ubq: result considered unsigned long long. */
+/* mperm: control value considered unsigned int. */
+/* mshalds, mshard, mshards, mshlld, mshlrd: shift count is unsigned int. */
+/* mshards_q: returns signed short. */
+/* nsb: takes long long arg, returns unsigned char. */
+static const struct builtin_description bdesc[] =
+{
+ { CODE_FOR_absv2si2, "__builtin_absv2si2", SH_BLTIN_V2SI2 },
+ { CODE_FOR_absv4hi2, "__builtin_absv4hi2", SH_BLTIN_V4HI2 },
+ { CODE_FOR_addv2si3, "__builtin_addv2si3", SH_BLTIN_V2SI3 },
+ { CODE_FOR_addv4hi3, "__builtin_addv4hi3", SH_BLTIN_V4HI3 },
+ { CODE_FOR_ssaddv2si3,"__builtin_ssaddv2si3", SH_BLTIN_V2SI3 },
+ { CODE_FOR_usaddv8qi3,"__builtin_usaddv8qi3", SH_BLTIN_V8QI3 },
+ { CODE_FOR_ssaddv4hi3,"__builtin_ssaddv4hi3", SH_BLTIN_V4HI3 },
+ { CODE_FOR_alloco_i, "__builtin_sh_media_ALLOCO", SH_BLTIN_PV },
+ { CODE_FOR_negcmpeqv8qi,"__builtin_sh_media_MCMPEQ_B", SH_BLTIN_V8QI3 },
+ { CODE_FOR_negcmpeqv2si,"__builtin_sh_media_MCMPEQ_L", SH_BLTIN_V2SI3 },
+ { CODE_FOR_negcmpeqv4hi,"__builtin_sh_media_MCMPEQ_W", SH_BLTIN_V4HI3 },
+ { CODE_FOR_negcmpgtuv8qi,"__builtin_sh_media_MCMPGT_UB", SH_BLTIN_V8QI3 },
+ { CODE_FOR_negcmpgtv2si,"__builtin_sh_media_MCMPGT_L", SH_BLTIN_V2SI3 },
+ { CODE_FOR_negcmpgtv4hi,"__builtin_sh_media_MCMPGT_W", SH_BLTIN_V4HI3 },
+ { CODE_FOR_mcmv, "__builtin_sh_media_MCMV", SH_BLTIN_UUUU },
+ { CODE_FOR_mcnvs_lw, "__builtin_sh_media_MCNVS_LW", SH_BLTIN_3 },
+ { CODE_FOR_mcnvs_wb, "__builtin_sh_media_MCNVS_WB", SH_BLTIN_V4HI2V8QI },
+ { CODE_FOR_mcnvs_wub, "__builtin_sh_media_MCNVS_WUB", SH_BLTIN_V4HI2V8QI },
+ { CODE_FOR_mextr1, "__builtin_sh_media_MEXTR1", SH_BLTIN_V8QI3 },
+ { CODE_FOR_mextr2, "__builtin_sh_media_MEXTR2", SH_BLTIN_V8QI3 },
+ { CODE_FOR_mextr3, "__builtin_sh_media_MEXTR3", SH_BLTIN_V8QI3 },
+ { CODE_FOR_mextr4, "__builtin_sh_media_MEXTR4", SH_BLTIN_V8QI3 },
+ { CODE_FOR_mextr5, "__builtin_sh_media_MEXTR5", SH_BLTIN_V8QI3 },
+ { CODE_FOR_mextr6, "__builtin_sh_media_MEXTR6", SH_BLTIN_V8QI3 },
+ { CODE_FOR_mextr7, "__builtin_sh_media_MEXTR7", SH_BLTIN_V8QI3 },
+ { CODE_FOR_mmacfx_wl, "__builtin_sh_media_MMACFX_WL", SH_BLTIN_MAC_HISI },
+ { CODE_FOR_mmacnfx_wl,"__builtin_sh_media_MMACNFX_WL", SH_BLTIN_MAC_HISI },
+ { CODE_FOR_mulv2si3, "__builtin_mulv2si3", SH_BLTIN_V2SI3, },
+ { CODE_FOR_mulv4hi3, "__builtin_mulv4hi3", SH_BLTIN_V4HI3 },
+ { CODE_FOR_mmulfx_l, "__builtin_sh_media_MMULFX_L", SH_BLTIN_V2SI3 },
+ { CODE_FOR_mmulfx_w, "__builtin_sh_media_MMULFX_W", SH_BLTIN_V4HI3 },
+ { CODE_FOR_mmulfxrp_w,"__builtin_sh_media_MMULFXRP_W", SH_BLTIN_V4HI3 },
+ { CODE_FOR_mmulhi_wl, "__builtin_sh_media_MMULHI_WL", SH_BLTIN_V4HI2V2SI },
+ { CODE_FOR_mmullo_wl, "__builtin_sh_media_MMULLO_WL", SH_BLTIN_V4HI2V2SI },
+ { CODE_FOR_mmulsum_wq,"__builtin_sh_media_MMULSUM_WQ", SH_BLTIN_XXUU },
+ { CODE_FOR_mperm_w, "__builtin_sh_media_MPERM_W", SH_BLTIN_SH_HI },
+ { CODE_FOR_msad_ubq, "__builtin_sh_media_MSAD_UBQ", SH_BLTIN_XXUU },
+ { CODE_FOR_mshalds_l, "__builtin_sh_media_MSHALDS_L", SH_BLTIN_SH_SI },
+ { CODE_FOR_mshalds_w, "__builtin_sh_media_MSHALDS_W", SH_BLTIN_SH_HI },
+ { CODE_FOR_ashrv2si3, "__builtin_ashrv2si3", SH_BLTIN_SH_SI },
+ { CODE_FOR_ashrv4hi3, "__builtin_ashrv4hi3", SH_BLTIN_SH_HI },
+ { CODE_FOR_mshards_q, "__builtin_sh_media_MSHARDS_Q", SH_BLTIN_SUS },
+ { CODE_FOR_mshfhi_b, "__builtin_sh_media_MSHFHI_B", SH_BLTIN_V8QI3 },
+ { CODE_FOR_mshfhi_l, "__builtin_sh_media_MSHFHI_L", SH_BLTIN_V2SI3 },
+ { CODE_FOR_mshfhi_w, "__builtin_sh_media_MSHFHI_W", SH_BLTIN_V4HI3 },
+ { CODE_FOR_mshflo_b, "__builtin_sh_media_MSHFLO_B", SH_BLTIN_V8QI3 },
+ { CODE_FOR_mshflo_l, "__builtin_sh_media_MSHFLO_L", SH_BLTIN_V2SI3 },
+ { CODE_FOR_mshflo_w, "__builtin_sh_media_MSHFLO_W", SH_BLTIN_V4HI3 },
+ { CODE_FOR_ashlv2si3, "__builtin_ashlv2si3", SH_BLTIN_SH_SI },
+ { CODE_FOR_ashlv4hi3, "__builtin_ashlv4hi3", SH_BLTIN_SH_HI },
+ { CODE_FOR_lshrv2si3, "__builtin_lshrv2si3", SH_BLTIN_SH_SI },
+ { CODE_FOR_lshrv4hi3, "__builtin_lshrv4hi3", SH_BLTIN_SH_HI },
+ { CODE_FOR_subv2si3, "__builtin_subv2si3", SH_BLTIN_V2SI3 },
+ { CODE_FOR_subv4hi3, "__builtin_subv4hi3", SH_BLTIN_V4HI3 },
+ { CODE_FOR_sssubv2si3,"__builtin_sssubv2si3", SH_BLTIN_V2SI3 },
+ { CODE_FOR_ussubv8qi3,"__builtin_ussubv8qi3", SH_BLTIN_V8QI3 },
+ { CODE_FOR_sssubv4hi3,"__builtin_sssubv4hi3", SH_BLTIN_V4HI3 },
+ { CODE_FOR_fcosa_s, "__builtin_sh_media_FCOSA_S", SH_BLTIN_SISF },
+ { CODE_FOR_fsina_s, "__builtin_sh_media_FSINA_S", SH_BLTIN_SISF },
+ { CODE_FOR_fipr, "__builtin_sh_media_FIPR_S", SH_BLTIN_3 },
+ { CODE_FOR_ftrv, "__builtin_sh_media_FTRV_S", SH_BLTIN_3 },
+ { CODE_FOR_mac_media, "__builtin_sh_media_FMAC_S", SH_BLTIN_3 },
+ { CODE_FOR_sqrtdf2, "__builtin_sh_media_FSQRT_D", SH_BLTIN_2 },
+ { CODE_FOR_sqrtsf2, "__builtin_sh_media_FSQRT_S", SH_BLTIN_2 },
+ { CODE_FOR_fsrra_s, "__builtin_sh_media_FSRRA_S", SH_BLTIN_2 },
+ { CODE_FOR_ldhi_l, "__builtin_sh_media_LDHI_L", SH_BLTIN_LDUA_L },
+ { CODE_FOR_ldhi_q, "__builtin_sh_media_LDHI_Q", SH_BLTIN_LDUA_Q },
+ { CODE_FOR_ldlo_l, "__builtin_sh_media_LDLO_L", SH_BLTIN_LDUA_L },
+ { CODE_FOR_ldlo_q, "__builtin_sh_media_LDLO_Q", SH_BLTIN_LDUA_Q },
+ { CODE_FOR_sthi_l, "__builtin_sh_media_STHI_L", SH_BLTIN_STUA_L },
+ { CODE_FOR_sthi_q, "__builtin_sh_media_STHI_Q", SH_BLTIN_STUA_Q },
+ { CODE_FOR_stlo_l, "__builtin_sh_media_STLO_L", SH_BLTIN_STUA_L },
+ { CODE_FOR_stlo_q, "__builtin_sh_media_STLO_Q", SH_BLTIN_STUA_Q },
+ { CODE_FOR_ldhi_l64, "__builtin_sh_media_LDHI_L", SH_BLTIN_LDUA_L64 },
+ { CODE_FOR_ldhi_q64, "__builtin_sh_media_LDHI_Q", SH_BLTIN_LDUA_Q64 },
+ { CODE_FOR_ldlo_l64, "__builtin_sh_media_LDLO_L", SH_BLTIN_LDUA_L64 },
+ { CODE_FOR_ldlo_q64, "__builtin_sh_media_LDLO_Q", SH_BLTIN_LDUA_Q64 },
+ { CODE_FOR_sthi_l64, "__builtin_sh_media_STHI_L", SH_BLTIN_STUA_L64 },
+ { CODE_FOR_sthi_q64, "__builtin_sh_media_STHI_Q", SH_BLTIN_STUA_Q64 },
+ { CODE_FOR_stlo_l64, "__builtin_sh_media_STLO_L", SH_BLTIN_STUA_L64 },
+ { CODE_FOR_stlo_q64, "__builtin_sh_media_STLO_Q", SH_BLTIN_STUA_Q64 },
+ { CODE_FOR_nsb, "__builtin_sh_media_NSB", SH_BLTIN_SU },
+ { CODE_FOR_byterev, "__builtin_sh_media_BYTEREV", SH_BLTIN_2 },
+ { CODE_FOR_prefetch, "__builtin_sh_media_PREFO", SH_BLTIN_PSSV },
+};
+
+static void
+sh_media_init_builtins (void)
+{
+ tree shared[SH_BLTIN_NUM_SHARED_SIGNATURES];
+ const struct builtin_description *d;
+
+ memset (shared, 0, sizeof shared);
+ for (d = bdesc; d - bdesc < (int) ARRAY_SIZE (bdesc); d++)
+ {
+ tree type, arg_type = 0;
+ int signature = d->signature;
+ int i;
+
+ if (signature < SH_BLTIN_NUM_SHARED_SIGNATURES && shared[signature])
+ type = shared[signature];
+ else
+ {
+ int has_result = signature_args[signature][0] != 0;
+
+ if ((signature_args[signature][1] & 8)
+ && (((signature_args[signature][1] & 1) && TARGET_SHMEDIA32)
+ || ((signature_args[signature][1] & 2) && TARGET_SHMEDIA64)))
+ continue;
+ if (! TARGET_FPU_ANY
+ && FLOAT_MODE_P (insn_data[d->icode].operand[0].mode))
+ continue;
+ type = void_list_node;
+ for (i = 3; ; i--)
+ {
+ int arg = signature_args[signature][i];
+ int opno = i - 1 + has_result;
+
+ if (arg & 8)
+ arg_type = ptr_type_node;
+ else if (arg)
+ arg_type = (*lang_hooks.types.type_for_mode)
+ (insn_data[d->icode].operand[opno].mode,
+ (arg & 1));
+ else if (i)
+ continue;
+ else
+ arg_type = void_type_node;
+ if (i == 0)
+ break;
+ type = tree_cons (NULL_TREE, arg_type, type);
+ }
+ type = build_function_type (arg_type, type);
+ if (signature < SH_BLTIN_NUM_SHARED_SIGNATURES)
+ shared[signature] = type;
+ }
+ add_builtin_function (d->name, type, d - bdesc, BUILT_IN_MD,
+ NULL, NULL_TREE);
+ }
+}
+
+/* Implements target hook vector_mode_supported_p. */
+bool
+sh_vector_mode_supported_p (enum machine_mode mode)
+{
+ if (TARGET_FPU_ANY
+ && ((mode == V2SFmode)
+ || (mode == V4SFmode)
+ || (mode == V16SFmode)))
+ return true;
+
+ else if (TARGET_SHMEDIA
+ && ((mode == V8QImode)
+ || (mode == V2HImode)
+ || (mode == V4HImode)
+ || (mode == V2SImode)))
+ return true;
+
+ return false;
+}
+
+/* Implements target hook dwarf_calling_convention. Return an enum
+ of dwarf_calling_convention. */
+int
+sh_dwarf_calling_convention (const_tree func)
+{
+ if (sh_attr_renesas_p (func))
+ return DW_CC_GNU_renesas_sh;
+
+ return DW_CC_normal;
+}
+
+static void
+sh_init_builtins (void)
+{
+ if (TARGET_SHMEDIA)
+ sh_media_init_builtins ();
+}
+
+/* Expand an expression EXP that calls a built-in function,
+ with result going to TARGET if that's convenient
+ (and in mode MODE if that's convenient).
+ SUBTARGET may be used as the target for computing one of EXP's operands.
+ IGNORE is nonzero if the value is to be ignored. */
+
+static rtx
+sh_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
+ enum machine_mode mode ATTRIBUTE_UNUSED, int ignore)
+{
+ tree fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0);
+ unsigned int fcode = DECL_FUNCTION_CODE (fndecl);
+ const struct builtin_description *d = &bdesc[fcode];
+ enum insn_code icode = d->icode;
+ int signature = d->signature;
+ enum machine_mode tmode = VOIDmode;
+ int nop = 0, i;
+ rtx op[4];
+ rtx pat = 0;
+
+ if (signature_args[signature][0])
+ {
+ if (ignore)
+ return 0;
+
+ tmode = insn_data[icode].operand[0].mode;
+ if (! target
+ || GET_MODE (target) != tmode
+ || ! (*insn_data[icode].operand[0].predicate) (target, tmode))
+ target = gen_reg_rtx (tmode);
+ op[nop++] = target;
+ }
+ else
+ target = 0;
+
+ for (i = 1; i <= 3; i++, nop++)
+ {
+ tree arg;
+ enum machine_mode opmode, argmode;
+ tree optype;
+
+ if (! signature_args[signature][i])
+ break;
+ arg = CALL_EXPR_ARG (exp, i - 1);
+ if (arg == error_mark_node)
+ return const0_rtx;
+ if (signature_args[signature][i] & 8)
+ {
+ opmode = ptr_mode;
+ optype = ptr_type_node;
+ }
+ else
+ {
+ opmode = insn_data[icode].operand[nop].mode;
+ optype = (*lang_hooks.types.type_for_mode) (opmode, 0);
+ }
+ argmode = TYPE_MODE (TREE_TYPE (arg));
+ if (argmode != opmode)
+ arg = build1 (NOP_EXPR, optype, arg);
+ op[nop] = expand_expr (arg, NULL_RTX, opmode, 0);
+ if (! (*insn_data[icode].operand[nop].predicate) (op[nop], opmode))
+ op[nop] = copy_to_mode_reg (opmode, op[nop]);
+ }
+
+ switch (nop)
+ {
+ case 1:
+ pat = (*insn_data[d->icode].genfun) (op[0]);
+ break;
+ case 2:
+ pat = (*insn_data[d->icode].genfun) (op[0], op[1]);
+ break;
+ case 3:
+ pat = (*insn_data[d->icode].genfun) (op[0], op[1], op[2]);
+ break;
+ case 4:
+ pat = (*insn_data[d->icode].genfun) (op[0], op[1], op[2], op[3]);
+ break;
+ default:
+ gcc_unreachable ();
+ }
+ if (! pat)
+ return 0;
+ emit_insn (pat);
+ return target;
+}
+
+void
+sh_expand_unop_v2sf (enum rtx_code code, rtx op0, rtx op1)
+{
+ rtx sel0 = const0_rtx;
+ rtx sel1 = const1_rtx;
+ rtx (*fn) (rtx, rtx, rtx, rtx, rtx) = gen_unary_sf_op;
+ rtx op = gen_rtx_fmt_e (code, SFmode, op1);
+
+ emit_insn ((*fn) (op0, op1, op, sel0, sel0));
+ emit_insn ((*fn) (op0, op1, op, sel1, sel1));
+}
+
+void
+sh_expand_binop_v2sf (enum rtx_code code, rtx op0, rtx op1, rtx op2)
+{
+ rtx op = gen_rtx_fmt_ee (code, SFmode, op1, op2);
+
+ emit_insn (gen_binary_sf_op0 (op0, op1, op2, op));
+ emit_insn (gen_binary_sf_op1 (op0, op1, op2, op));
+}
+
+/* Return true if hard register REGNO can hold a value of machine-mode MODE.
+ We can allow any mode in any general register. The special registers
+ only allow SImode. Don't allow any mode in the PR.
+
+ We cannot hold DCmode values in the XD registers because alter_reg
+ handles subregs of them incorrectly. We could work around this by
+ spacing the XD registers like the DR registers, but this would require
+ additional memory in every compilation to hold larger register vectors.
+ We could hold SFmode / SCmode values in XD registers, but that
+ would require a tertiary reload when reloading from / to memory,
+ and a secondary reload to reload from / to general regs; that
+ seems to be a loosing proposition.
+
+ 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
+sh_hard_regno_mode_ok (unsigned int regno, enum machine_mode mode)
+{
+ if (SPECIAL_REGISTER_P (regno))
+ return mode == SImode;
+
+ if (regno == FPUL_REG)
+ return (mode == SImode || mode == SFmode);
+
+ if (FP_REGISTER_P (regno) && mode == SFmode)
+ return true;
+
+ if (mode == V2SFmode)
+ {
+ if (((FP_REGISTER_P (regno) && (regno - FIRST_FP_REG) % 2 == 0)
+ || GENERAL_REGISTER_P (regno)))
+ return true;
+ else
+ return false;
+ }
+
+ if (mode == V4SFmode)
+ {
+ if ((FP_REGISTER_P (regno) && (regno - FIRST_FP_REG) % 4 == 0)
+ || GENERAL_REGISTER_P (regno))
+ return true;
+ else
+ return false;
+ }
+
+ if (mode == V16SFmode)
+ {
+ if (TARGET_SHMEDIA)
+ {
+ if (FP_REGISTER_P (regno) && (regno - FIRST_FP_REG) % 16 == 0)
+ return true;
+ else
+ return false;
+ }
+ else
+ return regno == FIRST_XD_REG;
+ }
+
+ if (FP_REGISTER_P (regno))
+ {
+ if (mode == SFmode
+ || mode == SImode
+ || ((TARGET_SH2E || TARGET_SHMEDIA) && mode == SCmode)
+ || ((((TARGET_SH4 || TARGET_SH2A_DOUBLE) && mode == DFmode)
+ || mode == DCmode
+ || (TARGET_SHMEDIA
+ && (mode == DFmode || mode == DImode
+ || mode == V2SFmode || mode == TImode)))
+ && ((regno - FIRST_FP_REG) & 1) == 0)
+ || ((TARGET_SH4 || TARGET_SHMEDIA) && mode == TImode
+ && ((regno - FIRST_FP_REG) & 3) == 0))
+ return true;
+ else
+ return false;
+ }
+
+ 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;
+
+ if (regno == FPSCR_REG)
+ return mode == PSImode;
+
+ /* FIXME. This works around PR target/37633 for -O0. */
+ if (!optimize && TARGET_SHMEDIA32 && GET_MODE_SIZE (mode) > 4)
+ {
+ unsigned int n = GET_MODE_SIZE (mode) / 8;
+
+ if (regno >= FIRST_GENERAL_REG + 10 - n + 1
+ && regno <= FIRST_GENERAL_REG + 14)
+ return false;
+ }
+
+ return true;
+}
+
+/* Return the class of registers for which a mode change from FROM to TO
+ is invalid. */
+bool
+sh_cannot_change_mode_class (enum machine_mode from, enum machine_mode to,
+ enum reg_class rclass)
+{
+ /* We want to enable the use of SUBREGs as a means to
+ VEC_SELECT a single element of a vector. */
+ if (to == SFmode && VECTOR_MODE_P (from) && GET_MODE_INNER (from) == SFmode)
+ return (reg_classes_intersect_p (GENERAL_REGS, rclass));
+
+ 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);
+ }
+ else
+ {
+ if (GET_MODE_SIZE (from) < 8)
+ return reg_classes_intersect_p (DF_HI_REGS, rclass);
+ }
+ }
+ return 0;
+}
+
+
+/* If ADDRESS refers to a CODE_LABEL, add NUSES to the number of times
+ that label is used. */
+
+void
+sh_mark_label (rtx address, int nuses)
+{
+ if (GOTOFF_P (address))
+ {
+ /* Extract the label or symbol. */
+ address = XEXP (address, 0);
+ if (GET_CODE (address) == PLUS)
+ address = XEXP (address, 0);
+ address = XVECEXP (address, 0, 0);
+ }
+ if (GET_CODE (address) == LABEL_REF
+ && GET_CODE (XEXP (address, 0)) == CODE_LABEL)
+ LABEL_NUSES (XEXP (address, 0)) += nuses;
+}
+
+/* Compute extra cost of moving data between one register class
+ and another. */
+
+/* If SECONDARY*_RELOAD_CLASS says something about the src/dst pair, regclass
+ uses this information. Hence, the general register <-> floating point
+ register information here is not used for SFmode. */
+
+int
+sh_register_move_cost (enum machine_mode mode,
+ enum reg_class srcclass, enum reg_class dstclass)
+{
+ if (dstclass == T_REGS || dstclass == PR_REGS)
+ return 10;
+
+ if (dstclass == MAC_REGS && srcclass == MAC_REGS)
+ return 4;
+
+ if (mode == SImode && ! TARGET_SHMEDIA && TARGET_FMOVD
+ && REGCLASS_HAS_FP_REG (srcclass)
+ && REGCLASS_HAS_FP_REG (dstclass))
+ return 4;
+
+ if (REGCLASS_HAS_FP_REG (dstclass) && srcclass == T_REGS)
+ return ((TARGET_HARD_SH4 && !optimize_size) ? 10 : 7);
+
+ if ((REGCLASS_HAS_FP_REG (dstclass) && srcclass == MAC_REGS)
+ || (dstclass == MAC_REGS && REGCLASS_HAS_FP_REG (srcclass)))
+ return 9;
+
+ if ((REGCLASS_HAS_FP_REG (dstclass)
+ && REGCLASS_HAS_GENERAL_REG (srcclass))
+ || (REGCLASS_HAS_GENERAL_REG (dstclass)
+ && REGCLASS_HAS_FP_REG (srcclass)))
+ return ((TARGET_SHMEDIA ? 4 : TARGET_FMOVD ? 8 : 12)
+ * ((GET_MODE_SIZE (mode) + 7) / 8U));
+
+ if ((dstclass == FPUL_REGS
+ && REGCLASS_HAS_GENERAL_REG (srcclass))
+ || (srcclass == FPUL_REGS
+ && REGCLASS_HAS_GENERAL_REG (dstclass)))
+ return 5;
+
+ if ((dstclass == FPUL_REGS
+ && (srcclass == PR_REGS || srcclass == MAC_REGS || srcclass == T_REGS))
+ || (srcclass == FPUL_REGS
+ && (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;
+
+ /* ??? ptabs faults on (value & 0x3) == 0x3 */
+ if (TARGET_SHMEDIA
+ && ((srcclass) == TARGET_REGS || (srcclass) == SIBCALL_REGS))
+ {
+ if (sh_gettrcost >= 0)
+ return sh_gettrcost;
+ else if (!TARGET_PT_FIXED)
+ return 100;
+ }
+
+ if ((srcclass == FPSCR_REGS && ! REGCLASS_HAS_GENERAL_REG (dstclass))
+ || (dstclass == FPSCR_REGS && ! REGCLASS_HAS_GENERAL_REG (srcclass)))
+ return 4;
+
+ if (TARGET_SHMEDIA
+ || (TARGET_FMOVD
+ && ! REGCLASS_HAS_GENERAL_REG (srcclass)
+ && ! REGCLASS_HAS_GENERAL_REG (dstclass)))
+ return 2 * ((GET_MODE_SIZE (mode) + 7) / 8U);
+
+ return 2 * ((GET_MODE_SIZE (mode) + 3) / 4U);
+}
+
+static rtx emit_load_ptr (rtx, rtx);
+
+static rtx
+emit_load_ptr (rtx reg, rtx addr)
+{
+ rtx mem = gen_const_mem (ptr_mode, addr);
+
+ if (Pmode != ptr_mode)
+ mem = gen_rtx_SIGN_EXTEND (Pmode, mem);
+ return emit_move_insn (reg, mem);
+}
+
+static void
+sh_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
+ HOST_WIDE_INT delta, HOST_WIDE_INT vcall_offset,
+ tree function)
+{
+ CUMULATIVE_ARGS cum;
+ int structure_value_byref = 0;
+ rtx this_rtx, this_value, sibcall, insns, funexp;
+ tree funtype = TREE_TYPE (function);
+ int simple_add = CONST_OK_FOR_ADD (delta);
+ int did_load = 0;
+ rtx scratch0, scratch1, scratch2;
+ unsigned i;
+
+ reload_completed = 1;
+ epilogue_completed = 1;
+ current_function_uses_only_leaf_regs = 1;
+
+ emit_note (NOTE_INSN_PROLOGUE_END);
+
+ /* Find the "this" pointer. We have such a wide range of ABIs for the
+ SH that it's best to do this completely machine independently.
+ "this" is passed as first argument, unless a structure return pointer
+ comes first, in which case "this" comes second. */
+ INIT_CUMULATIVE_ARGS (cum, funtype, NULL_RTX, 0, 1);
+#ifndef PCC_STATIC_STRUCT_RETURN
+ if (aggregate_value_p (TREE_TYPE (TREE_TYPE (function)), function))
+ structure_value_byref = 1;
+#endif /* not PCC_STATIC_STRUCT_RETURN */
+ if (structure_value_byref && sh_struct_value_rtx (function, 0) == 0)
+ {
+ tree ptype = build_pointer_type (TREE_TYPE (funtype));
+
+ FUNCTION_ARG_ADVANCE (cum, Pmode, ptype, 1);
+ }
+ this_rtx = FUNCTION_ARG (cum, Pmode, ptr_type_node, 1);
+
+ /* 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
+ right now, someone might implement them sometime), and the rest of the
+ 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])
+ error ("r0 needs to be available as a call-clobbered register");
+ scratch0 = scratch1 = scratch2 = gen_rtx_REG (Pmode, 0);
+ if (! TARGET_SH5)
+ {
+ if (call_used_regs[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])
+ scratch2 = gen_rtx_REG (Pmode, 3);
+ }
+ else if (TARGET_SHMEDIA)
+ {
+ for (i = FIRST_GENERAL_REG; i <= LAST_GENERAL_REG; i++)
+ if (i != REGNO (scratch0) &&
+ call_used_regs[i] && ! fixed_regs[i] && ! FUNCTION_ARG_REGNO_P (i))
+ {
+ scratch1 = gen_rtx_REG (ptr_mode, i);
+ break;
+ }
+ if (scratch1 == scratch0)
+ error ("Need a second call-clobbered general purpose register");
+ for (i = FIRST_TARGET_REG; i <= LAST_TARGET_REG; i++)
+ if (call_used_regs[i] && ! fixed_regs[i])
+ {
+ scratch2 = gen_rtx_REG (Pmode, i);
+ break;
+ }
+ if (scratch2 == scratch0)
+ error ("Need a call-clobbered target register");
+ }
+
+ this_value = plus_constant (this_rtx, delta);
+ if (vcall_offset
+ && (simple_add || scratch0 != scratch1)
+ && strict_memory_address_p (ptr_mode, this_value))
+ {
+ emit_load_ptr (scratch0, this_value);
+ did_load = 1;
+ }
+
+ if (!delta)
+ ; /* Do nothing. */
+ else if (simple_add)
+ emit_move_insn (this_rtx, this_value);
+ else
+ {
+ emit_move_insn (scratch1, GEN_INT (delta));
+ emit_insn (gen_add2_insn (this_rtx, scratch1));
+ }
+
+ if (vcall_offset)
+ {
+ rtx offset_addr;
+
+ if (!did_load)
+ emit_load_ptr (scratch0, this_rtx);
+
+ offset_addr = plus_constant (scratch0, vcall_offset);
+ if (strict_memory_address_p (ptr_mode, offset_addr))
+ ; /* Do nothing. */
+ else if (! TARGET_SH5 && scratch0 != scratch1)
+ {
+ /* scratch0 != scratch1, and we have indexed loads. Get better
+ schedule by loading the offset into r1 and using an indexed
+ load - then the load of r1 can issue before the load from
+ (this_rtx + delta) finishes. */
+ emit_move_insn (scratch1, GEN_INT (vcall_offset));
+ offset_addr = gen_rtx_PLUS (Pmode, scratch0, scratch1);
+ }
+ else if (CONST_OK_FOR_ADD (vcall_offset))
+ {
+ 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);
+
+ if (Pmode != ptr_mode)
+ scratch0 = gen_rtx_TRUNCATE (ptr_mode, scratch0);
+ emit_insn (gen_add2_insn (this_rtx, scratch0));
+ }
+
+ /* Generate a tail call to the target function. */
+ if (! TREE_USED (function))
+ {
+ assemble_external (function);
+ TREE_USED (function) = 1;
+ }
+ funexp = XEXP (DECL_RTL (function), 0);
+ /* If the function is overridden, so is the thunk, hence we don't
+ need GOT addressing even if this is a public symbol. */
+#if 0
+ if (TARGET_SH1 && ! flag_weak)
+ sibcall = gen_sibcalli_thunk (funexp, const0_rtx);
+ else
+#endif
+ if (TARGET_SH2 && flag_pic)
+ {
+ sibcall = gen_sibcall_pcrel (funexp, const0_rtx);
+ XEXP (XVECEXP (sibcall, 0, 2), 0) = scratch2;
+ }
+ else
+ {
+ if (TARGET_SHMEDIA && flag_pic)
+ {
+ funexp = gen_sym2PIC (funexp);
+ PUT_MODE (funexp, Pmode);
+ }
+ emit_move_insn (scratch2, funexp);
+ funexp = gen_rtx_MEM (FUNCTION_MODE, scratch2);
+ sibcall = gen_sibcall (funexp, const0_rtx, NULL_RTX);
+ }
+ sibcall = emit_call_insn (sibcall);
+ SIBLING_CALL_P (sibcall) = 1;
+ use_reg (&CALL_INSN_FUNCTION_USAGE (sibcall), this_rtx);
+ 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. */
+
+ insn_locators_alloc ();
+ insns = get_insns ();
+
+#if 0
+ if (optimize > 0)
+ {
+ /* Initialize the bitmap obstacks. */
+ bitmap_obstack_initialize (NULL);
+ bitmap_obstack_initialize (®_obstack);
+ if (! cfun->cfg)
+ init_flow ();
+ rtl_register_cfg_hooks ();
+ init_rtl_bb_info (ENTRY_BLOCK_PTR);
+ init_rtl_bb_info (EXIT_BLOCK_PTR);
+ ENTRY_BLOCK_PTR->flags |= BB_RTL;
+ EXIT_BLOCK_PTR->flags |= BB_RTL;
+ find_basic_blocks (insns);
+
+ if (flag_schedule_insns_after_reload)
+ {
+ life_analysis (PROP_FINAL);
+
+ split_all_insns (1);
+
+ schedule_insns ();
+ }
+ /* We must split jmp insn in PIC case. */
+ else if (flag_pic)
+ split_all_insns_noflow ();
+ }
+#else
+ if (optimize > 0)
+ {
+ if (! cfun->cfg)
+ init_flow (cfun);
+ split_all_insns_noflow ();
+ }
+#endif
+
+ sh_reorg ();
+
+ if (optimize > 0 && flag_delayed_branch)
+ dbr_schedule (insns);
+
+ shorten_branches (insns);
+ final_start_function (insns, file, 1);
+ final (insns, file, 1);
+ final_end_function ();
+ free_after_compilation (cfun);
+
+ reload_completed = 0;
+ epilogue_completed = 0;
+}
+
+rtx
+function_symbol (rtx target, const char *name, enum sh_function_kind kind)
+{
+ rtx sym;
+
+ /* If this is not an ordinary function, the name usually comes from a
+ string literal or an sprintf buffer. Make sure we use the same
+ string consistently, so that cse will be able to unify address loads. */
+ if (kind != FUNCTION_ORDINARY)
+ name = IDENTIFIER_POINTER (get_identifier (name));
+ sym = gen_rtx_SYMBOL_REF (Pmode, name);
+ SYMBOL_REF_FLAGS (sym) = SYMBOL_FLAG_FUNCTION;
+ if (flag_pic)
+ switch (kind)
+ {
+ case FUNCTION_ORDINARY:
+ break;
+ case SFUNC_GOT:
+ {
+ rtx reg = target ? target : gen_reg_rtx (Pmode);
+
+ emit_insn (gen_symGOT2reg (reg, sym));
+ sym = reg;
+ break;
+ }
+ case SFUNC_STATIC:
+ {
+ /* ??? To allow cse to work, we use GOTOFF relocations.
+ we could add combiner patterns to transform this into
+ straight pc-relative calls with sym2PIC / bsrf when
+ label load and function call are still 1:1 and in the
+ same basic block during combine. */
+ rtx reg = target ? target : gen_reg_rtx (Pmode);
+
+ emit_insn (gen_symGOTOFF2reg (reg, sym));
+ sym = reg;
+ break;
+ }
+ }
+ if (target && sym != target)
+ {
+ emit_move_insn (target, sym);
+ return target;
+ }
+ return sym;
+}
+
+/* Find the number of a general purpose register in S. */
+static int
+scavenge_reg (HARD_REG_SET *s)
+{
+ int r;
+ for (r = FIRST_GENERAL_REG; r <= LAST_GENERAL_REG; r++)
+ if (TEST_HARD_REG_BIT (*s, r))
+ return r;
+ return -1;
+}
+
+rtx
+sh_get_pr_initial_val (void)
+{
+ rtx val;
+
+ /* ??? Unfortunately, get_hard_reg_initial_val doesn't always work for the
+ PR register on SHcompact, because it might be clobbered by the prologue.
+ We check first if that is known to be the case. */
+ if (TARGET_SHCOMPACT
+ && ((crtl->args.info.call_cookie
+ & ~ CALL_COOKIE_RET_TRAMP (1))
+ || crtl->saves_all_registers))
+ return gen_frame_mem (SImode, return_address_pointer_rtx);
+
+ /* If we haven't finished rtl generation, there might be a nonlocal label
+ that we haven't seen yet.
+ ??? get_hard_reg_initial_val fails if it is called after register
+ allocation has started, unless it has been called before for the
+ same register. And even then, we end in trouble if we didn't use
+ the register in the same basic block before. So call
+ get_hard_reg_initial_val now and wrap it in an unspec if we might
+ need to replace it. */
+ /* ??? We also must do this for TARGET_SH1 in general, because otherwise
+ combine can put the pseudo returned by get_hard_reg_initial_val into
+ instructions that need a general purpose registers, which will fail to
+ be recognized when the pseudo becomes allocated to PR. */
+ val
+ = get_hard_reg_initial_val (Pmode, TARGET_SHMEDIA ? PR_MEDIA_REG : PR_REG);
+ if (TARGET_SH1)
+ return gen_rtx_UNSPEC (SImode, gen_rtvec (1, val), UNSPEC_RA);
+ return val;
+}
+
+int
+sh_expand_t_scc (enum rtx_code code, rtx target)
+{
+ rtx result = target;
+ HOST_WIDE_INT val;
+
+ if (GET_CODE (sh_compare_op0) != REG || REGNO (sh_compare_op0) != T_REG
+ || GET_CODE (sh_compare_op1) != CONST_INT)
+ return 0;
+ if (GET_CODE (result) != REG)
+ result = gen_reg_rtx (SImode);
+ val = INTVAL (sh_compare_op1);
+ if ((code == EQ && val == 1) || (code == NE && val == 0))
+ emit_insn (gen_movt (result));
+ else if (TARGET_SH2A && ((code == EQ && val == 0)
+ || (code == NE && val == 1)))
+ emit_insn (gen_movrt (result));
+ else if ((code == EQ && val == 0) || (code == NE && val == 1))
+ {
+ emit_clobber (result);
+ emit_insn (gen_subc (result, result, result));
+ emit_insn (gen_addsi3 (result, result, const1_rtx));
+ }
+ else if (code == EQ || code == NE)
+ emit_insn (gen_move_insn (result, GEN_INT (code == NE)));
+ else
+ return 0;
+ if (result != target)
+ emit_move_insn (target, result);
+ return 1;
+}
+
+/* INSN is an sfunc; return the rtx that describes the address used. */
+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++)
+ {
+ 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);
+ }
+ gcc_assert (GET_CODE (XVECEXP (pattern, 0, 0)) == UNSPEC_VOLATILE);
+ return XVECEXP (XVECEXP (pattern, 0, 0), 0, 1);