diff mbox series

c++: Elide inactive initializer fns from init array

Message ID d939e17d-9c5d-770b-2185-b8e09d621f80@acm.org
State New
Headers show
Series c++: Elide inactive initializer fns from init array | expand

Commit Message

Nathan Sidwell June 16, 2022, 6:42 p.m. UTC
There's no point adding no-op initializer fns (that a module might
have) to the static initializer list.  Also, we can add any objc
initializer call to a partial initializer function and simplify some
control flow.

nathan
diff mbox series

Patch

From c970d0072e3f962afa278e28f918fdcd1b3e755c Mon Sep 17 00:00:00 2001
From: Nathan Sidwell <nathan@acm.org>
Date: Thu, 16 Jun 2022 10:14:56 -0700
Subject: [PATCH] c++: Elide inactive initializer fns from init array

There's no point adding no-op initializer fns (that a module might
have) to the static initializer list.  Also, we can add any objc
initializer call to a partial initializer function and simplify some
control flow.

	gcc/cp/
	* decl2.cc (finish_objects): Add startp parameter, adjust.
	(generate_ctor_or_dtor_function): Detect empty fn, and don't
	generate unnecessary code.  Remove objc startup here ...
	(c_parse_final_cleanyps): ... do it here.

	gcc/testsuite/
	* g++.dg/modules/init-2_b.C: Add init check.
	* g++.dg/modules/init-2_c.C: Add init check.
---
 gcc/cp/decl2.cc                         | 97 +++++++++++++------------
 gcc/testsuite/g++.dg/modules/init-2_b.C |  1 +
 gcc/testsuite/g++.dg/modules/init-2_c.C |  1 +
 3 files changed, 53 insertions(+), 46 deletions(-)

diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc
index 0c4492f7354..3737e5f010c 100644
--- a/gcc/cp/decl2.cc
+++ b/gcc/cp/decl2.cc
@@ -56,7 +56,7 @@  int raw_dump_id;
 extern cpp_reader *parse_in;
 
 static tree start_objects (bool, unsigned, bool);
-static tree finish_objects (bool, unsigned, tree);
+static tree finish_objects (bool, unsigned, tree, bool = true);
 static tree start_partial_init_fini_fn (bool, unsigned, unsigned);
 static void finish_partial_init_fini_fn (tree);
 static void emit_partial_init_fini_fn (bool, unsigned, tree,
@@ -3932,16 +3932,19 @@  start_objects (bool initp, unsigned priority, bool has_body)
   return body;
 }
 
-/* Finish a global constructor or destructor.  */
+/* Finish a global constructor or destructor.  Add it to the global
+   ctors or dtors, if STARTP is true.  */
 
 static tree
-finish_objects (bool initp, unsigned priority, tree body)
+finish_objects (bool initp, unsigned priority, tree body, bool startp)
 {
   /* Finish up.  */
   finish_compound_stmt (body);
   tree fn = finish_function (/*inline_p=*/false);
 
-  if (initp)
+  if (!startp)
+    ; // Neither ctor nor dtor I be.
+  else if (initp)
     {
       DECL_STATIC_CONSTRUCTOR (fn) = 1;
       decl_init_priority_insert (fn, priority);
@@ -4307,58 +4310,54 @@  write_out_vars (tree vars)
     }
 }
 
