@@ -992,11 +992,24 @@ precompute_register_parameters (int num_actuals, struct arg_data *args,
/* If we are to promote the function arg to a wider mode,
do it now. */
- if (args[i].mode != TYPE_MODE (TREE_TYPE (args[i].tree_value)))
- args[i].value
- = convert_modes (args[i].mode,
- TYPE_MODE (TREE_TYPE (args[i].tree_value)),
- args[i].value, args[i].unsignedp);
+ machine_mode old_mode = TYPE_MODE (TREE_TYPE (args[i].tree_value));
+
+ /* 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 (args[i].mode)
+ && SCALAR_FLOAT_MODE_P (old_mode)
+ && known_gt (GET_MODE_SIZE (args[i].mode),
+ GET_MODE_SIZE (old_mode)))
+ {
+ scalar_int_mode imode = int_mode_for_mode (old_mode).require ();
+ rtx tmp = force_reg (imode, gen_lowpart (imode, args[i].value));
+ args[i].value = convert_modes (args[i].mode, imode, tmp, 1);
+ }
+ else if (args[i].mode != old_mode)
+ args[i].value = convert_modes (args[i].mode, old_mode,
+ args[i].value, args[i].unsignedp);
/* If the value is a non-legitimate constant, force it into a
pseudo now. TLS symbols sometimes need a call to resolve. */
@@ -3825,18 +3838,28 @@ expand_call (tree exp, rtx target, int ignore)
{
tree type = rettype;
int unsignedp = TYPE_UNSIGNED (type);
+ machine_mode ret_mode = TYPE_MODE (type);
machine_mode pmode;
/* Ensure we promote as expected, and get the new unsignedness. */
- pmode = promote_function_mode (type, TYPE_MODE (type), &unsignedp,
+ pmode = promote_function_mode (type, ret_mode, &unsignedp,
funtype, 1);
gcc_assert (GET_MODE (target) == pmode);
- poly_uint64 offset = subreg_lowpart_offset (TYPE_MODE (type),
- GET_MODE (target));
- target = gen_rtx_SUBREG (TYPE_MODE (type), target, offset);
- SUBREG_PROMOTED_VAR_P (target) = 1;
- SUBREG_PROMOTED_SET (target, unsignedp);
+ if (SCALAR_INT_MODE_P (pmode)
+ && SCALAR_FLOAT_MODE_P (ret_mode)
+ && known_gt (GET_MODE_SIZE (pmode), GET_MODE_SIZE (ret_mode)))
+ {
+ scalar_int_mode imode = int_mode_for_mode (ret_mode).require ();
+ target = force_reg (imode, gen_lowpart (imode, target));
+ target = gen_lowpart_SUBREG (ret_mode, target);
+ }
+ else
+ {
+ target = gen_lowpart_SUBREG (ret_mode, target);
+ SUBREG_PROMOTED_VAR_P (target) = 1;
+ SUBREG_PROMOTED_SET (target, unsignedp);
+ }
}
/* If size of args is variable or this was a constructor call for a stack
@@ -3720,7 +3720,22 @@ expand_value_return (rtx val)
mode = promote_function_mode (type, old_mode, &unsignedp, funtype, 1);
if (mode != old_mode)
- val = convert_modes (mode, old_mode, val, unsignedp);
+ {
+ /* Some ABIs require scalar floating point modes to be returned
+ 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 (mode)
+ && SCALAR_FLOAT_MODE_P (old_mode)
+ && known_gt (GET_MODE_SIZE (mode), GET_MODE_SIZE (old_mode)))
+ {
+ scalar_int_mode imode = int_mode_for_mode (old_mode).require ();
+ val = force_reg (imode, gen_lowpart (imode, val));
+ val = convert_modes (mode, imode, val, 1);
+ }
+ else
+ val = convert_modes (mode, old_mode, val, unsignedp);
+ }
if (GET_CODE (return_reg) == PARALLEL)
emit_group_load (return_reg, val, type, int_size_in_bytes (type));
@@ -10771,6 +10771,19 @@ expand_expr_real_1 (tree exp, rtx target, machine_mode tmode,
pmode = promote_ssa_mode (ssa_name, &unsignedp);
gcc_assert (GET_MODE (decl_rtl) == pmode);
+ /* Some ABIs require scalar floating point modes to be passed
+ in a wider scalar integer mode. We need to explicitly
+ truncate to an integer mode of the correct precision before
+ using a SUBREG to reinterpret as a floating point value. */
+ if (SCALAR_FLOAT_MODE_P (mode)
+ && SCALAR_INT_MODE_P (pmode)
+ && known_lt (GET_MODE_SIZE (mode), GET_MODE_SIZE (pmode)))
+ {
+ scalar_int_mode imode = int_mode_for_mode (mode).require ();
+ temp = force_reg (imode, gen_lowpart (imode, decl_rtl));
+ return gen_lowpart_SUBREG (mode, temp);
+ }
+
temp = gen_lowpart_SUBREG (mode, decl_rtl);
SUBREG_PROMOTED_VAR_P (temp) = 1;
SUBREG_PROMOTED_SET (temp, unsignedp);
@@ -3472,6 +3472,21 @@ assign_parm_setup_stack (struct assign_parm_data_all *all, tree parm,
emit_move_insn (tempreg, validize_mem (copy_rtx (data->entry_parm)));
+ /* Some ABIs require scalar floating point modes to be passed
+ in a wider scalar integer mode. We need to explicitly
+ truncate to an integer mode of the correct precision before
+ using a SUBREG to reinterpret as a floating point value. */
+ if (SCALAR_FLOAT_MODE_P (data->nominal_mode)
+ && SCALAR_INT_MODE_P (data->arg.mode)
+ && known_lt (GET_MODE_SIZE (data->nominal_mode),
+ GET_MODE_SIZE (data->arg.mode)))
+ {
+ scalar_int_mode imode
+ = int_mode_for_mode (data->nominal_mode).require ();
+ tempreg = force_reg (imode, gen_lowpart (imode, tempreg));
+ tempreg = gen_lowpart_SUBREG (data->nominal_mode, tempreg);
+ }
+
push_to_sequence2 (all->first_conversion_insn, all->last_conversion_insn);
to_conversion = true;