diff options
-rw-r--r-- | gcc/calls.cc | 38 | ||||
-rw-r--r-- | gcc/cfgexpand.cc | 13 | ||||
-rw-r--r-- | gcc/expr.cc | 35 | ||||
-rw-r--r-- | gcc/expr.h | 11 | ||||
-rw-r--r-- | gcc/function.cc | 11 |
5 files changed, 95 insertions, 13 deletions
diff --git a/gcc/calls.cc b/gcc/calls.cc index f4e1299..7f3cf5f 100644 --- a/gcc/calls.cc +++ b/gcc/calls.cc @@ -992,11 +992,21 @@ 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 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. */ @@ -3825,18 +3835,24 @@ 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))) + 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 diff --git a/gcc/cfgexpand.cc b/gcc/cfgexpand.cc index 1d7f6b2..dd29fff 100644 --- a/gcc/cfgexpand.cc +++ b/gcc/cfgexpand.cc @@ -3720,7 +3720,18 @@ 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))) + 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)); diff --git a/gcc/expr.cc b/gcc/expr.cc index 53af027..6229737 100644 --- a/gcc/expr.cc +++ b/gcc/expr.cc @@ -776,6 +776,32 @@ convert_modes (machine_mode mode, machine_mode oldmode, rtx x, int unsignedp) 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); +} /* Return the largest alignment we can use for doing a move (or store) of MAX_PIECES. ALIGN is the largest alignment we could use. */ @@ -10771,6 +10797,15 @@ 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))) + 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); @@ -69,7 +69,16 @@ extern void convert_move (rtx, rtx, int); 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, diff --git a/gcc/function.cc b/gcc/function.cc index 0c3e4ff..31256b5 100644 --- a/gcc/function.cc +++ b/gcc/function.cc @@ -3472,6 +3472,17 @@ 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))) + 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; |