extern int csky_default_branch_cost (bool, bool);
extern bool csky_default_logical_op_non_short_circuit (void);
+
+extern void csky_init_cumulative_args (CUMULATIVE_ARGS *, tree, rtx, tree);
#endif /* GCC_CSKY_PROTOS_H */
{
builtin_define ("__csky_hard_float__");
builtin_define ("__CSKY_HARD_FLOAT__");
+ if (TARGET_HARD_FLOAT_ABI)
+ {
+ builtin_define ("__csky_hard_float_abi__");
+ builtin_define ("__CSKY_HARD_FLOAT_ABI__");
+ }
+ if (TARGET_SINGLE_FPU)
+ {
+ builtin_define ("__csky_hard_float_fpu_sf__");
+ builtin_define ("__CSKY_HARD_FLOAT_FPU_SF__");
+ }
}
else
{
csky_function_arg (cumulative_args_t pcum_v, const function_arg_info &arg)
{
CUMULATIVE_ARGS *pcum = get_cumulative_args (pcum_v);
+ int reg = pcum->reg;
+ machine_mode mode = arg.mode;
- if (*pcum < CSKY_NPARM_REGS)
- return gen_rtx_REG (arg.mode, CSKY_FIRST_PARM_REGNUM + *pcum);
+ if (FUNCTION_VARG_MODE_P(mode)
+ && !pcum->is_stdarg)
+ {
+ reg = pcum->freg;
+
+ if (reg < CSKY_NPARM_FREGS)
+ return gen_rtx_REG (mode, CSKY_FIRST_VFP_REGNUM + reg);
+ else
+ return NULL_RTX;
+ }
+
+ if (reg < CSKY_NPARM_REGS)
+ return gen_rtx_REG (mode, CSKY_FIRST_PARM_REGNUM + reg);
return NULL_RTX;
}
MODE and TYPE. */
static int
-csky_num_arg_regs (machine_mode mode, const_tree type)
+csky_num_arg_regs (machine_mode mode, const_tree type, bool is_stdarg)
{
int size;
else
size = GET_MODE_SIZE (mode);
+ if (TARGET_HARD_FLOAT_ABI
+ && !is_stdarg)
+ {
+ if (CSKY_VREG_MODE_P(mode)
+ && !TARGET_SINGLE_FPU)
+ return ((CSKY_NUM_WORDS (size) + 1) / 2);
+ }
+
return CSKY_NUM_WORDS (size);
}
const function_arg_info &arg)
{
CUMULATIVE_ARGS *pcum = get_cumulative_args (pcum_v);
- int param_size = csky_num_arg_regs (arg.mode, arg.type);
+ int *reg = &pcum->reg;
+ machine_mode mode = arg.mode;
- if (*pcum + param_size > CSKY_NPARM_REGS)
- *pcum = CSKY_NPARM_REGS;
+ int param_size = csky_num_arg_regs (mode, arg.type, pcum->is_stdarg);
+ int param_regs_nums = CSKY_NPARM_REGS;
+
+ if (FUNCTION_VARG_MODE_P(mode)
+ && !pcum->is_stdarg)
+ {
+ reg = &pcum->freg;
+ param_regs_nums = CSKY_NPARM_FREGS;
+ }
+
+ if (*reg + param_size > param_regs_nums)
+ *reg = param_regs_nums;
else
- *pcum += param_size;
+ *reg += param_size;
}
mode = TYPE_MODE (type);
size = int_size_in_bytes (type);
+ if (FUNCTION_VARG_MODE_P(mode))
+ {
+ mode = promote_function_mode (type, mode, &unsignedp, func, 1);
+ return gen_rtx_REG (mode, CSKY_FIRST_VFP_REGNUM);
+ }
+
/* Since we promote return types, we must promote the mode here too. */
if (INTEGRAL_TYPE_P (type))
{
csky_libcall_value (machine_mode mode,
const_rtx libcall ATTRIBUTE_UNUSED)
{
+ if (FUNCTION_VARG_MODE_P(mode))
+ {
+ return gen_rtx_REG (mode, CSKY_FIRST_VFP_REGNUM);
+ }
return gen_rtx_REG (mode, CSKY_FIRST_RET_REGNUM);
}
static bool
csky_function_value_regno_p (const unsigned int regno)
{
- return (regno == CSKY_FIRST_RET_REGNUM);
+ if (regno == CSKY_FIRST_RET_REGNUM
+ || (TARGET_HARD_FLOAT_ABI
+ && regno == CSKY_FIRST_VFP_REGNUM))
+ return true;
+ return false;
}
csky_arg_partial_bytes (cumulative_args_t pcum_v, const function_arg_info &arg)
{
CUMULATIVE_ARGS *pcum = get_cumulative_args (pcum_v);
- int param_size = csky_num_arg_regs (arg.mode, arg.type);
+ int param_size = csky_num_arg_regs (arg.mode, arg.type, pcum->is_stdarg);
+ int reg = pcum->reg;
+
+ if (FUNCTION_VARG_MODE_P(arg.mode)
+ && !pcum->is_stdarg)
+ return 0;
- if (*pcum < CSKY_NPARM_REGS
- && *pcum + param_size > CSKY_NPARM_REGS)
- return (CSKY_NPARM_REGS - *pcum) * UNITS_PER_WORD;
+ if (reg < CSKY_NPARM_REGS
+ && reg + param_size > CSKY_NPARM_REGS)
+ return (CSKY_NPARM_REGS - reg) * UNITS_PER_WORD;
return 0;
}
cfun->machine->uses_anonymous_args = 1;
local_cum = *pcum;
csky_function_arg_advance (local_cum_v, arg);
- regs_to_push = CSKY_NPARM_REGS - local_cum;
+ regs_to_push = CSKY_NPARM_REGS - local_cum.reg;
if (regs_to_push)
*pretend_size = regs_to_push * UNITS_PER_WORD;
}
return true;
}
+void
+csky_init_cumulative_args (CUMULATIVE_ARGS *pcum, tree fntype,
+ rtx libname ATTRIBUTE_UNUSED,
+ tree fndecl ATTRIBUTE_UNUSED)
+{
+ memset(pcum, 0, sizeof(*pcum));
+ if (stdarg_p (fntype))
+ pcum->is_stdarg = true;
+}
struct gcc_target targetm = TARGET_INITIALIZER;
/* Use hardware floating point calling convention. */
#define TARGET_HARD_FLOAT_ABI (csky_float_abi == CSKY_FLOAT_ABI_HARD)
+#define TARGET_SINGLE_FPU (csky_fpu_index == TARGET_FPU_fpv2_sf)
+#define TARGET_DOUBLE_FPU (TARGET_HARD_FLOAT && !TARGET_SINGLE_FPU)
+
+#define FUNCTION_VARG_REGNO_P(REGNO) \
+ (TARGET_HARD_FLOAT_ABI \
+ && IN_RANGE ((REGNO), CSKY_FIRST_VFP_REGNUM, \
+ CSKY_FIRST_VFP_REGNUM + CSKY_NPARM_FREGS - 1))
+
+#define CSKY_VREG_MODE_P(mode) \
+ ((mode) == SFmode || (mode) == DFmode)
+
+#define FUNCTION_VARG_MODE_P(mode) \
+ (TARGET_HARD_FLOAT_ABI \
+ && CSKY_VREG_MODE_P(mode) \
+ && !(mode == DFmode && TARGET_SINGLE_FPU))
+
/* Number of loads/stores handled by ldm/stm. */
#define CSKY_MIN_MULTIPLE_STLD 3
#define CSKY_MAX_MULTIPLE_STLD 12
/* A C type for declaring a variable that is used as the first argument of
TARGET_ FUNCTION_ARG and other related values. */
-#define CUMULATIVE_ARGS int
+#if !defined (USED_FOR_TARGET)
+typedef struct
+{
+ int reg;
+ int freg;
+ bool is_stdarg;
+} CUMULATIVE_ARGS;
+#endif
/* Initialize a variable CUM of type CUMULATIVE_ARGS
for a call to a function whose data type is FNTYPE.
On CSKY, the offset always starts at 0: the first parm reg is always
the same reg. */
#define INIT_CUMULATIVE_ARGS(CUM, FNTYPE, LIBNAME, INDIRECT, N_NAMED_ARGS) \
- ((CUM) = 0)
+ csky_init_cumulative_args (&(CUM), (FNTYPE), (LIBNAME), (INDIRECT))
/* True if N is a possible register number for function argument passing.
On the CSKY, r0-r3 are used to pass args.
The int cast is to prevent a complaint about unsigned comparison to
zero, since CSKY_FIRST_PARM_REGNUM is zero. */
-#define FUNCTION_ARG_REGNO_P(REGNO) \
- (((int)(REGNO) >= CSKY_FIRST_PARM_REGNUM) && \
- ((REGNO) < (CSKY_NPARM_REGS + CSKY_FIRST_PARM_REGNUM)))
+#define FUNCTION_ARG_REGNO_P(REGNO) \
+ (((REGNO) >= CSKY_FIRST_PARM_REGNUM \
+ && (REGNO) < (CSKY_NPARM_REGS + CSKY_FIRST_PARM_REGNUM)) \
+ || FUNCTION_VARG_REGNO_P(REGNO))
/* How Large Values Are Returned */
(CSKY_LAST_EH_RETDATA_REGNUM 1)
(CSKY_EH_STACKADJ_REGNUM 2)
(CSKY_STACKADJUST_REGNUM 4)
+ (CSKY_NPARM_FREGS 4)
])
;; Supported TLS relocations.
; Support for the eh_return pattern.
VUNSPEC_EH_RETURN
+ VUNSPEC_BLOCKAGE
])
force_reg (Pmode, XEXP (operands[1], 0)));
}")
+;; Call subroutine returning any type.
+
+(define_expand "untyped_call"
+ [(parallel [(call (match_operand 0 "" "")
+ (const_int 0))
+ (match_operand 1 "" "")
+ (match_operand 2 "" "")])]
+ ""
+{
+ int i;
+
+ emit_call_insn (gen_call (operands[0], const0_rtx));
+
+ for (i = 0; i < XVECLEN (operands[2], 0); i++)
+ {
+ rtx set = XVECEXP (operands[2], 0, i);
+ emit_move_insn (SET_DEST (set), SET_SRC (set));
+ }
+
+ /* The optimizer does not know that the call sets the function value
+ registers we stored in the result block. We avoid problems by
+ claiming that all hard registers are used and clobbered at this
+ point. */
+ emit_insn (gen_blockage ());
+
+ DONE;
+})
+
+;; UNSPEC_VOLATILE is considered to use and clobber all hard registers and
+;; all of memory. This blocks insns from being moved across this point.
+
+(define_insn "blockage"
+ [(unspec_volatile [(const_int 0)] VUNSPEC_BLOCKAGE)]
+ ""
+ ""
+ [(set_attr "length" "0")])
+
+(define_insn "*call_value_internal_vs"
+ [(set (match_operand:SF 0 "register_operand" "=v,v,v")
+ (call (mem:SI (match_operand:SI 1 "csky_call_address_operand" "b, r,S"))
+ (match_operand 2 "" "")))
+ (clobber (reg:SI CSKY_LR_REGNUM))]
+ "TARGET_HARD_FLOAT_ABI"
+ "@
+ jsr\t%1
+ jsr\t%1
+ jbsr\t%1"
+ [(set_attr "length" "2,4,4")
+ (set_attr "type" "call_jsr,call_jsr,call")]
+)
+
+(define_insn "*call_value_internal_vd"
+ [(set (match_operand:DF 0 "register_operand" "=v,v,v")
+ (call (mem:SI (match_operand:SI 1 "csky_call_address_operand" "b, r,S"))
+ (match_operand 2 "" "")))
+ (clobber (reg:SI CSKY_LR_REGNUM))]
+ "TARGET_HARD_FLOAT_ABI && TARGET_DOUBLE_FPU"
+ "@
+ jsr\t%1
+ jsr\t%1
+ jbsr\t%1"
+ [(set_attr "length" "2,4,4")
+ (set_attr "type" "call_jsr,call_jsr,call")]
+)
+
+(define_insn "*call_value_internal_pic_vs"
+ [(set (match_operand:SF 0 "register_operand" "=v")
+ (call (mem:SI (match_operand:SI 1 "csky_unspec_operand" "X"))
+ (match_operand 2 "" "")))
+ (clobber (reg:SI CSKY_LR_REGNUM))]
+ "flag_pic && TARGET_HARD_FLOAT_ABI"
+ "* return csky_output_call (operands, 1);"
+)
+
+(define_insn "*call_value_internal_pic_vd"
+ [(set (match_operand:DF 0 "register_operand" "=v")
+ (call (mem:SI (match_operand:SI 1 "csky_unspec_operand" "X"))
+ (match_operand 2 "" "")))
+ (clobber (reg:SI CSKY_LR_REGNUM))]
+ "flag_pic && TARGET_HARD_FLOAT_ABI && TARGET_DOUBLE_FPU"
+ "* return csky_output_call (operands, 1);"
+)
(define_insn "*call_value_internal"
[(set (match_operand 0 "register_operand" "=r,r,r")
/* { dg-do run } */
/* { dg-require-effective-target untyped_assembly } */
/* { dg-skip-if "Variadic funcs have all args on stack. Normal funcs have args in registers." { "avr-*-* nds32*-*-* amdgcn-*-*" } } */
-/* { dg-skip-if "Variadic funcs use different argument passing from normal funcs." { "riscv*-*-* or1k*-*-* msp430-*-* pru-*-*" } } */
+/* { dg-skip-if "Variadic funcs use different argument passing from normal funcs." { "csky*-*-* riscv*-*-* or1k*-*-* msp430-*-* pru-*-*" } } */
/* { dg-skip-if "Variadic funcs use Base AAPCS. Normal funcs use VFP variant." { arm*-*-* && arm_hf_eabi } } */
/* PR target/12503 */
/* arm_hf_eabi: Variadic funcs use Base AAPCS. Normal funcs use VFP variant.
avr: Variadic funcs don't pass arguments in registers, while normal funcs
do. */
-/* { dg-skip-if "Variadic funcs use different argument passing from normal funcs" { arm_hf_eabi || { avr-*-* riscv*-*-* or1k*-*-* msp430-*-* amdgcn-*-* pru-*-* } } } */
+/* { dg-skip-if "Variadic funcs use different argument passing from normal funcs" { arm_hf_eabi || { csky*-*-* avr-*-* riscv*-*-* or1k*-*-* msp430-*-* amdgcn-*-* pru-*-* } } } */
/* { dg-skip-if "Variadic funcs have all args on stack. Normal funcs have args in registers." { nds32*-*-* } { v850*-*-* } } */
/* { dg-require-effective-target untyped_assembly } */