diff mbox

[7/67] Add scalar_float_mode

Message ID 87mvg5ntv3.fsf@e105548-lin.cambridge.arm.com
State New
Headers show

Commit Message

Richard Sandiford Dec. 9, 2016, 12:56 p.m. UTC
This patch adds a scalar_float_mode class, which wraps a mode enum
that is known to satisfy SCALAR_FLOAT_MODE_P.  Things like "SFmode"
now give a scalar_float_mode object instead of a machine_mode.
This in turn needs a change to the real.h format_helper, so that
it can accept both machine_modes and scalar_float_modes.

gcc/
2016-11-30  Richard Sandiford  <richard.sandiford@arm.com>
	    Alan Hayward  <alan.hayward@arm.com>
	    David Sherwood  <david.sherwood@arm.com>

	* coretypes.h (scalar_float_mode): New type.
	* machmode.h (PROTECT_ENUM_CONVERSION): New macro.
	(is_a): New function.
	(as_a): Likewise.
	(dyn_cast): Likewise.
	(scalar_float_mode): New class.
	(scalar_float_mode::includes_p): New function.
	(scalar_float_mode::from_int): Likewise.
	* real.h (FLOAT_MODE_FORMAT): Use as_a <scalar_float_mode>.
	(format_helper::format_helper): Turn into a template.
	* genmodes.c (get_mode_class): New function.
	(emit_insn_modes_h): Give modes the class returned by get_mode_class,
	or machine_mode if none.
	* config/aarch64/aarch64.c (aarch64_simd_valid_immediate): Use
	as_a <scalar_float_mode>.
	* dwarf2out.c (mem_loc_descriptor): Likewise.
	(insert_float): Likewise.
	(add_const_value_attribute): Likewise.
	* simplify-rtx.c (simplify_immed_subreg): Likewise.
	* optabs.c (expand_absneg_bit): Take a scalar_float_mode.
	(expand_unop): Update accordingly.
	(expand_abs_nojump): Likewise.
	(expand_copysign_absneg): Take a scalar_float_mode.
	(expand_copysign_bit): Likewise.
	(expand_copysign): Update accordingly.

gcc/ada/
2016-11-24  Richard Sandiford  <richard.sandiford@arm.com>
	    Alan Hayward  <alan.hayward@arm.com>
	    David Sherwood  <david.sherwood@arm.com>

	* gcc-interface/utils.c (gnat_type_for_mode): Use is_a
	<scalar_float_mode> instead of SCALAR_FLOAT_MODE_P.

Comments

David Malcolm Dec. 9, 2016, 2:19 p.m. UTC | #1
On Fri, 2016-12-09 at 12:56 +0000, Richard Sandiford wrote:
> This patch adds a scalar_float_mode class, which wraps a mode enum
> that is known to satisfy SCALAR_FLOAT_MODE_P.  Things like "SFmode"
> now give a scalar_float_mode object instead of a machine_mode.
> This in turn needs a change to the real.h format_helper, so that
> it can accept both machine_modes and scalar_float_modes.

[...]

