@@ -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)
@@ -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);
@@ -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 ();