diff --git a/gcc/c-family/c-opts.cc b/gcc/c-family/c-opts.cc
index 32b929e3ece..e6880c23444 100644
--- a/gcc/c-family/c-opts.cc
+++ b/gcc/c-family/c-opts.cc
@@ -182,6 +182,11 @@ void
 c_common_diagnostics_set_defaults (diagnostic_context *context)
 {
   diagnostic_finalizer (context) = c_diagnostic_finalizer;
+
+  /* Our finalizer is the same as the default one, except it also outputs macro
+     tracking information.  This is safe to do in the middle end.  */
+  context->preserve_on_reset[diagnostic_context::PRESERVE_FINALIZER] = true;
+
   context->opt_permissive = OPT_fpermissive;
 }
 
diff --git a/gcc/diagnostic.cc b/gcc/diagnostic.cc
index 22f7b0b6d6e..3fe4fdb42ca 100644
--- a/gcc/diagnostic.cc
+++ b/gcc/diagnostic.cc
@@ -243,6 +243,8 @@ diagnostic_initialize (diagnostic_context *context, int n_opts)
   context->final_cb = default_diagnostic_final_cb;
   context->includes_seen = NULL;
   context->m_client_data_hooks = NULL;
+  memset (context->preserve_on_reset, 0,
+	  ARRAY_SIZE (context->preserve_on_reset));
 }
 
 /* Maybe initialize the color support. We require clients to do this
diff --git a/gcc/diagnostic.h b/gcc/diagnostic.h
index ae6f2dfb7f4..a429ad4846f 100644
--- a/gcc/diagnostic.h
+++ b/gcc/diagnostic.h
@@ -414,6 +414,26 @@ struct diagnostic_context
      Used by SARIF output to give metadata about the client that's
      producing diagnostics.  */
   diagnostic_client_data_hooks *m_client_data_hooks;
+
+  /* When transitioning from the frontend to the middle end, the
+     ipa-free-lang-data pass will reset the diagnostic context to the default
+     configuration so that frontend customizations, which may no longer have
+     access to the data structures they expect to exist, will not cause any
+     problems with subsequent diagnostics.  Setting these variables to true
+     allows a frontend to communicate that its customizations are safe to run
+     post ipa-free-lang-data and need not be reset.  */
+  enum {
+    PRESERVE_STARTER = 0,
+    PRESERVE_FINALIZER,
+    PRESERVE_FORMAT_DECODER,
+
+    /* This is a langhook rather than a member of the DC, but it is still
+       convenient to handle it here.  */
+    PRESERVE_DECL_PRINTABLE_NAME,
+
+    PRESERVE_NUM
+  };
+  bool preserve_on_reset[PRESERVE_NUM];
 };
 
 static inline void
diff --git a/gcc/ipa-free-lang-data.cc b/gcc/ipa-free-lang-data.cc
index ccdbf849c25..fbdeab8f641 100644
--- a/gcc/ipa-free-lang-data.cc
+++ b/gcc/ipa-free-lang-data.cc
@@ -1115,7 +1115,9 @@ free_lang_data (void)
   /* Reset some langhooks.  Do not reset types_compatible_p, it may
      still be used indirectly via the get_alias_set langhook.  */
   lang_hooks.dwarf_name = lhd_dwarf_name;
-  lang_hooks.decl_printable_name = gimple_decl_printable_name;
+  if (!global_dc->preserve_on_reset[diagnostic_context::
+				    PRESERVE_DECL_PRINTABLE_NAME])
+    lang_hooks.decl_printable_name = gimple_decl_printable_name;
   lang_hooks.gimplify_expr = lhd_gimplify_expr;
   lang_hooks.overwrite_decl_assembler_name = lhd_overwrite_decl_assembler_name;
   lang_hooks.print_xnode = lhd_print_tree_nothing;
@@ -1142,7 +1144,7 @@ free_lang_data (void)
      devise a separate, middle-end private scheme for it.  */
 
   /* Reset diagnostic machinery.  */
-  tree_diagnostics_defaults (global_dc);
+  tree_diagnostics_defaults (global_dc, /* enable_preserve= */ true);
 
   rebuild_type_inheritance_graph ();
 