> diff --git a/gcc/machmode.h b/gcc/machmode.h
> index 44a8ad4..996f991 100644
> --- a/gcc/machmode.h
> +++ b/gcc/machmode.h
> @@ -29,6 +29,16 @@ extern const unsigned short
> mode_unit_precision[NUM_MACHINE_MODES];
>  extern const unsigned char mode_wider[NUM_MACHINE_MODES];
>  extern const unsigned char mode_2xwider[NUM_MACHINE_MODES];
>  
> +/* Allow direct conversion of enums to specific mode classes only
> +   when USE_ENUM_MODES is defined.  This is only intended for use
> +   by gencondmd, so that it can tell more easily when .md conditions
> +   are always false.  */
> +#ifdef USE_ENUM_MODES
> +#define PROTECT_ENUM_CONVERSION public
> +#else
> +#define PROTECT_ENUM_CONVERSION protected
> +#endif
> +
>  /* Get the name of mode MODE as a string.  */
>  
>  extern const char * const mode_name[NUM_MACHINE_MODES];
> @@ -237,6 +247,85 @@ opt_mode<T>::exists (U *mode) const
>    return false;
>  }
>  
> +/* Return true if mode M has type T.  */
> +
> +template<typename T>
> +inline bool
> +is_a (machine_mode_enum m)
> +{
> +  return T::includes_p (m);
> +}
> +
> +/* Assert that mode M has type T, and return it in that form.  */
> +
> +template<typename T>
> +inline T
> +as_a (machine_mode_enum m)
> +{
> +  gcc_checking_assert (T::includes_p (m));
> +  return T::from_int (m);
> +}
> +
> +/* Convert M to an opt_mode<T>.  */
> +
> +template<typename T>
> +inline opt_mode<T>
> +dyn_cast (machine_mode_enum m)
> +{
> +  if (T::includes_p (m))
> +    return T::from_int (m);
> +  return opt_mode<T> ();
> +}
> +
> +/* Return true if mode M has type T, storing it as a T in *RESULT
> +   if so.  */
> +
> +template<typename T, typename U>
> +inline bool
> +is_a (machine_mode_enum m, U *result)
> +{
> +  if (T::includes_p (m))
> +    {
> +      *result = T::from_int (m);
> +      return true;
> +    }
> +  return false;
> +}
> +
> +/* Represents a machine mode that is known to be a
> SCALAR_FLOAT_MODE_P.  */
> +class scalar_float_mode

Should this be a subclass of machine_mode?  Or does this lead to name
clashes? (e.g. for "from_int")

(Similar questions for the other more specialized wrapper classes; it
looks like you have a set of independent wrapper classes around the
machine_mode_enum, without using inheritance, with a trip through the
enum needed to convert between wrappers)

> +{
> +public:
> +  ALWAYS_INLINE scalar_float_mode () {}
> +  ALWAYS_INLINE operator machine_mode_enum () const { return m_mode;
> }
> +
> +  static bool includes_p (machine_mode_enum);
> +  static scalar_float_mode from_int (int i);
> +
> +PROTECT_ENUM_CONVERSION:
> +  ALWAYS_INLINE scalar_float_mode (machine_mode_enum m) : m_mode (m)
> {}

Should this ctor have some kind of:
  gcc_checking_assert (SCALAR_FLOAT_MODE_P (m));
or somesuch?  (or does that slow down the debug build too much?)

> +
> +protected:
> +  machine_mode_enum m_mode;
> +};
> +
> +/* Return true if M is a scalar_float_mode.  */
> +
> +inline bool
> +scalar_float_mode::includes_p (machine_mode_enum m)
> +{
> +  return SCALAR_FLOAT_MODE_P (m);
> +}
> +
> +/* Return M as a scalar_float_mode.  This function should only be
> used by
> +   utility functions; general code should use as_a<T> instead.  */
> +
> +ALWAYS_INLINE scalar_float_mode
> +scalar_float_mode::from_int (int i)
> +{
> +  return machine_mode_enum (i);
> +}

...or put an assertion in here?  (I *think* it's the only public
function that can (implicitly) call the ctor without checking the
invariant)

[...]


