/* 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 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 (args[i].mode)
+ && SCALAR_FLOAT_MODE_P (old_mode)
+ && known_gt (GET_MODE_SIZE (args[i].mode),
+ GET_MODE_SIZE (old_mode)))
+ args[i].value = convert_float_to_wider_int (args[i].mode, old_mode,
+ args[i].value);
+ 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. */
{
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)))
+ target = convert_wider_int_to_float (ret_mode, pmode, 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
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)))
+ val = convert_float_to_wider_int (mode, old_mode, val);
+ 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));
convert_move (temp, x, unsignedp);
return temp;
}
+
+/* Variant of convert_modes for ABI parameter passing/return.
+ Return an rtx for a value that would result from converting X from
+ a floating point mode FMODE to wider integer mode MODE. */
+
+rtx
+convert_float_to_wider_int (machine_mode mode, machine_mode fmode, rtx x)
+{
+ gcc_assert (SCALAR_INT_MODE_P (mode) && SCALAR_FLOAT_MODE_P (fmode));
+ scalar_int_mode tmp_mode = int_mode_for_mode (fmode).require ();
+ rtx tmp = force_reg (tmp_mode, gen_lowpart (tmp_mode, x));
+ return convert_modes (mode, tmp_mode, tmp, 1);
+}
+
+/* Variant of convert_modes for ABI parameter passing/return.
+ Return an rtx for a value that would result from converting X from
+ an integer mode IMODE to a narrower floating point mode MODE. */
+
+rtx
+convert_wider_int_to_float (machine_mode mode, machine_mode imode, rtx x)
+{
+ gcc_assert (SCALAR_FLOAT_MODE_P (mode) && SCALAR_INT_MODE_P (imode));
+ scalar_int_mode tmp_mode = int_mode_for_mode (mode).require ();
+ rtx tmp = force_reg (tmp_mode, gen_lowpart (tmp_mode, x));
+ return gen_lowpart_SUBREG (mode, tmp);
+}
\f
/* Return the largest alignment we can use for doing a move (or store)
of MAX_PIECES. ALIGN is the largest alignment we could use. */
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)))
+ return convert_wider_int_to_float (mode, pmode, decl_rtl);
+
temp = gen_lowpart_SUBREG (mode, decl_rtl);
SUBREG_PROMOTED_VAR_P (temp) = 1;
SUBREG_PROMOTED_SET (temp, unsignedp);
extern rtx convert_to_mode (machine_mode, rtx, int);
/* Convert an rtx to MODE from OLDMODE and return the result. */
-extern rtx convert_modes (machine_mode, machine_mode, rtx, int);
+extern rtx convert_modes (machine_mode mode, machine_mode oldmode,
+ rtx x, int unsignedp);
+
+/* Variant of convert_modes for ABI parameter passing/return. */
+extern rtx convert_float_to_wider_int (machine_mode mode, machine_mode fmode,
+ rtx x);
+
+/* Variant of convert_modes for ABI parameter passing/return. */
+extern rtx convert_wider_int_to_float (machine_mode mode, machine_mode imode,
+ rtx x);
/* Expand a call to memcpy or memmove or memcmp, and return the result. */
extern rtx emit_block_op_via_libcall (enum built_in_function, rtx, rtx, rtx,
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)))
+ tempreg = convert_wider_int_to_float (data->nominal_mode,
+ data->arg.mode, tempreg);
+
push_to_sequence2 (all->first_conversion_insn, all->last_conversion_insn);
to_conversion = true;