[gold][patch]Fold only ctors and dtors with --icf=safe

Sriraman Tallam tmsriram@google.com
Tue Oct 13 21:12:00 GMT 2009


Hi,

   I am committing this patch to gold to fold only ctors and dtors
with ICF as folding functions could be unsafe when function pointer
comparisons are made.

       * gc.h (gc_process_relocs): Check if icf is enabled using new
       function.
       * gold.cc (queue_initial_tasks): Likewise.
       (queue_middle_tasks): Likewise.
       * object.cc (do_layout): Likewise.
       * symtab.cc (is_section_folded): Likewise.
       * main.cc (main): Likewise.
       * reloc.cc (Read_relocs::run): Likewise.
       (Sized_relobj::do_scan_relocs): Likewise.
       * icf.cc (is_function_ctor_or_dtor): New function.
       (Icf::find_identical_sections): Check if function is ctor or dtor when
       safe icf is chosen.
       * options.h (General_options::icf): Change option to be an enum.
       (Icf): New enum.
       (icf_enabled): New method.
       (icf_safe_folding): New method.
       (set_icf_status): New method.
       (icf_status_): New variable.
       * (options.cc) (General_options::finalize): Set icf_status_.
       * testsuite/Makefile.am: Add commands to build icf_safe_test. Modify
       icf_test and icf_keep_unique_test to use the --icf enum flag.
       * testsuite/icf_safe_test.sh: New file.
       * testsuite/icf_safe_test.cc: New file.