Hope this is constructive
Dave
Richard Sandiford Dec. 9, 2016, 2:29 p.m. UTC | #2
David Malcolm <dmalcolm@redhat.com> writes:
> On Fri, 2016-12-09 at 12:56 +0000, Richard Sandiford wrote:
>> This patch adds a scalar_float_mode class, which wraps a mode enum
>> that is known to satisfy SCALAR_FLOAT_MODE_P.  Things like "SFmode"
>> now give a scalar_float_mode object instead of a machine_mode.
>> This in turn needs a change to the real.h format_helper, so that
>> it can accept both machine_modes and scalar_float_modes.
>
> [...]
>
>> diff --git a/gcc/machmode.h b/gcc/machmode.h
>> index 44a8ad4..996f991 100644
>> --- a/gcc/machmode.h
>> +++ b/gcc/machmode.h
>> @@ -29,6 +29,16 @@ extern const unsigned short
>> mode_unit_precision[NUM_MACHINE_MODES];
>>  extern const unsigned char mode_wider[NUM_MACHINE_MODES];
>>  extern const unsigned char mode_2xwider[NUM_MACHINE_MODES];
>>  
>> +/* Allow direct conversion of enums to specific mode classes only
>> +   when USE_ENUM_MODES is defined.  This is only intended for use
>> +   by gencondmd, so that it can tell more easily when .md conditions
>> +   are always false.  */
>> +#ifdef USE_ENUM_MODES
>> +#define PROTECT_ENUM_CONVERSION public
>> +#else
>> +#define PROTECT_ENUM_CONVERSION protected
>> +#endif
>> +
>>  /* Get the name of mode MODE as a string.  */
>>  
>>  extern const char * const mode_name[NUM_MACHINE_MODES];
>> @@ -237,6 +247,85 @@ opt_mode<T>::exists (U *mode) const
>>    return false;
>>  }
>>  
>> +/* Return true if mode M has type T.  */
>> +
>> +template<typename T>
>> +inline bool
>> +is_a (machine_mode_enum m)
>> +{
>> +  return T::includes_p (m);
>> +}
>> +
>> +/* Assert that mode M has type T, and return it in that form.  */
>> +
>> +template<typename T>
>> +inline T
>> +as_a (machine_mode_enum m)
>> +{
>> +  gcc_checking_assert (T::includes_p (m));
>> +  return T::from_int (m);
>> +}
>> +
>> +/* Convert M to an opt_mode<T>.  */
>> +
>> +template<typename T>
>> +inline opt_mode<T>
>> +dyn_cast (machine_mode_enum m)
>> +{
>> +  if (T::includes_p (m))
>> +    return T::from_int (m);
>> +  return opt_mode<T> ();
>> +}
>> +
>> +/* Return true if mode M has type T, storing it as a T in *RESULT
>> +   if so.  */
>> +
>> +template<typename T, typename U>
>> +inline bool
>> +is_a (machine_mode_enum m, U *result)
>> +{
>> +  if (T::includes_p (m))
>> +    {
>> +      *result = T::from_int (m);
>> +      return true;
>> +    }
>> +  return false;
>> +}
>> +
>> +/* Represents a machine mode that is known to be a
>> SCALAR_FLOAT_MODE_P.  */
>> +class scalar_float_mode
>
> Should this be a subclass of machine_mode?  Or does this lead to name
> clashes? (e.g. for "from_int")
>
> (Similar questions for the other more specialized wrapper classes; it
> looks like you have a set of independent wrapper classes around the
> machine_mode_enum, without using inheritance, with a trip through the
> enum needed to convert between wrappers)

Making it a subclass would be the natural thing to do if the class
actually defined the mode (and if we for example used pointers to
the definitions to represent the mode).  With wrappers it's kind-of
the "wrong way round" though: having scalar_int_mode a subclass of
machine_mode would allow things like:

  void foo (machine_mode *x) { *x = SFmode; }
  void bar () { scalar_int_mode y; foo (&y); ... }

and so isn't type-safe.

>> +{
>> +public:
>> +  ALWAYS_INLINE scalar_float_mode () {}
>> +  ALWAYS_INLINE operator machine_mode_enum () const { return m_mode;
>> }
>> +
>> +  static bool includes_p (machine_mode_enum);
>> +  static scalar_float_mode from_int (int i);
>> +
>> +PROTECT_ENUM_CONVERSION:
>> +  ALWAYS_INLINE scalar_float_mode (machine_mode_enum m) : m_mode (m)
>> {}
>
> Should this ctor have some kind of:
>   gcc_checking_assert (SCALAR_FLOAT_MODE_P (m));
> or somesuch?  (or does that slow down the debug build too much?)

The idea was to have the asserts in as_a <> calls instead, which is
the point at which forcible conversion occurs.  These routines are for
cases where we've already done the check or know due to static type
checking that a check isn't needed.

>> +
>> +protected:
>> +  machine_mode_enum m_mode;
>> +};
>> +
>> +/* Return true if M is a scalar_float_mode.  */
>> +
>> +inline bool
>> +scalar_float_mode::includes_p (machine_mode_enum m)
>> +{
>> +  return SCALAR_FLOAT_MODE_P (m);
>> +}
>> +
>> +/* Return M as a scalar_float_mode.  This function should only be
>> used by
>> +   utility functions; general code should use as_a<T> instead.  */
>> +
>> +ALWAYS_INLINE scalar_float_mode
>> +scalar_float_mode::from_int (int i)
>> +{
>> +  return machine_mode_enum (i);
>> +}
>
> ...or put an assertion in here?  (I *think* it's the only public
> function that can (implicitly) call the ctor without checking the
> invariant)

