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..2f83c9981e1 100644
--- a/gcc/config/i386/i386-c.cc
+++ b/gcc/config/i386/i386-c.cc
@@ -817,6 +817,21 @@ 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 (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..633a0f41e60 100644
--- a/gcc/config/i386/i386.cc
+++ b/gcc/config/i386/i386.cc
@@ -2651,7 +2651,8 @@ 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 +2662,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 +4029,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 +22479,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 +22495,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 +22825,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;
+      if (!float16_type_node)
+	float16_type_node = ix86_float16_type_node;
+      if (!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 +24280,86 @@ 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)
+{
+  machine_mode from_mode = element_mode (fromtype);
+  machine_mode to_mode = element_mode (totype);
+
+  if (!TARGET_SSE2 && from_mode != to_mode)
+    {
+      /* Do no allow conversions to/from BFmode/HFmode scalar types
+	 when TARGET_SSE2 is not available.  */
+      if (from_mode == BFmode)
+	return N_("invalid conversion from type %<__bf16%> "
+		  "without option %<-msse2%>");
+      if (from_mode == HFmode)
+	return N_("invalid conversion from type %<_Float16%> "
+		  "without option %<-msse2%>");
+      if (to_mode == BFmode)
+	return N_("invalid conversion to type %<__bf16%> "
+		  "without option %<-msse2%>");
+      if (to_mode == HFmode)
+	return N_("invalid conversion to type %<_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)
+{
+  machine_mode mmode = element_mode (type);
+  /* Reject all single-operand operations on BFmode/HFmode except for &
+     when TARGET_SSE2 is not available.  */
+  if (!TARGET_SSE2 && op != ADDR_EXPR)
+    {
+      if (mmode == BFmode)
+	return N_("operation not permitted on type %<__bf16%> "
+		  "without option %<-msse2%>");
+      if (mmode == HFmode)
+	return N_("operation not permitted on type %<_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)
+{
+  machine_mode type1_mode = element_mode (type1);
+  machine_mode type2_mode = element_mode (type2);
+  /* Reject all 2-operand operations on BFmode or HFmode
+     when TARGET_SSE2 is not available.  */
+  if (!TARGET_SSE2)
+    {
+      if (type1_mode == BFmode || type2_mode == BFmode)
+	return N_("operation not permitted on type %<__bf16%> "
+		  "without option %<-msse2%>");
+
+      if (type1_mode == HFmode || type2_mode == HFmode)
+	return N_("operation not permitted on type %<_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 +25349,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/pr109504.c b/gcc/testsuite/gcc.target/i386/pr109504.c
new file mode 100644
index 00000000000..fe5bcda10ad
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr109504.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;
+}
diff --git a/libgcc/config/i386/t-softfp b/libgcc/config/i386/t-softfp
index 69d0f819822..80d1fac121b 100644
--- a/libgcc/config/i386/t-softfp
+++ b/libgcc/config/i386/t-softfp
@@ -31,3 +31,10 @@ CFLAGS-trunchfbf2.c += -msse2
 CFLAGS-eqhf2.c += -msse2
 CFLAGS-_divhc3.c += -msse2
 CFLAGS-_mulhc3.c += -msse2
+
+CFLAGS-_hf_to_sd.c += -msse2
+CFLAGS-_hf_to_dd.c += -msse2
+CFLAGS-_hf_to_td.c += -msse2
+CFLAGS-_sd_to_hf.c += -msse2
+CFLAGS-_dd_to_hf.c += -msse2
+CFLAGS-_td_to_hf.c += -msse2
