diff --git a/gcc/config/csky/csky-protos.h b/gcc/config/csky/csky-protos.h
index cc1a033..2c02399 100644
--- a/gcc/config/csky/csky-protos.h
+++ b/gcc/config/csky/csky-protos.h
@@ -68,4 +68,6 @@ extern int csky_compute_pushpop_length (rtx *);
 
 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 */
diff --git a/gcc/config/csky/csky.c b/gcc/config/csky/csky.c
index 7ba3ed3..8463d8f 100644
--- a/gcc/config/csky/csky.c
+++ b/gcc/config/csky/csky.c
@@ -328,6 +328,16 @@ csky_cpu_cpp_builtins (cpp_reader *pfile)
     {
       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
     {
@@ -1790,9 +1800,22 @@ static rtx
 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;
 }
@@ -1802,7 +1825,7 @@ csky_function_arg (cumulative_args_t pcum_v, const function_arg_info &arg)
    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;
 
@@ -1811,6 +1834,14 @@ csky_num_arg_regs (machine_mode mode, const_tree type)
   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);
 }
 
@@ -1822,12 +1853,23 @@ csky_function_arg_advance (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 *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;
 }
 
 
@@ -1843,6 +1885,12 @@ csky_function_value (const_tree type, const_tree func,
   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))
     {
@@ -1877,6 +1925,10 @@ static rtx
 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);
 }
 
@@ -1887,7 +1939,11 @@ csky_libcall_value (machine_mode mode,
 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;
 }
 
 
@@ -1912,11 +1968,16 @@ static int
 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;
 }
@@ -1941,7 +2002,7 @@ csky_setup_incoming_varargs (cumulative_args_t pcum_v,
   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;
 }
@@ -6775,6 +6836,15 @@ csky_fixed_condition_code_regs (unsigned int *p1, unsigned int *p2)
   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;
 
diff --git a/gcc/config/csky/csky.h b/gcc/config/csky/csky.h
index 8f4090b..190a668 100644
--- a/gcc/config/csky/csky.h
+++ b/gcc/config/csky/csky.h
@@ -133,6 +133,22 @@
 /* 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
@@ -360,7 +376,14 @@ extern int csky_arch_isa_features[];
 
 /* 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.
@@ -369,15 +392,16 @@ extern int csky_arch_isa_features[];
    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  */
 
diff --git a/gcc/config/csky/csky.md b/gcc/config/csky/csky.md
index 15f68f9..7f01fbc 100644
--- a/gcc/config/csky/csky.md
+++ b/gcc/config/csky/csky.md
@@ -50,6 +50,7 @@
    (CSKY_LAST_EH_RETDATA_REGNUM		1)
    (CSKY_EH_STACKADJ_REGNUM		2)
    (CSKY_STACKADJUST_REGNUM		4)
+   (CSKY_NPARM_FREGS 4)
 ])
 
 ;; Supported TLS relocations.
@@ -100,6 +101,7 @@
 
    ; Support for the eh_return pattern.
    VUNSPEC_EH_RETURN
+   VUNSPEC_BLOCKAGE
   ])
 
 
@@ -3310,6 +3312,88 @@
 				 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")
diff --git a/gcc/testsuite/gcc.dg/builtin-apply2.c b/gcc/testsuite/gcc.dg/builtin-apply2.c
index 06ef24e..9049af5 100644
--- a/gcc/testsuite/gcc.dg/builtin-apply2.c
+++ b/gcc/testsuite/gcc.dg/builtin-apply2.c
@@ -1,7 +1,7 @@
 /* { 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 */
diff --git a/gcc/testsuite/gcc.dg/torture/stackalign/builtin-apply-2.c b/gcc/testsuite/gcc.dg/torture/stackalign/builtin-apply-2.c
index 31585a0..5ec0558 100644
--- a/gcc/testsuite/gcc.dg/torture/stackalign/builtin-apply-2.c
+++ b/gcc/testsuite/gcc.dg/torture/stackalign/builtin-apply-2.c
@@ -9,7 +9,7 @@
 /* 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 } */
    
