diff --git a/gcc/calls.cc b/gcc/calls.cc
index 7f3cf5f..50d0495 100644
--- a/gcc/calls.cc
+++ b/gcc/calls.cc
@@ -4791,14 +4791,20 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
       else
 	{
 	  /* Convert to the proper mode if a promotion has been active.  */
-	  if (GET_MODE (valreg) != outmode)
+	  enum machine_mode valmode = GET_MODE (valreg);
+	  if (valmode != outmode)
 	    {
 	      int unsignedp = TYPE_UNSIGNED (tfom);
 
 	      gcc_assert (promote_function_mode (tfom, outmode, &unsignedp,
 						 fndecl ? TREE_TYPE (fndecl) : fntype, 1)
-			  == GET_MODE (valreg));
-	      valreg = convert_modes (outmode, GET_MODE (valreg), valreg, 0);
+			  == valmode);
+	      if (SCALAR_INT_MODE_P (valmode)
+		  && SCALAR_FLOAT_MODE_P (outmode)
+		  && known_gt (GET_MODE_SIZE (valmode), GET_MODE_SIZE (outmode)))
+		valreg = convert_wider_int_to_float (outmode, valmode, valreg);
+	      else
+		valreg = convert_modes (outmode, valmode, valreg, 0);
 	    }
 
 	  if (value != 0)