Same idea here: this should only be called when no check is needed,
and like you say shouldn't have an assert for speed reasons.  The
number of callers to from_int is small and so it's relatively easy
to check that the calls are safe.

> Hope this is constructive

Yeah, definitely :-)

Thanks,
Richard
diff mbox

Patch

diff --git a/gcc/ada/gcc-interface/utils.c b/gcc/ada/gcc-interface/utils.c
index 0ae381f..29e5203 100644
--- a/gcc/ada/gcc-interface/utils.c
+++ b/gcc/ada/gcc-interface/utils.c
@@ -3411,8 +3411,10 @@  gnat_type_for_mode (machine_mode mode, int unsignedp)
   if (COMPLEX_MODE_P (mode))
     return NULL_TREE;
 
-  if (SCALAR_FLOAT_MODE_P (mode))
-    return float_type_for_precision (GET_MODE_PRECISION (mode), mode);
+  scalar_float_mode float_mode;
+  if (is_a <scalar_float_mode> (mode, &float_mode))
+    return float_type_for_precision (GET_MODE_PRECISION (float_mode),
+				     float_mode);
 
   if (SCALAR_INT_MODE_P (mode))
     return gnat_type_for_size (GET_MODE_BITSIZE (mode), unsignedp);
diff --git a/gcc/config/aarch64/aarch64.c b/gcc/config/aarch64/aarch64.c
index 944577d..cc97b7a 100644
--- a/gcc/config/aarch64/aarch64.c
+++ b/gcc/config/aarch64/aarch64.c
@@ -11035,8 +11035,12 @@  aarch64_simd_valid_immediate (rtx op, machine_mode mode, bool inverse,
 
       if (info)
 	{
-	  info->value = CONST_VECTOR_ELT (op, 0);
-	  info->element_width = GET_MODE_BITSIZE (GET_MODE (info->value));
+	  rtx elt = CONST_VECTOR_ELT (op, 0);
+	  scalar_float_mode elt_mode
+	    = as_a <scalar_float_mode> (GET_MODE (elt));
+
+	  info->value = elt;
+	  info->element_width = GET_MODE_BITSIZE (elt_mode);
 	  info->mvn = false;
 	  info->shift = 0;
 	}
diff --git a/gcc/coretypes.h b/gcc/coretypes.h
index 9f81995..f4c1859 100644
--- a/gcc/coretypes.h
+++ b/gcc/coretypes.h
@@ -56,6 +56,7 @@  struct rtx_def;
 typedef struct rtx_def *rtx;
 typedef const struct rtx_def *const_rtx;
 class machine_mode;
+class scalar_float_mode;
 template<typename> class opt_mode;
 
 /* Subclasses of rtx_def, using indentation to show the class
diff --git a/gcc/dwarf2out.c b/gcc/dwarf2out.c
index 8dc8523..5130b64 100644
--- a/gcc/dwarf2out.c
+++ b/gcc/dwarf2out.c
@@ -15121,7 +15121,8 @@  mem_loc_descriptor (rtx rtl, machine_mode mode,
 	  else
 #endif
 	    {
-	      unsigned int length = GET_MODE_SIZE (mode);
+	      scalar_float_mode float_mode = as_a <scalar_float_mode> (mode);
+	      unsigned int length = GET_MODE_SIZE (float_mode);
 	      unsigned char *array = ggc_vec_alloc<unsigned char> (length);
 
 	      insert_float (rtl, array);
@@ -18343,11 +18344,12 @@  insert_float (const_rtx rtl, unsigned char *array)
 {
   long val[4];
   int i;
+  scalar_float_mode mode = as_a <scalar_float_mode> (GET_MODE (rtl));
 
-  real_to_target (val, CONST_DOUBLE_REAL_VALUE (rtl), GET_MODE (rtl));
+  real_to_target (val, CONST_DOUBLE_REAL_VALUE (rtl), mode);
 
   /* real_to_target puts 32-bit pieces in each long.  Pack them.  */
-  for (i = 0; i < GET_MODE_SIZE (GET_MODE (rtl)) / 4; i++)
+  for (i = 0; i < GET_MODE_SIZE (mode) / 4; i++)
     {
       insert_int (val[i], 4, array);
       array += 4;
@@ -18391,21 +18393,19 @@  add_const_value_attribute (dw_die_ref die, rtx rtl)
 	 floating-point constant.  A CONST_DOUBLE is used whenever the
 	 constant requires more than one word in order to be adequately
 	 represented.  */
-      {
-	machine_mode mode = GET_MODE (rtl);
-
-	if (TARGET_SUPPORTS_WIDE_INT == 0 && !SCALAR_FLOAT_MODE_P (mode))
-	  add_AT_double (die, DW_AT_const_value,
-			 CONST_DOUBLE_HIGH (rtl), CONST_DOUBLE_LOW (rtl));
-	else
-	  {
-	    unsigned int length = GET_MODE_SIZE (mode);
-	    unsigned char *array = ggc_vec_alloc<unsigned char> (length);
+      if (TARGET_SUPPORTS_WIDE_INT == 0
+	  && !SCALAR_FLOAT_MODE_P (GET_MODE (rtl)))
+	add_AT_double (die, DW_AT_const_value,
+		       CONST_DOUBLE_HIGH (rtl), CONST_DOUBLE_LOW (rtl));
+      else
+	{
+	  scalar_float_mode mode = as_a <scalar_float_mode> (GET_MODE (rtl));
+	  unsigned int length = GET_MODE_SIZE (mode);
+	  unsigned char *array = ggc_vec_alloc<unsigned char> (length);
 
-	    insert_float (rtl, array);
-	    add_AT_vec (die, DW_AT_const_value, length / 4, 4, array);
-	  }
-      }
+	  insert_float (rtl, array);
+	  add_AT_vec (die, DW_AT_const_value, length / 4, 4, array);
+	}
       return true;
 
     case CONST_VECTOR:
diff --git a/gcc/genmodes.c b/gcc/genmodes.c
index 4dbced2..3cc426e 100644
--- a/gcc/genmodes.c
+++ b/gcc/genmodes.c
@@ -1129,6 +1129,23 @@  mode_unit_precision_inline (machine_mode_enum mode)\n\
 }\n");
 }
 
