+bool
+sh_promote_prototypes (tree type)
+{
+ if (TARGET_HITACHI)
+ return 0;
+ if (! type)
+ return 1;
+ return ! sh_attr_renesas_p (type);
+}
+
+/* Define where to put the arguments to a function.
+ 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).
+
+ 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. */
+
+
+rtx
+sh_function_arg (CUMULATIVE_ARGS *ca, enum machine_mode mode,
+ tree type, int named)
+{
+ if (! TARGET_SH5 && mode == VOIDmode)
+ return GEN_INT (ca->renesas_abi ? 1 : 0);
+
+ if (! TARGET_SH5
+ && PASS_IN_REG_P (*ca, mode, type)
+ && (named || ! (TARGET_HITACHI || ca->renesas_abi)))
+ {
+ int regno;
+
+ if (mode == SCmode && TARGET_SH4 && TARGET_LITTLE_ENDIAN
+ && (! FUNCTION_ARG_SCmode_WART || (ROUND_REG (*ca, mode) & 1)))
+ {
+ rtx r1 = gen_rtx_EXPR_LIST (VOIDmode,
+ gen_rtx_REG (SFmode,
+ BASE_ARG_REG (mode)
+ + (ROUND_REG (*ca, mode) ^ 1)),
+ const0_rtx);
+ rtx r2 = gen_rtx_EXPR_LIST (VOIDmode,
+ gen_rtx_REG (SFmode,
+ BASE_ARG_REG (mode)
+ + ((ROUND_REG (*ca, mode) + 1) ^ 1)),
+ GEN_INT (4));
+ return gen_rtx_PARALLEL(SCmode, gen_rtvec(2, r1, r2));
+ }
+
+ /* If the alignment of a DF value causes an SF register to be
+ skipped, we will use that skipped register for the next SF
+ value. */
+ if ((TARGET_HITACHI || ca->renesas_abi)
+ && ca->free_single_fp_reg
+ && mode == SFmode)
+ return gen_rtx_REG (mode, ca->free_single_fp_reg);
+
+ regno = (BASE_ARG_REG (mode) + ROUND_REG (*ca, mode))
+ ^ (mode == SFmode && TARGET_SH4
+ && TARGET_LITTLE_ENDIAN != 0
+ && ! TARGET_HITACHI && ! ca->renesas_abi);
+ return gen_rtx_REG (mode, regno);
+
+ }
+
+ if (TARGET_SH5)
+ {
+ if (mode == VOIDmode && TARGET_SHCOMPACT)
+ return GEN_INT (ca->call_cookie);
+
+ /* The following test assumes unnamed arguments are promoted to
+ DFmode. */
+ if (mode == SFmode && ca->free_single_fp_reg)
+ return SH5_PROTOTYPED_FLOAT_ARG (*ca, mode, ca->free_single_fp_reg);
+
+ if ((GET_SH_ARG_CLASS (mode) == SH_ARG_FLOAT)
+ && (named || ! ca->prototype_p)
+ && ca->arg_count[(int) SH_ARG_FLOAT] < NPARM_REGS (SFmode))
+ {
+ if (! ca->prototype_p && TARGET_SHMEDIA)
+ return SH5_PROTOTYPELESS_FLOAT_ARG (*ca, mode);
+
+ return SH5_PROTOTYPED_FLOAT_ARG (*ca, mode,
+ FIRST_FP_PARM_REG
+ + ca->arg_count[(int) SH_ARG_FLOAT]);
+ }
+
+ if (ca->arg_count[(int) SH_ARG_INT] < NPARM_REGS (SImode)
+ && (! TARGET_SHCOMPACT
+ || (! SHCOMPACT_FORCE_ON_STACK (mode, type)
+ && ! SH5_WOULD_BE_PARTIAL_NREGS (*ca, mode,
+ type, named))))
+ {
+ return gen_rtx_REG (mode, (FIRST_PARM_REG
+ + ca->arg_count[(int) SH_ARG_INT]));
+ }
+
+ return 0;
+ }
+
+ return 0;
+}
+
+/* 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.) */
+
+void
+sh_function_arg_advance (CUMULATIVE_ARGS *ca, enum machine_mode mode,
+ tree type, int named)
+{
+ if (ca->force_mem)
+ ca->force_mem = 0;
+ else if (TARGET_SH5)
+ {
+ tree type2 = (ca->byref && type
+ ? TREE_TYPE (type)
+ : type);
+ enum machine_mode mode2 = (ca->byref && type
+ ? TYPE_MODE (type2)
+ : mode);
+ int dwords = ((ca->byref
+ ? ca->byref
+ : mode2 == BLKmode
+ ? int_size_in_bytes (type2)
+ : GET_MODE_SIZE (mode2)) + 7) / 8;
+ int numregs = MIN (dwords, NPARM_REGS (SImode)
+ - ca->arg_count[(int) SH_ARG_INT]);
+
+ if (numregs)
+ {
+ ca->arg_count[(int) SH_ARG_INT] += numregs;
+ if (TARGET_SHCOMPACT
+ && SHCOMPACT_FORCE_ON_STACK (mode2, type2))
+ {
+ ca->call_cookie
+ |= CALL_COOKIE_INT_REG (ca->arg_count[(int) SH_ARG_INT]
+ - numregs, 1);
+ /* N.B. We want this also for outgoing. */
+ ca->stack_regs += numregs;
+ }
+ else if (ca->byref)
+ {
+ if (! ca->outgoing)
+ ca->stack_regs += numregs;
+ ca->byref_regs += numregs;
+ ca->byref = 0;
+ do
+ ca->call_cookie
+ |= CALL_COOKIE_INT_REG (ca->arg_count[(int) SH_ARG_INT]
+ - numregs, 2);
+ while (--numregs);
+ ca->call_cookie
+ |= CALL_COOKIE_INT_REG (ca->arg_count[(int) SH_ARG_INT]
+ - 1, 1);
+ }
+ else if (dwords > numregs)
+ {
+ int pushregs = numregs;
+
+ if (TARGET_SHCOMPACT)
+ ca->stack_regs += numregs;
+ while (pushregs < NPARM_REGS (SImode) - 1
+ && (CALL_COOKIE_INT_REG_GET
+ (ca->call_cookie,
+ NPARM_REGS (SImode) - pushregs)
+ == 1))
+ {
+ ca->call_cookie
+ &= ~ CALL_COOKIE_INT_REG (NPARM_REGS (SImode)
+ - pushregs, 1);
+ pushregs++;
+ }
+ if (numregs == NPARM_REGS (SImode))
+ ca->call_cookie
+ |= CALL_COOKIE_INT_REG (0, 1)
+ | CALL_COOKIE_STACKSEQ (numregs - 1);
+ else
+ ca->call_cookie
+ |= CALL_COOKIE_STACKSEQ (numregs);
+ }
+ }
+ if (GET_SH_ARG_CLASS (mode2) == SH_ARG_FLOAT
+ && (named || ! ca->prototype_p))
+ {
+ if (mode2 == SFmode && ca->free_single_fp_reg)
+ ca->free_single_fp_reg = 0;
+ else if (ca->arg_count[(int) SH_ARG_FLOAT]
+ < NPARM_REGS (SFmode))
+ {
+ int numfpregs
+ = MIN ((GET_MODE_SIZE (mode2) + 7) / 8 * 2,
+ NPARM_REGS (SFmode)
+ - ca->arg_count[(int) SH_ARG_FLOAT]);
+
+ ca->arg_count[(int) SH_ARG_FLOAT] += numfpregs;
+
+ if (TARGET_SHCOMPACT && ! ca->prototype_p)
+ {
+ if (ca->outgoing && numregs > 0)
+ do
+ {
+ ca->call_cookie
+ |= (CALL_COOKIE_INT_REG
+ (ca->arg_count[(int) SH_ARG_INT]
+ - numregs + ((numfpregs - 2) / 2),
+ 4 + (ca->arg_count[(int) SH_ARG_FLOAT]
+ - numfpregs) / 2));
+ }
+ while (numfpregs -= 2);
+ }
+ else if (mode2 == SFmode && (named)
+ && (ca->arg_count[(int) SH_ARG_FLOAT]
+ < NPARM_REGS (SFmode)))
+ ca->free_single_fp_reg
+ = FIRST_FP_PARM_REG - numfpregs
+ + ca->arg_count[(int) SH_ARG_FLOAT] + 1;
+ }
+ }
+ return;
+ }
+
+ 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)
+ {
+ ca->free_single_fp_reg = 0;
+ return;
+ }
+ /* When we have a DF after an SF, there's an SF register that get
+ 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
+ && ROUND_REG (*ca, DFmode) != ROUND_REG (*ca, SFmode))
+ {
+ ca->free_single_fp_reg = (ROUND_REG (*ca, SFmode)
+ + BASE_ARG_REG (mode));
+ }
+ }
+
+ if (! (TARGET_SH4 || ca->renesas_abi)
+ || PASS_IN_REG_P (*ca, mode, type))
+ (ca->arg_count[(int) GET_SH_ARG_CLASS (mode)]
+ = (ROUND_REG (*ca, mode)
+ + (mode == BLKmode
+ ? ROUND_ADVANCE (int_size_in_bytes (type))
+ : ROUND_ADVANCE (GET_MODE_SIZE (mode)))));
+}
+
+/* The Renesas calling convention doesn't quite fit into this scheme since
+ the address is passed like an invisible argument, but one that is always
+ passed in memory. */
+static rtx
+sh_struct_value_rtx (tree fndecl, int incoming ATTRIBUTE_UNUSED)
+{
+ if (TARGET_HITACHI || sh_attr_renesas_p (fndecl))
+ return 0;
+ return gen_rtx_REG (Pmode, 2);
+}
+
+/* Worker function for TARGET_RETURN_IN_MEMORY. */
+
+static bool
+sh_return_in_memory (tree type, tree fndecl)
+{
+ if (TARGET_SH5)
+ {
+ if (TYPE_MODE (type) == BLKmode)
+ return ((unsigned HOST_WIDE_INT) int_size_in_bytes (type)) > 8;
+ else
+ return GET_MODE_SIZE (TYPE_MODE (type)) > 8;
+ }
+ else
+ {
+ return (TYPE_MODE (type) == BLKmode
+ || ((TARGET_HITACHI || sh_attr_renesas_p (fndecl))
+ && TREE_CODE (type) == RECORD_TYPE));
+ }
+}
+
+/* We actually emit the code in sh_expand_prologue. We used to use
+ a static variable to flag that we need to emit this code, but that
+ doesn't when inlining, when functions are deferred and then emitted
+ 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,
+ int second_time ATTRIBUTE_UNUSED)
+{
+ if (! current_function_stdarg)
+ abort ();
+}
+
+static bool
+sh_strict_argument_naming (CUMULATIVE_ARGS *ca ATTRIBUTE_UNUSED)
+{
+ return TARGET_SH5;
+}
+
+static bool
+sh_pretend_outgoing_varargs_named (CUMULATIVE_ARGS *ca)
+{
+ return ! (TARGET_HITACHI || ca->renesas_abi) && ! TARGET_SH5;
+}
+
+