diff --git a/gcc/calls.cc b/gcc/calls.cc index f4e1299..06d8a95 100644 --- a/gcc/calls.cc +++ b/gcc/calls.cc @@ -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 diff --git a/gcc/cfgexpand.cc b/gcc/cfgexpand.cc index bb33c1b..a9afec6 100644 --- a/gcc/cfgexpand.cc +++ b/gcc/cfgexpand.cc @@ -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)); diff --git a/gcc/expr.cc b/gcc/expr.cc index c90cde3..c1ce49e 100644 --- a/gcc/expr.cc +++ b/gcc/expr.cc @@ -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); diff --git a/gcc/function.cc b/gcc/function.cc index ad0096a..657b95d 100644 --- a/gcc/function.cc +++ b/gcc/function.cc @@ -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;