-/* Generate a static constructor (if CONSTRUCTOR_P) or destructor
-   (otherwise) that will initialize all global objects with static
-   storage duration having the indicated PRIORITY.  */
+/* Generate a static constructor or destructor that calls the given
+   init/fini fns at the indicated priority.  */
 
 static void
 generate_ctor_or_dtor_function (bool initp, unsigned priority,
 				tree fns, location_t locus)
 {
   input_location = locus;
-
   tree body = start_objects (initp, priority, bool (fns));
 
-  /* To make sure dynamic construction doesn't access globals from other
-     compilation units where they might not be yet constructed, for
-     -fsanitize=address insert __asan_before_dynamic_init call that
-     prevents access to either all global variables that need construction
-     in other compilation units, or at least those that haven't been
-     initialized yet.  Variables that need dynamic construction in
-     the current compilation unit are kept accessible.  */
-  if (initp && (flag_sanitize & SANITIZE_ADDRESS))
-    finish_expr_stmt (asan_dynamic_init_call (/*after_p=*/false));
-
-  if (initp && priority == DEFAULT_INIT_PRIORITY
-      && c_dialect_objc () && objc_static_init_needed_p ())
-    /* For Objective-C++, we may need to initialize metadata found in
-       this module.  This must be done _before_ any other static
-       initializations.  */
-    objc_generate_static_init_call (NULL_TREE);
-
-  /* Call the static init/fini functions.  */
-  for (tree node = fns; node; node = TREE_CHAIN (node))
+  if (fns)
     {
-      tree fn = TREE_PURPOSE (node);
+      /* To make sure dynamic construction doesn't access globals from
+	 other compilation units where they might not be yet
+	 constructed, for -fsanitize=address insert
+	 __asan_before_dynamic_init call that prevents access to
+	 either all global variables that need construction in other
+	 compilation units, or at least those that haven't been
+	 initialized yet.  Variables that need dynamic construction in
+	 the current compilation unit are kept accessible.  */
+      if (initp && (flag_sanitize & SANITIZE_ADDRESS))
+	finish_expr_stmt (asan_dynamic_init_call (/*after_p=*/false));
 
-      // We should never find a pure or constant cdtor.
-      gcc_checking_assert (!(flags_from_decl_or_type (fn)
-			     & (ECF_CONST | ECF_PURE)));
+      /* Call the static init/fini functions.  */
+      for (tree node = fns; node; node = TREE_CHAIN (node))
+	{
+	  tree fn = TREE_PURPOSE (node);
 
-      tree call = cp_build_function_call_nary (fn, tf_warning_or_error,
-					       NULL_TREE);
-      finish_expr_stmt (call);
+	  // We should never find a pure or constant cdtor.
+	  gcc_checking_assert (!(flags_from_decl_or_type (fn)
+				 & (ECF_CONST | ECF_PURE)));
+
+	  tree call = cp_build_function_call_nary (fn, tf_warning_or_error,
+						   NULL_TREE);
+	  finish_expr_stmt (call);
+	}
+
+      /* Revert what __asan_before_dynamic_init did by calling
+	 __asan_after_dynamic_init.  */
+      if (initp && (flag_sanitize & SANITIZE_ADDRESS))
+	finish_expr_stmt (asan_dynamic_init_call (/*after_p=*/true));
     }
 
-  /* Revert what __asan_before_dynamic_init did by calling
-     __asan_after_dynamic_init.  */
-  if (initp && (flag_sanitize & SANITIZE_ADDRESS))
-    finish_expr_stmt (asan_dynamic_init_call (/*after_p=*/true));
-
   /* Close out the function, and arrange for it to be called at init
-     or fini time.  (Even module initializer functions need this, as
-     we cannot guarantee the module is imported somewhere in the programq.)  */
-  expand_or_defer_fn (finish_objects (initp, priority, body));
+     or fini time, if non-empty.  (Even non-nop module initializer
+     functions need this, as we cannot guarantee the module is
+     imported somewhere in the program.)  */
+  expand_or_defer_fn (finish_objects (initp, priority, body, fns != NULL_TREE));
 }
 
 /* Return C++ property of T, based on given operation OP.  */
@@ -5206,18 +5205,24 @@  c_parse_final_cleanups (void)
     objc_write_global_declarations ();
 
   bool has_module_inits = module_determine_import_inits ();
-  if (has_module_inits)
+  bool has_objc_init = c_dialect_objc () && objc_static_init_needed_p ();
+  if (has_module_inits || has_objc_init)
     {
       input_location = locus_at_end_of_parsing;
       tree body = start_partial_init_fini_fn (true, DEFAULT_INIT_PRIORITY,
 					      ssdf_count++);
-      module_add_import_initializers ();
+      /* For Objective-C++, we may need to initialize metadata found
+	 in this module.  This must be done _before_ any other static
+	 initializations.  */
+      if (has_objc_init)
+	objc_generate_static_init_call (NULL_TREE);
+      if (has_module_inits)
+	module_add_import_initializers ();
       input_location = locus_at_end_of_parsing;
       finish_partial_init_fini_fn (body);
     }
 
-  if ((c_dialect_objc () && objc_static_init_needed_p ())
-      || module_global_init_needed ())
+  if (module_global_init_needed ())
     {
       // Make sure there's a default priority entry.
       if (!static_init_fini_fns[true])
diff --git a/gcc/testsuite/g++.dg/modules/init-2_b.C b/gcc/testsuite/g++.dg/modules/init-2_b.C
index 4350944139f..a98e67616a2 100644
--- a/gcc/testsuite/g++.dg/modules/init-2_b.C
+++ b/gcc/testsuite/g++.dg/modules/init-2_b.C
@@ -8,3 +8,4 @@  import Foo;
 // There should be an idempotency check
 // { dg-final { scan-assembler {_ZZ9_ZGIW3BarE9__in_chrg} } }
 // { dg-final { scan-assembler {call[ \t]+_?_ZGIW3Foo} { target i?86-*-* x86_64-*-* } } }
+// { dg-final { scan-assembler {.(quad|long)[ \t]+_ZGIW3Bar} { target i?86-*-* x86_64-*-* } } }
diff --git a/gcc/testsuite/g++.dg/modules/init-2_c.C b/gcc/testsuite/g++.dg/modules/init-2_c.C
index 2b1315e2892..c386582b9c0 100644
--- a/gcc/testsuite/g++.dg/modules/init-2_c.C
+++ b/gcc/testsuite/g++.dg/modules/init-2_c.C
@@ -5,3 +5,4 @@  export module Baz;
 // { dg-final { scan-assembler {_ZGIW3Baz:} } }
 // But it is empty, and so no idempotency bool
 // { dg-final { scan-assembler-not {_ZZ9_ZGIW3BazE9__in_chrg} } }
+// { dg-final { scan-assembler-not {.(quad|long)[ \t]+_ZGIW3Baz} { target i?86-*-* x86_64-*-* } } }
-- 
2.30.2