[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#1057653: Fix #pragma GCC target for C++



Package: gcc-13
Version: 13.2.0-7

#pragma GCC target is useful to compile specific functions with SIMD
extensions, for instance. However, that feature was long broken for
C++ where the relevant macros were not defined on the first
preprocessing pass, thus preventing some target-specific enablement.
e.g. some library wrappers for SIMD extensions usually check for SIMD
ISA macros to define vector sizes and adequate functions. The missing
define would mean a missed optimization.

This patch series addresses this issue, and is a direct backport from
the GCC mainline. Since this is not a regression vs. previous versions
of GCC, I believe there is little chance to get them from r13 branch.
(this looks broken as far as gcc 4.4.x)

0001-preprocessor-c-Support-pragma-GCC-target-macros-PR87.patch
0002-preprocessor-Reinitialize-frontend-parser-after-load.patch
0003-c-Handle-pragma-GCC-target-optimize-early.patch

Patch1 is the core fix, Patch2 is a colateral patch that addresses
ICEs from the first one. Relevant upstream issue numbers are PR
c++/41201, PR c++/48026 and PR preprocessor/87299.

IMHO, this is a nice-to-have series for testing/trixie (and Ubuntu
24.04-LTS). Note: I only regtested on bullseye and bookworm. Thanks!
From c9e56f9ea8c56aa321ce47ca2e677b0dd98b3d21 Mon Sep 17 00:00:00 2001
From: Gwenole Beauchesne <gb.devel@gmail.com>
Date: Sat, 25 Nov 2023 11:43:21 +0100
Subject: [PATCH 3/3] c++: Handle '#pragma GCC target optimize' early.

Handle '#pragma GCC optimize' earlier as the __OPTIMIZE__ macro may need
to be defined as well for certain usages. Add additional tests for the
'#pragma GCC target' case with auto-vectorization enabled and multiple
combinations of namespaces and/or class member functions.

Add more complete tests for PR c++/41201 after git commit 8697d3a1.

gcc/testsuite/ChangeLog:

        PR c++/41201
        * g++.target/i386/vect-pragma-target-1.C: New test.
        * g++.target/i386/vect-pragma-target-2.C: New test.
        * gcc.target/i386/vect-pragma-target-1.c: New test.
        * gcc.target/i386/vect-pragma-target-1.c: New test.

Signed-off-by: Gwenole Beauchesne <gb.devel@gmail.com>
---
 gcc/c-family/c-pragma.cc                      |   4 +-
 .../g++.target/i386/vect-pragma-target-1.C    |   6 +
 .../g++.target/i386/vect-pragma-target-2.C    |   6 +
 .../gcc.target/i386/vect-pragma-target-1.c    | 194 ++++++++++++++++++
 .../gcc.target/i386/vect-pragma-target-2.c    |   7 +
 5 files changed, 216 insertions(+), 1 deletion(-)
 create mode 100644 gcc/testsuite/g++.target/i386/vect-pragma-target-1.C
 create mode 100644 gcc/testsuite/g++.target/i386/vect-pragma-target-2.C
 create mode 100644 gcc/testsuite/gcc.target/i386/vect-pragma-target-1.c
 create mode 100644 gcc/testsuite/gcc.target/i386/vect-pragma-target-2.c

diff --git a/gcc/c-family/c-pragma.cc b/gcc/c-family/c-pragma.cc
index 8931b5e9cbb..915fbaf3676 100644
--- a/gcc/c-family/c-pragma.cc
+++ b/gcc/c-family/c-pragma.cc
@@ -1848,7 +1848,9 @@ init_pragma (void)
   c_register_pragma_with_early_handler ("GCC", "target",
 					handle_pragma_target,
 					handle_pragma_target);
-  c_register_pragma ("GCC", "optimize", handle_pragma_optimize);
+  c_register_pragma_with_early_handler ("GCC", "optimize",
+					handle_pragma_optimize,
+					handle_pragma_optimize);
   c_register_pragma_with_early_handler ("GCC", "push_options",
 					handle_pragma_push_options,
 					handle_pragma_push_options);
diff --git a/gcc/testsuite/g++.target/i386/vect-pragma-target-1.C b/gcc/testsuite/g++.target/i386/vect-pragma-target-1.C
new file mode 100644
index 00000000000..2f360cf50e1
--- /dev/null
+++ b/gcc/testsuite/g++.target/i386/vect-pragma-target-1.C
@@ -0,0 +1,6 @@
+/* { dg-do compile { target { i?86-*-* x86_64-*-* } } }                 */
+/* { dg-options "-O0" }                                                 */
+/* { dg-final { scan-assembler-times "paddd.+xmm\[0-9]+"        1 } }   */
+/* { dg-final { scan-assembler-times "vfmadd132ps.+ymm\[0-9]+"  1 } }   */
+/* { dg-final { scan-assembler-times "vpaddw.+zmm\[0-9]+"       1 } }   */
+#include "../../gcc.target/i386/vect-pragma-target-1.c"
diff --git a/gcc/testsuite/g++.target/i386/vect-pragma-target-2.C b/gcc/testsuite/g++.target/i386/vect-pragma-target-2.C
new file mode 100644
index 00000000000..b85bc93d845
--- /dev/null
+++ b/gcc/testsuite/g++.target/i386/vect-pragma-target-2.C
@@ -0,0 +1,6 @@
+/* { dg-do compile { target { i?86-*-* x86_64-*-* } } }                 */
+/* { dg-options "-O0" }                                                 */
+/* { dg-final { scan-assembler-times "paddd.+xmm\[0-9]+"        1 } }   */
+/* { dg-final { scan-assembler-times "vfmadd132ps.+ymm\[0-9]+"  1 } }   */
+/* { dg-final { scan-assembler-times "vpaddw.+zmm\[0-9]+"       1 } }   */
+#include "../../gcc.target/i386/vect-pragma-target-2.c"
diff --git a/gcc/testsuite/gcc.target/i386/vect-pragma-target-1.c b/gcc/testsuite/gcc.target/i386/vect-pragma-target-1.c
new file mode 100644
index 00000000000..f5e71e453ec
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/vect-pragma-target-1.c
@@ -0,0 +1,194 @@
+/* { dg-do compile { target { i?86-*-* x86_64-*-* } } }                 */
+/* { dg-options "-O0" }                                                 */
+/* { dg-final { scan-assembler-times "paddd.+xmm\[0-9]+"        1 } }   */
+/* { dg-final { scan-assembler-times "vfmadd132ps.+ymm\[0-9]+"  1 } }   */
+/* { dg-final { scan-assembler-times "vpaddw.+zmm\[0-9]+"       1 } }   */
+#ifndef CHECK_DEFINES
+#define CHECK_DEFINES 0
+#endif
+
+#define N 1024
+
+/* Optimization flags and tree vectorizer shall be disabled at this point */
+#if CHECK_DEFINES && defined(__OPTIMIZE__)
+#error "__OPTIMIZE__ is defined (not compiled with -O0?)"
+#endif
+
+#pragma GCC push_options
+#pragma GCC optimize ("O2", "tree-vectorize")
+
+/* Optimization flags and tree vectorizer shall be enabled at this point */
+#if CHECK_DEFINES && !defined(__OPTIMIZE__)
+#error "__OPTIMIZE__ is not defined"
+#endif
+
+#pragma GCC push_options
+#pragma GCC target ("sse4.2")
+#ifdef __cplusplus
+namespace {
+#endif
+
+/* Target flags up to including SSE4.2 shall be enabled at this point */
+#if CHECK_DEFINES && !defined(__SSE3__)
+#error "Target flag (SSE3) is not defined"
+#endif
+#if CHECK_DEFINES && !defined(__SSSE3__)
+#error "Target flag (SSSE3) is not defined"
+#endif
+#if CHECK_DEFINES && !defined(__SSE4_1__)
+#error "Target flag (SSE4.1) is not defined"
+#endif
+#if CHECK_DEFINES && !defined(__SSE4_2__)
+#error "Target flag (SSE4.2) is not defined"
+#endif
+
+void
+__attribute__((__noinline__, __used__))
+vec_saxpy_i32(int y[N], const int a[N], const int x[N])
+{
+    int i;
+    for (i = 0; i < N; i++)
+        y[i] += a[i] * x[i];
+}
+
+#ifdef __cplusplus
+}
+#endif
+#pragma GCC pop_options
+
+/* Target flags up to including SSE4.2 shall be disabled at this point */
+#if CHECK_DEFINES && defined(__SSE3__)
+#error "Target flag (SSE3) is still defined"
+#endif
+#if CHECK_DEFINES && defined(__SSSE3__)
+#error "Target flag (SSSE3) is still defined"
+#endif
+#if CHECK_DEFINES && defined(__SSE4_1__)
+#error "Target flag (SSE4.1) is still defined"
+#endif
+#if CHECK_DEFINES && defined(__SSE4_2__)
+#error "Target flag (SSE4.2) is still defined"
+#endif
+
+#pragma GCC push_options
+#pragma GCC target ("avx2", "fma")
+#ifdef __cplusplus
+struct A {
+#endif
+
+/* Target flags up to including AVX2+FMA shall be enabled at this point */
+#if CHECK_DEFINES && !defined(__SSE3__)
+#error "Target flag (SSE3) is not defined"
+#endif
+#if CHECK_DEFINES && !defined(__SSSE3__)
+#error "Target flag (SSSE3) is not defined"
+#endif
+#if CHECK_DEFINES && !defined(__SSE4_1__)
+#error "Target flag (SSE4.1) is not defined"
+#endif
+#if CHECK_DEFINES && !defined(__SSE4_2__)
+#error "Target flag (SSE4.2) is not defined"
+#endif
+#if CHECK_DEFINES && !defined(__AVX__)
+#error "Target flag (AVX) is not defined"
+#endif
+#if CHECK_DEFINES && !defined(__AVX2__)
+#error "Target flag (AVX2) is not defined"
+#endif
+#if CHECK_DEFINES && !defined(__FMA__)
+#error "Target flag (FMA) is not defined"
+#endif
+
+void
+__attribute__((__noinline__, __used__))
+vec_saxpy_f32(float y[N], const float a[N], const float x[N])
+{
+    int i;
+    for (i = 0; i < N; i++)
+        y[i] += a[i] * x[i];
+}
+
+#ifdef __cplusplus
+};
+#endif
+#pragma GCC pop_options
+
+/* Target flags up to including AVX2+FMA shall be disabled at this point */
+#if CHECK_DEFINES && defined(__SSE3__)
+#error "Target flag (SSE3) is still defined"
+#endif
+#if CHECK_DEFINES && defined(__SSSE3__)
+#error "Target flag (SSSE3) is still defined"
+#endif
+#if CHECK_DEFINES && defined(__SSE4_1__)
+#error "Target flag (SSE4.1) is still defined"
+#endif
+#if CHECK_DEFINES && defined(__SSE4_2__)
+#error "Target flag (SSE4.2) is still defined"
+#endif
+#if CHECK_DEFINES && defined(__AVX__)
+#error "Target flag (AVX) is still defined"
+#endif
+#if CHECK_DEFINES && defined(__AVX2__)
+#error "Target flag (AVX2) is still defined"
+#endif
+#if CHECK_DEFINES && defined(__FMA__)
+#error "Target flag (FMA) is still defined"
+#endif
+
+#pragma GCC push_options
+#pragma GCC target ("arch=x86-64-v4")
+#ifdef __cplusplus
+namespace avx512 {
+struct A {
+#endif
+
+/* Essential AVX512 target flags shall be enabled at this point */
+#if CHECK_DEFINES && !defined(__AVX512F__)
+#error "Target flag (AVX512F) is not defined"
+#endif
+#if CHECK_DEFINES && !defined(__AVX512VL__)
+#error "Target flag (AVX512VL) is not defined"
+#endif
+#if CHECK_DEFINES && !defined(__AVX512DQ__)
+#error "Target flag (AVX512DQ) is not defined"
+#endif
+#if CHECK_DEFINES && !defined(__AVX512BW__)
+#error "Target flag (AVX512BW) is not defined"
+#endif
+
+void
+__attribute__((__noinline__, __used__))
+vec_saxpy_i16(short y[N], const short a[N], const short x[N])
+{
+    int i;
+    for (i = 0; i < N; i++)
+        y[i] += a[i] * x[i];
+}
+
+#ifdef __cplusplus
+};
+}
+#endif
+#pragma GCC pop_options
+
+/* Essential AVX512 target flags shall be disabled at this point */
+#if CHECK_DEFINES && defined(__AVX512F__)
+#error "Target flag (AVX512F) is still defined"
+#endif
+#if CHECK_DEFINES && defined(__AVX512VL__)
+#error "Target flag (AVX512VL) is still defined"
+#endif
+#if CHECK_DEFINES && defined(__AVX512DQ__)
+#error "Target flag (AVX512DQ) is still defined"
+#endif
+#if CHECK_DEFINES && defined(__AVX512BW__)
+#error "Target flag (AVX512BW) is still defined"
+#endif
+
+#pragma GCC pop_options
+
+/* Optimization flags and tree vectorizer shall be disabled at this point */
+#if CHECK_DEFINES && defined(__OPTIMIZE__)
+#error "__OPTIMIZE__ is still defined"
+#endif
diff --git a/gcc/testsuite/gcc.target/i386/vect-pragma-target-2.c b/gcc/testsuite/gcc.target/i386/vect-pragma-target-2.c
new file mode 100644
index 00000000000..349680453a4
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/vect-pragma-target-2.c
@@ -0,0 +1,7 @@
+/* { dg-do compile { target { i?86-*-* x86_64-*-* } } }                 */
+/* { dg-options "-O0" }                                                 */
+/* { dg-final { scan-assembler-times "paddd.+xmm\[0-9]+"        1 } }   */
+/* { dg-final { scan-assembler-times "vfmadd132ps.+ymm\[0-9]+"  1 } }   */
+/* { dg-final { scan-assembler-times "vpaddw.+zmm\[0-9]+"       1 } }   */
+#define CHECK_DEFINES 1
+#include "vect-pragma-target-1.c"
-- 
2.39.2

From 69fe5faf1cf46d80684e42d6f209db0504418986 Mon Sep 17 00:00:00 2001
From: Lewis Hyatt <lhyatt@gmail.com>
Date: Fri, 27 Oct 2023 04:32:50 -0400
Subject: [PATCH 1/3] preprocessor: c++: Support `#pragma GCC target' macros
 [PR87299]

`#pragma GCC target' is not currently handled in preprocess-only mode (e.g.,
when running gcc -E or gcc -save-temps). As noted in the PR, this means that
if the target pragma defines any macros, those macros are not effective in
preprocess-only mode. Similarly, such macros are not effective when
compiling with C++ (even when compiling without -save-temps), because C++
does not process the pragma until after all tokens have been obtained from
libcpp, at which point it is too late for macro expansion to take place.