Thanks,
-Sriraman.
-------------- next part --------------
Index: gc.h
===================================================================
RCS file: /cvs/src/src/gold/gc.h,v
retrieving revision 1.3
diff -u -u -p -r1.3 gc.h
--- gc.h	13 Oct 2009 00:39:31 -0000	1.3
+++ gc.h	13 Oct 2009 20:39:08 -0000
@@ -163,7 +163,7 @@ gc_process_relocs(
   std::vector<std::pair<long long, long long> >* addendvec = NULL;
   bool is_icf_tracked = false;
 
-  if (parameters->options().icf()
+  if (parameters->options().icf_enabled()
       && is_prefix_of(".text.", (src_obj)->section_name(src_indx).c_str()))
     {
       is_icf_tracked = true;
Index: gold.cc
===================================================================
RCS file: /cvs/src/src/gold/gold.cc,v
retrieving revision 1.71
diff -u -u -p -r1.71 gold.cc
--- gold.cc	13 Oct 2009 00:39:31 -0000	1.71
+++ gold.cc	13 Oct 2009 20:39:08 -0000
@@ -221,10 +221,12 @@ queue_initial_tasks(const General_option
     }
 
   if (parameters->options().relocatable()
-      && (parameters->options().gc_sections() || parameters->options().icf()))
+      && (parameters->options().gc_sections()
+	  || parameters->options().icf_enabled()))
     gold_error(_("cannot mix -r with --gc-sections or --icf"));
 
-  if (parameters->options().gc_sections() || parameters->options().icf())
+  if (parameters->options().gc_sections()
+      || parameters->options().icf_enabled())
     {
       workqueue->queue(new Task_function(new Gc_runner(options,
                                                        input_objects,
@@ -332,7 +334,7 @@ queue_middle_tasks(const General_options
   // If identical code folding (--icf) is chosen it makes sense to do it 
   // only after garbage collection (--gc-sections) as we do not want to 
   // be folding sections that will be garbage.
-  if (parameters->options().icf())
+  if (parameters->options().icf_enabled())
     {
       symtab->icf()->find_identical_sections(input_objects, symtab);
     }
@@ -342,7 +344,8 @@ queue_middle_tasks(const General_options
   // --gc-sections or --icf is turned on, Object::layout is 
   // called twice.  It is called the first time when the 
   // symbols are added.
-  if (parameters->options().gc_sections() || parameters->options().icf())
+  if (parameters->options().gc_sections()
+      || parameters->options().icf_enabled())
     {
       for (Input_objects::Relobj_iterator p = input_objects->relobj_begin();
            p != input_objects->relobj_end();
@@ -360,7 +363,8 @@ queue_middle_tasks(const General_options
       plugins->layout_deferred_objects();
     }     
 
-  if (parameters->options().gc_sections() || parameters->options().icf())
+  if (parameters->options().gc_sections()
+      || parameters->options().icf_enabled())
     {
       for (Input_objects::Relobj_iterator p = input_objects->relobj_begin();
            p != input_objects->relobj_end();
@@ -472,7 +476,8 @@ queue_middle_tasks(const General_options
 
   // If doing garbage collection, the relocations have already been read.
   // Otherwise, read and scan the relocations.
-  if (parameters->options().gc_sections() || parameters->options().icf())
+  if (parameters->options().gc_sections()
+      || parameters->options().icf_enabled())
     {
       for (Input_objects::Relobj_iterator p = input_objects->relobj_begin();
            p != input_objects->relobj_end();
Index: icf.cc
===================================================================
RCS file: /cvs/src/src/gold/icf.cc,v
retrieving revision 1.3
diff -u -u -p -r1.3 icf.cc
--- icf.cc	13 Aug 2009 00:03:43 -0000	1.3
+++ icf.cc	13 Oct 2009 20:39:08 -0000
@@ -114,6 +114,7 @@
 #include "icf.h"
 #include "symtab.h"
 #include "libiberty.h"
+#include "demangle.h"
 
 namespace gold
 {
@@ -530,6 +531,22 @@ match_sections(unsigned int iteration_nu
   return converged;
 }
 
+// During safe icf (--icf=safe), only fold functions that are ctors or dtors.
+// This function returns true if the mangled function name is a ctor or a
+// dtor.
+
+static bool
+is_function_ctor_or_dtor(const char* mangled_func_name)
+{
+  if ((is_prefix_of("_ZN", mangled_func_name)
+       || is_prefix_of("_ZZ", mangled_func_name))
+      && (is_gnu_v3_mangled_ctor(mangled_func_name)
+          || is_gnu_v3_mangled_dtor(mangled_func_name)))
+    {
+      return true;
+    }
+  return false;
+}
 
 // This is the main ICF function called in gold.cc.  This does the
 // initialization and calls match_sections repeatedly (twice by default)
@@ -552,14 +569,19 @@ Icf::find_identical_sections(const Input
     {
       for (unsigned int i = 0;i < (*p)->shnum(); ++i)
         {
+	  const char* section_name = (*p)->section_name(i).c_str();
           // Only looking to fold functions, so just look at .text sections.
-          if (!is_prefix_of(".text.", (*p)->section_name(i).c_str()))
+          if (!is_prefix_of(".text.", section_name))
             continue;
           if (!(*p)->is_section_included(i))
             continue;
           if (parameters->options().gc_sections()
               && symtab->gc()->is_section_garbage(*p, i))
               continue;
+	  // With --icf=safe, check if mangled name is a ctor or a dtor.
+	  if (parameters->options().icf_safe_folding()
+	      && !is_function_ctor_or_dtor(section_name + 6))
+	    continue;
           this->id_section_.push_back(Section_id(*p, i));
           this->section_id_[Section_id(*p, i)] = section_num;
           this->kept_section_id_.push_back(section_num);
Index: main.cc
===================================================================
RCS file: /cvs/src/src/gold/main.cc,v
retrieving revision 1.33
diff -u -u -p -r1.33 main.cc
--- main.cc	5 Aug 2009 20:51:56 -0000	1.33
+++ main.cc	13 Oct 2009 20:39:08 -0000
@@ -220,7 +220,7 @@ main(int argc, char** argv)
   if (parameters->options().gc_sections())
     symtab.set_gc(&gc);
 
-  if (parameters->options().icf())
+  if (parameters->options().icf_enabled())
     symtab.set_icf(&icf);
 
   // The layout object.
Index: object.cc
===================================================================
RCS file: /cvs/src/src/gold/object.cc,v
retrieving revision 1.103
diff -u -u -p -r1.103 object.cc
--- object.cc	9 Oct 2009 23:18:19 -0000	1.103
+++ object.cc	13 Oct 2009 20:39:08 -0000
@@ -934,16 +934,16 @@ Sized_relobj<size, big_endian>::do_layou
   const unsigned int shnum = this->shnum();
   bool is_gc_pass_one = ((parameters->options().gc_sections() 
                           && !symtab->gc()->is_worklist_ready())
-                         || (parameters->options().icf()
+                         || (parameters->options().icf_enabled()
                              && !symtab->icf()->is_icf_ready()));
  
   bool is_gc_pass_two = ((parameters->options().gc_sections() 
                           && symtab->gc()->is_worklist_ready())
-                         || (parameters->options().icf()
+                         || (parameters->options().icf_enabled()
                              && symtab->icf()->is_icf_ready()));
 
   bool is_gc_or_icf = (parameters->options().gc_sections()
-                       || parameters->options().icf()); 
+                       || parameters->options().icf_enabled()); 
 
   // Both is_gc_pass_one and is_gc_pass_two should not be true.
   gold_assert(!(is_gc_pass_one  && is_gc_pass_two));
@@ -1238,7 +1238,7 @@ Sized_relobj<size, big_endian>::do_layou
               }
         }
 
-      if (is_gc_pass_two && parameters->options().icf())
+      if (is_gc_pass_two && parameters->options().icf_enabled())
         {
           if (out_sections[i] == NULL)
             {
Index: options.cc
===================================================================
RCS file: /cvs/src/src/gold/options.cc,v
retrieving revision 1.92
diff -u -u -p -r1.92 options.cc
--- options.cc	10 Oct 2009 07:39:04 -0000	1.92
+++ options.cc	13 Oct 2009 20:39:08 -0000
@@ -874,6 +874,15 @@ General_options::finalize()
   else if (this->noexecstack())
     this->set_execstack_status(EXECSTACK_NO);
 
+  // icf_status_ is a three-state variable; update it based on the
+  // value of this->icf().
+  if (strcmp(this->icf(), "none") == 0)
+    this->set_icf_status(ICF_NONE);
+  else if (strcmp(this->icf(), "safe") == 0)
+    this->set_icf_status(ICF_SAFE);
+  else
+    this->set_icf_status(ICF_ALL);
+
   // Handle the optional argument for --demangle.
   if (this->user_set_demangle())
     {
Index: options.h
===================================================================
RCS file: /cvs/src/src/gold/options.h,v
retrieving revision 1.111
diff -u -u -p -r1.111 options.h
--- options.h	10 Oct 2009 07:39:04 -0000	1.111
+++ options.h	13 Oct 2009 20:39:08 -0000
@@ -836,9 +836,11 @@ class General_options
   DEFINE_special(static, options::ONE_DASH, '\0',
                  N_("Do not link against shared libraries"), NULL);
 
-  DEFINE_bool(icf, options::TWO_DASHES, '\0', false,
-              N_("Identical Code Folding (Fold identical functions)"),
-              N_("Don't fold identical functions (default)"));
+  DEFINE_enum(icf, options::TWO_DASHES, '\0', "none",
+              N_("Identical Code Folding. "
+                 "\'--icf=safe\' folds only ctors and dtors."),
+	      ("[none,all,safe]"),	
+              {"none", "all", "safe"});
 
   DEFINE_uint(icf_iterations, options::TWO_DASHES , '\0', 0,
               N_("Number of iterations of ICF (default 2)"), N_("COUNT"));
@@ -1065,6 +1067,14 @@ class General_options
   is_stack_executable() const
   { return this->execstack_status_ == EXECSTACK_YES; }
 
+  bool
+  icf_enabled() const
+  { return this->icf_status_ != ICF_NONE; }
+
+  bool
+  icf_safe_folding() const
+  { return this->icf_status_ == ICF_SAFE; }
+
   // The --demangle option takes an optional string, and there is also
   // a --no-demangle option.  This is the best way to decide whether
   // to demangle or not.
@@ -1115,6 +1125,20 @@ class General_options
     EXECSTACK_NO
   };
 
+  enum Icf_status
+  {
+    // Do not fold any functions (Default or --icf=none).
+    ICF_NONE,
+    // All functions are candidates for folding. (--icf=all).
+    ICF_ALL,	
+    // Only ctors and dtors are candidates for folding. (--icf=safe).
+    ICF_SAFE
+  };
+
+  void
+  set_icf_status(Icf_status value)
+  { this->icf_status_ = value; }
+
   void
   set_execstack_status(Execstack value)
   { this->execstack_status_ = value; }
@@ -1148,6 +1172,8 @@ class General_options
   bool printed_version_;
   // Whether to mark the stack as executable.
   Execstack execstack_status_;
+  // Whether to do code folding.
+  Icf_status icf_status_;
   // Whether to do a static link.
   bool static_;
   // Whether to do demangling.
Index: reloc.cc
===================================================================
RCS file: /cvs/src/src/gold/reloc.cc,v
retrieving revision 1.46
diff -u -u -p -r1.46 reloc.cc
--- reloc.cc	6 Oct 2009 22:58:27 -0000	1.46
+++ reloc.cc	13 Oct 2009 20:39:08 -0000
@@ -31,6 +31,7 @@
 #include "object.h"
 #include "target-reloc.h"
 #include "reloc.h"
+#include "icf.h"
 
 namespace gold
 {
@@ -68,7 +69,8 @@ Read_relocs::run(Workqueue* workqueue)
   // If garbage collection or identical comdat folding is desired, we  
   // process the relocs first before scanning them.  Scanning of relocs is
   // done only after garbage or identical sections is identified.
-  if (parameters->options().gc_sections() || parameters->options().icf())
+  if (parameters->options().gc_sections()
+      || parameters->options().icf_enabled())
     {
       workqueue->queue_next(new Gc_process_relocs(this->options_,
                                                   this->symtab_,
@@ -420,7 +422,8 @@ Sized_relobj<size, big_endian>::do_scan_
       // When garbage collection is on, unreferenced sections are not included
       // in the link that would have been included normally. This is known only
       // after Read_relocs hence this check has to be done again.
-      if (parameters->options().gc_sections() || parameters->options().icf())
+      if (parameters->options().gc_sections()
+	  || parameters->options().icf_enabled())
         {
           if (p->output_section == NULL)
             continue;
Index: symtab.cc
===================================================================
RCS file: /cvs/src/src/gold/symtab.cc,v
retrieving revision 1.124
diff -u -u -p -r1.124 symtab.cc
--- symtab.cc	9 Oct 2009 23:18:19 -0000	1.124
+++ symtab.cc	13 Oct 2009 20:39:08 -0000
@@ -519,7 +519,7 @@ Symbol_table::Symbol_table_eq::operator(
 bool
 Symbol_table::is_section_folded(Object* obj, unsigned int shndx) const
 {
-  return (parameters->options().icf()
+  return (parameters->options().icf_enabled()
           && this->icf_->is_section_folded(obj, shndx));
 }
 
cvs diff: Diffing po
cvs diff: Diffing testsuite
Index: testsuite/Makefile.am
===================================================================
RCS file: /cvs/src/src/gold/testsuite/Makefile.am,v
retrieving revision 1.105
diff -u -u -p -r1.105 Makefile.am
--- testsuite/Makefile.am	10 Oct 2009 07:39:04 -0000	1.105
+++ testsuite/Makefile.am	13 Oct 2009 20:39:08 -0000
@@ -134,7 +134,7 @@ MOSTLYCLEANFILES += icf_test
 icf_test.o: icf_test.cc 
 	$(CXXCOMPILE) -O0 -c -ffunction-sections -g -o $@ $<
 icf_test: icf_test.o gcctestdir/ld
-	$(CXXLINK) -Bgcctestdir/ -Wl,--icf icf_test.o
+	$(CXXLINK) -Bgcctestdir/ -Wl,--icf=all icf_test.o
 icf_test.stdout: icf_test
 	$(TEST_NM) -C icf_test > icf_test.stdout
 
@@ -144,10 +144,20 @@ MOSTLYCLEANFILES += icf_keep_unique_test
 icf_keep_unique_test.o: icf_keep_unique_test.cc
 	$(CXXCOMPILE) -O0 -c -ffunction-sections -g -o $@ $<
 icf_keep_unique_test: icf_keep_unique_test.o gcctestdir/ld
-	$(CXXLINK) -Bgcctestdir/ -Wl,--icf -Wl,--keep-unique,_Z11unique_funcv icf_keep_unique_test.o
+	$(CXXLINK) -Bgcctestdir/ -Wl,--icf=all -Wl,--keep-unique,_Z11unique_funcv icf_keep_unique_test.o
 icf_keep_unique_test.stdout: icf_keep_unique_test
 	$(TEST_NM) -C icf_keep_unique_test > icf_keep_unique_test.stdout
 
+check_SCRIPTS += icf_safe_test.sh
+check_DATA += icf_safe_test.stdout
+MOSTLYCLEANFILES += icf_safe_test
+icf_safe_test.o: icf_safe_test.cc 
+	$(CXXCOMPILE) -O0 -c -ffunction-sections -g -o $@ $<
+icf_safe_test: icf_safe_test.o gcctestdir/ld
+	$(CXXLINK) -Bgcctestdir/ -Wl,--icf=safe icf_safe_test.o
+icf_safe_test.stdout: icf_safe_test
+	$(TEST_NM) icf_safe_test > icf_safe_test.stdout
+
 check_PROGRAMS += basic_test
 check_PROGRAMS += basic_static_test
 check_PROGRAMS += basic_pic_test
Index: testsuite/icf_safe_test.cc
===================================================================
RCS file: testsuite/icf_safe_test.cc
diff -N testsuite/icf_safe_test.cc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ testsuite/icf_safe_test.cc	13 Oct 2009 20:39:09 -0000
@@ -0,0 +1,54 @@
+// icf_safe_test.cc -- a test case for gold
+
+// Copyright 2009 Free Software Foundation, Inc.
+// Written by Sriraman Tallam <tmsriram@google.com>.
+
+// This file is part of gold.
+
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+// MA 02110-1301, USA.
+
+// The goal of this program is to verify if identical code folding
+// in safe mode correctly folds only ctors and dtors. kept_func_1 must
+// not be folded into kept_func_2.  The ctor and dtor of class A must
+// be folded.
+
+class A
+{
+  public:
+    A()
+    {
+    }
+    ~A()
+    {
+    }
+};
+
+A a;
+
+int kept_func_1()
+{
+  return 1;
+}
+
+int kept_func_2()
+{
+  return 1;
+}
+
+int main()
+{
+  return 0;
+}
Index: testsuite/icf_safe_test.sh
===================================================================
RCS file: testsuite/icf_safe_test.sh
diff -N testsuite/icf_safe_test.sh
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ testsuite/icf_safe_test.sh	13 Oct 2009 20:39:09 -0000
@@ -0,0 +1,50 @@
+# icf_safe_test.sh -- test --icf=safe
+
+# Copyright 2009 Free Software Foundation, Inc.
+# Written by Sriraman Tallam <tmsriram@google.com>.
+
+# This file is part of gold.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+# The goal of this program is to verify if --icf=safe  works as expected.
+# File icf_safe_test.cc is in this test. This program checks if only
+# ctors and dtors are folded.
+
+check_nofold()
+{
+    func_addr_1=`grep $2 $1 | awk '{print $1}'`
+    func_addr_2=`grep $3 $1 | awk '{print $1}'`
+    if [ $func_addr_1 = $func_addr_2 ]
+    then
+        echo "Safe Identical Code Folding folded" $2 "and" $3
+	exit 1
+    fi
+}
+
+check_fold()
+{
+    func_addr_1=`grep $2 $1 | awk '{print $1}'`
+    func_addr_2=`grep $3 $1 | awk '{print $1}'`
+    if [ $func_addr_1 != $func_addr_2 ]
+    then
+        echo "Safe Identical Code Folding did not fold " $2 "and" $3
+	exit 1
+    fi
+}
+
+check_nofold icf_safe_test.stdout "kept_func_1" "kept_func_2"
+check_fold   icf_safe_test.stdout "_ZN1AD1Ev" "_ZN1AC1Ev"


More information about the Binutils mailing list