diff mbox series

preprocessor: c++: Support `#pragma GCC target' macros [PR87299]

Message ID 20230801022253.3286257-1-lhyatt@gmail.com
State New
Headers show
Series preprocessor: c++: Support `#pragma GCC target' macros [PR87299] | expand

Commit Message

Lewis Hyatt Aug. 1, 2023, 2:22 a.m. UTC
`#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 to some of them, to test that it now works 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-2.c: Add -save-temps to the
	options, to test preprocess-only mode as well.
	* gcc.target/aarch64/pragma_cpp_predefs_1.c: Likewise.
	* gcc.target/arm/pragma_arch_attribute.c: Likewise.
	* gcc.target/nios2/custom-fp-2.c: Likewise.
	* gcc.target/powerpc/float128-3.c: Likewise.
---

Notes:
    Hello-
    
    This patch fixes the PR by enabling early pragma handling for `#pragma GCC
    target' and related pragmas such as `#pragma GCC push_options'. I did not
    need to touch any target-specific code, however I did need to make a change
    to toplev.cc, affecting all targets, to make it safe to call target_reinit()
    in preprocess-only mode. (Otherwise, it would be necessary to modify the
    implementation of target pragmas in every target, to avoid this code path.)
    That was the only complication I ran into.
    
    Regarding testing, I did: (thanks to GCC compile farm for the non-x86
    targets)
    
    bootstrap + regtest all languages - x86_64-pc-linux-gnu
    bootstrap + regtest c/c++ - powerpc64le-unknown-linux-gnu,
                                aarch64-unknown-linux-gnu
    
    The following backends also implement this pragma so ought to be tested:
        arm
        nios2
        s390
    
    I am not able to test those directly. I did add coverage to their testsuites
    (basically, adding -save-temps to any existing test, causes it to test the
    pragma in preprocess-only mode.) Then, I verified on x86_64 with a cross
    compiler, that the modified testcases fail before the patch and pass
    afterwards. nios2 is an exception, it does not set any libcpp macros when
    handling the pragma, so there is nothing to test, but I did verify that
    processing the pragma in preprocess-only mode does not cause any problems.
    The cross compilers tested were targets arm-unknown-linux-gnueabi,
    nios2-unknown-linux, and s390-ibm-linux.
    
    Please let me know if it looks OK? Thanks!
    
    -Lewis

 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 +++
 .../gcc.target/aarch64/pragma_cpp_predefs_1.c |  2 +-
 .../gcc.target/arm/pragma_arch_attribute.c    |  1 +
 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-2.c  |  2 +-
 gcc/testsuite/gcc.target/powerpc/float128-3.c |  2 +-
 .../s390/target-attribute/tattr-2.c           |  4 +-
 gcc/toplev.cc                                 | 21 +++++---
 15 files changed, 135 insertions(+), 32 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/i386/pr87299-1.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr87299-2.c

Comments

Joseph Myers Aug. 1, 2023, 3:01 p.m. UTC | #1
On Mon, 31 Jul 2023, Lewis Hyatt via Gcc-patches wrote:

> 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 to some of them, to test that it now works in
> preprocess-only mode as well.

It would seem better to have copies of the tests with and without 
-save-temps, to test in both modes, rather than changing what's tested by 
an existing test here.  Or a test variant that #includes the original test 
but uses different options, if the original test isn't doing anything that 
would fail to work with that approach.
Jason Merrill Aug. 8, 2023, 9:53 p.m. UTC | #2
On 7/31/23 22:22, Lewis Hyatt via Gcc-patches wrote:
> `#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 to some of them, to test that it now works 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-2.c: Add -save-temps to the
> 	options, to test preprocess-only mode as well.
> 	* gcc.target/aarch64/pragma_cpp_predefs_1.c: Likewise.
> 	* gcc.target/arm/pragma_arch_attribute.c: Likewise.
> 	* gcc.target/nios2/custom-fp-2.c: Likewise.
> 	* gcc.target/powerpc/float128-3.c: Likewise.
> ---
> 
> Notes:
>      Hello-
>      
>      This patch fixes the PR by enabling early pragma handling for `#pragma GCC
>      target' and related pragmas such as `#pragma GCC push_options'. I did not
>      need to touch any target-specific code, however I did need to make a change
>      to toplev.cc, affecting all targets, to make it safe to call target_reinit()
>      in preprocess-only mode. (Otherwise, it would be necessary to modify the
>      implementation of target pragmas in every target, to avoid this code path.)
>      That was the only complication I ran into.
>      
>      Regarding testing, I did: (thanks to GCC compile farm for the non-x86
>      targets)
>      
>      bootstrap + regtest all languages - x86_64-pc-linux-gnu
>      bootstrap + regtest c/c++ - powerpc64le-unknown-linux-gnu,
>                                  aarch64-unknown-linux-gnu
>      
>      The following backends also implement this pragma so ought to be tested:
>          arm
>          nios2
>          s390
>      
>      I am not able to test those directly. I did add coverage to their testsuites
>      (basically, adding -save-temps to any existing test, causes it to test the
>      pragma in preprocess-only mode.) Then, I verified on x86_64 with a cross
>      compiler, that the modified testcases fail before the patch and pass
>      afterwards. nios2 is an exception, it does not set any libcpp macros when
>      handling the pragma, so there is nothing to test, but I did verify that
>      processing the pragma in preprocess-only mode does not cause any problems.
>      The cross compilers tested were targets arm-unknown-linux-gnueabi,
>      nios2-unknown-linux, and s390-ibm-linux.
>      
>      Please let me know if it looks OK? Thanks!