Since r13-1544 and r14-2893, there is a general mechanism to handle pragmas
under these conditions as well, so resolve the PR by using the new "early
pragma" support.

toplev.cc required some changes because the target-specific handlers for
`#pragma GCC target' may call target_reinit(), and toplev.cc was not expecting
that function to be called in preprocess-only mode.

I added some additional testcases from the PR for x86. The other targets
that support `#pragma GCC target' (aarch64, arm, nios2, powerpc, s390)
already had tests verifying that the pragma sets macros as expected; here I
have added -save-temps versions of some of them, to test that they now work
in preprocess-only mode as well.

gcc/c-family/ChangeLog:

	PR preprocessor/87299
	* c-pragma.cc (init_pragma): Register `#pragma GCC target' and
	related pragmas in preprocess-only mode, and enable early handling.
	(c_reset_target_pragmas): New function refactoring code from...
	(handle_pragma_reset_options): ...here.
	* c-pragma.h (c_reset_target_pragmas): Declare.

gcc/cp/ChangeLog:

	PR preprocessor/87299
	* parser.cc (cp_lexer_new_main): Call c_reset_target_pragmas ()
	after preprocessing is complete, before starting compilation.

gcc/ChangeLog:

	PR preprocessor/87299
	* toplev.cc (no_backend): New static global.
	(finalize): Remove argument no_backend, which is now a
	static global.
	(process_options): Likewise.
	(do_compile): Likewise.
	(target_reinit): Don't do anything in preprocess-only mode.
	(toplev::main): Adapt to no_backend change.
	(toplev::finalize): Likewise.

gcc/testsuite/ChangeLog:

	PR preprocessor/87299
	* c-c++-common/pragma-target-1.c: New test.
	* c-c++-common/pragma-target-2.c: New test.
	* g++.target/i386/pr87299-1.C: New test.
	* g++.target/i386/pr87299-2.C: New test.
	* gcc.target/i386/pr87299-1.c: New test.
	* gcc.target/i386/pr87299-2.c: New test.
	* gcc.target/s390/target-attribute/tattr-2b.c: New test.
	* gcc.target/aarch64/pragma_cpp_predefs_1b.c: New test.
	* gcc.target/arm/pragma_arch_attribute_1b.c: New test.
	* gcc.target/nios2/custom-fp-2b.c: New test.
	* gcc.target/powerpc/float128-3b.c: New test.

(cherry picked from commit 8697d3a1dcf32750a3b9dc007586eb5f9ba5f17a)
Signed-off-by: Gwenole Beauchesne <gb.devel@gmail.com>
---
 gcc/c-family/c-pragma.cc                      | 49 +++++++++++-------
 gcc/c-family/c-pragma.h                       |  2 +-
 gcc/cp/parser.cc                              |  6 +++
 gcc/testsuite/c-c++-common/pragma-target-1.c  | 19 +++++++
 gcc/testsuite/c-c++-common/pragma-target-2.c  | 27 ++++++++++
 gcc/testsuite/g++.target/i386/pr87299-1.C     |  8 +++
 gcc/testsuite/g++.target/i386/pr87299-2.C     |  8 +++
 .../aarch64/pragma_cpp_predefs_1b.c           |  3 ++
 .../gcc.target/arm/pragma_arch_attribute_1b.c |  6 +++
 gcc/testsuite/gcc.target/i386/pr87299-1.c     |  8 +++
 gcc/testsuite/gcc.target/i386/pr87299-2.c     |  8 +++
 gcc/testsuite/gcc.target/nios2/custom-fp-2b.c | 26 ++++++++++
 .../gcc.target/powerpc/float128-3b.c          |  4 ++
 .../s390/target-attribute/tattr-2b.c          | 51 +++++++++++++++++++
 gcc/toplev.cc                                 | 21 +++++---
 15 files changed, 219 insertions(+), 27 deletions(-)
 create mode 100644 gcc/testsuite/c-c++-common/pragma-target-1.c
 create mode 100644 gcc/testsuite/c-c++-common/pragma-target-2.c
 create mode 100644 gcc/testsuite/g++.target/i386/pr87299-1.C
 create mode 100644 gcc/testsuite/g++.target/i386/pr87299-2.C
 create mode 100644 gcc/testsuite/gcc.target/aarch64/pragma_cpp_predefs_1b.c
 create mode 100644 gcc/testsuite/gcc.target/arm/pragma_arch_attribute_1b.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr87299-1.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr87299-2.c
 create mode 100644 gcc/testsuite/gcc.target/nios2/custom-fp-2b.c
 create mode 100644 gcc/testsuite/gcc.target/powerpc/float128-3b.c
 create mode 100644 gcc/testsuite/gcc.target/s390/target-attribute/tattr-2b.c

diff --git a/gcc/c-family/c-pragma.cc b/gcc/c-family/c-pragma.cc
index c49ab9d27ac..8931b5e9cbb 100644
--- a/gcc/c-family/c-pragma.cc
+++ b/gcc/c-family/c-pragma.cc
@@ -1289,24 +1289,16 @@ handle_pragma_pop_options (cpp_reader *)
   current_optimize_pragma = p->optimize_strings;
 }
 
-/* Handle #pragma GCC reset_options to restore the current target and
-   optimization options to the original options used on the command line.  */
+/* This is mostly a helper for handle_pragma_reset_options () to do the actual
+   work, but the C++ frontend, for example, needs an external interface to
+   perform this operation, since it processes target pragmas twice.  (Once for
+   preprocessing purposes, and then again during compilation.)  */
 
-static void
-handle_pragma_reset_options (cpp_reader *)
+void
+c_reset_target_pragmas ()
 {
-  enum cpp_ttype token;
-  tree x = 0;
   tree new_optimize = optimization_default_node;
   tree new_target = target_option_default_node;
-
-  token = pragma_lex (&x);
-  if (token != CPP_EOF)
-    {
-      warning (OPT_Wpragmas, "junk at end of %<#pragma reset_options%>");
-      return;
-    }
-
   if (new_target != target_option_current_node)
     {
       (void) targetm.target_option.pragma_parse (NULL_TREE, new_target);
@@ -1326,6 +1318,19 @@ handle_pragma_reset_options (cpp_reader *)
   current_optimize_pragma = NULL_TREE;
 }
 
+/* Handle #pragma GCC reset_options to restore the current target and
+   optimization options to the original options used on the command line.  */
+
+static void
+handle_pragma_reset_options (cpp_reader *)
+{
+  tree x;
+  if (pragma_lex (&x) != CPP_EOF)
+    warning (OPT_Wpragmas, "junk at end of %<#pragma reset_options%>");
+  else
+    c_reset_target_pragmas ();
+}
+
 /* Print a plain user-specified message.  */
 
 static void
@@ -1840,11 +1845,19 @@ init_pragma (void)
     c_register_pragma_with_early_handler ("GCC", "diagnostic",
 					  handle_pragma_diagnostic,
 					  handle_pragma_diagnostic_early);
-  c_register_pragma ("GCC", "target", handle_pragma_target);
+  c_register_pragma_with_early_handler ("GCC", "target",
+					handle_pragma_target,
+					handle_pragma_target);
   c_register_pragma ("GCC", "optimize", handle_pragma_optimize);
-  c_register_pragma ("GCC", "push_options", handle_pragma_push_options);
-  c_register_pragma ("GCC", "pop_options", handle_pragma_pop_options);
-  c_register_pragma ("GCC", "reset_options", handle_pragma_reset_options);
+  c_register_pragma_with_early_handler ("GCC", "push_options",
+					handle_pragma_push_options,
+					handle_pragma_push_options);
+  c_register_pragma_with_early_handler ("GCC", "pop_options",
+					handle_pragma_pop_options,
+					handle_pragma_pop_options);
+  c_register_pragma_with_early_handler ("GCC", "reset_options",
+					handle_pragma_reset_options,
+					handle_pragma_reset_options);
 
   c_register_pragma (0, "region", handle_pragma_ignore);
   c_register_pragma (0, "endregion", handle_pragma_ignore);
diff --git a/gcc/c-family/c-pragma.h b/gcc/c-family/c-pragma.h
index 198fa7723e5..de5c1c7cced 100644
--- a/gcc/c-family/c-pragma.h
+++ b/gcc/c-family/c-pragma.h
@@ -255,7 +255,7 @@ c_register_pragma_with_early_handler (const char *space, const char *name,
 				      pragma_handler_1arg early_handler);
 extern void c_invoke_early_pragma_handler (unsigned int);
 extern void c_pp_invoke_early_pragma_handler (unsigned int);
-
+extern void c_reset_target_pragmas ();
 
 extern void maybe_apply_pragma_weak (tree);
 extern void maybe_apply_pending_pragma_weaks (void);
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index c5a48c9c6c1..39b034a3ed1 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -761,6 +761,12 @@ cp_lexer_new_main (void)
 
   maybe_check_all_macros (parse_in);
 
+  /* If we processed any #pragma GCC target directives, we handled them early so
+     any macros they defined would be effective during preprocessing.  Now, we
+     need to reset to the default state to begin compilation, and we will
+     process them again at the correct time as needed.  */
+  c_reset_target_pragmas ();
+
   gcc_assert (!lexer->next_token->purged_p);
   return lexer;
 }
diff --git a/gcc/testsuite/c-c++-common/pragma-target-1.c b/gcc/testsuite/c-c++-common/pragma-target-1.c
new file mode 100644
index 00000000000..2584c228318
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/pragma-target-1.c
@@ -0,0 +1,19 @@
+/* { dg-do compile { target { i?86-*-* x86_64-*-* } } } */
+/* { dg-options "-mno-avx -O3" } */
+
+void f (double *x)
+{
+  for (int i = 0; i != 8; ++i)
+    x[i] *= 2;
+}
+
+#pragma GCC target("avx")
+
+void g (double *x)
+{
+  for (int i = 0; i != 8; ++i)
+    x[i] *= 2;
+}
+
+/* Make sure the target pragma affected only g() and not also f().  */
+/* { dg-final { scan-assembler-times vzeroupper 1 } } */
diff --git a/gcc/testsuite/c-c++-common/pragma-target-2.c b/gcc/testsuite/c-c++-common/pragma-target-2.c
new file mode 100644
index 00000000000..e7bf305a0eb
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/pragma-target-2.c
@@ -0,0 +1,27 @@
+/* { dg-do preprocess { target { i?86-*-* x86_64-*-* } } } */
+/* { dg-options "-mno-avx" } */
+
+#ifdef __AVX__
+#error "__AVX__ should not be defined #1"
+#endif
+
+#pragma GCC target("avx")
+#ifndef __AVX__
+#error "__AVX__ should be defined #1"
+#endif
+
+#pragma GCC reset_options
+#ifdef __AVX__
+#error "__AVX__ should not be defined #2"
+#endif
+
+#pragma GCC push_options
+#pragma GCC target("avx")
+#ifndef __AVX__
+#error "__AVX__ should be defined #2"
+#endif
+
+#pragma GCC pop_options
+#ifdef __AVX__
+#error "__AVX__ should not be defined #3"
+#endif
diff --git a/gcc/testsuite/g++.target/i386/pr87299-1.C b/gcc/testsuite/g++.target/i386/pr87299-1.C
new file mode 100644
index 00000000000..38d4c3bbc26
--- /dev/null
+++ b/gcc/testsuite/g++.target/i386/pr87299-1.C
@@ -0,0 +1,8 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-mno-avx" } */
+
+#pragma GCC target("avx")
+const int x1 = __AVX__;
+
+_Pragma("GCC target(\"avx512f\")")
+const int x2 = __AVX512F__;
diff --git a/gcc/testsuite/g++.target/i386/pr87299-2.C b/gcc/testsuite/g++.target/i386/pr87299-2.C
new file mode 100644
index 00000000000..263dfb710a0
--- /dev/null
+++ b/gcc/testsuite/g++.target/i386/pr87299-2.C
@@ -0,0 +1,8 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-save-temps -mno-avx" } */
+
+#pragma GCC target("avx")
+const int x1 = __AVX__;
+
+_Pragma("GCC target(\"avx512f\")")
+const int x2 = __AVX512F__;
diff --git a/gcc/testsuite/gcc.target/aarch64/pragma_cpp_predefs_1b.c b/gcc/testsuite/gcc.target/aarch64/pragma_cpp_predefs_1b.c
new file mode 100644
index 00000000000..65bfab8740c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/pragma_cpp_predefs_1b.c
@@ -0,0 +1,3 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=armv8-a+crypto -save-temps" } */
+#include "pragma_cpp_predefs_1.c"
diff --git a/gcc/testsuite/gcc.target/arm/pragma_arch_attribute_1b.c b/gcc/testsuite/gcc.target/arm/pragma_arch_attribute_1b.c
new file mode 100644
index 00000000000..652d084e2e0
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/pragma_arch_attribute_1b.c
@@ -0,0 +1,6 @@
+/* Test for #pragma target macros.  */
+/* { dg-do compile } */
+/* { dg-require-effective-target arm_arch_v8a_ok } */
+/* { dg-additional-options "-save-temps" } */
+/* { dg-add-options arm_arch_v8a } */
+#include "pragma_arch_attribute.c"
diff --git a/gcc/testsuite/gcc.target/i386/pr87299-1.c b/gcc/testsuite/gcc.target/i386/pr87299-1.c
new file mode 100644
index 00000000000..38d4c3bbc26
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr87299-1.c
@@ -0,0 +1,8 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-mno-avx" } */
+
+#pragma GCC target("avx")
+const int x1 = __AVX__;
+
+_Pragma("GCC target(\"avx512f\")")
+const int x2 = __AVX512F__;
diff --git a/gcc/testsuite/gcc.target/i386/pr87299-2.c b/gcc/testsuite/gcc.target/i386/pr87299-2.c
new file mode 100644
index 00000000000..263dfb710a0
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr87299-2.c
@@ -0,0 +1,8 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-save-temps -mno-avx" } */
+
+#pragma GCC target("avx")
+const int x1 = __AVX__;
+
+_Pragma("GCC target(\"avx512f\")")
+const int x2 = __AVX512F__;
diff --git a/gcc/testsuite/gcc.target/nios2/custom-fp-2b.c b/gcc/testsuite/gcc.target/nios2/custom-fp-2b.c
new file mode 100644
index 00000000000..ce35c63baa8
--- /dev/null
+++ b/gcc/testsuite/gcc.target/nios2/custom-fp-2b.c
@@ -0,0 +1,26 @@
+/* Test specification of custom instructions via pragmas.  */
+
+/* { dg-do compile } */
+/* { dg-options "-O1 -ffinite-math-only -save-temps" } */
+
+/* -O1 in the options is significant.  Without it FP operations may not be
+   optimized to custom instructions.  */
+
+#include <stdio.h> 
+#include <math.h>
+
+#pragma GCC target ("custom-fmaxs=246")
+#pragma GCC target ("custom-fmins=247")
+#pragma GCC target ("custom-fsqrts=251")
+
+void
+custom_fp (float operand_a, float operand_b, float *result)
+{
+  result[0] = fmaxf (operand_a, operand_b);
+  result[1] = fminf (operand_a, operand_b);
+  result[2] = sqrtf (operand_a);
+}
+
+/* { dg-final { scan-assembler "custom\\t246, .* # fmaxs .*" } } */
+/* { dg-final { scan-assembler "custom\\t247, .* # fmins .*" } } */
+/* { dg-final { scan-assembler "custom\\t251, .* # fsqrts .*" } } */
diff --git a/gcc/testsuite/gcc.target/powerpc/float128-3b.c b/gcc/testsuite/gcc.target/powerpc/float128-3b.c
new file mode 100644
index 00000000000..6b409c13d1c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/powerpc/float128-3b.c
@@ -0,0 +1,4 @@
+/* { dg-do compile { target { powerpc*-*-linux* } } } */
+/* { dg-require-effective-target powerpc_vsx_ok } */
+/* { dg-options "-O2 -mvsx -mno-float128 -save-temps" } */
+#include "float128-3.c"
diff --git a/gcc/testsuite/gcc.target/s390/target-attribute/tattr-2b.c b/gcc/testsuite/gcc.target/s390/target-attribute/tattr-2b.c
new file mode 100644
index 00000000000..7ae78fcb9c2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/target-attribute/tattr-2b.c
@@ -0,0 +1,51 @@
+/* Functional tests for the "target" attribute and pragma.  */
+
+/* { dg-do compile } */
+/* { dg-require-effective-target target_attribute } */
+/* { dg-options "-O3 -march=zEC12 -mno-htm -fno-ipa-icf -save-temps" } */
+
+#pragma GCC target("htm")
+void p1(void)
+{
+#ifndef __HTM__
+#error __HTM__ is not defined
+#endif
+  __builtin_tend ();
+}
+#pragma GCC reset_options
+
+#pragma GCC target("no-htm")
+void p0(void)
+{
+#ifdef __HTM__
+#error __HTM__ is defined
+#endif
+  __builtin_tend (); /* { dg-error "is not supported without '-mhtm'" } */
+}
+#pragma GCC reset_options
+
+__attribute__ ((target("htm")))
+void a1(void)
+{
+#ifdef __HTM__
+#error __HTM__ is defined
+#endif
+  __builtin_tend ();
+}
+
+__attribute__ ((target("no-htm")))
+void a0(void)
+{
+#ifdef __HTM__
+#error __HTM__ is defined
+#endif
+  __builtin_tend (); /* { dg-error "is not supported without '-mhtm'" } */
+}
+
+void htmd(void)
+{
+#ifdef __HTM__
+#error __HTM__ is defined
+#endif
+  __builtin_tend (); /* { dg-error "is not supported without '-mhtm'" } */
+}
diff --git a/gcc/toplev.cc b/gcc/toplev.cc
index 109c9d58cbd..f00b8acc592 100644
--- a/gcc/toplev.cc
+++ b/gcc/toplev.cc
@@ -99,7 +99,7 @@ static void general_init (const char *, bool);
 static void backend_init (void);
 static int lang_dependent_init (const char *);
 static void init_asm_output (const char *);
-static void finalize (bool);
+static void finalize ();
 
 static void crash_signal (int) ATTRIBUTE_NORETURN;
 static void compile_file (void);
@@ -163,6 +163,7 @@ FILE *aux_info_file;
 FILE *callgraph_info_file = NULL;
 static bitmap callgraph_info_external_printed;
 FILE *stack_usage_file = NULL;
+static bool no_backend = false;
 
 /* The current working directory of a translation.  It's generally the
    directory from which compilation was initiated, but a preprocessed
@@ -1220,7 +1221,7 @@ parse_alignment_opts (void)
 
 /* Process the options that have been parsed.  */
 static void
-process_options (bool no_backend)
+process_options ()
 {
   const char *language_string = lang_hooks.name;
 
@@ -1870,6 +1871,9 @@ lang_dependent_init (const char *name)
 void
 target_reinit (void)
 {
+  if (no_backend)
+    return;
+
   struct rtl_data saved_x_rtl;
   rtx *saved_regno_reg_rtx;
   tree saved_optimization_current_node;
@@ -1962,7 +1966,7 @@ dump_memory_report (const char *header)
 /* Clean up: close opened files, etc.  */
 
 static void
-finalize (bool no_backend)
+finalize ()
 {
   /* Close the dump files.  */
   if (flag_gen_aux_info)
@@ -2044,7 +2048,7 @@ standard_type_bitsize (int bitsize)
 
 /* Initialize the compiler, and compile the input file.  */
 static void
-do_compile (bool no_backend)
+do_compile ()
 {
   /* Don't do any more if an error has already occurred.  */
   if (!seen_error ())
@@ -2131,7 +2135,7 @@ do_compile (bool no_backend)
 
       timevar_start (TV_PHASE_FINALIZE);
 
-      finalize (no_backend);
+      finalize ();
 
       timevar_stop (TV_PHASE_FINALIZE);
     }
@@ -2268,13 +2272,13 @@ toplev::main (int argc, char **argv)
 	 initialization based on the command line options.  This hook also
 	 sets the original filename if appropriate (e.g. foo.i -> foo.c)
 	 so we can correctly initialize debug output.  */
-      bool no_backend = lang_hooks.post_options (&main_input_filename);
+      no_backend = lang_hooks.post_options (&main_input_filename);
 
-      process_options (no_backend);
+      process_options ();
 
       if (m_use_TV_TOTAL)
 	start_timevars ();
-      do_compile (no_backend);
+      do_compile ();
 
       if (flag_self_test && !seen_error ())
 	{
@@ -2319,6 +2323,7 @@ toplev::main (int argc, char **argv)
 void
 toplev::finalize (void)
 {
+  no_backend = false;
   rtl_initialized = false;
   this_target_rtl->target_specific_initialized = false;
 
-- 
2.39.2

From e45c564e923f06f2c6459ef42c32a7ec29c8c1e9 Mon Sep 17 00:00:00 2001
From: Lewis Hyatt <lhyatt@gmail.com>
Date: Wed, 1 Nov 2023 13:01:12 -0400
Subject: [PATCH 2/3] preprocessor: Reinitialize frontend parser after loading
 a PCH [PR112319]

Since r14-2893, the frontend parser object needs to exist when running in
preprocess-only mode, because pragma_lex() is now called in that mode and
needs to make use of it. This is handled by calling c_init_preprocess() at
startup. If -fpch-preprocess is in effect (commonly, because of
-save-temps), a PCH file may be loaded during preprocessing, in which
case the parser will be destroyed, causing the issue noted in the
PR. Resolve it by reinitializing the frontend parser after loading the PCH.

gcc/c-family/ChangeLog:

	PR pch/112319
	* c-ppoutput.cc (cb_read_pch): Reinitialize the frontend parser
	after loading a PCH.

gcc/testsuite/ChangeLog:

	PR pch/112319
	* g++.dg/pch/pr112319.C: New test.
	* g++.dg/pch/pr112319.Hs: New test.
	* gcc.dg/pch/pr112319.c: New test.
	* gcc.dg/pch/pr112319.hs: New test.

(cherry picked from commit 5d4abd9219dfa53b52b341255e99139bb6cad302)
Signed-off-by: Gwenole Beauchesne <gb.devel@gmail.com>
---
 gcc/c-family/c-ppoutput.cc           | 5 +++++
 gcc/testsuite/g++.dg/pch/pr112319.C  | 5 +++++
 gcc/testsuite/g++.dg/pch/pr112319.Hs | 1 +
 gcc/testsuite/gcc.dg/pch/pr112319.c  | 5 +++++
 gcc/testsuite/gcc.dg/pch/pr112319.hs | 1 +
 5 files changed, 17 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/pch/pr112319.C
 create mode 100644 gcc/testsuite/g++.dg/pch/pr112319.Hs
 create mode 100644 gcc/testsuite/gcc.dg/pch/pr112319.c
 create mode 100644 gcc/testsuite/gcc.dg/pch/pr112319.hs

diff --git a/gcc/c-family/c-ppoutput.cc b/gcc/c-family/c-ppoutput.cc
index 4aa2bef2c0f..4f973767976 100644
--- a/gcc/c-family/c-ppoutput.cc
+++ b/gcc/c-family/c-ppoutput.cc
@@ -862,4 +862,9 @@ cb_read_pch (cpp_reader *pfile, const char *name,
 
   fprintf (print.outf, "#pragma GCC pch_preprocess \"%s\"\n", name);
   print.src_line++;
+
+  /* The process of reading the PCH has destroyed the frontend parser,
+     so ask the frontend to reinitialize it, in case we need it to
+     process any #pragma directives encountered while preprocessing.  */
+  c_init_preprocess ();
 }
diff --git a/gcc/testsuite/g++.dg/pch/pr112319.C b/gcc/testsuite/g++.dg/pch/pr112319.C
new file mode 100644
index 00000000000..9e0457e8aec
--- /dev/null
+++ b/gcc/testsuite/g++.dg/pch/pr112319.C
@@ -0,0 +1,5 @@
+/* { dg-additional-options "-Wpragmas -save-temps" } */
+#include "pr112319.H"
+#pragma GCC diagnostic error "-Wpragmas"
+#pragma GCC diagnostic ignored "oops" /* { dg-error "oops" } */
+/* { dg-regexp {[^[:space:]]*: some warnings being treated as errors} } */
diff --git a/gcc/testsuite/g++.dg/pch/pr112319.Hs b/gcc/testsuite/g++.dg/pch/pr112319.Hs
new file mode 100644
index 00000000000..3b6178bfae0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/pch/pr112319.Hs
@@ -0,0 +1 @@
+/* This space intentionally left blank.  */
diff --git a/gcc/testsuite/gcc.dg/pch/pr112319.c b/gcc/testsuite/gcc.dg/pch/pr112319.c
new file mode 100644
index 00000000000..043881463c5
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pch/pr112319.c
@@ -0,0 +1,5 @@
+/* { dg-additional-options "-Wpragmas -save-temps" } */
+#include "pr112319.h"
+#pragma GCC diagnostic error "-Wpragmas"
+#pragma GCC diagnostic ignored "oops" /* { dg-error "oops" } */
+/* { dg-regexp {[^[:space:]]*: some warnings being treated as errors} } */
diff --git a/gcc/testsuite/gcc.dg/pch/pr112319.hs b/gcc/testsuite/gcc.dg/pch/pr112319.hs
new file mode 100644
index 00000000000..3b6178bfae0
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pch/pr112319.hs
@@ -0,0 +1 @@
+/* This space intentionally left blank.  */
-- 
2.39.2


Reply to: