diff mbox series

middle-end: More support for ABIs that pass FP values as wider ints.

Message ID 00c101d8a10b$cfdc1990$6f944cb0$@nextmovesoftware.com
State New
Headers show
Series middle-end: More support for ABIs that pass FP values as wider ints. | expand

Commit Message

Roger Sayle July 26, 2022, 4:21 p.m. UTC
Firstly many thanks again to Jeff Law for reviewing/approving the previous
patch to add support for ABIs that pass FP values as wider integer modes.
That has allowed significant progress on PR target/104489.  As predicted
enabling HFmode on nvptx-none automatically enables more testcases in the
testsuite and making sure these all PASS has revealed a few missed spots
and a deficiency in the middle-end.  For example, support for HC mode,
where a complex value is encoded as two 16-bit HFmode parts was
insufficiently covered in my previous testing.  More interesting is
that __fixunshfti is required by GCC, and not natively supported by
the nvptx backend, requiring softfp support in libgcc, which in turn
revealed an interesting asymmetry in libcall handling in optabs.cc.

In the expand_fixed_convert function, which is responsible for
expanding libcalls for integer to floating point conversion, GCC
calls prepare_libcall_arg that (specifically for integer arguments)
calls promote_function_mode on the argument, so that the libcall
ABI matches the regular target ABI.  By comparison, the equivalent
expand_fix function, for floating point to integer conversion, doesn't
promote its argument.  On nvptx, where the assembler is strongly
typed, this produces a mismatch as the __fixunshfti function created
by libgcc doesn't precisely match the signature assumed by optabs.
The solution is to perform the same (or similar) prepare_libcall_arg
preparation in both cases.  In this patch, the existing (static)
prepare_libcall_arg, which assumes an integer argument, is renamed
prepare_libcall_int_arg, and a matching prepare_libcall_fp_arg is
introduced.  This should be safe on other platforms (fingers-crossed)
as floating point argument promotion is rare [floats are passed in
float registers, doubles are passed in double registers, etc.]

This patch has been tested on x86_64-pc-linux-gnu with make bootstrap
and make -k check, both with and without --target_board=unix{-m32},
and on nvptx-none with a backend patch that resolves the rest of
PR target/104489.  Ok for mainline?


2022-07-26  Roger Sayle  <roger@nextmovesoftware.com>

gcc/ChangeLog
        PR target/104489
        * calls.cc (emit_library_call_value_1): Enable the FP return value
        of a libcall to be returned as a wider integer, by converting the
        int result to be converted to the desired floating point mode.
        (store_one_arg): Allow floating point arguments to be passed on
        the stack as wider integers using convert_float_to_wider_int.
        * function.cc (assign_parms_unsplit_complex): Likewise, allow
        complex floating point modes to be passed as wider integer parts,
        using convert_wider_int_to_float.
        * optabs.cc (prepare_libcall_fp_arg): New function. A floating
        point version of the previous prepare_libcall_arg that calls
        promote_function_mode on its argument.
        (expand_fix): Call new prepare_libcall_fp_arg on FIX argument.
        (prepare_libcall_int_arg): Renamed from prepare_libcall_arg.
        (expand_fixed_convert): Update call of prepare_libcall_arg to
        the new name, prepare_libcall_int_arg.


Thanks again,
Roger
--
diff mbox series

Patch

diff --git a/gcc/calls.cc b/gcc/calls.cc
index 7f3cf5f..50d0495 100644
--- a/gcc/calls.cc
+++ b/gcc/calls.cc
@@ -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)
diff --git a/gcc/function.cc b/gcc/function.cc
index 31256b5..4deb6b0 100644
--- a/gcc/function.cc
+++ b/gcc/function.cc
@@ -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);
diff --git a/gcc/optabs.cc b/gcc/optabs.cc
index a50dd79..10ccfbc 100644
--- a/gcc/optabs.cc
+++ b/gcc/optabs.cc
@@ -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 ();