[i386] Support type _Float16/__bf16 independent of SSE2.

Message ID 20230419071551.3478647-1-hongtao.liu@intel.com
State Accepted
Headers
Series [i386] Support type _Float16/__bf16 independent of SSE2. |

Checks

Context Check Description
snail/gcc-patch-check success Github commit url

Commit Message

liuhongt April 19, 2023, 7:15 a.m. UTC
  -----Jakub's comments----------
That said, these fundamental types whose presence/absence depends on ISA flags
are quite problematic IMHO, as they are incompatible with the target
attribute/pragmas. Whether they are available or not available depends on
whether in this case SSE2 is enabled during compiler initialization (aka after
parsing command line options) and then they are available or unavailable to
everything else based on that.
-----comments end----------

Enable _Float16 and __bf16 all the time but issue errors when the
types are used in conversion, unary operation, binary operation,
parameter passing or value return when TARGET_SSE2 is not available.

Also undef macros which are used by libgcc/libstdc++ to check the
backend support of the _Float16/__bf16 types when TARGET_SSE2 is not
available.

Bootstrapped and regtested  on x86_64-pc-linux-gnu{-m32,}
Also successfully cross-build targte i686-linux-gnu.
Ok for trunk?

gcc/ChangeLog:

	PR target/109054
	* config/i386/i386-builtins.cc
	(ix86_register_float16_builtin_type): Remove TARGET_SSE2.
	(ix86_register_bf16_builtin_type): Ditto.
	* config/i386/i386-c.cc (ix86_target_macros): When TARGET_SSE2
	isn't available, undef the macros which are used to check the
	backend support of the _Float16/__bf16 types when building
	libstdc++ and libgcc.
	* config/i386/i386.cc (construct_container): Issue errors for
	HFmode/BFmode when TARGET_SSE2 is not available.
	(function_value_32): Ditto.
	(ix86_scalar_mode_supported_p): Remove TARGET_SSE2 for HFmode/BFmode.
	(ix86_libgcc_floating_mode_supported_p): Ditto.
	(ix86_emit_support_tinfos): Adjust codes.
	(ix86_invalid_conversion): New function.
	(ix86_invalid_unary_op): Ditto.
	(ix86_invalid_binary_op): Ditto.
	(TARGET_INVALID_CONVERSION): Defined.
	(TARGET_INVALID_UNARY_OP): Defined.
	(TARGET_INVALID_BINARY_OP): Defined.
	* config/i386/immintrin.h: Remove #ifdef __SSE2__ for fp16/bf16
	related instrinsics header filers.
	* config/i386/i386.h (VALID_SSE2_TYPE_MODE): New macro.

gcc/testsuite/ChangeLog:

	* gcc.target/i386/pr109054.c: New test.
	* gcc.target/i386/sse2-bfloat16-1.c: Adjust error info.
	* gcc.target/i386/sse2-float16-1.c: Ditto.
	* gcc.target/i386/sse2-float16-4.c: New test.
	* gcc.target/i386/sse2-float16-5.c: New test.
	* g++.target/i386/float16-1.C: Adjust error info.
---
 gcc/config/i386/i386-builtins.cc              |   4 +-
 gcc/config/i386/i386-c.cc                     |  37 ++++++
 gcc/config/i386/i386.cc                       | 117 ++++++++++++++++--
 gcc/config/i386/i386.h                        |   4 +
 gcc/config/i386/immintrin.h                   |   4 -
 gcc/testsuite/g++.target/i386/float16-1.C     |   8 +-
 gcc/testsuite/gcc.target/i386/pr109054.c      |   6 +
 .../gcc.target/i386/sse2-bfloat16-1.c         |   8 +-
 .../gcc.target/i386/sse2-float16-1.c          |   8 +-
 .../gcc.target/i386/sse2-float16-4.c          |  25 ++++
 .../gcc.target/i386/sse2-float16-5.c          |  24 ++++
 11 files changed, 217 insertions(+), 28 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/i386/pr109054.c
 create mode 100644 gcc/testsuite/gcc.target/i386/sse2-float16-4.c
 create mode 100644 gcc/testsuite/gcc.target/i386/sse2-float16-5.c
  