The c-family and C++ changes look good.  David, any comments on the 
toplev changes?

Jason
Lewis Hyatt Aug. 9, 2023, 1:28 p.m. UTC | #3
On Tue, Aug 1, 2023 at 11:01 AM Joseph Myers <joseph@codesourcery.com> wrote:
>
> On Mon, 31 Jul 2023, Lewis Hyatt via Gcc-patches wrote:
>
> > 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 to some of them, to test that it now works in
> > preprocess-only mode as well.
>
> It would seem better to have copies of the tests with and without
> -save-temps, to test in both modes, rather than changing what's tested by
> an existing test here.  Or a test variant that #includes the original test
> but uses different options, if the original test isn't doing anything that
> would fail to work with that approach.

Thank you, I will adjust this.

-Lewis
Lewis Hyatt Sept. 12, 2023, 8:09 p.m. UTC | #4
On Tue, Aug 8, 2023 at 5:53 PM Jason Merrill <jason@redhat.com> wrote:
>
> On 7/31/23 22:22, Lewis Hyatt via Gcc-patches wrote:
> > `#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 to some of them, to test that it now works 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-2.c: Add -save-temps to the
> >       options, to test preprocess-only mode as well.
> >       * gcc.target/aarch64/pragma_cpp_predefs_1.c: Likewise.
> >       * gcc.target/arm/pragma_arch_attribute.c: Likewise.
> >       * gcc.target/nios2/custom-fp-2.c: Likewise.
> >       * gcc.target/powerpc/float128-3.c: Likewise.
> > ---
> >
> > Notes:
> >      Hello-
> >
> >      This patch fixes the PR by enabling early pragma handling for `#pragma GCC
> >      target' and related pragmas such as `#pragma GCC push_options'. I did not
> >      need to touch any target-specific code, however I did need to make a change
> >      to toplev.cc, affecting all targets, to make it safe to call target_reinit()
> >      in preprocess-only mode. (Otherwise, it would be necessary to modify the
> >      implementation of target pragmas in every target, to avoid this code path.)
> >      That was the only complication I ran into.
> >
> >      Regarding testing, I did: (thanks to GCC compile farm for the non-x86
> >      targets)
> >
> >      bootstrap + regtest all languages - x86_64-pc-linux-gnu
> >      bootstrap + regtest c/c++ - powerpc64le-unknown-linux-gnu,
> >                                  aarch64-unknown-linux-gnu
> >
> >      The following backends also implement this pragma so ought to be tested:
> >          arm
> >          nios2
> >          s390
> >
> >      I am not able to test those directly. I did add coverage to their testsuites
> >      (basically, adding -save-temps to any existing test, causes it to test the
> >      pragma in preprocess-only mode.) Then, I verified on x86_64 with a cross
> >      compiler, that the modified testcases fail before the patch and pass
> >      afterwards. nios2 is an exception, it does not set any libcpp macros when
> >      handling the pragma, so there is nothing to test, but I did verify that
> >      processing the pragma in preprocess-only mode does not cause any problems.
> >      The cross compilers tested were targets arm-unknown-linux-gnueabi,
> >      nios2-unknown-linux, and s390-ibm-linux.
> >
> >      Please let me know if it looks OK? Thanks!
>
> The c-family and C++ changes look good.  David, any comments on the
> toplev changes?
>
> Jason
>

Thanks for the review! To be clear, I should continue to wait for
additional feedback on the toplev.cc change, correct?
I have also implemented Joseph's suggestion, to add new testcases
rather than modifying existing ones. That's a pretty minor change just
to the testsuite part so I did not resend the whole patch because of
it, but I can if needed.

-Lewis
diff mbox series

Patch