@@ -5003,8 +5009,20 @@ store_one_arg (struct arg_data *arg, rtx argblock, int flags,
       /* If we are promoting object (or for any other reason) the mode
 	 doesn't agree, convert the mode.  */
 
-      if (arg->mode != TYPE_MODE (TREE_TYPE (pval)))
-	arg->value = convert_modes (arg->mode, TYPE_MODE (TREE_TYPE (pval)),
+      machine_mode old_mode = TYPE_MODE (TREE_TYPE (pval));
+
+      /* Some ABIs require scalar floating point modes to be passed
+	 in a wider scalar integer mode.  We need to explicitly
+	 reinterpret to an integer mode of the correct precision
+	 before extending to the desired result.  */
+      if (SCALAR_INT_MODE_P (arg->mode)
+	  && SCALAR_FLOAT_MODE_P (old_mode)
+	  && known_gt (GET_MODE_SIZE (arg->mode),
+		       GET_MODE_SIZE (old_mode)))
+	arg->value = convert_float_to_wider_int (arg->mode, old_mode,
+						 arg->value);
+      else if (arg->mode != old_mode)
+	arg->value = convert_modes (arg->mode, old_mode,
 				    arg->value, arg->unsignedp);
 
       if (arg->pass_on_stack)
diff --git a/gcc/function.cc b/gcc/function.cc
index 31256b5..4deb6b0 100644
--- a/gcc/function.cc
+++ b/gcc/function.cc
@@ -3584,7 +3584,16 @@ assign_parms_unsplit_complex (struct assign_parm_data_all *all,
 
 	  real = DECL_RTL (fnargs[i]);
 	  imag = DECL_RTL (fnargs[i + 1]);
-	  if (inner != GET_MODE (real))
+	  machine_mode orig_mode = GET_MODE (real);
+	  if (SCALAR_FLOAT_MODE_P (inner)
+	      && SCALAR_INT_MODE_P (orig_mode)
+	      && known_lt (GET_MODE_SIZE (inner),
+			   GET_MODE_SIZE (orig_mode)))
+	    {
+	      real = convert_wider_int_to_float (inner, orig_mode, real);
+	      imag = convert_wider_int_to_float (inner, orig_mode, imag);
+	    }
+	  else if (inner != orig_mode)
 	    {
 	      real = gen_lowpart_SUBREG (inner, real);
 	      imag = gen_lowpart_SUBREG (inner, imag);
@@ -3618,7 +3627,23 @@ assign_parms_unsplit_complex (struct assign_parm_data_all *all,
 
 	  real = DECL_INCOMING_RTL (fnargs[i]);
 	  imag = DECL_INCOMING_RTL (fnargs[i + 1]);
-	  if (inner != GET_MODE (real))
+	  orig_mode = GET_MODE (real);
+	  if (SCALAR_FLOAT_MODE_P (inner)
+	      && SCALAR_INT_MODE_P (orig_mode)
+	      && known_lt (GET_MODE_SIZE (inner),
+			   GET_MODE_SIZE (orig_mode)))
+	    {
+	      push_to_sequence2 (all->first_conversion_insn,
+				 all->last_conversion_insn);
+	      real = force_reg (orig_mode, real);
+	      imag = force_reg (orig_mode, imag);
+	      real = convert_wider_int_to_float (inner, orig_mode, real);
+	      imag = convert_wider_int_to_float (inner, orig_mode, imag);
+	      all->first_conversion_insn = get_insns ();
+	      all->last_conversion_insn = get_last_insn ();
+	      end_sequence ();
+	    }
+	  else if (inner != orig_mode)
 	    {
 	      real = gen_lowpart_SUBREG (inner, real);
 	      imag = gen_lowpart_SUBREG (inner, imag);
diff --git a/gcc/optabs.cc b/gcc/optabs.cc
index a50dd79..10ccfbc 100644
--- a/gcc/optabs.cc
+++ b/gcc/optabs.cc
@@ -5539,6 +5539,37 @@ expand_float (rtx to, rtx from, int unsignedp)
     }
 }
 
+/* Promote floating point arguments for a libcall if necessary.
+   There are no tree types defined for libcalls.  */
+
+static rtx
+prepare_libcall_fp_arg (rtx arg)
+{
+  scalar_float_mode mode;
+  machine_mode arg_mode;
+  if (is_a <scalar_float_mode> (GET_MODE (arg), &mode))
+    {
+      /*  If we need to promote the floating point function argument, do
+	  it here instead of inside emit_library_call_value matching the
+	  use of prepare_libcall_int_arg.  */
+
+      int unsigned_p = 0;
+      arg_mode = targetm.calls.promote_function_mode (NULL_TREE, mode,
+						      &unsigned_p,
+						      NULL_TREE, 0);
+        /* Some ABIs require scalar floating point modes to be passed
+	   in a wider scalar integer mode.  We need to explicitly
+	   reinterpret to an integer mode of the correct precision
+	   before extending to the desired result.  */
+      if (SCALAR_INT_MODE_P (arg_mode)
+	  && known_gt (GET_MODE_SIZE (arg_mode), GET_MODE_SIZE (mode)))
+	return convert_float_to_wider_int (arg_mode, mode, arg);
+      if (arg_mode != mode)
+	return convert_to_mode (arg_mode, arg, unsigned_p);
+    }
+    return arg;
+}
+
 /* Generate code to convert FROM to fixed point and store in TO.  FROM
    must be floating point.  */
 
@@ -5701,15 +5732,19 @@ expand_fix (rtx to, rtx from, int unsignedp)
       rtx_insn *insns;
       rtx value;
       rtx libfunc;
+      rtx promoted_from;
 
       convert_optab tab = unsignedp ? ufix_optab : sfix_optab;
       libfunc = convert_optab_libfunc (tab, GET_MODE (to), GET_MODE (from));
       gcc_assert (libfunc);
 
+      promoted_from = prepare_libcall_fp_arg (from);
+
       start_sequence ();
 
       value = emit_library_call_value (libfunc, NULL_RTX, LCT_CONST,
-				       GET_MODE (to), from, GET_MODE (from));
+				       GET_MODE (to), promoted_from,
+				       GET_MODE (promoted_from));
       insns = get_insns ();
       end_sequence ();
 
@@ -5734,7 +5769,7 @@ expand_fix (rtx to, rtx from, int unsignedp)
    there are no tree types defined for libcalls.  */
 
 static rtx
-prepare_libcall_arg (rtx arg, int uintp)
+prepare_libcall_int_arg (rtx arg, int uintp)
 {
   scalar_int_mode mode;
   machine_mode arg_mode;
@@ -5796,7 +5831,7 @@ expand_fixed_convert (rtx to, rtx from, int uintp, int satp)
   libfunc = convert_optab_libfunc (tab, to_mode, from_mode);
   gcc_assert (libfunc);
 
-  from = prepare_libcall_arg (from, uintp);
+  from = prepare_libcall_int_arg (from, uintp);
   from_mode = GET_MODE (from);
 
   start_sequence ();