Comments

Mike Stump April 19, 2023, 4:43 p.m. UTC | #1
LLM, machine learning and AI likes coding with data types that are weird, float16, bf16, 8 bit float and 4 bit floats. Longer term, would be nice to natively support these everywhere. Would be nice to trial run them in the compiler, sort it all out, so that the implementation experience can drive language adoption. A little speculative and a little narrow focus for the field, but, AI isn't going away in the next 20 years I don't think. Anyway, I like the direction.

On Apr 19, 2023, at 12:15 AM, liuhongt via Gcc-patches <gcc-patches@gcc.gnu.org> wrote:
> That said, these fundamental types whose presence/absence depends on ISA flags
> are quite problematic IMHO, as they are incompatible with the target
> attribute/pragmas. Whether they are available or not available depends on
> whether in this case SSE2 is enabled during compiler initialization (aka after
> parsing command line options) and then they are available or unavailable to
> everything else based on that.
> -----comments end----------
> 
> Enable _Float16 and __bf16 all the time but issue errors when the
  
Jakub Jelinek April 20, 2023, 12:18 p.m. UTC | #2
On Wed, Apr 19, 2023 at 03:15:51PM +0800, liuhongt wrote:
ChangeLog nits have been already reported earlier.

> --- a/gcc/config/i386/i386-c.cc
> +++ b/gcc/config/i386/i386-c.cc
> @@ -817,6 +817,43 @@ ix86_target_macros (void)
>    if (!TARGET_80387)
>      cpp_define (parse_in, "_SOFT_FLOAT");
>  
> +  /* HFmode/BFmode is supported without depending any isa
> +     in scalar_mode_supported_p and libgcc_floating_mode_supported_p,
> +     but according to psABI, they're really supported w/ SSE2 and above.
> +     Since libstdc++ uses __STDCPP_FLOAT16_T__ and __STDCPP_BFLOAT16_T__
> +     for backend support of the types, undef the macros to avoid
> +     build failure, see PR109504.  */
> +  if (!TARGET_SSE2)
> +    {
> +      if (c_dialect_cxx ()
> +	  && cxx_dialect > cxx20)

Formatting, both conditions are short, so just put them on one line.

> +	{
> +	  cpp_undef (parse_in, "__STDCPP_FLOAT16_T__");
> +	  cpp_undef (parse_in, "__STDCPP_BFLOAT16_T__");
> +	}

But for the C++23 macros, more importantly I think we really should
also in ix86_target_macros_internal add
  if (c_dialect_cxx ()
      && cxx_dialect > cxx20
      && (isa_flag & OPTION_MASK_ISA_SSE2))
    {
      def_or_undef (parse_in, "__STDCPP_FLOAT16_T__");
      def_or_undef (parse_in, "__STDCPP_BFLOAT16_T__");
    }
plus associated libstdc++ changes.  It can be done incrementally though.

> +
> +      if (flag_building_libgcc)
> +	{
> +	  /* libbid uses __LIBGCC_HAS_HF_MODE__ and __LIBGCC_HAS_BF_MODE__
> +	     to check backend support of _Float16 and __bf16 type.  */

That is actually the case only for HFmode, but not for BFmode right now.
So, we need further work.  One is to add the BFmode support in there,
and another one is make sure the _Float16 <-> _Decimal* and __bf16 <->
_Decimal* conversions are compiled in also if not -msse2 by default.
One way to do that is wrap the HF and BF mode related functions on x86
#ifndef __SSE2__ into the pragmas like intrin headers use (but then
perhaps we don't need to undef this stuff here), another is not provide
the hf/bf support in that case from the TUs where they are provided now,
but from a different one which would be compiled with -msse2.

> +	  cpp_undef (parse_in, "__LIBGCC_HAS_HF_MODE__");
> +	  cpp_undef (parse_in, "__LIBGCC_HF_FUNC_EXT__");
> +	  cpp_undef (parse_in, "__LIBGCC_HF_MANT_DIG__");
> +	  cpp_undef (parse_in, "__LIBGCC_HF_EXCESS_PRECISION__");
> +	  cpp_undef (parse_in, "__LIBGCC_HF_EPSILON__");
> +	  cpp_undef (parse_in, "__LIBGCC_HF_MAX__");
> +	  cpp_undef (parse_in, "__LIBGCC_HF_MIN__");
> +
> +	  cpp_undef (parse_in, "__LIBGCC_HAS_BF_MODE__");
> +	  cpp_undef (parse_in, "__LIBGCC_BF_FUNC_EXT__");
> +	  cpp_undef (parse_in, "__LIBGCC_BF_MANT_DIG__");
> +	  cpp_undef (parse_in, "__LIBGCC_BF_EXCESS_PRECISION__");
> +	  cpp_undef (parse_in, "__LIBGCC_BF_EPSILON__");
> +	  cpp_undef (parse_in, "__LIBGCC_BF_MAX__");
> +	  cpp_undef (parse_in, "__LIBGCC_BF_MIN__");
> +	}
> +    }
> +