diff --git a/gcc/c-family/c-pragma.cc b/gcc/c-family/c-pragma.cc
index 73d59df3bf4..d31d49bfea2 100644
--- a/gcc/c-family/c-pragma.cc
+++ b/gcc/c-family/c-pragma.cc
@@ -1288,24 +1288,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);
@@ -1325,6 +1317,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
@@ -1839,11 +1844,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 2e245865ea7..ab9262142ea 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_1.c b/gcc/testsuite/gcc.target/aarch64/pragma_cpp_predefs_1.c
index 307fa3d67da..46efa23cedf 100644
--- a/gcc/testsuite/gcc.target/aarch64/pragma_cpp_predefs_1.c
+++ b/gcc/testsuite/gcc.target/aarch64/pragma_cpp_predefs_1.c
@@ -1,5 +1,5 @@ 
 /* { dg-do compile } */
-/* { dg-options "-O2 -march=armv8-a+crypto" } */
+/* { dg-options "-O2 -march=armv8-a+crypto -save-temps" } */
 
 /* Test that pragma option pushing and popping works.
    Also that CPP predefines redefinitions on #pragma works.  */
diff --git a/gcc/testsuite/gcc.target/arm/pragma_arch_attribute.c b/gcc/testsuite/gcc.target/arm/pragma_arch_attribute.c
index a06dbf04037..76ffc4c0d1f 100644
--- a/gcc/testsuite/gcc.target/arm/pragma_arch_attribute.c
+++ b/gcc/testsuite/gcc.target/arm/pragma_arch_attribute.c
@@ -1,6 +1,7 @@ 
 /* 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 <arm_acle.h>
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-2.c b/gcc/testsuite/gcc.target/nios2/custom-fp-2.c
index fc7c643705e..ce35c63baa8 100644
--- a/gcc/testsuite/gcc.target/nios2/custom-fp-2.c
+++ b/gcc/testsuite/gcc.target/nios2/custom-fp-2.c
@@ -1,7 +1,7 @@ 
 /* Test specification of custom instructions via pragmas.  */
 
 /* { dg-do compile } */
-/* { dg-options "-O1 -ffinite-math-only" } */
+/* { 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.  */
diff --git a/gcc/testsuite/gcc.target/powerpc/float128-3.c b/gcc/testsuite/gcc.target/powerpc/float128-3.c
index 6daf290d12b..bf8b89d4755 100644
--- a/gcc/testsuite/gcc.target/powerpc/float128-3.c
+++ b/gcc/testsuite/gcc.target/powerpc/float128-3.c
@@ -1,6 +1,6 @@ 
 /* { dg-do compile { target { powerpc*-*-linux* } } } */
 /* { dg-require-effective-target powerpc_vsx_ok } */
-/* { dg-options "-O2 -mvsx -mno-float128" } */
+/* { dg-options "-O2 -mvsx -mno-float128 -save-temps" } */
 
 /* Test that we can use #pragma GCC target to enable -mfloat128.  */
 
diff --git a/gcc/testsuite/gcc.target/s390/target-attribute/tattr-2.c b/gcc/testsuite/gcc.target/s390/target-attribute/tattr-2.c
index 739c2eabc83..7ae78fcb9c2 100644
--- a/gcc/testsuite/gcc.target/s390/target-attribute/tattr-2.c
+++ b/gcc/testsuite/gcc.target/s390/target-attribute/tattr-2.c
@@ -1,8 +1,8 @@ 
 /* Functional tests for the "target" attribute and pragma.  */
 
-/* { dg-do compile */
+/* { dg-do compile } */
 /* { dg-require-effective-target target_attribute } */
-/* { dg-options "-O3 -march=zEC12 -mno-htm -fno-ipa-icf" } */
+/* { dg-options "-O3 -march=zEC12 -mno-htm -fno-ipa-icf -save-temps" } */
 
 #pragma GCC target("htm")
 void p1(void)
diff --git a/gcc/toplev.cc b/gcc/toplev.cc
index 6c1a6f443c1..685ad9484ac 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
@@ -1221,7 +1222,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;
 
@@ -1871,6 +1872,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;
@@ -1963,7 +1967,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)
@@ -2045,7 +2049,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 ())
@@ -2132,7 +2136,7 @@  do_compile (bool no_backend)
 
       timevar_start (TV_PHASE_FINALIZE);
 
-      finalize (no_backend);
+      finalize ();
 
       timevar_stop (TV_PHASE_FINALIZE);
     }
@@ -2273,13 +2277,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 ())
 	{
@@ -2324,6 +2328,7 @@  toplev::main (int argc, char **argv)
 void
 toplev::finalize (void)
 {
+  no_backend = false;
   rtl_initialized = false;
   this_target_rtl->target_specific_initialized = false;