diff --git a/gcc/testsuite/c-c++-common/diag-after-fld-1.c b/gcc/testsuite/c-c++-common/diag-after-fld-1.c
new file mode 100644
index 00000000000..c1fc87a03f3
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/diag-after-fld-1.c
@@ -0,0 +1,25 @@
+/* Make sure that post-ipa-free-lang-data diagnostics expand macros as
+   expected.  */
+
+/* { dg-do compile } */
+/* { dg-require-effective-target lto } */
+/* { dg-options "-flto -O2 -Wnonnull-compare" } */
+
+#define X(p) p == 0 /* { dg-warning {-Wnonnull-compare} } */
+int f (void *) __attribute__((nonnull));
+int f (void *p)
+{
+  return X (p); /* { dg-note {in expansion of macro 'X'} } */
+}
+
+#define X2(p) p == 0 /* { dg-warning {-Wnonnull-compare} } */
+#define Y2(p) X2(p) /* { dg-note {in expansion of macro 'X2'} } */
+
+#define MAKE_F2 \
+  int f2 (void *) __attribute__((nonnull)); \
+  int f2 (void *p) \
+  { \
+    return Y2 (p); /* { dg-note {in expansion of macro 'Y2'} } */ \
+  }
+
+MAKE_F2 /* { dg-note {in expansion of macro 'MAKE_F2'} } */
diff --git a/gcc/tree-diagnostic.cc b/gcc/tree-diagnostic.cc
index 0d79fe3c3c1..cbf6d0678b7 100644
--- a/gcc/tree-diagnostic.cc
+++ b/gcc/tree-diagnostic.cc
@@ -366,11 +366,15 @@ set_inlining_locations (diagnostic_context *,
 
 /* Sets CONTEXT to use language independent diagnostics.  */
 void
-tree_diagnostics_defaults (diagnostic_context *context)
+tree_diagnostics_defaults (diagnostic_context *context, bool enable_preserve)
 {
-  diagnostic_starter (context) = default_tree_diagnostic_starter;
-  diagnostic_finalizer (context) = default_diagnostic_finalizer;
-  diagnostic_format_decoder (context) = default_tree_printer;
+  const auto &p = context->preserve_on_reset;
+  if (!(enable_preserve && p[diagnostic_context::PRESERVE_STARTER]))
+    diagnostic_starter (context) = default_tree_diagnostic_starter;
+  if (!(enable_preserve && p[diagnostic_context::PRESERVE_FINALIZER]))
+    diagnostic_finalizer (context) = default_diagnostic_finalizer;
+  if (!(enable_preserve && p[diagnostic_context::PRESERVE_FORMAT_DECODER]))
+    diagnostic_format_decoder (context) = default_tree_printer;
   context->print_path = default_tree_diagnostic_path_printer;
   context->make_json_for_path = default_tree_make_json_for_path;
   context->set_locations_cb = set_inlining_locations;
diff --git a/gcc/tree-diagnostic.h b/gcc/tree-diagnostic.h
index f60f2320e95..f7c1f08105d 100644
--- a/gcc/tree-diagnostic.h
+++ b/gcc/tree-diagnostic.h
@@ -53,7 +53,8 @@ void diagnostic_report_current_function (diagnostic_context *,
 void virt_loc_aware_diagnostic_finalizer (diagnostic_context *,
 					  diagnostic_info *);
 
-void tree_diagnostics_defaults (diagnostic_context *context);
+void tree_diagnostics_defaults (diagnostic_context *context,
+				bool enable_preserve = false);
 bool default_tree_printer (pretty_printer *, text_info *, const char *,
 			   int, bool, bool, bool, bool *, const char **);
 
diff --git a/libgomp/testsuite/libgomp.oacc-c-c++-common/reduction-5.c b/libgomp/testsuite/libgomp.oacc-c-c++-common/reduction-5.c
index ddccfe89e73..a3f3f758e9d 100644
--- a/libgomp/testsuite/libgomp.oacc-c-c++-common/reduction-5.c
+++ b/libgomp/testsuite/libgomp.oacc-c-c++-common/reduction-5.c
@@ -47,9 +47,8 @@ main (void)
      gangs.  */
   check_reduction (num_workers (nw) vector_length (vl), worker); /* { dg-line check_reduction_loc } */
   /* { dg-warning "22:region is vector partitioned but does not contain vector partitioned code" "" { target *-*-* } pragma_loc }
-     { dg-note "1:in expansion of macro 'DO_PRAGMA'" "" { target *-*-* xfail offloading_enabled } DO_PRAGMA_loc }
-     { dg-note "3:in expansion of macro 'check_reduction'" "" { target *-*-* xfail offloading_enabled } check_reduction_loc }
-     TODO See PR101551 for 'offloading_enabled' XFAILs.  */
+     { dg-note "1:in expansion of macro 'DO_PRAGMA'" "" { target *-*-* } DO_PRAGMA_loc }
+     { dg-note "3:in expansion of macro 'check_reduction'" "" { target *-*-* } check_reduction_loc } */
   check_reduction (vector_length (vl), vector);
   check_reduction (num_gangs (ng) num_workers (nw) vector_length (vl), gang
 		   worker vector);
diff --git a/libgomp/testsuite/libgomp.oacc-c-c++-common/vred2d-128.c b/libgomp/testsuite/libgomp.oacc-c-c++-common/vred2d-128.c
index 84e6d51670b..614b527409a 100644
--- a/libgomp/testsuite/libgomp.oacc-c-c++-common/vred2d-128.c
+++ b/libgomp/testsuite/libgomp.oacc-c-c++-common/vred2d-128.c
@@ -42,46 +42,45 @@ gentest (test1, "acc parallel loop gang vector_length (128) firstprivate (t1, t2
 	 "acc loop vector reduction(+:t1) reduction(-:t2)")
 /* { dg-warning {'t1' is used uninitialized} {} { target *-*-* } outer }
    { dg-note {'t1' was declared here} {} { target *-*-* } vars }
-   { dg-note {in expansion of macro 'gentest'} {} { target { ! offloading_enabled } } .-4 }
-     TODO See PR101551 for 'offloading_enabled' differences.  */
-/* { dg-warning {'t2' is used uninitialized} {} { target *-*-* } outer }
+   { dg-note {in expansion of macro 'gentest'} {} { target *-*-* } .-4 }
+
+   { dg-warning {'t2' is used uninitialized} {} { target *-*-* } outer }
    { dg-note {'t2' was declared here} {} { target *-*-* } vars }
-   { DUP_dg-note {in expansion of macro 'gentest'} {} { target { ! offloading_enabled } } .-8 }
-     TODO See PR101551 for 'offloading_enabled' differences.  */
+   { DUP_dg-note {in expansion of macro 'gentest'} {} { target *-*-* } .-8 }
+*/
 
 gentest (test2, "acc parallel loop gang vector_length (128) firstprivate (t1, t2)",
 	 "acc loop worker vector reduction(+:t1) reduction(-:t2)")
 /* { DUPdg-warning {'t1' is used uninitialized} {} { target *-*-* } outer }
    { DUP_dg-note {'t1' was declared here} {} { target *-*-* } vars }
-   { dg-note {in expansion of macro 'gentest'} {} { target { ! offloading_enabled } } .-4 }
-     TODO See PR101551 for 'offloading_enabled' differences.  */
-/* { DUPdg-warning {'t2' is used uninitialized} {} { target *-*-* } outer }
+   { dg-note {in expansion of macro 'gentest'} {} { target *-*-* } .-4 }
+
+   { DUPdg-warning {'t2' is used uninitialized} {} { target *-*-* } outer }
    { DUP_dg-note {'t2' was declared here} {} { target *-*-* } vars }
-   { DUP_dg-note {in expansion of macro 'gentest'} {} { target { ! offloading_enabled } } .-8 }
-     TODO See PR101551 for 'offloading_enabled' differences.  */
+   { DUP_dg-note {in expansion of macro 'gentest'} {} { target *-*-* } .-8 }
+*/
 
 gentest (test3, "acc parallel loop gang worker vector_length (128) firstprivate (t1, t2)",
 	 "acc loop vector reduction(+:t1) reduction(-:t2)")
 /* { DUPdg-warning {'t1' is used uninitialized} {} { target *-*-* } outer }
    { DUP_dg-note {'t1' was declared here} {} { target *-*-* } vars }
-   { dg-note {in expansion of macro 'gentest'} {} { target { ! offloading_enabled } } .-4 }
-     TODO See PR101551 for 'offloading_enabled' differences.  */
-/* { DUPdg-warning {'t2' is used uninitialized} {} { target *-*-* } outer }
+   { dg-note {in expansion of macro 'gentest'} {} { target *-*-* } .-4 }
+
+   { DUPdg-warning {'t2' is used uninitialized} {} { target *-*-* } outer }
    { DUP_dg-note {'t2' was declared here} {} { target *-*-* } vars }
-   { DUP_dg-note {in expansion of macro 'gentest'} {} { target { ! offloading_enabled } } .-8 }
-     TODO See PR101551 for 'offloading_enabled' differences.  */
+   { DUP_dg-note {in expansion of macro 'gentest'} {} { target *-*-* } .-8 }
+*/
 
 gentest (test4, "acc parallel loop firstprivate (t1, t2)",
 	 "acc loop reduction(+:t1) reduction(-:t2)")
 /* { DUPdg-warning {'t1' is used uninitialized} {} { target *-*-* } outer }
    { DUP_dg-note {'t1' was declared here} {} { target *-*-* } vars }
-   { dg-note {in expansion of macro 'gentest'} {} { target { ! offloading_enabled } } .-4 }
-     TODO See PR101551 for 'offloading_enabled' differences.  */
-/* { DUPdg-warning {'t2' is used uninitialized} {} { target *-*-* } outer }
-   { DUP_dg-note {'t2' was declared here} {} { target *-*-* } vars }
-   { DUP_dg-note {in expansion of macro 'gentest'} {} { target { ! offloading_enabled } } .-8 }
-     TODO See PR101551 for 'offloading_enabled' differences.  */
+   { dg-note {in expansion of macro 'gentest'} {} { target *-*-* } .-4 }
 
+   { DUPdg-warning {'t2' is used uninitialized} {} { target *-*-* } outer }
+   { DUP_dg-note {'t2' was declared here} {} { target *-*-* } vars }
+   { DUP_dg-note {in expansion of macro 'gentest'} {} { target *-*-* } .-8 }
+*/
 
 int
 main ()