+/* Return the best machine mode class for MODE, or null if machine_mode
+   should be used.  */
+
+static const char *
+get_mode_class (struct mode_data *mode)
+{
+  switch (mode->cl)
+    {
+    case MODE_FLOAT:
+    case MODE_DECIMAL_FLOAT:
+      return "scalar_float_mode";
+
+    default:
+      return NULL;
+    }
+}
+
 static void
 emit_insn_modes_h (void)
 {
@@ -1158,8 +1175,12 @@  enum machine_mode_enum\n{");
 	printf ("#ifdef USE_ENUM_MODES\n");
 	printf ("#define %smode E_%smode\n", m->name, m->name);
 	printf ("#else\n");
-	printf ("#define %smode (machine_mode (E_%smode))\n",
-		m->name, m->name);
+	if (const char *mode_class = get_mode_class (m))
+	  printf ("#define %smode (%s::from_int (E_%smode))\n",
+		  m->name, mode_class, m->name);
+	else
+	  printf ("#define %smode (machine_mode (E_%smode))\n",
+		  m->name, m->name);
 	printf ("#endif\n");
       }
 
diff --git a/gcc/machmode.h b/gcc/machmode.h
index 44a8ad4..996f991 100644
--- a/gcc/machmode.h
+++ b/gcc/machmode.h
@@ -29,6 +29,16 @@  extern const unsigned short mode_unit_precision[NUM_MACHINE_MODES];
 extern const unsigned char mode_wider[NUM_MACHINE_MODES];
 extern const unsigned char mode_2xwider[NUM_MACHINE_MODES];
 
+/* Allow direct conversion of enums to specific mode classes only
+   when USE_ENUM_MODES is defined.  This is only intended for use
+   by gencondmd, so that it can tell more easily when .md conditions
+   are always false.  */
+#ifdef USE_ENUM_MODES
+#define PROTECT_ENUM_CONVERSION public
+#else
+#define PROTECT_ENUM_CONVERSION protected
+#endif
+
 /* Get the name of mode MODE as a string.  */
 
 extern const char * const mode_name[NUM_MACHINE_MODES];
@@ -237,6 +247,85 @@  opt_mode<T>::exists (U *mode) const
   return false;
 }
 