> --- a/gcc/config/i386/i386.cc
> +++ b/gcc/config/i386/i386.cc
> @@ -2651,7 +2651,10 @@ construct_container (machine_mode mode, machine_mode orig_mode,
>  
>    /* We allowed the user to turn off SSE for kernel mode.  Don't crash if
>       some less clueful developer tries to use floating-point anyway.  */
> -  if (needed_sseregs && !TARGET_SSE)
> +  if (needed_sseregs
> +      && (!TARGET_SSE
> +	  || (VALID_SSE2_TYPE_MODE (mode)
> +	      && !TARGET_SSE2)))

Formatting, no need to split this up that much.
  if (needed_sseregs
      && (!TARGET_SSE
	  || (VALID_SSE2_TYPE_MODE (mode) && !TARGET_SSE2)))
or even better
  if (needed_sseregs
      && (!TARGET_SSE || (VALID_SSE2_TYPE_MODE (mode) && !TARGET_SSE2)))
will do it.

> @@ -22805,9 +22827,10 @@ ix86_emit_support_tinfos (emit_support_tinfos_callback callback)
>  
>    if (!TARGET_SSE2)
>      {
> -      gcc_checking_assert (!float16_type_node && !bfloat16_type_node);
> -      float16_type_node = ix86_float16_type_node;
> -      bfloat16_type_node = ix86_bf16_type_node;
> +      float16_type_node
> +	= float16_type_node ? float16_type_node : ix86_float16_type_node;
> +      bfloat16_type_node
> +	= bfloat16_type_node ? bfloat16_type_node : ix86_bf16_type_node;
>        callback (float16_type_node);
>        callback (bfloat16_type_node);

Instead of this, just use
      if (!float16_type_node)
	{
	  float16_type_node = ix86_float16_type_node;
	  callback (float16_type_node);
	  float16_type_node = NULL_TREE;
	}
      if (!bfloat16_type_node)
	{
	  bfloat16_type_node = ix86_bf16_type_node;
	  callback (bfloat16_type_node);
	  bfloat16_type_node = NULL_TREE;
	}
?
> +/* Return the diagnostic message string if conversion from FROMTYPE to
> +   TOTYPE is not allowed, NULL otherwise.  */
> +
> +static const char *
> +ix86_invalid_conversion (const_tree fromtype, const_tree totype)
> +{
> +  if (element_mode (fromtype) != element_mode (totype))
> +    {
> +      /* Do no allow conversions to/from BFmode/HFmode scalar types
> +	 when TARGET_SSE2 is not available.  */
> +      if ((TYPE_MODE (fromtype) == BFmode
> +	   || TYPE_MODE (fromtype) == HFmode)
> +	  && !TARGET_SSE2)

First of all, not really sure if this should be purely about scalar
modes, not also complex and vector modes involving those inner modes.
Because complex or vector modes with BF/HF elements will be without
TARGET_SSE2 for sure lowered into scalar code and that can't be handled
either.
So if (!TARGET_SSE2 && GET_MODE_INNER (TYPE_MODE (fromtype)) == BFmode)
or even better
if (!TARGET_SSE2 && element_mode (fromtype) == BFmode)
?
Or even better remember the 2 modes above into machine_mode temporaries
and just use those in the != comparison and for the checks?

Also, I think it is weird to tell user %<__bf16%> or %<_Float16%> when
we know which one it is.  Just return separate messages?


> +	return N_("invalid conversion from type %<__bf16%> "
> +		  "or %<_Float16%> without option %<-msse2%>");
> +
> +      if ((TYPE_MODE (totype) == BFmode
> +	   || TYPE_MODE (totype) == HFmode)
> +	  && !TARGET_SSE2)
> +	return N_("invalid conversion to type %<__bf16%> "
> +		  "or %<_Float16%> without option %<-msse2%>");

Ditto.
> +    }
> +
> +  /* Conversion allowed.  */
> +  return NULL;
> +}
> +
> +/* Return the diagnostic message string if the unary operation OP is
> +   not permitted on TYPE, NULL otherwise.  */
> +
> +static const char *
> +ix86_invalid_unary_op (int op, const_tree type)
> +{
> +  /* Reject all single-operand operations on BFmode/HFmode except for &
> +     when TARGET_SSE2 is not available.  */
> +  if ((element_mode (type) == BFmode || element_mode (type) == HFmode)
> +      && !TARGET_SSE2 && op != ADDR_EXPR)
> +    return N_("operation not permitted on type %<__bf16%> "
> +	      "or %<_Float16%> without option %<-msse2%>");

Similarly.  Also, check !TARGET_SSE2 first as inexpensive one.
> +
> +  /* Operation allowed.  */
> +  return NULL;
> +}
> +
> +/* Return the diagnostic message string if the binary operation OP is
> +   not permitted on TYPE1 and TYPE2, NULL otherwise.  */
> +
> +static const char *
> +ix86_invalid_binary_op (int op ATTRIBUTE_UNUSED, const_tree type1,
> +			   const_tree type2)
> +{
> +  /* Reject all 2-operand operations on BFmode or HFmode
> +     when TARGET_SSE2 is not available.  */
> +  if ((element_mode (type1) == BFmode
> +       || element_mode (type2) == BFmode
> +       || element_mode (type1) == HFmode
> +       || element_mode (type2) == HFmode)
> +      && !TARGET_SSE2)
> +    return N_("operation not permitted on type %<__bf16%> "
> +	      "or %<_Float16%> without option %<-msse2%>");

Similarly.

	Jakub
  

Patch

diff --git a/gcc/config/i386/i386-builtins.cc b/gcc/config/i386/i386-builtins.cc
index fc0c82b156e..1cdabfd3a0a 100644
--- a/gcc/config/i386/i386-builtins.cc
+++ b/gcc/config/i386/i386-builtins.cc
@@ -1367,7 +1367,7 @@  ix86_register_float16_builtin_type (void)
   else
     ix86_float16_type_node = float16_type_node;
 
-  if (!maybe_get_identifier ("_Float16") && TARGET_SSE2)
+  if (!maybe_get_identifier ("_Float16"))
     lang_hooks.types.register_builtin_type (ix86_float16_type_node,
 					    "_Float16");
 }
@@ -1385,7 +1385,7 @@  ix86_register_bf16_builtin_type (void)
   else
     ix86_bf16_type_node = bfloat16_type_node;
 
-  if (!maybe_get_identifier ("__bf16") && TARGET_SSE2)
+  if (!maybe_get_identifier ("__bf16"))
     lang_hooks.types.register_builtin_type (ix86_bf16_type_node, "__bf16");
 }
 
diff --git a/gcc/config/i386/i386-c.cc b/gcc/config/i386/i386-c.cc
index e7bd7cc706c..eb77d0af226 100644
--- a/gcc/config/i386/i386-c.cc
+++ b/gcc/config/i386/i386-c.cc
@@ -817,6 +817,43 @@  ix86_target_macros (void)
   if (!TARGET_80387)
     cpp_define (parse_in, "_SOFT_FLOAT");
 
+  /* HFmode/BFmode is supported without depending any isa
+     in scalar_mode_supported_p and libgcc_floating_mode_supported_p,
+     but according to psABI, they're really supported w/ SSE2 and above.
+     Since libstdc++ uses __STDCPP_FLOAT16_T__ and __STDCPP_BFLOAT16_T__
+     for backend support of the types, undef the macros to avoid
+     build failure, see PR109504.  */
+  if (!TARGET_SSE2)
+    {
+      if (c_dialect_cxx ()
+	  && cxx_dialect > cxx20)
+	{
+	  cpp_undef (parse_in, "__STDCPP_FLOAT16_T__");
+	  cpp_undef (parse_in, "__STDCPP_BFLOAT16_T__");
+	}
+
+      if (flag_building_libgcc)
+	{
+	  /* libbid uses __LIBGCC_HAS_HF_MODE__ and __LIBGCC_HAS_BF_MODE__
+	     to check backend support of _Float16 and __bf16 type.  */
+	  cpp_undef (parse_in, "__LIBGCC_HAS_HF_MODE__");
+	  cpp_undef (parse_in, "__LIBGCC_HF_FUNC_EXT__");
+	  cpp_undef (parse_in, "__LIBGCC_HF_MANT_DIG__");
+	  cpp_undef (parse_in, "__LIBGCC_HF_EXCESS_PRECISION__");
+	  cpp_undef (parse_in, "__LIBGCC_HF_EPSILON__");
+	  cpp_undef (parse_in, "__LIBGCC_HF_MAX__");
+	  cpp_undef (parse_in, "__LIBGCC_HF_MIN__");
+
+	  cpp_undef (parse_in, "__LIBGCC_HAS_BF_MODE__");
+	  cpp_undef (parse_in, "__LIBGCC_BF_FUNC_EXT__");
+	  cpp_undef (parse_in, "__LIBGCC_BF_MANT_DIG__");
+	  cpp_undef (parse_in, "__LIBGCC_BF_EXCESS_PRECISION__");
+	  cpp_undef (parse_in, "__LIBGCC_BF_EPSILON__");
+	  cpp_undef (parse_in, "__LIBGCC_BF_MAX__");
+	  cpp_undef (parse_in, "__LIBGCC_BF_MIN__");
+	}
+    }
+
   if (TARGET_LONG_DOUBLE_64)
     cpp_define (parse_in, "__LONG_DOUBLE_64__");
 
diff --git a/gcc/config/i386/i386.cc b/gcc/config/i386/i386.cc
index fbd33a6bfd1..f31929b8752 100644
--- a/gcc/config/i386/i386.cc
+++ b/gcc/config/i386/i386.cc
@@ -2651,7 +2651,10 @@  construct_container (machine_mode mode, machine_mode orig_mode,
 
   /* We allowed the user to turn off SSE for kernel mode.  Don't crash if
      some less clueful developer tries to use floating-point anyway.  */
-  if (needed_sseregs && !TARGET_SSE)
+  if (needed_sseregs
+      && (!TARGET_SSE
+	  || (VALID_SSE2_TYPE_MODE (mode)
+	      && !TARGET_SSE2)))
     {
       /* Return early if we shouldn't raise an error for invalid
 	 calls.  */
@@ -2661,13 +2664,19 @@  construct_container (machine_mode mode, machine_mode orig_mode,
 	{
 	  if (!issued_sse_ret_error)
 	    {
-	      error ("SSE register return with SSE disabled");
+	      if (VALID_SSE2_TYPE_MODE (mode))
+		error ("SSE register return with SSE2 disabled");
+	      else
+		error ("SSE register return with SSE disabled");
 	      issued_sse_ret_error = true;
 	    }
 	}
       else if (!issued_sse_arg_error)
 	{
-	  error ("SSE register argument with SSE disabled");
+	  if (VALID_SSE2_TYPE_MODE (mode))
+	    error ("SSE register argument with SSE2 disabled");
+	  else
+	    error ("SSE register argument with SSE disabled");
 	  issued_sse_arg_error = true;
 	}
       return NULL;
@@ -4022,13 +4031,26 @@  function_value_32 (machine_mode orig_mode, machine_mode mode,
 
   /* Return __bf16/ _Float16/_Complex _Foat16 by sse register.  */
   if (mode == HFmode || mode == BFmode)
-    regno = FIRST_SSE_REG;
+    {
+      if (!TARGET_SSE2)
+	{
+	  error ("SSE register return with SSE2 disabled");
+	  regno = AX_REG;
+	}
+      else
+	regno = FIRST_SSE_REG;
+    }
+
   if (mode == HCmode)
     {
+      if (!TARGET_SSE2)
+	error ("SSE register return with SSE2 disabled");
+
       rtx ret = gen_rtx_PARALLEL (mode, rtvec_alloc(1));
       XVECEXP (ret, 0, 0)
 	= gen_rtx_EXPR_LIST (VOIDmode,
-			     gen_rtx_REG (SImode, FIRST_SSE_REG),
+			     gen_rtx_REG (SImode,
+					  TARGET_SSE2 ? FIRST_SSE_REG : AX_REG),
 			     GEN_INT (0));
       return ret;
     }
@@ -22459,7 +22481,7 @@  ix86_scalar_mode_supported_p (scalar_mode mode)
     return default_decimal_float_supported_p ();
   else if (mode == TFmode)
     return true;
-  else if ((mode == HFmode || mode == BFmode) && TARGET_SSE2)
+  else if (mode == HFmode || mode == BFmode)
     return true;
   else
     return default_scalar_mode_supported_p (mode);
@@ -22475,7 +22497,7 @@  ix86_libgcc_floating_mode_supported_p (scalar_float_mode mode)
      be defined by the C front-end for AVX512FP16 intrinsics.  We will
      issue an error in ix86_expand_move for HFmode if AVX512FP16 isn't
      enabled.  */
-  return (((mode == HFmode || mode == BFmode) && TARGET_SSE2)
+  return ((mode == HFmode || mode == BFmode)
 	  ? true
 	  : default_libgcc_floating_mode_supported_p (mode));
 }
@@ -22805,9 +22827,10 @@  ix86_emit_support_tinfos (emit_support_tinfos_callback callback)
 
   if (!TARGET_SSE2)
     {
-      gcc_checking_assert (!float16_type_node && !bfloat16_type_node);
-      float16_type_node = ix86_float16_type_node;
-      bfloat16_type_node = ix86_bf16_type_node;
+      float16_type_node
+	= float16_type_node ? float16_type_node : ix86_float16_type_node;
+      bfloat16_type_node
+	= bfloat16_type_node ? bfloat16_type_node : ix86_bf16_type_node;
       callback (float16_type_node);
       callback (bfloat16_type_node);
       float16_type_node = NULL_TREE;
@@ -24259,6 +24282,71 @@  ix86_init_libfuncs (void)
 #endif
 }
 
+/* Return the diagnostic message string if conversion from FROMTYPE to
+   TOTYPE is not allowed, NULL otherwise.  */
+
+static const char *
+ix86_invalid_conversion (const_tree fromtype, const_tree totype)
+{
+  if (element_mode (fromtype) != element_mode (totype))
+    {
+      /* Do no allow conversions to/from BFmode/HFmode scalar types
+	 when TARGET_SSE2 is not available.  */
+      if ((TYPE_MODE (fromtype) == BFmode
+	   || TYPE_MODE (fromtype) == HFmode)
+	  && !TARGET_SSE2)
+	return N_("invalid conversion from type %<__bf16%> "
+		  "or %<_Float16%> without option %<-msse2%>");
+
+      if ((TYPE_MODE (totype) == BFmode
+	   || TYPE_MODE (totype) == HFmode)
+	  && !TARGET_SSE2)
+	return N_("invalid conversion to type %<__bf16%> "
+		  "or %<_Float16%> without option %<-msse2%>");
+    }
+
+  /* Conversion allowed.  */
+  return NULL;
+}
+
+/* Return the diagnostic message string if the unary operation OP is
+   not permitted on TYPE, NULL otherwise.  */
+
+static const char *
+ix86_invalid_unary_op (int op, const_tree type)
+{
+  /* Reject all single-operand operations on BFmode/HFmode except for &
+     when TARGET_SSE2 is not available.  */
+  if ((element_mode (type) == BFmode || element_mode (type) == HFmode)
+      && !TARGET_SSE2 && op != ADDR_EXPR)
+    return N_("operation not permitted on type %<__bf16%> "
+	      "or %<_Float16%> without option %<-msse2%>");
+
+  /* Operation allowed.  */
+  return NULL;
+}
+
+/* Return the diagnostic message string if the binary operation OP is
+   not permitted on TYPE1 and TYPE2, NULL otherwise.  */
+
+static const char *
+ix86_invalid_binary_op (int op ATTRIBUTE_UNUSED, const_tree type1,
+			   const_tree type2)
+{
+  /* Reject all 2-operand operations on BFmode or HFmode
+     when TARGET_SSE2 is not available.  */
+  if ((element_mode (type1) == BFmode
+       || element_mode (type2) == BFmode
+       || element_mode (type1) == HFmode
+       || element_mode (type2) == HFmode)
+      && !TARGET_SSE2)
+    return N_("operation not permitted on type %<__bf16%> "
+	      "or %<_Float16%> without option %<-msse2%>");
+
+  /* Operation allowed.  */
+  return NULL;
+}
+
 /* Set the value of FLT_EVAL_METHOD in float.h.  When using only the
    FPU, assume that the fpcw is set to extended precision; when using
    only SSE, rounding is correct; when using both SSE and the FPU,
@@ -25248,6 +25336,15 @@  ix86_libgcc_floating_mode_supported_p
 #undef TARGET_MEMTAG_TAG_SIZE
 #define TARGET_MEMTAG_TAG_SIZE ix86_memtag_tag_size
 
+#undef TARGET_INVALID_CONVERSION
+#define TARGET_INVALID_CONVERSION ix86_invalid_conversion
+
+#undef TARGET_INVALID_UNARY_OP
+#define TARGET_INVALID_UNARY_OP ix86_invalid_unary_op
+
+#undef TARGET_INVALID_BINARY_OP
+#define TARGET_INVALID_BINARY_OP ix86_invalid_binary_op
+
 static bool ix86_libc_has_fast_function (int fcode ATTRIBUTE_UNUSED)
 {
 #ifdef OPTION_GLIBC
diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h
index 1da6dce8e0b..7e839bc5c7e 100644
--- a/gcc/config/i386/i386.h
+++ b/gcc/config/i386/i386.h
@@ -1046,6 +1046,10 @@  extern const char *host_detect_local_cpu (int argc, const char **argv);
 #define VALID_AVX512FP16_REG_MODE(MODE)					\
   ((MODE) == V8HFmode || (MODE) == V16HFmode || (MODE) == V32HFmode)
 
+#define VALID_SSE2_TYPE_MODE(MODE)		\
+  ((MODE) == HFmode || (MODE) == BFmode		\
+   || (MODE) == HCmode || (MODE) == BCmode)
+
 #define VALID_SSE2_REG_MODE(MODE)					\
   ((MODE) == V16QImode || (MODE) == V8HImode || (MODE) == V2DFmode	\
    || (MODE) == V8HFmode || (MODE) == V4HFmode || (MODE) == V2HFmode	\
diff --git a/gcc/config/i386/immintrin.h b/gcc/config/i386/immintrin.h
index b220d871942..cc78df56940 100644
--- a/gcc/config/i386/immintrin.h
+++ b/gcc/config/i386/immintrin.h
@@ -98,11 +98,9 @@ 
 
 #include <avx512vp2intersectvlintrin.h>
 
-#ifdef __SSE2__
 #include <avx512fp16intrin.h>
 
 #include <avx512fp16vlintrin.h>
-#endif
 
 #include <shaintrin.h>
 
@@ -118,13 +116,11 @@ 
 
 #include <vpclmulqdqintrin.h>
 
-#ifdef __SSE2__
 #include <avx512bf16vlintrin.h>
 
 #include <avx512bf16intrin.h>
 
 #include <avxneconvertintrin.h>
-#endif
 
 #include <amxtileintrin.h>
 
diff --git a/gcc/testsuite/g++.target/i386/float16-1.C b/gcc/testsuite/g++.target/i386/float16-1.C
index f96b932b698..938852ee9ad 100644
--- a/gcc/testsuite/g++.target/i386/float16-1.C
+++ b/gcc/testsuite/g++.target/i386/float16-1.C
@@ -1,8 +1,8 @@ 
 /* { dg-do compile } */
 /* { dg-options "-O2 -mno-sse2" } */
 
-_Float16	/* { dg-error "expected unqualified-id before '_Float16'" } */
-foo (_Float16 x) 
+_Float16
+foo (_Float16 x)/* { dg-error "SSE register return with SSE2 disabled" } */ 
 {
-  return x;
-}		/* { dg-error "'_Float16' is not supported on this target" } */
+  return x;/* { dg-error "SSE register return with SSE2 disabled" "" { target ia32 } } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/pr109054.c b/gcc/testsuite/gcc.target/i386/pr109054.c
new file mode 100644
index 00000000000..fe5bcda10ad
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr109054.c
@@ -0,0 +1,6 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-sse" } */
+
+#pragma GCC target("sse4.1")
+#include <immintrin.h>
+int main(){return 0;}
diff --git a/gcc/testsuite/gcc.target/i386/sse2-bfloat16-1.c b/gcc/testsuite/gcc.target/i386/sse2-bfloat16-1.c
index 612d55be826..717055bc9ad 100644
--- a/gcc/testsuite/gcc.target/i386/sse2-bfloat16-1.c
+++ b/gcc/testsuite/gcc.target/i386/sse2-bfloat16-1.c
@@ -1,8 +1,8 @@ 
 /* { dg-do compile } */
 /* { dg-options "-O2 -mno-sse2" } */
 
-__bf16/* { dg-error "unknown type name '__bf16'" } */
-foo (__bf16 x) /* { dg-error "unknown type name '__bf16'" } */
-{
-  return x;
+__bf16
+foo (__bf16 x)  /* { dg-error "SSE register return with SSE2 disabled" } */
+{  /* { dg-error "SSE register return with SSE2 disabled" "" { target ia32 } } */
+  return x;  /* { dg-error "SSE register return with SSE2 disabled" "" { target ia32} } */
 }
diff --git a/gcc/testsuite/gcc.target/i386/sse2-float16-1.c b/gcc/testsuite/gcc.target/i386/sse2-float16-1.c
index 1b645eb499d..faf818df75f 100644
--- a/gcc/testsuite/gcc.target/i386/sse2-float16-1.c
+++ b/gcc/testsuite/gcc.target/i386/sse2-float16-1.c
@@ -1,8 +1,8 @@ 
 /* { dg-do compile } */
 /* { dg-options "-O2 -mno-sse2" } */
 
-_Float16/* { dg-error "is not supported on this target" } */
-foo (_Float16 x) /* { dg-error "is not supported on this target" } */
-{
-  return x;
+_Float16
+foo (_Float16 x) /* { dg-error "SSE register return with SSE2 disabled" } */
+{  /* { dg-error "SSE register return with SSE2 disabled" "" { target ia32 } } */
+  return x;  /* { dg-error "SSE register return with SSE2 disabled" "" { target ia32} } */
 }
diff --git a/gcc/testsuite/gcc.target/i386/sse2-float16-4.c b/gcc/testsuite/gcc.target/i386/sse2-float16-4.c
new file mode 100644
index 00000000000..64baf92ff56
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/sse2-float16-4.c
@@ -0,0 +1,25 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-sse2" } */
+
+_Float16 a;
+__bf16 c;
+_Complex _Float16 ac;
+
+void
+foo (_Float16* p)
+{
+  a = *p;
+}
+
+void
+foo1 (__bf16 *p)
+{
+  c = *p;
+}
+
+
+void
+foo2 (_Complex _Float16* p)
+{
+  ac = *p;
+}
diff --git a/gcc/testsuite/gcc.target/i386/sse2-float16-5.c b/gcc/testsuite/gcc.target/i386/sse2-float16-5.c
new file mode 100644
index 00000000000..c3ed23b8ab3
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/sse2-float16-5.c
@@ -0,0 +1,24 @@ 
+/* { dg-do compile { target ia32} } */
+/* { dg-options "-O2 -mno-sse2" } */
+
+_Float16 a;
+__bf16 c;
+_Complex ac;
+void
+foo (_Float16 p)
+{
+  a = p;
+}
+
+void
+foo1 (__bf16 p)
+{
+  c = p;
+}
+
+
+void
+foo2 (_Complex p)
+{
+  ac = p;
+}