+/* Return true if mode M has type T.  */
+
+template<typename T>
+inline bool
+is_a (machine_mode_enum m)
+{
+  return T::includes_p (m);
+}
+
+/* Assert that mode M has type T, and return it in that form.  */
+
+template<typename T>
+inline T
+as_a (machine_mode_enum m)
+{
+  gcc_checking_assert (T::includes_p (m));
+  return T::from_int (m);
+}
+
+/* Convert M to an opt_mode<T>.  */
+
+template<typename T>
+inline opt_mode<T>
+dyn_cast (machine_mode_enum m)
+{
+  if (T::includes_p (m))
+    return T::from_int (m);
+  return opt_mode<T> ();
+}
+
+/* Return true if mode M has type T, storing it as a T in *RESULT
+   if so.  */
+
+template<typename T, typename U>
+inline bool
+is_a (machine_mode_enum m, U *result)
+{
+  if (T::includes_p (m))
+    {
+      *result = T::from_int (m);
+      return true;
+    }
+  return false;
+}
+
+/* Represents a machine mode that is known to be a SCALAR_FLOAT_MODE_P.  */
+class scalar_float_mode
+{
+public:
+  ALWAYS_INLINE scalar_float_mode () {}
+  ALWAYS_INLINE operator machine_mode_enum () const { return m_mode; }
+
+  static bool includes_p (machine_mode_enum);
+  static scalar_float_mode from_int (int i);
+
+PROTECT_ENUM_CONVERSION:
+  ALWAYS_INLINE scalar_float_mode (machine_mode_enum m) : m_mode (m) {}
+
+protected:
+  machine_mode_enum m_mode;
+};
+
+/* Return true if M is a scalar_float_mode.  */
+
+inline bool
+scalar_float_mode::includes_p (machine_mode_enum m)
+{
+  return SCALAR_FLOAT_MODE_P (m);
+}
+
+/* Return M as a scalar_float_mode.  This function should only be used by
+   utility functions; general code should use as_a<T> instead.  */
+
+ALWAYS_INLINE scalar_float_mode
+scalar_float_mode::from_int (int i)
+{
+  return machine_mode_enum (i);
+}
+
 /* Represents a general machine mode (scalar or non-scalar).  */
 class machine_mode
 {
diff --git a/gcc/optabs.c b/gcc/optabs.c
index 5dab1b4..58c26e4 100644
--- a/gcc/optabs.c
+++ b/gcc/optabs.c
@@ -2550,7 +2550,7 @@  lowpart_subreg_maybe_copy (machine_mode omode, rtx val,
    logical operation on the sign bit.  */
 
 static rtx
-expand_absneg_bit (enum rtx_code code, machine_mode mode,
+expand_absneg_bit (enum rtx_code code, scalar_float_mode mode,
 		   rtx op0, rtx target)
 {
   const struct real_format *fmt;
@@ -2696,6 +2696,7 @@  expand_unop (machine_mode mode, optab unoptab, rtx op0, rtx target,
 {
   enum mode_class mclass = GET_MODE_CLASS (mode);
   machine_mode wider_mode;
+  scalar_float_mode float_mode;
   rtx temp;
   rtx libfunc;
 
@@ -2886,9 +2887,9 @@  expand_unop (machine_mode mode, optab unoptab, rtx op0, rtx target,
   if (optab_to_code (unoptab) == NEG)
     {
       /* Try negating floating point values by flipping the sign bit.  */
-      if (SCALAR_FLOAT_MODE_P (mode))
+      if (is_a <scalar_float_mode> (mode, &float_mode))
 	{
-	  temp = expand_absneg_bit (NEG, mode, op0, target);
+	  temp = expand_absneg_bit (NEG, float_mode, op0, target);
 	  if (temp)
 	    return temp;
 	}
@@ -3085,9 +3086,10 @@  expand_abs_nojump (machine_mode mode, rtx op0, rtx target,
     return temp;
 
   /* For floating point modes, try clearing the sign bit.  */
-  if (SCALAR_FLOAT_MODE_P (mode))
+  scalar_float_mode float_mode;
+  if (is_a <scalar_float_mode> (mode, &float_mode))
     {
-      temp = expand_absneg_bit (ABS, mode, op0, target);
+      temp = expand_absneg_bit (ABS, float_mode, op0, target);
       if (temp)
 	return temp;
     }
@@ -3241,7 +3243,7 @@  expand_one_cmpl_abs_nojump (machine_mode mode, rtx op0, rtx target)
    and not playing with subregs so much, will help the register allocator.  */
 
 static rtx
-expand_copysign_absneg (machine_mode mode, rtx op0, rtx op1, rtx target,
+expand_copysign_absneg (scalar_float_mode mode, rtx op0, rtx op1, rtx target,
 		        int bitpos, bool op0_is_abs)
 {
   machine_mode imode;
@@ -3325,7 +3327,7 @@  expand_copysign_absneg (machine_mode mode, rtx op0, rtx op1, rtx target,
    is true if op0 is known to have its sign bit clear.  */
 
 static rtx
-expand_copysign_bit (machine_mode mode, rtx op0, rtx op1, rtx target,
+expand_copysign_bit (scalar_float_mode mode, rtx op0, rtx op1, rtx target,
 		     int bitpos, bool op0_is_abs)
 {
   machine_mode imode;
@@ -3423,12 +3425,12 @@  expand_copysign_bit (machine_mode mode, rtx op0, rtx op1, rtx target,
 rtx
 expand_copysign (rtx op0, rtx op1, rtx target)
 {
-  machine_mode mode = GET_MODE (op0);
+  scalar_float_mode mode;
   const struct real_format *fmt;
   bool op0_is_abs;
   rtx temp;
 
-  gcc_assert (SCALAR_FLOAT_MODE_P (mode));
+  mode = as_a <scalar_float_mode> (GET_MODE (op0));
   gcc_assert (GET_MODE (op1) == mode);
 
   /* First try to do it with a special instruction.  */
diff --git a/gcc/real.h b/gcc/real.h
index 59af580..e861b03 100644
--- a/gcc/real.h
+++ b/gcc/real.h
@@ -183,8 +183,7 @@  extern const struct real_format *
 			: (gcc_unreachable (), 0)])
 
 #define FLOAT_MODE_FORMAT(MODE) \
-  (REAL_MODE_FORMAT (SCALAR_FLOAT_MODE_P (MODE)? (MODE) \
-					       : GET_MODE_INNER (MODE)))
+  (REAL_MODE_FORMAT (as_a <scalar_float_mode> (GET_MODE_INNER (MODE))))
 
 /* The following macro determines whether the floating point format is
    composite, i.e. may contain non-consecutive mantissa bits, in which
@@ -212,7 +211,7 @@  class format_helper
 {
 public:
   format_helper (const real_format *format) : m_format (format) {}
-  format_helper (machine_mode m);
+  template<typename T> format_helper (const T &);
   const real_format *operator-> () const { return m_format; }
   operator const real_format *() const { return m_format; }
 
@@ -222,7 +221,8 @@  private:
   const real_format *m_format;
 };
 
-inline format_helper::format_helper (machine_mode m)
+template<typename T>
+inline format_helper::format_helper (const T &m)
   : m_format (m == VOIDmode ? 0 : REAL_MODE_FORMAT (m))
 {}
 
diff --git a/gcc/simplify-rtx.c b/gcc/simplify-rtx.c
index 0251bd4..907c1e7 100644
--- a/gcc/simplify-rtx.c
+++ b/gcc/simplify-rtx.c
@@ -5816,9 +5816,11 @@  simplify_immed_subreg (machine_mode outermode, rtx op,
 	    {
 	      /* This is big enough for anything on the platform.  */
 	      long tmp[MAX_BITSIZE_MODE_ANY_MODE / 32];
-	      int bitsize = GET_MODE_BITSIZE (GET_MODE (el));
+	      scalar_float_mode el_mode;
+
+	      el_mode = as_a <scalar_float_mode> (GET_MODE (el));
+	      int bitsize = GET_MODE_BITSIZE (el_mode);
 
-	      gcc_assert (SCALAR_FLOAT_MODE_P (GET_MODE (el)));
 	      gcc_assert (bitsize <= elem_bitsize);
 	      gcc_assert (bitsize % value_bit == 0);