public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH 4/5] Existing tests fix
  2014-06-16 10:07 [PATCH 1/5] New Identical Code Folding IPA pass mliska
@ 2014-06-16 10:07 ` mliska
  2014-06-17 19:52   ` Jeff Law
  2014-06-16 10:07 ` [PATCH 5/5] New tests introduction mliska
                   ` (5 subsequent siblings)
  6 siblings, 1 reply; 70+ messages in thread
From: mliska @ 2014-06-16 10:07 UTC (permalink / raw)
  To: gcc-patches; +Cc: hubicka

Hi,
  many tests rely on a precise number of scanned functions in a dump file. If IPA ICF decides to merge some function and(or) read-only variables, counts do not match.

Martin

Changelog:

2014-06-13  Martin Liska  <mliska@suse.cz>
	    Honza Hubicka  <hubicka@ucw.cz>

	* c-c++-common/rotate-1.c: Text
	* c-c++-common/rotate-2.c: New test.
	* c-c++-common/rotate-3.c: Likewise.
	* c-c++-common/rotate-4.c: Likewise.
	* g++.dg/cpp0x/rv-return.C: Likewise.
	* g++.dg/cpp0x/rv1n.C: Likewise.
	* g++.dg/cpp0x/rv1p.C: Likewise.
	* g++.dg/cpp0x/rv2n.C: Likewise.
	* g++.dg/cpp0x/rv3n.C: Likewise.
	* g++.dg/cpp0x/rv4n.C: Likewise.
	* g++.dg/cpp0x/rv5n.C: Likewise.
	* g++.dg/cpp0x/rv6n.C: Likewise.
	* g++.dg/cpp0x/rv7n.C: Likewise.
	* gcc.dg/ipa/ipacost-1.c: Likewise.
	* gcc.dg/ipa/ipacost-2.c: Likewise.
	* gcc.dg/ipa/ipcp-agg-6.c: Likewise.
	* gcc.dg/ipa/remref-2a.c: Likewise.
	* gcc.dg/ipa/remref-2b.c: Likewise.
	* gcc.dg/pr46309-2.c: Likewise.
	* gcc.dg/torture/ipa-pta-1.c: Likewise.
	* gcc.dg/tree-ssa/andor-3.c: Likewise.
	* gcc.dg/tree-ssa/andor-4.c: Likewise.
	* gcc.dg/tree-ssa/andor-5.c: Likewise.
	* gcc.dg/vect/no-vfa-pr29145.c: Likewise.
	* gcc.dg/vect/vect-cond-10.c: Likewise.
	* gcc.dg/vect/vect-cond-9.c: Likewise.
	* gcc.dg/vect/vect-widen-mult-const-s16.c: Likewise.
	* gcc.dg/vect/vect-widen-mult-const-u16.c: Likewise.
	* gcc.dg/vect/vect-widen-mult-half-u8.c: Likewise.
	* gcc.target/i386/bmi-1.c: Likewise.
	* gcc.target/i386/bmi-2.c: Likewise.
	* gcc.target/i386/pr56564-2.c: Likewise.
	* g++.dg/opt/pr30965.C: Likewise.
	* g++.dg/tree-ssa/pr19637.C: Likewise.
	* gcc.dg/guality/csttest.c: Likewise.
	* gcc.dg/ipa/iinline-4.c: Likewise.
	* gcc.dg/ipa/iinline-7.c: Likewise.
	* gcc.dg/ipa/ipa-pta-13.c: Likewise.

diff --git a/gcc/testsuite/c-c++-common/rotate-1.c b/gcc/testsuite/c-c++-common/rotate-1.c
index afdaa28..bca9dd8 100644
--- a/gcc/testsuite/c-c++-common/rotate-1.c
+++ b/gcc/testsuite/c-c++-common/rotate-1.c
@@ -1,6 +1,6 @@
 /* Check rotate pattern detection.  */
 /* { dg-do compile } */
-/* { dg-options "-O2 -fdump-tree-optimized" } */
+/* { dg-options "-O2 -fno-ipa-icf -fdump-tree-optimized" } */
 /* { dg-final { scan-tree-dump-times "r\[<>]\[<>]" 96 "optimized" } } */
 /* { dg-final { cleanup-tree-dump "optimized" } } */
 
diff --git a/gcc/testsuite/c-c++-common/rotate-2.c b/gcc/testsuite/c-c++-common/rotate-2.c
index 109fd32..4ffa218 100644
--- a/gcc/testsuite/c-c++-common/rotate-2.c
+++ b/gcc/testsuite/c-c++-common/rotate-2.c
@@ -1,6 +1,6 @@
 /* Check rotate pattern detection.  */
 /* { dg-do compile } */
-/* { dg-options "-O2 -fdump-tree-optimized" } */
+/* { dg-options "-O2 -fno-ipa-icf -fdump-tree-optimized" } */
 /* Rotates should be recognized only in functions with | instead of + or ^,
    or in functions that have constant shift counts (unused attribute on y).  */
 /* { dg-final { scan-tree-dump-times "r\[<>]\[<>]" 48 "optimized" } } */
diff --git a/gcc/testsuite/c-c++-common/rotate-3.c b/gcc/testsuite/c-c++-common/rotate-3.c
index 8dc8313..aaa9f50 100644
--- a/gcc/testsuite/c-c++-common/rotate-3.c
+++ b/gcc/testsuite/c-c++-common/rotate-3.c
@@ -1,6 +1,6 @@
 /* Check rotate pattern detection.  */
 /* { dg-do compile } */
-/* { dg-options "-O2 -fdump-tree-optimized" } */
+/* { dg-options "-O2 -fno-ipa-icf -fdump-tree-optimized" } */
 /* { dg-final { scan-tree-dump-times "r\[<>]\[<>]" 96 "optimized" } } */
 /* { dg-final { cleanup-tree-dump "optimized" } } */
 
diff --git a/gcc/testsuite/c-c++-common/rotate-4.c b/gcc/testsuite/c-c++-common/rotate-4.c
index 2f433b3..0a21177 100644
--- a/gcc/testsuite/c-c++-common/rotate-4.c
+++ b/gcc/testsuite/c-c++-common/rotate-4.c
@@ -1,6 +1,6 @@
 /* Check rotate pattern detection.  */
 /* { dg-do compile } */
-/* { dg-options "-O2 -fdump-tree-optimized" } */
+/* { dg-options "-O2 -fno-ipa-icf -fdump-tree-optimized" } */
 /* Rotates should be recognized only in functions with | instead of + or ^,
    or in functions that have constant shift counts (unused attribute on y).  */
 /* { dg-final { scan-tree-dump-times "r\[<>]\[<>]" 48 "optimized" } } */
diff --git a/gcc/testsuite/g++.dg/cpp0x/rv-return.C b/gcc/testsuite/g++.dg/cpp0x/rv-return.C
index 12a15aa..6d57209 100644
--- a/gcc/testsuite/g++.dg/cpp0x/rv-return.C
+++ b/gcc/testsuite/g++.dg/cpp0x/rv-return.C
@@ -1,5 +1,6 @@
 // PR c++/41815
 // { dg-do compile { target c++11 } }
+// { dg-options "-fno-ipa-icf" }
 
 template<typename T, typename U> struct same_type;
 template<typename T> struct same_type<T, T> {};
diff --git a/gcc/testsuite/g++.dg/cpp0x/rv1n.C b/gcc/testsuite/g++.dg/cpp0x/rv1n.C
index 204ca31..f5e7568 100644
--- a/gcc/testsuite/g++.dg/cpp0x/rv1n.C
+++ b/gcc/testsuite/g++.dg/cpp0x/rv1n.C
@@ -1,8 +1,10 @@
 // I, Howard Hinnant, hereby place this code in the public domain.
+/* { dg-additional-options "-fno-ipa-icf" } */
 
 // Test overload resolution among reference types
 
 // { dg-do compile { target c++11 } }
+// { dg-additional-options "-fno-ipa-icf" }
 
 template <bool> struct sa;
 template <> struct sa<true> {};
diff --git a/gcc/testsuite/g++.dg/cpp0x/rv1p.C b/gcc/testsuite/g++.dg/cpp0x/rv1p.C
index e4c0ab1..e87ec0e 100644
--- a/gcc/testsuite/g++.dg/cpp0x/rv1p.C
+++ b/gcc/testsuite/g++.dg/cpp0x/rv1p.C
@@ -4,6 +4,7 @@
 
 // { dg-do compile { target c++11 } }
 // { dg-skip-if "packed attribute missing for struct one/three/five/seven" { "epiphany-*-*" } { "*" } { "" } }
+// { dg-additional-options "-fno-ipa-icf" }
 
 template <bool> struct sa;
 template <> struct sa<true> {};
diff --git a/gcc/testsuite/g++.dg/cpp0x/rv2n.C b/gcc/testsuite/g++.dg/cpp0x/rv2n.C
index 9677f58..663a66b 100644
--- a/gcc/testsuite/g++.dg/cpp0x/rv2n.C
+++ b/gcc/testsuite/g++.dg/cpp0x/rv2n.C
@@ -3,7 +3,7 @@
 // Test overload resolution among reference types
 
 // { dg-do compile { target c++11 } }
-// { dg-options "" }
+// { dg-options "-fno-ipa-icf" }
 
 template <bool> struct sa;
 template <> struct sa<true> {};
diff --git a/gcc/testsuite/g++.dg/cpp0x/rv3n.C b/gcc/testsuite/g++.dg/cpp0x/rv3n.C
index 8a1730b..b7c1d7a 100644
--- a/gcc/testsuite/g++.dg/cpp0x/rv3n.C
+++ b/gcc/testsuite/g++.dg/cpp0x/rv3n.C
@@ -3,7 +3,7 @@
 // Test overload resolution among reference types
 
 // { dg-do compile { target c++11 } }
-// { dg-options "" }
+// { dg-options "-fno-ipa-icf" }
 
 template <bool> struct sa;
 template <> struct sa<true> {};
diff --git a/gcc/testsuite/g++.dg/cpp0x/rv4n.C b/gcc/testsuite/g++.dg/cpp0x/rv4n.C
index e64856d..29deb3f 100644
--- a/gcc/testsuite/g++.dg/cpp0x/rv4n.C
+++ b/gcc/testsuite/g++.dg/cpp0x/rv4n.C
@@ -3,7 +3,7 @@
 // Test overload resolution among reference types
 
 // { dg-do compile { target c++11 } }
-// { dg-options "" }
+// { dg-options "-fno-ipa-icf" }
 
 template <bool> struct sa;
 template <> struct sa<true> {};
diff --git a/gcc/testsuite/g++.dg/cpp0x/rv5n.C b/gcc/testsuite/g++.dg/cpp0x/rv5n.C
index 90d5418..f11d07a 100644
--- a/gcc/testsuite/g++.dg/cpp0x/rv5n.C
+++ b/gcc/testsuite/g++.dg/cpp0x/rv5n.C
@@ -3,7 +3,7 @@
 // Test overload resolution among reference types
 
 // { dg-do compile { target c++11 } }
-// { dg-options "" }
+// { dg-options "-fno-ipa-icf" }
 
 template <bool> struct sa;
 template <> struct sa<true> {};
diff --git a/gcc/testsuite/g++.dg/cpp0x/rv6n.C b/gcc/testsuite/g++.dg/cpp0x/rv6n.C
index 5bd9a23..0ebbe33 100644
--- a/gcc/testsuite/g++.dg/cpp0x/rv6n.C
+++ b/gcc/testsuite/g++.dg/cpp0x/rv6n.C
@@ -3,7 +3,7 @@
 // Test overload resolution among reference types
 
 // { dg-do compile { target c++11 } }
-// { dg-options "" }
+// { dg-options "-fno-ipa-icf" }
 
 template <bool> struct sa;
 template <> struct sa<true> {};
diff --git a/gcc/testsuite/g++.dg/cpp0x/rv7n.C b/gcc/testsuite/g++.dg/cpp0x/rv7n.C
index 38ca7b8..d9e371b 100644
--- a/gcc/testsuite/g++.dg/cpp0x/rv7n.C
+++ b/gcc/testsuite/g++.dg/cpp0x/rv7n.C
@@ -3,7 +3,7 @@
 // Test overload resolution among reference types
 
 // { dg-do compile { target c++11 } }
-// { dg-options "" }
+// { dg-options "-fno-ipa-icf" }
 
 template <bool> struct sa;
 template <> struct sa<true> {};
diff --git a/gcc/testsuite/g++.dg/opt/pr30965.C b/gcc/testsuite/g++.dg/opt/pr30965.C
index 91bb55c..45393fd 100644
--- a/gcc/testsuite/g++.dg/opt/pr30965.C
+++ b/gcc/testsuite/g++.dg/opt/pr30965.C
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O3 -fdump-tree-optimized" } */
+/* { dg-options "-O3 -fno-ipa-icf -fdump-tree-optimized" } */
 
 #include <tr1/functional>
 #include <algorithm>
diff --git a/gcc/testsuite/g++.dg/tree-ssa/pr19637.C b/gcc/testsuite/g++.dg/tree-ssa/pr19637.C
index 2d1dcce..92f673f 100644
--- a/gcc/testsuite/g++.dg/tree-ssa/pr19637.C
+++ b/gcc/testsuite/g++.dg/tree-ssa/pr19637.C
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O2 -fdump-tree-dom1" } */
+/* { dg-options "-O2 -fno-ipa-icf -fdump-tree-dom1" } */
 
 #include <new>
 
diff --git a/gcc/testsuite/gcc.dg/guality/csttest.c b/gcc/testsuite/gcc.dg/guality/csttest.c
index 4480c71..05e1ad7 100644
--- a/gcc/testsuite/gcc.dg/guality/csttest.c
+++ b/gcc/testsuite/gcc.dg/guality/csttest.c
@@ -1,6 +1,6 @@
 /* PR debug/49676 */
 /* { dg-do run { target lp64 } } */
-/* { dg-options "-g" } */
+/* { dg-options "-g -fno-ipa-icf" } */
 
 volatile int v;
 
diff --git a/gcc/testsuite/gcc.dg/ipa/iinline-4.c b/gcc/testsuite/gcc.dg/ipa/iinline-4.c
index 71faae2..3f295df 100644
--- a/gcc/testsuite/gcc.dg/ipa/iinline-4.c
+++ b/gcc/testsuite/gcc.dg/ipa/iinline-4.c
@@ -1,7 +1,7 @@
 /* Verify that simple indirect calls are inlined even without early
    inlining..  */
 /* { dg-do compile } */
-/* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining"  } */
+/* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining -fno-ipa-icf"  } */
 
 struct S
 {
diff --git a/gcc/testsuite/gcc.dg/ipa/iinline-7.c b/gcc/testsuite/gcc.dg/ipa/iinline-7.c
index c95d374..61e8d0b 100644
--- a/gcc/testsuite/gcc.dg/ipa/iinline-7.c
+++ b/gcc/testsuite/gcc.dg/ipa/iinline-7.c
@@ -1,7 +1,7 @@
 /* Verify that simple indirect calls are inlined even without early
    inlining..  */
 /* { dg-do run } */
-/* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining"  } */
+/* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining -fno-ipa-icf"  } */
 
 extern void abort (void);
 
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-pta-13.c b/gcc/testsuite/gcc.dg/ipa/ipa-pta-13.c
index 0f46e98..f7f95f4 100644
--- a/gcc/testsuite/gcc.dg/ipa/ipa-pta-13.c
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-pta-13.c
@@ -1,5 +1,5 @@
 /* { dg-do link } */
-/* { dg-options "-O2 -fipa-pta -fdump-ipa-pta-details -fdump-tree-fre2" } */
+/* { dg-options "-O2 -fipa-pta -fdump-ipa-pta-details -fdump-tree-fre2 -fno-ipa-icf" } */
 
 static int x, y;
 
diff --git a/gcc/testsuite/gcc.dg/ipa/ipacost-1.c b/gcc/testsuite/gcc.dg/ipa/ipacost-1.c
index 4fce41e..9603afe 100644
--- a/gcc/testsuite/gcc.dg/ipa/ipacost-1.c
+++ b/gcc/testsuite/gcc.dg/ipa/ipacost-1.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-Os -fipa-cp -fdump-ipa-cp -fno-early-inlining -fdump-tree-optimized"  } */
+/* { dg-options "-Os -fipa-cp -fdump-ipa-cp -fno-early-inlining -fdump-tree-optimized -fno-ipa-icf"  } */
 
 int array[100];
 
diff --git a/gcc/testsuite/gcc.dg/ipa/ipacost-2.c b/gcc/testsuite/gcc.dg/ipa/ipacost-2.c
index ceb524e..e7074f1 100644
--- a/gcc/testsuite/gcc.dg/ipa/ipacost-2.c
+++ b/gcc/testsuite/gcc.dg/ipa/ipacost-2.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O3 -fipa-cp -fipa-cp-clone -fdump-ipa-cp -fno-early-inlining -fdump-tree-optimized"  } */
+/* { dg-options "-O3 -fipa-cp -fipa-cp-clone -fdump-ipa-cp -fno-early-inlining -fdump-tree-optimized -fno-ipa-icf"  } */
 /* { dg-add-options bind_pic_locally } */
 
 int array[100];
diff --git a/gcc/testsuite/gcc.dg/ipa/ipcp-agg-6.c b/gcc/testsuite/gcc.dg/ipa/ipcp-agg-6.c
index 050e13b..5d6425b 100644
--- a/gcc/testsuite/gcc.dg/ipa/ipcp-agg-6.c
+++ b/gcc/testsuite/gcc.dg/ipa/ipcp-agg-6.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O3 -fno-ipa-sra -fdump-ipa-cp-details -fdump-tree-optimized-slim"  } */
+/* { dg-options "-O3 -fno-ipa-sra -fdump-ipa-cp-details -fdump-tree-optimized-slim -fno-ipa-icf"  } */
 /* { dg-add-options bind_pic_locally } */
 
 struct S
diff --git a/gcc/testsuite/gcc.dg/ipa/remref-2a.c b/gcc/testsuite/gcc.dg/ipa/remref-2a.c
index 1e0df2e..4697a18 100644
--- a/gcc/testsuite/gcc.dg/ipa/remref-2a.c
+++ b/gcc/testsuite/gcc.dg/ipa/remref-2a.c
@@ -1,7 +1,7 @@
 /* Verify that indirect inlining can also remove references of the functions it
    discovers calls for.  */
 /* { dg-do compile } */
-/* { dg-options "-O3 -fno-early-inlining -fno-ipa-cp -fdump-ipa-inline -fdump-tree-optimized"  } */
+/* { dg-options "-O3 -fno-early-inlining -fno-ipa-cp -fdump-ipa-inline -fdump-tree-optimized -fno-ipa-icf"  } */
 
 int global;
 
diff --git a/gcc/testsuite/gcc.dg/ipa/remref-2b.c b/gcc/testsuite/gcc.dg/ipa/remref-2b.c
index 554f306..7799033 100644
--- a/gcc/testsuite/gcc.dg/ipa/remref-2b.c
+++ b/gcc/testsuite/gcc.dg/ipa/remref-2b.c
@@ -2,7 +2,7 @@
    discovers calls for, even when nodes being inlined are virtual IPA-CP
    clones.  */
 /* { dg-do compile } */
-/* { dg-options "-O3 -fno-early-inlining -fdump-ipa-cp-details -fdump-ipa-inline -fdump-tree-optimized"  } */
+/* { dg-options "-O3 -fno-early-inlining -fdump-ipa-cp-details -fdump-ipa-inline -fdump-tree-optimized -fno-ipa-icf"  } */
 
 
 int global;
diff --git a/gcc/testsuite/gcc.dg/pr46309-2.c b/gcc/testsuite/gcc.dg/pr46309-2.c
index f407e66..00ffee1 100644
--- a/gcc/testsuite/gcc.dg/pr46309-2.c
+++ b/gcc/testsuite/gcc.dg/pr46309-2.c
@@ -1,6 +1,6 @@
 /* PR tree-optimization/46309 */
 /* { dg-do compile } */
-/* { dg-options "-O2 -fdump-tree-reassoc-details" } */
+/* { dg-options "-O2 -fno-ipa-icf -fdump-tree-reassoc-details" } */
 
 int foo (void);
 
diff --git a/gcc/testsuite/gcc.dg/torture/ipa-pta-1.c b/gcc/testsuite/gcc.dg/torture/ipa-pta-1.c
index aae987d..80303a5 100644
--- a/gcc/testsuite/gcc.dg/torture/ipa-pta-1.c
+++ b/gcc/testsuite/gcc.dg/torture/ipa-pta-1.c
@@ -1,5 +1,5 @@
 /* { dg-do compile { target { nonpic } } } */
-/* { dg-options "-fipa-pta -fdump-ipa-pta" } */
+/* { dg-options "-fipa-pta -fdump-ipa-pta -fno-ipa-icf" } */
 /* { dg-skip-if "" { *-*-* } { "-O0" "-fno-fat-lto-objects" } { "" } } */
 
 struct X { char x; char y; };
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/andor-3.c b/gcc/testsuite/gcc.dg/tree-ssa/andor-3.c
index a1401c0..8b2f206 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/andor-3.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/andor-3.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O2 -fdump-tree-optimized" } */
+/* { dg-options "-O2 -fno-ipa-icf -fdump-tree-optimized" } */
 
 int f(int y, int x)
 {
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/andor-4.c b/gcc/testsuite/gcc.dg/tree-ssa/andor-4.c
index 1dbdca7..46a4826 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/andor-4.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/andor-4.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O2 -fdump-tree-optimized" } */
+/* { dg-options "-O2 -fno-ipa-icf -fdump-tree-optimized" } */
 
 int f(int y, int x)
 {
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/andor-5.c b/gcc/testsuite/gcc.dg/tree-ssa/andor-5.c
index 1509727..929851c 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/andor-5.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/andor-5.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O2 -fdump-tree-optimized" } */
+/* { dg-options "-O2 -fno-ipa-icf -fdump-tree-optimized" } */
 
 int f(int y, int x)
 {
diff --git a/gcc/testsuite/gcc.dg/vect/no-vfa-pr29145.c b/gcc/testsuite/gcc.dg/vect/no-vfa-pr29145.c
index e475fff..5896271 100644
--- a/gcc/testsuite/gcc.dg/vect/no-vfa-pr29145.c
+++ b/gcc/testsuite/gcc.dg/vect/no-vfa-pr29145.c
@@ -1,4 +1,5 @@
 /* { dg-require-effective-target vect_int } */
+/* { dg-additional-options "-fno-ipa-icf" } */
 
 #include <stdarg.h>
 #include "tree-vect.h"
diff --git a/gcc/testsuite/gcc.dg/vect/vect-cond-10.c b/gcc/testsuite/gcc.dg/vect/vect-cond-10.c
index 687d42f..da2eb05 100644
--- a/gcc/testsuite/gcc.dg/vect/vect-cond-10.c
+++ b/gcc/testsuite/gcc.dg/vect/vect-cond-10.c
@@ -1,4 +1,5 @@
 /* { dg-require-effective-target vect_cond_mixed } */
+/* { dg-additional-options "-fno-ipa-icf" } */
 
 #include "tree-vect.h"
 
diff --git a/gcc/testsuite/gcc.dg/vect/vect-cond-9.c b/gcc/testsuite/gcc.dg/vect/vect-cond-9.c
index cfa0363..de88fc5 100644
--- a/gcc/testsuite/gcc.dg/vect/vect-cond-9.c
+++ b/gcc/testsuite/gcc.dg/vect/vect-cond-9.c
@@ -1,4 +1,5 @@
 /* { dg-require-effective-target vect_cond_mixed } */
+/* { dg-additional-options "-fno-ipa-icf" } */
 
 #include "tree-vect.h"
 
diff --git a/gcc/testsuite/gcc.dg/vect/vect-widen-mult-const-s16.c b/gcc/testsuite/gcc.dg/vect/vect-widen-mult-const-s16.c
index a2fe975..895bbd0 100644
--- a/gcc/testsuite/gcc.dg/vect/vect-widen-mult-const-s16.c
+++ b/gcc/testsuite/gcc.dg/vect/vect-widen-mult-const-s16.c
@@ -1,4 +1,5 @@
 /* { dg-require-effective-target vect_int } */
+/* { dg-additional-options "-fno-ipa-icf" } */
 
 #include "tree-vect.h"
 #include <stdlib.h>
diff --git a/gcc/testsuite/gcc.dg/vect/vect-widen-mult-const-u16.c b/gcc/testsuite/gcc.dg/vect/vect-widen-mult-const-u16.c
index e712da9..f69abfd 100644
--- a/gcc/testsuite/gcc.dg/vect/vect-widen-mult-const-u16.c
+++ b/gcc/testsuite/gcc.dg/vect/vect-widen-mult-const-u16.c
@@ -1,4 +1,5 @@
 /* { dg-require-effective-target vect_int } */
+/* { dg-additional-options "-fno-ipa-icf" } */
 
 #include "tree-vect.h"
 #include <stdlib.h>
@@ -74,4 +75,3 @@ int main (void)
 /* { dg-final { scan-tree-dump-times "vect_recog_widen_mult_pattern: detected" 2 "vect" { target vect_widen_mult_hi_to_si_pattern } } } */
 /* { dg-final { scan-tree-dump-times "pattern recognized" 2 "vect" { target vect_widen_mult_hi_to_si_pattern } } } */
 /* { dg-final { cleanup-tree-dump "vect" } } */
-
diff --git a/gcc/testsuite/gcc.dg/vect/vect-widen-mult-half-u8.c b/gcc/testsuite/gcc.dg/vect/vect-widen-mult-half-u8.c
index 39078df..3f07585 100644
--- a/gcc/testsuite/gcc.dg/vect/vect-widen-mult-half-u8.c
+++ b/gcc/testsuite/gcc.dg/vect/vect-widen-mult-half-u8.c
@@ -1,4 +1,5 @@
 /* { dg-require-effective-target vect_int } */
+/* { dg-additional-options "-fno-ipa-icf" } */
 
 #include "tree-vect.h"
 #include <stdlib.h>
diff --git a/gcc/testsuite/gcc.target/i386/bmi-1.c b/gcc/testsuite/gcc.target/i386/bmi-1.c
index c66a9d8..738705e 100644
--- a/gcc/testsuite/gcc.target/i386/bmi-1.c
+++ b/gcc/testsuite/gcc.target/i386/bmi-1.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O2 -mbmi " } */
+/* { dg-options "-O2 -fno-ipa-icf -mbmi " } */
 /* { dg-final { scan-assembler "andn\[^\\n]*eax" } } */
 /* { dg-final { scan-assembler-times "bextr\[ \\t]+\[^\\n]*eax" 2 } } */
 /* { dg-final { scan-assembler-times "blsi\[^\\n]*eax" 2 } } */
diff --git a/gcc/testsuite/gcc.target/i386/bmi-2.c b/gcc/testsuite/gcc.target/i386/bmi-2.c
index 6eea66a..25fb86b 100644
--- a/gcc/testsuite/gcc.target/i386/bmi-2.c
+++ b/gcc/testsuite/gcc.target/i386/bmi-2.c
@@ -1,5 +1,5 @@
 /* { dg-do compile { target { ! { ia32 }  } } } */
-/* { dg-options "-O2 -mbmi " } */
+/* { dg-options "-O2 -fno-ipa-icf -mbmi " } */
 /* { dg-final { scan-assembler "andn\[^\\n]*rax" } } */
 /* { dg-final { scan-assembler-times "bextr\[ \\t]+\[^\\n]*rax" 2 } } */
 /* { dg-final { scan-assembler-times "blsi\[^\\n]*rax" 2 } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr56564-2.c b/gcc/testsuite/gcc.target/i386/pr56564-2.c
index fc89a4c..c42bfae 100644
--- a/gcc/testsuite/gcc.target/i386/pr56564-2.c
+++ b/gcc/testsuite/gcc.target/i386/pr56564-2.c
@@ -1,6 +1,6 @@
 /* PR target/56564 */
 /* { dg-do compile { target { *-*-linux* && lp64 } } } */
-/* { dg-options "-O3 -fno-pic -fdump-tree-optimized" } */
+/* { dg-options "-O3 -fno-pic -fno-ipa-icf -fdump-tree-optimized" } */
 
 struct S { long a, b; } s = { 5, 6 };
 char t[16] = { 7 };
-- 
1.8.4.5


^ permalink raw reply	[flat|nested] 70+ messages in thread

* [PATCH 3/5] IPA ICF pass
  2014-06-16 10:07 [PATCH 1/5] New Identical Code Folding IPA pass mliska
                   ` (2 preceding siblings ...)
  2014-06-16 10:07 ` [PATCH 2/5] Existing call graph infrastructure enhancement mliska
@ 2014-06-16 10:07 ` mliska
  2014-06-20  7:32   ` Trevor Saunders
  2014-06-24 20:31   ` Jeff Law
  2014-06-17 19:49 ` [PATCH 1/5] New Identical Code Folding IPA pass Jeff Law
                   ` (2 subsequent siblings)
  6 siblings, 2 replies; 70+ messages in thread
From: mliska @ 2014-06-16 10:07 UTC (permalink / raw)
  To: gcc-patches; +Cc: hubicka

Hello,
   this is core of IPA ICF patchset. It adds new pass and registers all needed stuff related to a newly introduced interprocedural optimization.

Algorithm description:
  In LGEN, we visit all read-only variables and functions. For each symbol, a hash value based on e.g. number of arguments,
  number of BB, GIMPLE CODES is computed (similar hash is computed for read-only variables). This kind of information is streamed
  for LTO.

  In WPA, we build congruence classes for all symbols having a same hash value. For functions, these classes are subdivided in WPA by argument type comparison. Each reference (a call or a variable reference) to another semantic item candidate is marked and stored for further congruence class reduction (similar algorithm as Value Numbering:  www.cs.ucr.edu/~gupta/teaching/553-07/Papers/value.pdf).

  For every congruence class of functions with more than one semantic function, we load function body. Having this information, we can
  process complete semantic function equality and subdivide such congruence class. Read-only variable class members are also deeply compared.

  After that, we process Value numbering algorithm to do a final subdivision. Finally, all items belonging to a congruence class with more than one
  item are merged.

Martin

Changelog:

2014-06-13  Martin Liska  <mliska@suse.cz>
	    Jan Hubicka  <hubicka@ucw.cz>

	* Makefile.in: New pass object file added.
	* common.opt: New -fipa-icf flag introduced.
	* doc/invoke.texi: Documentation enhanced for the pass.
	* lto-section-in.c: New LTO section for a summary created by IPA-ICF.
	* lto-streamer.h: New section name introduced.
	* opts.c: Optimization is added to -O2.
	* passes.def: New pass added.
	* timevar.def: New time var for IPA-ICF.
	* tree-pass.h: Pass construction function.
	* ipa-icf.h: New pass header file added.
	* ipa-icf.c: New pass source file added.

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 5587b75..4b59418 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1276,6 +1276,7 @@ OBJS = \
 	ipa-profile.o \
 	ipa-prop.o \
 	ipa-pure-const.o \
+	ipa-icf.o \
 	ipa-reference.o \
 	ipa-ref.o \
 	ipa-utils.o \
diff --git a/gcc/common.opt b/gcc/common.opt
index 7f05092..3661dcc 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -1409,6 +1409,10 @@ fipa-pure-const
 Common Report Var(flag_ipa_pure_const) Init(0) Optimization
 Discover pure and const functions
 
+fipa-icf
+Common Report Var(flag_ipa_icf) Optimization
+Perform Identical Code Folding for functions and read-only variables
+
 fipa-reference
 Common Report Var(flag_ipa_reference) Init(0) Optimization
 Discover readonly and non addressable static variables
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 3c02341..b2bbe69 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -377,7 +377,7 @@ Objective-C and Objective-C++ Dialects}.
 -fif-conversion2 -findirect-inlining @gol
 -finline-functions -finline-functions-called-once -finline-limit=@var{n} @gol
 -finline-small-functions -fipa-cp -fipa-cp-clone @gol
--fipa-pta -fipa-profile -fipa-pure-const -fipa-reference @gol
+-fipa-pta -fipa-profile -fipa-pure-const -fipa-reference -fipa-icf @gol
 -fira-algorithm=@var{algorithm} @gol
 -fira-region=@var{region} -fira-hoist-pressure @gol
 -fira-loop-pressure -fno-ira-share-save-slots @gol
@@ -6947,6 +6947,7 @@ also turns on the following optimization flags:
 -finline-small-functions @gol
 -findirect-inlining @gol
 -fipa-sra @gol
+-fipa-icf @gol
 -fisolate-erroneous-paths-dereference @gol
 -foptimize-sibling-calls @gol
 -fpartial-inlining @gol
@@ -7862,6 +7863,14 @@ it may significantly increase code size
 (see @option{--param ipcp-unit-growth=@var{value}}).
 This flag is enabled by default at @option{-O3}.
 
+@item -fipa-icf
+@opindex fipa-icf
+Perform Identical Code Folding for functions and read-only variables.
+Behavior is similar to Gold Linker ICF optimization. Symbols proved
+as semantically equivalent are redirected to corresponding symbol. The pass
+sensitively decides for usage of alias, thunk or local redirection.
+This flag is enabled by default at @option{-O2}.
+
 @item -fisolate-erroneous-paths-dereference
 Detect paths which trigger erroneous or undefined behaviour due to
 dereferencing a NULL pointer.  Isolate those paths from the main control
diff --git a/gcc/ipa-icf.c b/gcc/ipa-icf.c
new file mode 100644
index 0000000..628d257
--- /dev/null
+++ b/gcc/ipa-icf.c
@@ -0,0 +1,3247 @@
+/* Interprocedural Identical Code Folding pass
+   Copyright (C) 2014 Free Software Foundation, Inc.
+
+   Contributed by Jan Hubicka <hubicka@ucw.cz> and Martin Liska <mliska@suse.cz>
+
+This file is part of GCC.
+
+GCC 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, or (at your option) any later
+version.
+
+GCC 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 GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+/* Interprocedural Identical Code Folding for functions and
+   read-only variables.
+
+   The goal of this transformation is to discover functions and read-only
+   variables which do have exactly the same semantics.
+
+   In case of functions,
+   we could either create a virtual clone or do a simple function wrapper
+   that will call equivalent function. If the function is just locally visible,
+   all function calls can be redirected. For read-only variables, we create
+   aliases if possible.
+
+   Optimization pass arranges as follows:
+   1) All functions and read-only variables are visited and internal
+      data structure, either sem_function or sem_variables is created.
+   2) For every symbol from the previoues step, VAR_DECL and FUNCTION_DECL are
+      saved and matched to corresponding sem_items.
+   3) These declaration are ignored for equality check and are solved
+      by Value Numbering algorithm published by Alpert, Zadeck in 1992.
+   4) We compute hash value for each symbol.
+   5) Congruence classes are created based on hash value. If hash value are
+      equal, equals function is called and symbols are deeply compared.
+      We must prove that all SSA names, declarations and other items
+      correspond.
+   6) Value Numbering is executed for these classes. At the end of the process
+      all symbol members in remaining classes can be mrged.
+   7) Merge operation creates alias in case of read-only variables. For
+      callgraph node, we must decide if we can redirect local calls,
+      create an alias or a thunk.
+
+*/
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tree.h"
+#include "basic-block.h"
+#include "tree-ssa-alias.h"
+#include "internal-fn.h"
+#include "gimple-expr.h"
+#include "is-a.h"
+#include "gimple.h"
+#include "expr.h"
+#include "gimple-iterator.h"
+#include "gimple-ssa.h"
+#include "tree-cfg.h"
+#include "tree-phinodes.h"
+#include "stringpool.h"
+#include "tree-ssanames.h"
+#include "tree-dfa.h"
+#include "tree-pass.h"
+#include "gimple-pretty-print.h"
+#include "ipa-inline.h"
+#include "cfgloop.h"
+#include "except.h"
+#include "hash-table.h"
+#include "coverage.h"
+#include "pointer-set.h"
+#include "attribs.h"
+#include "print-tree.h"
+#include "lto-streamer.h"
+#include "data-streamer.h"
+#include "ipa-utils.h"
+#include "ipa-icf.h"
+
+namespace {
+
+func_checker::func_checker (): initialized (false)
+{
+}
+
+/* Itializes internal structures according to given number of
+   source and target SSA names. The number of source names is SSA_SOURCE,
+   respectively SSA_TARGET.  */
+
+void
+func_checker::initialize (unsigned ssa_source, unsigned ssa_target)
+{
+  release ();
+  initialized = true;
+
+  source_ssa_names.create (ssa_source);
+  target_ssa_names.create (ssa_target);
+
+  for (unsigned int i = 0; i < ssa_source; i++)
+    source_ssa_names.safe_push (-1);
+
+  for (unsigned int i = 0; i < ssa_target; i++)
+    target_ssa_names.safe_push (-1);
+
+  edge_map = new pointer_map <edge> ();
+
+  decl_map = new pointer_map <tree> ();
+}
+
+/* Memory release routine.  */
+
+void
+func_checker::release (void)
+{
+  if (!initialized)
+    return;
+
+  delete edge_map;
+  delete decl_map;
+  source_ssa_names.release();
+  target_ssa_names.release();
+}
+
+/* Verifies that trees T1 and T2 do correspond.  */
+
+bool
+func_checker::compare_ssa_name (tree t1, tree t2)
+{
+  unsigned i1 = SSA_NAME_VERSION (t1);
+  unsigned i2 = SSA_NAME_VERSION (t2);
+
+  if (source_ssa_names[i1] == -1)
+    source_ssa_names[i1] = i2;
+  else if (source_ssa_names[i1] != (int) i2)
+    return false;
+
+  if(target_ssa_names[i2] == -1)
+    target_ssa_names[i2] = i1;
+  else if (target_ssa_names[i2] != (int) i1)
+    return false;
+
+  return true;
+}
+
+/* Verification function for edges E1 and E2.  */
+
+bool
+func_checker::compare_edge (edge e1, edge e2)
+{
+  if (e1->flags != e2->flags)
+    return false;
+
+  edge *slot = edge_map->contains (e1);
+  if (slot)
+    {
+      SE_EXIT_DEBUG (*slot == e2);
+    }
+  else
+    {
+      slot = edge_map->insert (e1);
+      *slot = e2;
+    }
+
+  return true;
+}
+
+/* Verification function for declaration trees T1 and T2 that
+   come from functions FUNC1 and FUNC2.  */
+
+bool
+func_checker::compare_decl (tree t1, tree t2, tree func1, tree func2)
+{
+  if (!auto_var_in_fn_p (t1, func1) || !auto_var_in_fn_p (t2, func2))
+    SE_EXIT_DEBUG (t1 == t2);
+
+  if (!types_compatible_p (TREE_TYPE (t1), TREE_TYPE (t2)))
+    SE_EXIT_FALSE ();
+
+  tree *slot = decl_map->contains (t1);
+  if (slot)
+    {
+      SE_EXIT_DEBUG (*slot == t2);
+    }
+  else
+    {
+      slot = decl_map->insert (t1);
+      *slot = t2;
+    }
+
+  return true;
+}
+
+/* Congruence class constructor for a new class with _ID.  */
+
+congruence_class::congruence_class (unsigned int _id): id(_id)
+{
+  members.create (2);
+}
+
+/* Constructor for key value pair, where _ITEM is key and _INDEX is a target.  */
+
+sem_usage_pair::sem_usage_pair (sem_item *_item, unsigned int _index):
+  item (_item), index (_index)
+{
+}
+
+/* Semantic item constructor for a node of _TYPE, where STACK is used
+   for bitmap memory allocation.  */
+
+sem_item::sem_item (enum sem_item_type _type,
+		    bitmap_obstack *stack): type(_type), hash(0)
+{
+  setup (stack);
+}
+
+/* Semantic item constructor for a node of _TYPE, where STACK is used
+   for bitmap memory allocation. The item is based on symtab node _NODE
+   with computed _HASH.  */
+
+sem_item::sem_item (enum sem_item_type _type, struct symtab_node *_node,
+		    hashval_t _hash, bitmap_obstack *stack): type(_type),
+  node (_node), hash (_hash)
+{
+  decl = node->decl;
+  setup (stack);
+}
+
+/* Initialize internal data structures. Bitmap STACK is used for
+   bitmap memory allocation process.  */
+
+void
+sem_item::setup (bitmap_obstack *stack)
+{
+  gcc_checking_assert (node);
+
+  refs.create (0);
+  tree_refs.create (0);
+  usages.create (0);
+  tree_refs_set = pointer_set_create ();
+  usage_index_bitmap = BITMAP_ALLOC (stack);
+}
+
+sem_item::~sem_item ()
+{
+  if (tree_refs_set)
+    pointer_set_destroy (tree_refs_set);
+
+  for (unsigned i = 0; i < usages.length (); i++)
+    delete usages[i];
+}
+
+/* Dump function for debugging purpose.  */
+
+DEBUG_FUNCTION void
+sem_item::dump (void)
+{
+  if (dump_file)
+    {
+      fprintf (dump_file, "[%s] %s (%u) (tree:%p)\n", type == FUNC ? "func" : "var",
+	       name(), node->order, (void *) node->decl);
+      fprintf (dump_file, "  hash: %u\n", get_hash ());
+      fprintf (dump_file, "  references: ");
+
+      for (unsigned i = 0; i < refs.length (); i++)
+	fprintf (dump_file, "%s%s ", refs[i]->name (),
+		 i < refs.length() - 1 ? "," : "");
+
+      fprintf (dump_file, "\n");
+    }
+}
+
+/* Compare two types if are same aliases in case of strict aliasing
+   is enabled.  */
+bool
+sem_item::compare_for_aliasing (tree t1, tree t2)
+{
+  if (flag_strict_aliasing)
+    {
+      alias_set_type s1 = get_deref_alias_set (TREE_TYPE (t1));
+      alias_set_type s2 = get_deref_alias_set (TREE_TYPE (t2));
+
+      return s1 == s2;
+    }
+
+  return true;
+}
+
+/* Semantic function constructor that uses STACK as bitmap memory stack.  */
+
+sem_function::sem_function (bitmap_obstack *stack): sem_item (FUNC, stack),
+  compared_func (NULL)
+{
+  arg_types.create (0);
+}
+
+/*  Constructor based on callgraph node _NODE with computed hash _HASH.
+    Bitmap STACK is used for memory allocation.  */
+sem_function::sem_function (cgraph_node *node, hashval_t hash,
+			    bitmap_obstack *stack):
+  sem_item (FUNC, node, hash, stack), bb_sorted (NULL), compared_func (NULL)
+{
+  arg_types.create (0);
+}
+
+sem_function::~sem_function ()
+{
+  if (!bb_sorted)
+    return;
+
+  for (unsigned i = 0; i < bb_count; i++)
+    free (bb_sorted[i]);
+
+  free (bb_sizes);
+  free (bb_sorted);
+}
+
+/* Gets symbol name of the item.  */
+
+const char *
+sem_function::name (void)
+{
+  return node->name ();
+}
+
+/* Gets assembler name of the item.  */
+
+const char *
+sem_function::asm_name (void)
+{
+  return node->asm_name ();
+}
+
+/* Calculates hash value based on a BASIC_BLOCK.  */
+
+hashval_t
+sem_function::get_bb_hash (const sem_bb_t *basic_block)
+{
+  hashval_t hash = basic_block->nondbg_stmt_count;
+  hash = iterative_hash_object (basic_block->edge_count, hash);
+
+  return hash;
+}
+
+/* References independent hash function.  */
+
+hashval_t
+sem_function::get_hash (void)
+{
+  if(!hash)
+    {
+      hash = 177454; /* Random number for function type.  */
+
+      hash = iterative_hash_object (arg_count, hash);
+      hash = iterative_hash_object (bb_count, hash);
+      hash = iterative_hash_object (edge_count, hash);
+      hash = iterative_hash_object (cfg_checksum, hash);
+      hash = iterative_hash_object (gcode_hash, hash);
+
+      for (unsigned i = 0; i < bb_count; i++)
+	hash = iterative_hash_object (hash, get_bb_hash (bb_sorted[i]));
+
+      for (unsigned i = 0; i < bb_count; i++)
+	hash = iterative_hash_object (bb_sizes[i], hash);
+    }
+
+  return hash;
+}
+
+/* Fast equality function based on knowledge known in WPA.  */
+
+bool
+sem_function::equals_wpa (sem_item *item)
+{
+  if (item->type != FUNC)
+    return false;
+
+  compared_func = static_cast<sem_function *> (item);
+
+  if (arg_count != compared_func->arg_count)
+    SE_EXIT_FALSE_WITH_MSG ("different number of arguments");
+
+  gcc_assert (arg_count == arg_types.length ());
+  gcc_assert (compared_func->arg_count == compared_func->arg_types.length ());
+
+  /* Checking types of arguments.  */
+  for (unsigned i = 0; i < arg_count; i++)
+    if (!types_compatible_p (arg_types[i], compared_func->arg_types[i]))
+      SE_EXIT_FALSE_WITH_MSG("argument type is different");
+
+  /* Result type checking.  */
+  if (!types_compatible_p (result_type, compared_func->result_type))
+    SE_EXIT_FALSE_WITH_MSG("result types are different");
+
+  return true;
+}
+
+/* Returns true if the item equals to ITEM given as arguemnt.  */
+
+bool
+sem_function::equals (sem_item *item)
+{
+  bool eq = equals_private (item);
+
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    fprintf (dump_file,
+	     "Equals called for:%s:%s (%u:%u) (%s:%s) with result: %s\n\n",
+	     name(), item->name (), node->order, item->node->order, asm_name (),
+	     item->asm_name (), eq ? "true" : "false");
+
+  return eq;
+}
+
+/* Processes function equality comparison.  */
+
+bool
+sem_function::equals_private (sem_item *item)
+{
+  if (item->type != FUNC)
+    return false;
+
+  basic_block bb1, bb2;
+  edge e1, e2;
+  edge_iterator ei1, ei2;
+  int *bb_dict = NULL;
+  bool result = true;
+  tree arg1, arg2;
+
+  compared_func = static_cast<sem_function *> (item);
+
+  gcc_assert (decl != item->decl);
+
+  if (arg_count != compared_func->arg_count
+      || bb_count != compared_func->bb_count
+      || edge_count != compared_func->edge_count
+      || cfg_checksum != compared_func->cfg_checksum)
+    SE_EXIT_FALSE();
+
+  if (!equals_wpa (item))
+    return false;
+
+  /* Checking function arguments.  */
+  tree decl1 = DECL_ATTRIBUTES (decl);
+  tree decl2 = DECL_ATTRIBUTES (compared_func->decl);
+
+  while (decl1)
+    {
+      if (decl2 == NULL)
+	SE_EXIT_FALSE();
+
+      if (get_attribute_name (decl1) != get_attribute_name (decl2))
+	SE_EXIT_FALSE();
+
+      tree attr_value1 = TREE_VALUE (decl1);
+      tree attr_value2 = TREE_VALUE (decl2);
+
+      if (attr_value1 && attr_value2)
+	{
+	  bool ret = compare_operand (TREE_VALUE (attr_value1),
+				      TREE_VALUE (attr_value2), decl,
+				      compared_func->decl);
+	  if (!ret)
+	    SE_EXIT_FALSE_WITH_MSG ("attribute values are different")
+	  }
+      else if (!attr_value1 && !attr_value2)
+	{}
+      else
+	SE_EXIT_FALSE ();
+
+      decl1 = TREE_CHAIN (decl1);
+      decl2 = TREE_CHAIN (decl2);
+    }
+
+  if (decl1 != decl2)
+    SE_EXIT_FALSE();
+
+  checker.initialize (ssa_names_size, compared_func->ssa_names_size);
+
+  for (arg1 = DECL_ARGUMENTS (decl), arg2 = DECL_ARGUMENTS (compared_func->decl);
+       arg1; arg1 = DECL_CHAIN (arg1), arg2 = DECL_CHAIN (arg2))
+    checker.compare_decl (arg1, arg2, decl, compared_func->decl);
+
+  /* Exception handling regions comparison.  */
+  if (!compare_eh_region (region_tree, compared_func->region_tree, decl,
+			  compared_func->decl))
+    SE_EXIT_FALSE();
+
+  /* Checking all basic blocks.  */
+  for (unsigned i = 0; i < bb_count; ++i)
+    if(!compare_bb (bb_sorted[i], compared_func->bb_sorted[i], decl,
+		    compared_func->decl))
+      SE_EXIT_FALSE();
+
+  SE_DUMP_MESSAGE ("All BBs are equal\n");
+
+  /* Basic block edges check.  */
+  for (unsigned i = 0; i < bb_count; ++i)
+    {
+      bb_dict = XNEWVEC (int, bb_count + 2);
+      memset (bb_dict, -1, (bb_count + 2) * sizeof (int));
+
+      bb1 = bb_sorted[i]->bb;
+      bb2 = compared_func->bb_sorted[i]->bb;
+
+      ei2 = ei_start (bb2->preds);
+
+      for (ei1 = ei_start (bb1->preds); ei_cond (ei1, &e1); ei_next (&ei1))
+	{
+	  ei_cond (ei2, &e2);
+
+	  if (!bb_dict_test (bb_dict, e1->src->index, e2->src->index))
+	    SE_EXIT_FALSE_WITH_MSG("edge comparison returns false");
+
+	  if (!bb_dict_test (bb_dict, e1->dest->index, e2->dest->index))
+	    SE_EXIT_FALSE_WITH_MSG("BB comparison returns false");
+
+	  if (e1->flags != e2->flags)
+	    SE_EXIT_FALSE_WITH_MSG("flags comparison returns false");
+
+	  if (!checker.compare_edge (e1, e2))
+	    SE_EXIT_FALSE_WITH_MSG("edge comparison returns false");
+
+	  ei_next (&ei2);
+	}
+    }
+
+  /* Basic block PHI nodes comparison.  */
+  for (unsigned i = 0; i < bb_count; i++)
+    if (!compare_phi_node (bb_sorted[i]->bb, compared_func->bb_sorted[i]->bb,
+			   decl, compared_func->decl))
+      SE_EXIT_FALSE_WITH_MSG ("PHI node comparison returns false");
+
+  return result;
+}
+
+/* Initializes references to another sem_item for tree T.  */
+
+void
+sem_function::init_refs_for_tree (tree t)
+{
+  switch (TREE_CODE (t))
+    {
+    case VAR_DECL:
+    case FUNCTION_DECL:
+      tree_refs.safe_push (t);
+      break;
+    case MEM_REF:
+    case ADDR_EXPR:
+    case OBJ_TYPE_REF:
+      init_refs_for_tree (TREE_OPERAND (t, 0));
+      break;
+    case FIELD_DECL:
+      init_refs_for_tree (DECL_FCONTEXT (t));
+      break;
+    default:
+      break;
+    }
+}
+
+/* Initializes references to another sem_item for gimple STMT of type assign.  */
+
+void
+sem_function::init_refs_for_assign (gimple stmt)
+{
+  if (gimple_num_ops (stmt) != 2)
+    return;
+
+  tree rhs = gimple_op (stmt, 1);
+
+  init_refs_for_tree (rhs);
+}
+
+/* Initializes references to other semantic functions/variables.  */
+
+void
+sem_function::init_refs (void)
+{
+  for (unsigned i = 0; i < bb_count; i++)
+    {
+      basic_block bb = bb_sorted[i]->bb;
+
+      for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi);
+	   gsi_next (&gsi))
+	{
+	  gimple stmt = gsi_stmt (gsi);
+	  hashval_t code = (hashval_t) gimple_code (stmt);
+
+	  switch (code)
+	    {
+	    case GIMPLE_CALL:
+	      {
+		tree funcdecl = gimple_call_fndecl (stmt);
+
+		/* Function pointer variables are not support yet.  */
+		if (funcdecl)
+		  tree_refs.safe_push (funcdecl);
+
+		break;
+	      }
+	    case GIMPLE_ASSIGN:
+	      init_refs_for_assign (stmt);
+	      break;
+	    default:
+	      break;
+	    }
+	}
+    }
+}
+
+/* Merges instance with an ALIAS_ITEM, where alias, thunk or redirection can
+   be applied.  */
+bool
+sem_function::merge (sem_item *alias_item)
+{
+  gcc_assert (alias_item->type == FUNC);
+
+  sem_function *alias_func = static_cast<sem_function *> (alias_item);
+
+  struct cgraph_node *original = get_node ();
+  struct cgraph_node *local_original = original;
+  struct cgraph_node *alias = alias_func->get_node ();
+  bool original_address_matters;
+  bool alias_address_matters;
+
+  bool create_thunk = false;
+  bool create_alias = false;
+  bool redirect_callers = false;
+  bool original_discardable = false;
+
+  /* Do not attempt to mix functions from different user sections;
+     we do not know what user intends with those.  */
+  if (((DECL_SECTION_NAME (original->decl) && !original->implicit_section)
+       || (DECL_SECTION_NAME (alias->decl) && !alias->implicit_section))
+      && DECL_SECTION_NAME (original->decl) != DECL_SECTION_NAME (alias->decl))
+    {
+      if (dump_file)
+	fprintf (dump_file,
+		 "Not unifying; original and alias are in different sections.\n\n");
+      return false;
+    }
+
+  /* See if original is in a section that can be discarded if the main
+     symbol is not used.  */
+  if (DECL_EXTERNAL (original->decl))
+    original_discardable = true;
+  if (original->resolution == LDPR_PREEMPTED_REG
+      || original->resolution == LDPR_PREEMPTED_IR)
+    original_discardable = true;
+  if (symtab_can_be_discarded (original))
+    original_discardable = true;
+
+  /* See if original and/or alias address can be compared for equality.  */
+  original_address_matters
+    = (!DECL_VIRTUAL_P (original->decl)
+       && (original->externally_visible
+	   || address_taken_from_non_vtable_p (original)));
+  alias_address_matters
+    = (!DECL_VIRTUAL_P (alias->decl)
+       && (alias->externally_visible
+	   || address_taken_from_non_vtable_p (alias)));
+
+  /* If alias and original can be compared for address equality, we need
+     to create a thunk.  Also we can not create extra aliases into discardable
+     section (or we risk link failures when section is discarded).  */
+  if ((original_address_matters
+       && alias_address_matters)
+      || original_discardable)
+    {
+      create_thunk = !stdarg_p (TREE_TYPE (alias->decl));
+      create_alias = false;
+      /* When both alias and original are not overwritable, we can save
+         the extra thunk wrapper for direct calls.  */
+      redirect_callers
+	= (!original_discardable
+	   && cgraph_function_body_availability (alias) > AVAIL_OVERWRITABLE
+	   && cgraph_function_body_availability (original) > AVAIL_OVERWRITABLE);
+    }
+  else
+    {
+      create_alias = true;
+      create_thunk = false;
+      redirect_callers = false;
+    }
+
+  if (create_alias && DECL_COMDAT_GROUP (alias->decl))
+    {
+      create_alias = false;
+      create_thunk = true;
+    }
+
+  /* We want thunk to always jump to the local function body
+     unless the body is comdat and may be optimized out.  */
+  if ((create_thunk || redirect_callers)
+      && (!original_discardable
+	  || (DECL_COMDAT_GROUP (original->decl)
+	      && (DECL_COMDAT_GROUP (original->decl)
+		  == DECL_COMDAT_GROUP (alias->decl)))))
+    local_original
+      = cgraph (symtab_nonoverwritable_alias (original));
+
+  if (redirect_callers)
+    {
+      /* If alias is non-overwritable then
+         all direct calls are safe to be redirected to the original.  */
+      bool redirected = false;
+      while (alias->callers)
+	{
+	  struct cgraph_edge *e = alias->callers;
+	  cgraph_redirect_edge_callee (e, local_original);
+	  push_cfun (DECL_STRUCT_FUNCTION (e->caller->decl));
+
+	  if (e->call_stmt)
+	    cgraph_redirect_edge_call_stmt_to_callee (e);
+
+	  pop_cfun ();
+	  redirected = true;
+	}
+
+      /* The alias function is removed just if symbol address
+         does not matters.  */
+      if (!alias_address_matters)
+	cgraph_remove_node (alias);
+
+      if (dump_file && redirected)
+	fprintf (dump_file, "Callgraph local calls have been redirected.\n\n");
+    }
+  /* If the condtion above is not met, we are lucky and can turn the
+     function into real alias.  */
+  else if (create_alias)
+    {
+      /* Remove the function's body.  */
+      ipa_merge_profiles (original, alias);
+      cgraph_release_function_body (alias);
+      cgraph_reset_node (alias);
+
+      /* Create the alias.  */
+      cgraph_create_function_alias (alias_func->decl, decl);
+      symtab_resolve_alias (alias, original);
+
+      if (dump_file)
+	fprintf (dump_file, "Callgraph alias has been created.\n\n");
+    }
+  else if (create_thunk)
+    {
+      if (DECL_COMDAT_GROUP (alias->decl))
+	{
+	  if (dump_file)
+	    fprintf (dump_file, "Callgraph thunk cannot be created because of COMDAT\n");
+
+	  return 0;
+	}
+
+      ipa_merge_profiles (local_original, alias);
+      cgraph_make_wrapper (alias, local_original);
+
+      if (dump_file)
+	fprintf (dump_file, "Callgraph thunk has been created.\n\n");
+    }
+  else if (dump_file)
+    fprintf (dump_file, "Callgraph merge operation cannot be performed.\n\n");
+
+  return true;
+}
+
+/* Dump symbol to FILE.  */
+
+void
+sem_function::dump_to_file (FILE *file)
+{
+  gcc_assert (file);
+
+  dump_function_to_file (decl, file, TDF_DETAILS);
+}
+
+/* Returns cgraph_node.  */
+
+struct cgraph_node *
+sem_function::get_node (void)
+{
+  return cgraph (node);
+}
+
+/* Initialize semantic item by info reachable during LTO WPA phase.  */
+
+void
+sem_function::init_wpa (void)
+{
+  parse_tree_args ();
+}
+
+/* Semantic item initialization function.  */
+
+void
+sem_function::init (void)
+{
+  if (in_lto_p)
+    cgraph_get_body (get_node ());
+
+  tree fndecl = node->decl;
+  struct function *func = DECL_STRUCT_FUNCTION (fndecl);
+
+  gcc_assert (func);
+  gcc_assert (SSANAMES (func));
+
+  ssa_names_size = SSANAMES (func)->length ();
+  node = node;
+
+  decl = fndecl;
+  region_tree = func->eh->region_tree;
+
+  /* iterating all function arguments.  */
+  arg_count = count_formal_params (fndecl);
+
+  /* basic block iteration.  */
+  bb_count = n_basic_blocks_for_fn (func) - 2;
+
+  edge_count = n_edges_for_fn (func);
+  bb_sizes = XNEWVEC (unsigned int, bb_count);
+
+  bb_sorted = XNEWVEC (sem_bb_t *, bb_count);
+  cfg_checksum = coverage_compute_cfg_checksum (func);
+
+  unsigned bb_count = 0;
+  gcode_hash = 0;
+
+  basic_block bb;
+  FOR_EACH_BB_FN (bb, func)
+  {
+    unsigned nondbg_stmt_count = 0;
+
+    edge e;
+    for (edge_iterator ei = ei_start (bb->preds); ei_cond (ei, &e); ei_next (&ei))
+      cfg_checksum = iterative_hash_host_wide_int (e->flags,
+		     cfg_checksum);
+
+    for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi);
+	 gsi_next (&gsi))
+      {
+	gimple stmt = gsi_stmt (gsi);
+	hashval_t code = (hashval_t) gimple_code (stmt);
+
+	/* We ignore all debug statements.  */
+	if (code != GIMPLE_DEBUG)
+	  {
+	    nondbg_stmt_count++;
+	    gcode_hash = iterative_hash_object (code, gcode_hash);
+	  }
+      }
+
+    bb_sizes[bb_count] = nondbg_stmt_count;
+
+    /* Inserting basic block to hash table.  */
+    sem_bb_t *sem_bb = XNEW (sem_bb_t);
+    sem_bb->bb = bb;
+    sem_bb->nondbg_stmt_count = nondbg_stmt_count;
+    sem_bb->edge_count = EDGE_COUNT (bb->preds) + EDGE_COUNT (bb->succs);
+
+    bb_sorted[bb_count++] = sem_bb;
+  }
+
+  parse_tree_args ();
+}
+
+/* For a given call graph NODE, the function constructs new
+   semantic function item.  */
+
+sem_function *
+sem_function::parse (struct cgraph_node *node, bitmap_obstack *stack)
+{
+  tree fndecl = node->decl;
+  struct function *func = DECL_STRUCT_FUNCTION (fndecl);
+
+  if (!func || !cgraph_function_with_gimple_body_p (node))
+    return NULL;
+
+  if (lookup_attribute_by_prefix ("omp ", DECL_ATTRIBUTES (node->decl)) != NULL)
+    return NULL;
+
+  sem_function *f = new sem_function (node, 0, stack);
+
+  f->init ();
+
+  return f;
+}
+
+/* Parses function arguments and result type.  */
+
+void
+sem_function::parse_tree_args (void)
+{
+  tree result;
+  arg_types.create (4);
+  tree fnargs = DECL_ARGUMENTS (decl);
+
+  for (tree parm = fnargs; parm; parm = DECL_CHAIN (parm))
+    arg_types.safe_push (TYPE_CANONICAL (DECL_ARG_TYPE (parm)));
+
+  /* Function result type.  */
+  result = DECL_RESULT (decl);
+  result_type = result ? TYPE_CANONICAL (TREE_TYPE (result)) : NULL;
+
+  /* During WPA, we can get arguments by following method.  */
+  if (!fnargs)
+    {
+      tree type = TYPE_ARG_TYPES (TREE_TYPE (decl));
+      for (tree parm = type; parm; parm = TREE_CHAIN (parm))
+	arg_types.safe_push (TYPE_CANONICAL (TREE_VALUE (parm)));
+
+      result_type = TREE_TYPE (TREE_TYPE (decl));
+    }
+
+  arg_count = arg_types.length ();
+}
+
+/* Basic block equivalence comparison function that returns true if
+   basic blocks BB1 and BB2 (from functions FUNC1 and FUNC2) correspond.  */
+
+bool
+sem_function::compare_bb (sem_bb_t *bb1, sem_bb_t *bb2, tree func1, tree func2)
+{
+  unsigned i;
+  gimple_stmt_iterator gsi1, gsi2;
+  gimple s1, s2;
+
+  if (bb1->nondbg_stmt_count != bb2->nondbg_stmt_count
+      || bb1->edge_count != bb2->edge_count)
+    SE_EXIT_FALSE ();
+
+  gsi1 = gsi_start_bb (bb1->bb);
+  gsi2 = gsi_start_bb (bb2->bb);
+
+  for (i = 0; i < bb1->nondbg_stmt_count; i++)
+    {
+      gsi_next_nondebug_stmt (gsi1);
+      gsi_next_nondebug_stmt (gsi2);
+
+      s1 = gsi_stmt (gsi1);
+      s2 = gsi_stmt (gsi2);
+
+      if (gimple_code (s1) != gimple_code (s2))
+	SE_EXIT_FALSE_WITH_MSG ("gimple codes are different");
+
+      switch (gimple_code (s1))
+	{
+	case GIMPLE_CALL:
+	  if (!compare_gimple_call (s1, s2, func1, func2))
+	    SE_DIFF_STATEMENT (s1, s2, "GIMPLE_CALL");
+	  break;
+	case GIMPLE_ASSIGN:
+	  if (!compare_gimple_assign (s1, s2, func1, func2))
+	    SE_DIFF_STATEMENT (s1, s2, "GIMPLE_ASSIGN");
+	  break;
+	case GIMPLE_COND:
+	  if (!compare_gimple_cond (s1, s2, func1, func2))
+	    SE_DIFF_STATEMENT (s1, s2, "GIMPLE_COND");
+	  break;
+	case GIMPLE_SWITCH:
+	  if (!compare_gimple_switch (s1, s2, func1, func2))
+	    SE_DIFF_STATEMENT (s1, s2, "GIMPLE_SWITCH");
+	  break;
+	case GIMPLE_DEBUG:
+	case GIMPLE_EH_DISPATCH:
+	  break;
+	case GIMPLE_RESX:
+	  if (!compare_gimple_resx (s1, s2))
+	    SE_DIFF_STATEMENT (s1, s2, "GIMPLE_RESX");
+	  break;
+	case GIMPLE_LABEL:
+	  if (!compare_gimple_label (s1, s2, func1, func2))
+	    SE_DIFF_STATEMENT (s1, s2, "GIMPLE_LABEL");
+	  break;
+	case GIMPLE_RETURN:
+	  if (!compare_gimple_return (s1, s2, func1, func2))
+	    SE_DIFF_STATEMENT (s1, s2, "GIMPLE_RETURN");
+	  break;
+	case GIMPLE_GOTO:
+	  if (!compare_gimple_goto (s1, s2, func1, func2))
+	    SE_DIFF_STATEMENT (s1, s2, "GIMPLE_GOTO");
+	  break;
+	case GIMPLE_ASM:
+	  if (!compare_gimple_asm (s1, s2))
+	    SE_DIFF_STATEMENT (s1, s2, "GIMPLE_ASM");
+	  break;
+	case GIMPLE_PREDICT:
+	case GIMPLE_NOP:
+	  return true;
+	default:
+	  SE_EXIT_FALSE_WITH_MSG ("Unknown GIMPLE code reached")
+	}
+
+      gsi_next (&gsi1);
+      gsi_next (&gsi2);
+    }
+
+  return true;
+}
+
+/* For given basic blocks BB1 and BB2 (from functions FUNC1 and FUNC),
+   true value is returned if phi nodes are sematically
+   equivalent in these blocks .  */
+
+bool
+sem_function::compare_phi_node (basic_block bb1, basic_block bb2,
+				tree func1, tree func2)
+{
+  gimple_stmt_iterator si1, si2;
+  gimple phi1, phi2;
+  unsigned size1, size2, i;
+  tree t1, t2;
+  edge e1, e2;
+
+  gcc_assert (bb1 != NULL);
+  gcc_assert (bb2 != NULL);
+
+  si2 = gsi_start_phis (bb2);
+  for (si1 = gsi_start_phis (bb1); !gsi_end_p (si1);
+       gsi_next (&si1))
+    {
+      gsi_next_nonvirtual_phi (si1);
+      gsi_next_nonvirtual_phi (si2);
+
+      if (gsi_end_p (si1) && gsi_end_p (si2))
+	break;
+
+      if (gsi_end_p (si1) || gsi_end_p (si2))
+	SE_EXIT_FALSE ();
+
+      phi1 = gsi_stmt (si1);
+      phi2 = gsi_stmt (si2);
+
+      size1 = gimple_phi_num_args (phi1);
+      size2 = gimple_phi_num_args (phi2);
+
+      if (size1 != size2)
+	SE_EXIT_FALSE ();
+
+      for (i = 0; i < size1; ++i)
+	{
+	  t1 = gimple_phi_arg (phi1, i)->def;
+	  t2 = gimple_phi_arg (phi2, i)->def;
+
+	  if (!compare_operand (t1, t2, func1, func2))
+	    SE_EXIT_FALSE ();
+
+	  e1 = gimple_phi_arg_edge (phi1, i);
+	  e2 = gimple_phi_arg_edge (phi2, i);
+
+	  if (!checker.compare_edge (e1, e2))
+	    SE_EXIT_FALSE ();
+	}
+
+      gsi_next (&si2);
+    }
+
+  return true;
+}
+
+/* For given basic blocks BB1 and BB2 (from functions FUNC1 and FUNC),
+   true value is returned if exception handling regions are equivalent
+   in these blocks.  */
+
+bool
+sem_function::compare_eh_region (eh_region r1, eh_region r2, tree func1,
+				 tree func2)
+{
+  eh_landing_pad lp1, lp2;
+  eh_catch c1, c2;
+  tree t1, t2;
+
+  while (1)
+    {
+      if (!r1 && !r2)
+	return true;
+
+      if (!r1 || !r2)
+	return false;
+
+      if (r1->index != r2->index || r1->type != r2->type)
+	return false;
+
+      /* Landing pads comparison */
+      lp1 = r1->landing_pads;
+      lp2 = r2->landing_pads;
+
+      while (lp1 && lp2)
+	{
+	  if (lp1->index != lp2->index)
+	    return false;
+
+	  /* Comparison of post landing pads. */
+	  if (lp1->post_landing_pad && lp2->post_landing_pad)
+	    {
+	      t1 = lp1->post_landing_pad;
+	      t2 = lp2->post_landing_pad;
+
+	      gcc_assert (TREE_CODE (t1) == LABEL_DECL);
+	      gcc_assert (TREE_CODE (t2) == LABEL_DECL);
+
+	      if (!compare_tree_ssa_label (t1, t2, func1, func2))
+		return false;
+	    }
+	  else if (lp1->post_landing_pad || lp2->post_landing_pad)
+	    return false;
+
+	  lp1 = lp1->next_lp;
+	  lp2 = lp2->next_lp;
+	}
+
+      if (lp1 || lp2)
+	return false;
+
+      switch (r1->type)
+	{
+	case ERT_TRY:
+	  c1 = r1->u.eh_try.first_catch;
+	  c2 = r2->u.eh_try.first_catch;
+
+	  while (c1 && c2)
+	    {
+	      /* Catch label checking */
+	      if (c1->label && c2->label)
+		{
+		  if (!compare_tree_ssa_label (c1->label, c2->label,
+					       func1, func2))
+		    return false;
+		}
+	      else if (c1->label || c2->label)
+		return false;
+
+	      /* Type list checking */
+	      if (!compare_type_list (c1->type_list, c2->type_list))
+		return false;
+
+	      c1 = c1->next_catch;
+	      c2 = c2->next_catch;
+	    }
+
+	  break;
+
+	case ERT_ALLOWED_EXCEPTIONS:
+	  if (r1->u.allowed.filter != r2->u.allowed.filter)
+	    return false;
+
+	  if (!compare_type_list (r1->u.allowed.type_list,
+				  r2->u.allowed.type_list))
+	    return false;
+
+	  break;
+	case ERT_CLEANUP:
+	  break;
+	case ERT_MUST_NOT_THROW:
+	  if (r1->u.must_not_throw.failure_decl != r1->u.must_not_throw.failure_decl)
+	    return false;
+	  break;
+	default:
+	  gcc_unreachable ();
+	  break;
+	}
+
+      /* If there are sub-regions, process them.  */
+      if ((!r1->inner && r2->inner) || (!r1->next_peer && r2->next_peer))
+	return false;
+
+      if (r1->inner)
+	{
+	  r1 = r1->inner;
+	  r2 = r2->inner;
+	}
+
+      /* If there are peers, process them.  */
+      else if (r1->next_peer)
+	{
+	  r1 = r1->next_peer;
+	  r2 = r2->next_peer;
+	}
+      /* Otherwise, step back up the tree to the next peer.  */
+      else
+	{
+	  do
+	    {
+	      r1 = r1->outer;
+	      r2 = r2->outer;
+
+	      /* All nodes have been visited. */
+	      if (!r1 && !r2)
+		return true;
+	    }
+	  while (r1->next_peer == NULL);
+
+	  r1 = r1->next_peer;
+	  r2 = r2->next_peer;
+	}
+    }
+
+  return false;
+}
+
+/* Iterates GSI statement iterator to the next non-debug statement.  */
+
+void
+sem_function::gsi_next_nondebug_stmt (gimple_stmt_iterator &gsi)
+{
+  gimple s;
+
+  s = gsi_stmt (gsi);
+
+  while (gimple_code (s) == GIMPLE_DEBUG)
+    {
+      gsi_next (&gsi);
+      gcc_assert (!gsi_end_p (gsi));
+
+      s = gsi_stmt (gsi);
+    }
+}
+
+/* Iterates GSI statement iterator to the next non-virtual statement.  */
+
+void
+sem_function::gsi_next_nonvirtual_phi (gimple_stmt_iterator &it)
+{
+  gimple phi;
+
+  if (gsi_end_p (it))
+    return;
+
+  phi = gsi_stmt (it);
+  gcc_assert (phi != NULL);
+
+  while (virtual_operand_p (gimple_phi_result (phi)))
+    {
+      gsi_next (&it);
+
+      if (gsi_end_p (it))
+	return;
+
+      phi = gsi_stmt (it);
+    }
+}
+
+/* Verifies that trees T1 and T2 do correspond.  */
+
+bool
+sem_function::compare_function_decl (tree t1, tree t2)
+{
+  if (t1 == t2)
+    return true;
+
+  bool ret = pointer_set_contains (tree_refs_set, t1)
+	     && pointer_set_contains (compared_func->tree_refs_set, t2);
+
+  if (ret)
+    return true;
+
+  /* If function decl is WEAKREF, we compare targets.  */
+  struct cgraph_node *f1 = cgraph_get_node (t1);
+  struct cgraph_node *f2 = cgraph_get_node (t2);
+
+  if(f1 && f2 && f1->weakref && f2->weakref)
+    ret = f1->alias_target == f2->alias_target;
+
+  return ret;
+}
+
+/* Verifies that trees T1 and T2 do correspond.  */
+
+bool
+sem_function::compare_variable_decl (tree t1, tree t2, tree func1, tree func2)
+{
+  if (t1 == t2)
+    return true;
+
+  bool ret = pointer_set_contains (tree_refs_set, t1)
+	     && pointer_set_contains (compared_func->tree_refs_set, t2);
+
+  if (ret)
+    return true;
+
+  ret = checker.compare_decl (t1, t2, func1, func2);
+
+  SE_EXIT_DEBUG (ret);
+}
+
+/* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
+   call statements are semantically equivalent.  */
+bool
+sem_function::compare_gimple_call (gimple s1, gimple s2, tree func1, tree func2)
+{
+  unsigned i;
+  tree t1, t2;
+
+  if (gimple_call_num_args (s1) != gimple_call_num_args (s2))
+    return false;
+
+  t1 = gimple_call_fndecl (s1);
+  t2 = gimple_call_fndecl (s2);
+
+  /* Function pointer variables are not supported yet.  */
+  if (t1 == NULL || t2 == NULL)
+    {
+      if (!compare_operand (t1, t2, func1, func2))
+	SE_EXIT_FALSE();
+    }
+  else if (!compare_function_decl (t1, t2))
+    return false;
+
+  /* Checking of argument.  */
+  for (i = 0; i < gimple_call_num_args (s1); ++i)
+    {
+      t1 = gimple_call_arg (s1, i);
+      t2 = gimple_call_arg (s2, i);
+
+      if (!compare_operand (t1, t2, func1, func2))
+	return false;
+    }
+
+  /* Return value checking.  */
+  t1 = gimple_get_lhs (s1);
+  t2 = gimple_get_lhs (s2);
+
+  return compare_operand (t1, t2, func1, func2);
+}
+
+/* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
+   assignment statements are semantically equivalent.  */
+
+bool
+sem_function::compare_gimple_assign (gimple s1, gimple s2, tree func1,
+				     tree func2)
+{
+  tree arg1, arg2;
+  enum tree_code code1, code2;
+  unsigned i;
+
+  code1 = gimple_expr_code (s1);
+  code2 = gimple_expr_code (s2);
+
+  if (code1 != code2)
+    return false;
+
+  code1 = gimple_assign_rhs_code (s1);
+  code2 = gimple_assign_rhs_code (s2);
+
+  if (code1 != code2)
+    return false;
+
+  for (i = 0; i < gimple_num_ops (s1); i++)
+    {
+      arg1 = gimple_op (s1, i);
+      arg2 = gimple_op (s2, i);
+
+      if (!compare_operand (arg1, arg2, func1, func2))
+	return false;
+    }
+
+  return true;
+}
+
+/* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
+   condition statements are semantically equivalent.  */
+
+bool
+sem_function::compare_gimple_cond (gimple s1, gimple s2, tree func1, tree func2)
+{
+  tree t1, t2;
+  enum tree_code code1, code2;
+
+  code1 = gimple_expr_code (s1);
+  code2 = gimple_expr_code (s2);
+
+  if (code1 != code2)
+    return false;
+
+  t1 = gimple_cond_lhs (s1);
+  t2 = gimple_cond_lhs (s2);
+
+  if (!compare_operand (t1, t2, func1, func2))
+    return false;
+
+  t1 = gimple_cond_rhs (s1);
+  t2 = gimple_cond_rhs (s2);
+
+  return compare_operand (t1, t2, func1, func2);
+}
+
+/* Verifies that tree labels T1 and T2 correspond in FUNC1 and FUNC2.  */
+
+bool
+sem_function::compare_tree_ssa_label (tree t1, tree t2, tree func1, tree func2)
+{
+  return compare_operand (t1, t2, func1, func2);
+}
+
+/* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
+   label statements are semantically equivalent.  */
+
+bool
+sem_function::compare_gimple_label (gimple g1, gimple g2, tree func1,
+				    tree func2)
+{
+  tree t1 = gimple_label_label (g1);
+  tree t2 = gimple_label_label (g2);
+
+  return compare_tree_ssa_label (t1, t2, func1, func2);
+}
+
+/* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
+   switch statements are semantically equivalent.  */
+
+bool
+sem_function::compare_gimple_switch (gimple g1, gimple g2, tree func1,
+				     tree func2)
+{
+  unsigned lsize1, lsize2, i;
+  tree t1, t2, low1, low2, high1, high2;
+
+  lsize1 = gimple_switch_num_labels (g1);
+  lsize2 = gimple_switch_num_labels (g2);
+
+  if (lsize1 != lsize2)
+    return false;
+
+  t1 = gimple_switch_index (g1);
+  t2 = gimple_switch_index (g2);
+
+  if (TREE_CODE (t1) != SSA_NAME || TREE_CODE(t2) != SSA_NAME)
+    return false;
+
+  if (!compare_operand (t1, t2, func1, func2))
+    return false;
+
+  for (i = 0; i < lsize1; i++)
+    {
+      low1 = CASE_LOW (gimple_switch_label (g1, i));
+      low2 = CASE_LOW (gimple_switch_label (g2, i));
+
+      if ((low1 != NULL) != (low2 != NULL)
+	  || (low1 && low2 && TREE_INT_CST_LOW (low1) != TREE_INT_CST_LOW (low2)))
+	return false;
+
+      high1 = CASE_HIGH (gimple_switch_label (g1, i));
+      high2 = CASE_HIGH (gimple_switch_label (g2, i));
+
+      if ((high1 != NULL) != (high2 != NULL)
+	  || (high1 && high2
+	      && TREE_INT_CST_LOW (high1) != TREE_INT_CST_LOW (high2)))
+	return false;
+    }
+
+  return true;
+}
+
+/* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
+   return statements are semantically equivalent.  */
+
+bool
+sem_function::compare_gimple_return (gimple g1, gimple g2, tree func1,
+				     tree func2)
+{
+  tree t1, t2;
+
+  t1 = gimple_return_retval (g1);
+  t2 = gimple_return_retval (g2);
+
+  /* Void return type.  */
+  if (t1 == NULL && t2 == NULL)
+    return true;
+  else
+    return compare_operand (t1, t2, func1, func2);
+}
+
+/* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
+   goto statements are semantically equivalent.  */
+
+bool
+sem_function::compare_gimple_goto (gimple g1, gimple g2, tree func1, tree func2)
+{
+  tree dest1, dest2;
+
+  dest1 = gimple_goto_dest (g1);
+  dest2 = gimple_goto_dest (g2);
+
+  if (TREE_CODE (dest1) != TREE_CODE (dest2) || TREE_CODE (dest1) != SSA_NAME)
+    return false;
+
+  return compare_operand (dest1, dest2, func1, func2);
+}
+
+/* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
+   resx statements are semantically equivalent.  */
+
+bool
+sem_function::compare_gimple_resx (gimple g1, gimple g2)
+{
+  return gimple_resx_region (g1) == gimple_resx_region (g2);
+}
+
+/* Verifies for given GIMPLEs S1 and S2 that ASM statements are equivalent.
+   For the beginning, the pass only supports equality for
+   '__asm__ __volatile__ ("", "", "", "memory")'.  */
+
+bool
+sem_function::compare_gimple_asm (gimple g1, gimple g2)
+{
+  if (gimple_asm_volatile_p (g1) != gimple_asm_volatile_p (g2))
+    return false;
+
+  if (gimple_asm_ninputs (g1) || gimple_asm_ninputs (g2))
+    return false;
+
+  if (gimple_asm_noutputs (g1) || gimple_asm_noutputs (g2))
+    return false;
+
+  if (gimple_asm_nlabels (g1) || gimple_asm_nlabels (g2))
+    return false;
+
+  if (gimple_asm_nclobbers (g1) != gimple_asm_nclobbers (g2))
+    return false;
+
+  for (unsigned i = 0; i < gimple_asm_nclobbers (g1); i++)
+    {
+      tree clobber1 = TREE_VALUE (gimple_asm_clobber_op (g1, i));
+      tree clobber2 = TREE_VALUE (gimple_asm_clobber_op (g2, i));
+
+      if (!operand_equal_p (clobber1, clobber2, OEP_ONLY_CONST))
+	return false;
+    }
+
+  return true;
+}
+
+/* If T1 and T2 are SSA names, dictionary comparison is processed. Otherwise,
+   declaration comparasion is executed.  */
+
+bool
+sem_function::compare_ssa_name (tree t1, tree t2, tree func1, tree func2)
+{
+  tree b1, b2;
+  bool ret;
+
+  if (!checker.compare_ssa_name (t1, t2))
+    SE_EXIT_FALSE ();
+
+  if (SSA_NAME_IS_DEFAULT_DEF (t1))
+    {
+      b1 = SSA_NAME_VAR (t1);
+      b2 = SSA_NAME_VAR (t2);
+
+      if (b1 == NULL && b2 == NULL)
+	return true;
+
+      if (b1 == NULL || b2 == NULL || TREE_CODE (b1) != TREE_CODE (b2))
+	SE_EXIT_FALSE ();
+
+      switch (TREE_CODE (b1))
+	{
+	case VAR_DECL:
+	  SE_EXIT_DEBUG (compare_variable_decl (t1, t2, func1, func2));
+	case PARM_DECL:
+	case RESULT_DECL:
+	  ret = checker.compare_decl (b1, b2, func1, func2);
+	  SE_EXIT_DEBUG (ret);
+	default:
+	  SE_EXIT_FALSE_WITH_MSG ("Unknown TREE code reached")
+	}
+    }
+  else
+    return true;
+}
+
+/* Function responsible for comparison of handled components T1 and T2.
+   If these components, from functions FUNC1 and FUNC2, are equal, true
+   is returned.  */
+
+bool
+sem_function::compare_operand (tree t1, tree t2,
+			       tree func1, tree func2)
+{
+  tree base1, base2, x1, x2, y1, y2, z1, z2;
+  HOST_WIDE_INT offset1, offset2;
+  bool ret;
+
+  if (!t1 && !t2)
+    return true;
+  else if (!t1 || !t2)
+    return false;
+
+  if (!types_compatible_p (TREE_TYPE (t1), TREE_TYPE (t2)))
+    SE_EXIT_FALSE_WITH_MSG ("types are not compatible");
+
+  if (TREE_CODE (t1) == RECORD_TYPE && TREE_CODE (t2) == RECORD_TYPE)
+    {
+      tree ctx1 = DECL_CONTEXT (t1);
+      tree ctx2 = DECL_CONTEXT (t2);
+
+      if (TYPE_BINFO (ctx1) && TYPE_BINFO (ctx2)
+	  && polymorphic_type_binfo_p (TYPE_BINFO (ctx1))
+	  && polymorphic_type_binfo_p (TYPE_BINFO (ctx2)))
+	if (!types_same_for_odr (t1, t2))
+	  SE_EXIT_FALSE_WITH_MSG ("polymorphic types detected");
+    }
+
+  base1 = get_addr_base_and_unit_offset (t1, &offset1);
+  base2 = get_addr_base_and_unit_offset (t2, &offset2);
+
+  if (base1 && base2)
+    {
+      if (offset1 != offset2)
+	SE_EXIT_FALSE_WITH_MSG ("base offsets are different");
+
+      t1 = base1;
+      t2 = base2;
+    }
+
+  if (TREE_CODE (t1) != TREE_CODE (t2))
+    SE_EXIT_FALSE_WITH_MSG ("");
+
+  switch (TREE_CODE (t1))
+    {
+    case CONSTRUCTOR:
+      {
+	unsigned length1 = vec_safe_length (CONSTRUCTOR_ELTS (t1));
+	unsigned length2 = vec_safe_length (CONSTRUCTOR_ELTS (t2));
+
+	if (length1 != length2)
+	  SE_EXIT_FALSE_WITH_MSG ("");
+
+	for (unsigned i = 0; i < length1; i++)
+	  if (!compare_operand (CONSTRUCTOR_ELT (t1, i)->value,
+				CONSTRUCTOR_ELT (t2, i)->value, func1, func2))
+	    SE_EXIT_FALSE_WITH_MSG ("");
+
+	return true;
+      }
+    case ARRAY_REF:
+    case ARRAY_RANGE_REF:
+      {
+	x1 = TREE_OPERAND (t1, 0);
+	x2 = TREE_OPERAND (t2, 0);
+	y1 = TREE_OPERAND (t1, 1);
+	y2 = TREE_OPERAND (t2, 1);
+
+	if (!compare_operand (array_ref_low_bound (t1),
+			      array_ref_low_bound (t2),
+			      func1, func2))
+	  SE_EXIT_FALSE_WITH_MSG ("");
+	if (!compare_operand (array_ref_element_size (t1),
+			      array_ref_element_size (t2),
+			      func1, func2))
+	  SE_EXIT_FALSE_WITH_MSG ("");
+	if (!compare_operand (x1, x2, func1, func2))
+	  SE_EXIT_FALSE_WITH_MSG ("");
+	return compare_operand (y1, y2, func1, func2);
+      }
+
+    case MEM_REF:
+      {
+	x1 = TREE_OPERAND (t1, 0);
+	x2 = TREE_OPERAND (t2, 0);
+	y1 = TREE_OPERAND (t1, 1);
+	y2 = TREE_OPERAND (t2, 1);
+
+	/* See if operand is an memory access (the test originate from
+	 gimple_load_p).
+
+	In this case the alias set of the function being replaced must
+	be subset of the alias set of the other function.  At the moment
+	we seek for equivalency classes, so simply require inclussion in
+	both directions.  */
+
+	if (!sem_item::compare_for_aliasing (y1, y2))
+	  SE_EXIT_FALSE_WITH_MSG ("strict aliasing types do not match");
+
+	if (!compare_operand (x1, x2, func1, func2))
+	  SE_EXIT_FALSE_WITH_MSG ("");
+
+	/* Type of the offset on MEM_REF does not matter.  */
+	return wi::to_offset  (y1) == wi::to_offset  (y2);
+      }
+    case COMPONENT_REF:
+      {
+	x1 = TREE_OPERAND (t1, 0);
+	x2 = TREE_OPERAND (t2, 0);
+	y1 = TREE_OPERAND (t1, 1);
+	y2 = TREE_OPERAND (t2, 1);
+
+	ret = compare_operand (x1, x2, func1, func2)
+	      && compare_operand (y1, y2, func1, func2);
+
+	SE_EXIT_DEBUG (ret);
+      }
+    /* Virtual table call.  */
+    case OBJ_TYPE_REF:
+      {
+	x1 = TREE_OPERAND (t1, 0);
+	x2 = TREE_OPERAND (t2, 0);
+	y1 = TREE_OPERAND (t1, 1);
+	y2 = TREE_OPERAND (t2, 1);
+	z1 = TREE_OPERAND (t1, 2);
+	z2 = TREE_OPERAND (t2, 2);
+
+	ret = compare_operand (x1, x2, func1, func2)
+	      && compare_operand (y1, y2, func1, func2)
+	      && compare_operand (z1, z2, func1, func2);
+
+	SE_EXIT_DEBUG (ret);
+      }
+    case ADDR_EXPR:
+      {
+	x1 = TREE_OPERAND (t1, 0);
+	x2 = TREE_OPERAND (t2, 0);
+
+	ret = compare_operand (x1, x2, func1, func2);
+	SE_EXIT_DEBUG (ret);
+      }
+    case SSA_NAME:
+      {
+	ret = compare_ssa_name (t1, t2, func1, func2);
+	SE_EXIT_DEBUG (ret);
+      }
+    case INTEGER_CST:
+      {
+	ret = types_compatible_p (TREE_TYPE (t1), TREE_TYPE (t2))
+	      && wi::to_offset  (t1) == wi::to_offset  (t2);
+
+	SE_EXIT_DEBUG (ret);
+      }
+    case COMPLEX_CST:
+    case VECTOR_CST:
+    case STRING_CST:
+    case REAL_CST:
+      {
+	ret = operand_equal_p (t1, t2, OEP_ONLY_CONST);
+	SE_EXIT_DEBUG (ret);
+      }
+    case FUNCTION_DECL:
+      {
+	ret = compare_function_decl (t1, t2);
+	SE_EXIT_DEBUG (ret);
+      }
+    case VAR_DECL:
+      SE_EXIT_DEBUG (compare_variable_decl (t1, t2, func1, func2));
+    case FIELD_DECL:
+      {
+	tree fctx1 = DECL_FCONTEXT (t1);
+	tree fctx2 = DECL_FCONTEXT (t2);
+
+	tree offset1 = DECL_FIELD_OFFSET (t1);
+	tree offset2 = DECL_FIELD_OFFSET (t1);
+
+	ret = compare_operand (fctx1, fctx2, func1, func2)
+	      && compare_operand (offset1, offset2, func1, func2);
+
+	SE_EXIT_DEBUG (ret);
+      }
+    case PARM_DECL:
+    case LABEL_DECL:
+    case RESULT_DECL:
+    case CONST_DECL:
+    case BIT_FIELD_REF:
+      {
+	ret = checker.compare_decl (t1, t2, func1, func2);
+	SE_EXIT_DEBUG (ret);
+      }
+    default:
+      SE_EXIT_FALSE_WITH_MSG ("Unknown TREE code reached")
+    }
+}
+
+/* Returns true if tree T can be compared as a handled component.  */
+
+bool
+sem_function::icf_handled_component_p (tree t)
+{
+  enum tree_code tc = TREE_CODE (t);
+
+  return ((handled_component_p (t) && handled_component_p (t))
+	  || tc == ADDR_EXPR || tc == MEM_REF || tc == REALPART_EXPR
+	  || tc == IMAGPART_EXPR || tc == OBJ_TYPE_REF);
+}
+
+/* Basic blocks dictionary BB_DICT returns true if SOURCE index BB
+   corresponds to TARGET.  */
+
+bool
+sem_function::bb_dict_test (int* bb_dict, int source, int target)
+{
+  if (bb_dict[source] == -1)
+    {
+      bb_dict[source] = target;
+      return true;
+    }
+  else
+    return bb_dict[source] == target;
+}
+
+/* Iterates all tree types in T1 and T2 and returns true if all types
+   are compatible.  */
+
+bool
+sem_function::compare_type_list (tree t1, tree t2)
+{
+  tree tv1, tv2;
+  enum tree_code tc1, tc2;
+
+  if (!t1 && !t2)
+    return true;
+
+  while (t1 != NULL && t2 != NULL)
+    {
+      tv1 = TREE_VALUE (t1);
+      tv2 = TREE_VALUE (t2);
+
+      tc1 = TREE_CODE (tv1);
+      tc2 = TREE_CODE (tv2);
+
+      if (tc1 == NOP_EXPR && tc2 == NOP_EXPR)
+	{}
+      else if (tc1 == NOP_EXPR || tc2 == NOP_EXPR)
+	return false;
+      else if (!types_compatible_p (tv1, tv2))
+	return false;
+
+      t1 = TREE_CHAIN (t1);
+      t2 = TREE_CHAIN (t2);
+    }
+
+  return !(t1 || t2);
+}
+
+/* Semantic variable constructor that uses STACK as bitmap memory stack.  */
+
+sem_variable::sem_variable (bitmap_obstack *stack): sem_item (VAR, stack)
+{
+}
+
+/*  Constructor based on varpool node _NODE with computed hash _HASH.
+    Bitmap STACK is used for memory allocation.  */
+
+sem_variable::sem_variable (varpool_node *node, hashval_t _hash,
+			    bitmap_obstack *stack): sem_item(VAR,
+				  node, _hash, stack)
+{
+  gcc_checking_assert (node);
+  gcc_checking_assert (get_node ());
+}
+
+/* Gets symbol name of the item.  */
+
+const char *
+sem_variable::name (void)
+{
+  return node->name ();
+}
+
+/* Gets assembler name of the item.  */
+
+const char *
+sem_variable::asm_name (void)
+{
+  return node->asm_name ();
+}
+
+/* Fast equality variable based on knowledge known in WPA.  */
+
+bool sem_variable::equals_wpa (sem_item *item)
+{
+  return item->type == VAR;
+}
+
+/* Returns true if the item equals to ITEM given as arguemnt.  */
+
+bool
+sem_variable::equals (sem_item *item)
+{
+  if (item->type != VAR)
+    return false;
+
+  return sem_variable::equals (ctor, static_cast<sem_variable *>(item)->ctor);
+}
+
+/* Compares trees T1 and T2 for semantic equality.  */
+
+bool
+sem_variable::equals (tree t1, tree t2)
+{
+  tree_code tc1 = TREE_CODE (t1);
+  tree_code tc2 = TREE_CODE (t2);
+
+  if (tc1 != tc2)
+    return false;
+
+  switch (tc1)
+    {
+    case CONSTRUCTOR:
+      {
+	unsigned len1 = vec_safe_length (CONSTRUCTOR_ELTS (t1));
+	unsigned len2 = vec_safe_length (CONSTRUCTOR_ELTS (t2));
+
+	if (len1 != len2)
+	  return false;
+
+	for (unsigned i = 0; i < len1; i++)
+	  if (!sem_variable::equals (CONSTRUCTOR_ELT (t1, i)->value,
+				     CONSTRUCTOR_ELT (t2, i)->value))
+	    return false;
+
+	return true;
+      }
+    case MEM_REF:
+      {
+	tree x1 = TREE_OPERAND (t1, 0);
+	tree x2 = TREE_OPERAND (t2, 0);
+	tree y1 = TREE_OPERAND (t1, 1);
+	tree y2 = TREE_OPERAND (t2, 1);
+
+	if (!sem_item::compare_for_aliasing (y1, y2))
+	  SE_EXIT_FALSE_WITH_MSG ("strict aliasing types do not match");
+
+	/* Type of the offset on MEM_REF does not matter.  */
+	return sem_variable::equals (x1, x2)
+	       && wi::to_offset  (y1) == wi::to_offset  (y2);
+      }
+    case NOP_EXPR:
+    case ADDR_EXPR:
+      {
+	tree op1 = TREE_OPERAND (t1, 0);
+	tree op2 = TREE_OPERAND (t2, 0);
+	return sem_variable::equals (op1, op2);
+      }
+    case FUNCTION_DECL:
+    case VAR_DECL:
+    case FIELD_DECL:
+    case LABEL_DECL:
+      return t1 == t2;
+    case INTEGER_CST:
+      return types_compatible_p (TREE_TYPE (t1), TREE_TYPE (t2))
+	     && wi::to_offset (t1) == wi::to_offset (t2);
+    case STRING_CST:
+    case REAL_CST:
+    case COMPLEX_CST:
+      return operand_equal_p (t1, t2, OEP_ONLY_CONST);
+    case COMPONENT_REF:
+    case ARRAY_REF:
+    case POINTER_PLUS_EXPR:
+      {
+	tree x1 = TREE_OPERAND (t1, 0);
+	tree x2 = TREE_OPERAND (t2, 0);
+	tree y1 = TREE_OPERAND (t1, 1);
+	tree y2 = TREE_OPERAND (t2, 1);
+
+	return sem_variable::equals (x1, x2) && sem_variable::equals (y1, y2);
+      }
+    case ERROR_MARK:
+      SE_EXIT_FALSE_WITH_MSG ("ERROR_MARK");
+    default:
+      SE_EXIT_FALSE_WITH_MSG ("Unknown TREE code reached")
+    }
+}
+
+/* Returns varpool_node.  */
+
+struct varpool_node *
+sem_variable::get_node (void)
+{
+  return varpool (node);
+}
+
+/* Initialize semantic variable by info reachable during LTO WPA phase.  */
+
+void
+sem_variable::init_wpa (void)
+{
+}
+
+/* Semantic variable initialization function.  */
+
+void
+sem_variable::init (void)
+{
+  decl = get_node ()->decl;
+
+  ctor = ctor_for_folding (decl);
+}
+
+/* Parser function that visits a varpool NODE.  */
+
+sem_variable *
+sem_variable::parse (struct varpool_node *node, bitmap_obstack *stack)
+{
+  tree decl = node->decl;
+
+  bool readonly = TYPE_P (decl) ? TYPE_READONLY (decl) : TREE_READONLY (decl);
+  bool can_handle = readonly && (DECL_VIRTUAL_P (decl)
+				 || !TREE_ADDRESSABLE (decl));
+
+  if (!can_handle)
+    return NULL;
+
+  tree ctor = ctor_for_folding (decl);
+  if (!ctor)
+    return NULL;
+
+  sem_variable *v = new sem_variable (node, 0, stack);
+
+  v->init ();
+
+  return v;
+}
+
+/* Initializes references to other semantic functions/variables.  */
+
+void
+sem_variable::init_refs (void)
+{
+  parse_tree_refs (ctor);
+}
+
+hashval_t
+sem_variable::get_hash (void)
+{
+  if (hash)
+    return hash;
+
+  hashval_t hash = 456346417;
+
+  tree_code tc = TREE_CODE (ctor);
+  hash = iterative_hash_object (tc, hash);
+
+  if (TREE_CODE (ctor) == CONSTRUCTOR)
+    {
+      unsigned length = vec_safe_length (CONSTRUCTOR_ELTS (ctor));
+      hash = iterative_hash_object (length, hash);
+    }
+
+  return hash;
+}
+
+/* References independent hash function.  */
+
+bool
+sem_variable::merge (sem_item *alias_item)
+{
+  gcc_assert (alias_item->type == VAR);
+
+  sem_variable *alias_var = static_cast<sem_variable *> (alias_item);
+
+  struct varpool_node *original = get_node ();
+  struct varpool_node *alias = alias_var->get_node ();
+  bool original_discardable = false;
+
+  /* See if original is in a section that can be discarded if the main
+     symbol is not used.  */
+  if (DECL_EXTERNAL (original->decl))
+    original_discardable = true;
+  if (original->resolution == LDPR_PREEMPTED_REG
+      || original->resolution == LDPR_PREEMPTED_IR)
+    original_discardable = true;
+  if (symtab_can_be_discarded (original))
+    original_discardable = true;
+
+  gcc_assert (!TREE_ASM_WRITTEN (alias->decl));
+
+  if (original_discardable || DECL_EXTERNAL (alias_var->decl))
+    {
+      if (dump_file)
+	fprintf (dump_file, "Varpool alias cannot be created\n\n");
+
+      return false;
+    }
+  else
+    {
+      // alias cycle creation check
+      varpool_node *n = original;
+
+      while (n->alias)
+	{
+	  n = varpool_alias_target (n);
+	  if (n == alias)
+	    {
+	      if (dump_file)
+		fprintf (dump_file, "Varpool alias cannot be created (alias cycle).\n\n");
+
+	      return false;
+	    }
+	}
+
+      alias->analyzed = false;
+
+      DECL_INITIAL (alias->decl) = DECL_INITIAL (alias_var->decl);
+      ipa_remove_all_references (&alias->ref_list);
+
+      varpool_create_variable_alias (alias_var->decl, decl);
+      symtab_resolve_alias (alias, original);
+
+      if (dump_file)
+	fprintf (dump_file, "Varpool alias has been created.\n\n");
+
+      return true;
+    }
+}
+
+/* Dump symbol to FILE.  */
+
+void
+sem_variable::dump_to_file (FILE *file)
+{
+  gcc_assert (file);
+
+  print_node (file, "", decl, 0);
+  fprintf (file, "\n\n");
+}
+
+/* Iterates though a constructor and identifies tree references
+   we are interested in semantic function equality.  */
+
+void
+sem_variable::parse_tree_refs (tree t)
+{
+  switch (TREE_CODE (t))
+    {
+    case CONSTRUCTOR:
+      {
+	unsigned length = vec_safe_length (CONSTRUCTOR_ELTS (t));
+
+	for (unsigned i = 0; i < length; i++)
+	  parse_tree_refs(CONSTRUCTOR_ELT (t, i)->value);
+
+	break;
+      }
+    case NOP_EXPR:
+    case ADDR_EXPR:
+      {
+	tree op = TREE_OPERAND (t, 0);
+	parse_tree_refs (op);
+	break;
+      }
+    case FUNCTION_DECL:
+      {
+	tree_refs.safe_push (t);
+	break;
+      }
+    default:
+      break;
+    }
+}
+
+inline hashval_t
+congruence_class_var_hash::hash (const congruence_class *item)
+{
+  return htab_hash_pointer (item);
+}
+
+inline int
+congruence_class_var_hash::equal (const congruence_class *item1,
+				  const congruence_class *item2)
+{
+  return item1 == item2;
+}
+
+inline hashval_t
+congruence_class_group_hash::hash (const congruence_class_group *item)
+{
+  return item->hash;
+}
+
+inline int
+congruence_class_group_hash::equal (const congruence_class_group *item1,
+				    const congruence_class_group *item2)
+{
+  return item1->hash == item2->hash;
+}
+
+unsigned int sem_item_optimizer::class_id = 0;
+
+sem_item_optimizer::sem_item_optimizer (): classes_count (0), split_map (NULL),
+  cgraph_node_hooks (NULL), varpool_node_hooks (NULL)
+{
+  items.create (0);
+  removed_items_set = pointer_set_create ();
+  classes.create (0);
+  worklist.create (0);
+  bitmap_obstack_initialize (&bmstack);
+}
+
+sem_item_optimizer::~sem_item_optimizer ()
+{
+  for (unsigned int i = 0; i < items.length (); i++)
+    delete items[i];
+
+  for (hash_table<congruence_class_group_hash>::iterator it = classes.begin ();
+       it != classes.end (); ++it)
+    for (unsigned int i = 0; i < (*it).classes.length (); i++)
+      delete (*it).classes[i];
+
+  classes.dispose ();
+
+  worklist.dispose ();
+
+  if (split_map != NULL)
+    delete split_map;
+
+  bitmap_obstack_release (&bmstack);
+  pointer_set_destroy (removed_items_set);
+}
+
+/* Write IPA ICF summary for symbols.  */
+
+void
+sem_item_optimizer::write_summary (void)
+{
+  unsigned int count = 0;
+
+  struct output_block *ob = create_output_block (LTO_section_ipa_icf);
+  lto_symtab_encoder_t encoder = ob->decl_state->symtab_node_encoder;
+  ob->cgraph_node = NULL;
+
+  /* Calculate number of symbols to be serialized.  */
+  for (lto_symtab_encoder_iterator lsei = lsei_start_in_partition (encoder);
+       !lsei_end_p (lsei);
+       lsei_next_in_partition (&lsei))
+    {
+      struct symtab_node *node = lsei_node (lsei);
+
+      if (symtab_node_map.contains (node))
+	count++;
+    }
+
+  streamer_write_uhwi (ob, count);
+
+  /* Process all of the symbols.  */
+  for (lto_symtab_encoder_iterator lsei = lsei_start_in_partition (encoder);
+       !lsei_end_p (lsei);
+       lsei_next_in_partition (&lsei))
+    {
+      struct symtab_node *node = lsei_node (lsei);
+
+      sem_item **item = symtab_node_map.contains (node);
+
+      if (item && *item)
+	{
+	  int node_ref = lto_symtab_encoder_encode (encoder, node);
+	  streamer_write_uhwi_stream (ob->main_stream, node_ref);
+
+	  streamer_write_uhwi (ob, (*item)->get_hash ());
+	}
+    }
+
+  streamer_write_char_stream (ob->main_stream, 0);
+  produce_asm (ob, NULL);
+  destroy_output_block (ob);
+}
+
+/* Reads a section from LTO stream file FILE_DATA. Input block for DATA
+   contains LEN bytes.  */
+
+void
+sem_item_optimizer::read_section (struct lto_file_decl_data *file_data,
+				  const char *data, size_t len)
+{
+  const struct lto_function_header *header =
+  (const struct lto_function_header *) data;
+  const int cfg_offset = sizeof (struct lto_function_header);
+  const int main_offset = cfg_offset + header->cfg_size;
+  const int string_offset = main_offset + header->main_size;
+  struct data_in *data_in;
+  struct lto_input_block ib_main;
+  unsigned int i;
+  unsigned int count;
+
+  LTO_INIT_INPUT_BLOCK (ib_main, (const char *) data + main_offset, 0,
+			header->main_size);
+
+  data_in =
+    lto_data_in_create (file_data, (const char *) data + string_offset,
+			header->string_size, vNULL);
+
+  count = streamer_read_uhwi (&ib_main);
+
+  for (i = 0; i < count; i++)
+    {
+      unsigned int index;
+      struct symtab_node *node;
+      lto_symtab_encoder_t encoder;
+
+      index = streamer_read_uhwi (&ib_main);
+      encoder = file_data->symtab_node_encoder;
+      node = lto_symtab_encoder_deref (encoder, index);
+
+      hashval_t hash = streamer_read_uhwi (&ib_main);
+
+      gcc_assert (node->definition);
+
+      if (dump_file)
+	fprintf (dump_file, "Symbol added:%s (tree: %p, uid:%u)\n", node->asm_name (),
+		 (void *) node->decl, node->order);
+
+      if (is_a_helper<cgraph_node *>::test (node))
+	{
+	  cgraph_node *cnode = cgraph (node);
+
+	  items.safe_push (new sem_function (cnode, hash, &bmstack));
+	}
+      else
+	{
+	  varpool_node *vnode = varpool (node);
+
+	  items.safe_push (new sem_variable (vnode, hash, &bmstack));
+	}
+    }
+
+  lto_free_section_data (file_data, LTO_section_ipa_icf, NULL, data,
+			 len);
+  lto_data_in_delete (data_in);
+}
+
+/* Read IPA IPA ICF summary for symbols.  */
+
+void
+sem_item_optimizer::read_summary (void)
+{
+  struct lto_file_decl_data **file_data_vec = lto_get_file_decl_data ();
+  struct lto_file_decl_data *file_data;
+  unsigned int j = 0;
+
+  while ((file_data = file_data_vec[j++]))
+    {
+      size_t len;
+      const char *data = lto_get_section_data (file_data,
+			 LTO_section_ipa_icf, NULL, &len);
+
+      if (data)
+	read_section (file_data, data, len);
+    }
+}
+
+/* Register callgraph and varpool hooks.  */
+
+void
+sem_item_optimizer::register_hooks (void)
+{
+  cgraph_node_hooks = cgraph_add_node_removal_hook (
+			&sem_item_optimizer::cgraph_removal_hook, this);
+
+  varpool_node_hooks = varpool_add_node_removal_hook (
+			 &sem_item_optimizer::varpool_removal_hook, this);
+}
+
+/* Unregister callgraph and varpool hooks.  */
+
+void
+sem_item_optimizer::unregister_hooks (void)
+{
+  if (cgraph_node_hooks)
+    cgraph_remove_node_removal_hook (cgraph_node_hooks);
+
+  if (varpool_node_hooks)
+    varpool_remove_node_removal_hook (varpool_node_hooks);
+}
+
+/* Adds a CLS to hashtable associated by hash value.  */
+
+void
+sem_item_optimizer::add_class (congruence_class *cls)
+{
+  gcc_assert (cls->members.length ());
+
+  congruence_class_group_t *group = get_group_by_hash (
+				      cls->members[0]->get_hash ());
+  group->classes.safe_push (cls);
+}
+
+/* Gets a congruence class group based on given HASH value.  */
+
+congruence_class_group_t *
+sem_item_optimizer::get_group_by_hash (hashval_t hash)
+{
+  congruence_class_group_t *item = XNEW (congruence_class_group_t);
+  item->hash = hash;
+
+  congruence_class_group **slot = classes.find_slot (item, INSERT);
+
+  if (*slot)
+    free (item);
+  else
+    {
+      item->classes.create (1);
+      *slot = item;
+    }
+
+  return *slot;
+}
+
+/* Callgraph removal hook called for a NODE with a custom DATA.  */
+
+void
+sem_item_optimizer::cgraph_removal_hook (struct cgraph_node *node, void *data)
+{
+  sem_item_optimizer *optimizer = (sem_item_optimizer *) data;
+  optimizer->remove_symtab_node (node);
+}
+
+/* Varpool removal hook called for a NODE with a custom DATA.  */
+
+void
+sem_item_optimizer::varpool_removal_hook (struct varpool_node *node, void *data)
+{
+  sem_item_optimizer *optimizer = (sem_item_optimizer *) data;
+  optimizer->remove_symtab_node (node);
+}
+
+/* Remove symtab NODE triggered by symtab removal hooks.  */
+
+void
+sem_item_optimizer::remove_symtab_node (struct symtab_node *node)
+{
+  gcc_assert (!classes.elements());
+
+  pointer_set_insert (removed_items_set, node);
+}
+
+/* Removes all callgraph and varpool nodes that are marked by symtab
+   as deleted.  */
+
+void
+sem_item_optimizer::filter_removed_items (void)
+{
+  vec <sem_item *> filtered;
+  filtered.create (items.length());
+
+  for (unsigned int i = 0; i < items.length(); i++)
+    {
+      sem_item *item = items[i];
+
+      bool no_body_function = false;
+
+      if (item->type == FUNC)
+	{
+	  struct cgraph_node *cnode = static_cast <sem_function *>(item)->get_node ();
+
+	  no_body_function = in_lto_p && (cnode->alias || cnode->body_removed);
+	}
+
+      if(!pointer_set_contains (removed_items_set, items[i]->node)
+	  && !no_body_function)
+	filtered.safe_push (items[i]);
+    }
+  items.release ();
+
+  for (unsigned int i = 0; i < filtered.length(); i++)
+    items.safe_push (filtered[i]);
+}
+
+/* Optimizer entry point.  */
+
+void
+sem_item_optimizer::execute (void)
+{
+  filter_removed_items ();
+  build_hash_based_classes ();
+
+  if (dump_file)
+    fprintf (dump_file, "Dump after hash based groups\n");
+  dump_cong_classes ();
+
+  for (unsigned int i = 0; i < items.length(); i++)
+    items[i]->init_wpa ();
+
+  subdivide_classes_by_equality (true);
+
+  if (dump_file)
+    fprintf (dump_file, "Dump after WPA based types groups\n");
+  dump_cong_classes ();
+
+  parse_nonsingleton_classes ();
+  subdivide_classes_by_equality ();
+
+  if (dump_file)
+    fprintf (dump_file, "Dump after full equality comparison of groups\n");
+
+  dump_cong_classes ();
+
+  unsigned int prev_class_count = classes_count;
+
+  process_cong_reduction ();
+  dump_cong_classes ();
+  merge_classes (prev_class_count);
+
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    dump_symtab (dump_file);
+}
+
+/* Function responsible for visiting all potential functions and
+   read-only variables that can be merged.  */
+
+void
+sem_item_optimizer::parse_funcs_and_vars (void)
+{
+  struct cgraph_node *cnode;
+  sem_item **slot;
+
+  FOR_EACH_DEFINED_FUNCTION (cnode)
+  {
+    sem_function *f = sem_function::parse (cnode, &bmstack);
+    if (f)
+      {
+	items.safe_push (f);
+	slot = symtab_node_map.insert (cnode);
+	*slot = f;
+
+	if (dump_file)
+	  fprintf (dump_file, "Parsed function:%s\n", f->asm_name ());
+
+	if (dump_file && (dump_flags & TDF_DETAILS))
+	  f->dump_to_file (dump_file);
+      }
+    else if (dump_file)
+      fprintf (dump_file, "Not parsed function:%s\n", cnode->asm_name ());
+  }
+
+  varpool_node *vnode;
+
+  FOR_EACH_DEFINED_VARIABLE (vnode)
+  {
+    sem_variable *v = sem_variable::parse (vnode, &bmstack);
+
+    if (v)
+      {
+	items.safe_push (v);
+	slot = symtab_node_map.insert (vnode);
+	*slot = v;
+      }
+  }
+}
+
+/* Makes pairing between a congruence class CLS and semantic ITEM.  */
+
+void
+sem_item_optimizer::add_item_to_class (congruence_class *cls, sem_item *item)
+{
+  item->index_in_class = cls->members.length ();
+  cls->members.safe_push (item);
+  item->cls = cls;
+}
+
+/* Congruence classes are built by hash value.  */
+
+void
+sem_item_optimizer::build_hash_based_classes (void)
+{
+  for (unsigned i = 0; i < items.length (); i++)
+    {
+      sem_item *item = items[i];
+
+      congruence_class_group_t *group = get_group_by_hash (item->get_hash ());
+
+      if (!group->classes.length ())
+	{
+	  classes_count++;
+	  group->classes.safe_push (new congruence_class (class_id++));
+	}
+
+      add_item_to_class (group->classes[0], item);
+    }
+}
+
+/* Semantic items in classes having more than one element and initialized.
+   In case of WPA, we load function body.  */
+
+void
+sem_item_optimizer::parse_nonsingleton_classes (void)
+{
+  for (hash_table <congruence_class_group_hash>::iterator it = classes.begin ();
+       it != classes.end (); ++it)
+    {
+      for (unsigned i = 0; i < (*it).classes.length (); i++)
+	{
+	  congruence_class *c = (*it).classes [i];
+
+	  if (c->members.length() > 1)
+	    for (unsigned j = 0; j < c->members.length (); j++)
+	      {
+		sem_item *item = c->members[j];
+		sem_item **slot;
+
+		slot = decl_map.insert (item->decl);
+		*slot = item;
+
+	      }
+	}
+    }
+
+  unsigned int init_called_count = 0;
+
+  for (hash_table <congruence_class_group_hash>::iterator it = classes.begin ();
+       it != classes.end (); ++it)
+    {
+      /* We fill in all declarations for sem_items.  */
+      for (unsigned i = 0; i < (*it).classes.length (); i++)
+	{
+	  congruence_class *c = (*it).classes [i];
+
+	  if (c->members.length() > 1)
+	    for (unsigned j = 0; j < c->members.length (); j++)
+	      {
+		sem_item *item = c->members[j];
+
+		item->init ();
+		item->init_refs ();
+		init_called_count++;
+
+		for (unsigned j = 0; j < item->tree_refs.length (); j++)
+		  {
+		    sem_item **result = decl_map.contains (item->tree_refs[j]);
+
+		    if(result)
+		      {
+			sem_item *target = *result;
+			item->refs.safe_push (target);
+
+			unsigned index = item->refs.length ();
+			target->usages.safe_push (new sem_usage_pair(item, index));
+			bitmap_set_bit (target->usage_index_bitmap, index);
+			pointer_set_insert (item->tree_refs_set, item->tree_refs[j]);
+		      }
+		  }
+	      }
+	}
+    }
+
+  if (dump_file)
+    fprintf (dump_file, "Init called for %u items (%.2f%%).\n", init_called_count,
+	     100.0f * init_called_count / items.length ());
+}
+
+/* Equality function for semantic items is used to subdivide existing
+   classes. If IN_WPA, fast equality function is invoked.  */
+
+void
+sem_item_optimizer::subdivide_classes_by_equality (bool in_wpa)
+{
+  for (hash_table <congruence_class_group_hash>::iterator it = classes.begin ();
+       it != classes.end (); ++it)
+    {
+      unsigned int class_count = (*it).classes.length ();
+
+      for (unsigned i = 0; i < class_count; i++)
+	{
+	  congruence_class *c = (*it).classes [i];
+
+	  if (c->members.length() > 1)
+	    {
+	      vec <sem_item *> new_vector;
+	      new_vector.create (c->members.length ());
+
+	      sem_item *first = c->members[0];
+	      new_vector.safe_push (first);
+
+	      unsigned class_split_first = (*it).classes.length ();
+
+	      for (unsigned j = 1; j < c->members.length (); j++)
+		{
+		  sem_item *item = c->members[j];
+
+		  bool equals = in_wpa ? first->equals_wpa (item) : first->equals (item);
+
+		  if (equals)
+		    new_vector.safe_push (item);
+		  else
+		    {
+		      bool integrated = false;
+
+		      for (unsigned k = class_split_first; k < (*it).classes.length (); k++)
+			{
+			  sem_item *x = (*it).classes[k]->members[0];
+			  bool equals = in_wpa ? x->equals_wpa (item) : x->equals (item);
+
+			  if (equals)
+			    {
+			      integrated = true;
+			      add_item_to_class ((*it).classes[k], item);
+
+			      break;
+			    }
+			}
+
+		      if (!integrated)
+			{
+			  congruence_class *c = new congruence_class (class_id++);
+			  classes_count++;
+			  add_item_to_class (c, item);
+
+			  (*it).classes.safe_push (c);
+			}
+		    }
+		}
+
+	      // we replace newly created new_vector for the class we've just splitted
+	      c->members.release ();
+	      c->members.create (new_vector.length ());
+
+	      for (unsigned int j = 0; j < new_vector.length (); j++)
+		add_item_to_class (c, new_vector[j]);
+	    }
+	}
+    }
+
+  verify_classes ();
+}
+
+/* Verify congruence classes if checking is enabled.  */
+
+void
+sem_item_optimizer::verify_classes (void)
+{
+#if ENABLE_CHECKING
+  for (hash_table <congruence_class_group_hash>::iterator it = classes.begin ();
+       it != classes.end (); ++it)
+    {
+      for (unsigned int i = 0; i < (*it).classes.length (); i++)
+	{
+	  congruence_class *cls = (*it).classes[i];
+
+	  gcc_checking_assert (cls);
+	  gcc_checking_assert (cls->members.length () > 0);
+
+	  for (unsigned int j = 0; j < cls->members.length (); j++)
+	    {
+	      sem_item *item = cls->members[j];
+
+	      gcc_checking_assert (item);
+
+	      for (unsigned k = 0; k < item->usages.length (); k++)
+		{
+		  sem_usage_pair *usage = item->usages[k];
+		  gcc_checking_assert (usage->item->index_in_class <
+				       usage->item->cls->members.length ());
+		}
+	    }
+	}
+    }
+#endif
+}
+
+/* Disposes split map traverse function. CLS_PTR is pointer to congruence
+   class, BSLOT is bitmap slot we want to release. DATA is mandatory,
+   but unused argument.  */
+
+bool
+sem_item_optimizer::release_split_map (__attribute__((__unused__)) const void
+				       *cls_ptr,
+				       __attribute__((__unused__)) bitmap *bslot,
+				       __attribute__((__unused__)) void *data)
+{
+  bitmap b = *bslot;
+
+  BITMAP_FREE (b);
+
+  return true;
+}
+
+/* Process split operation for a class given as pointer CLS_PTR,
+   where bitmap B splits congruence class members. DATA is used
+   as argument of split pair.  */
+
+bool
+sem_item_optimizer::traverse_congruence_split (const void *cls_ptr,
+    bitmap *bslot, void *data)
+{
+  const congruence_class *cls = (const congruence_class *) cls_ptr;
+  bitmap b = *bslot;
+
+  traverse_split_pair *pair = (traverse_split_pair *) data;
+  sem_item_optimizer *optimizer = pair->optimizer;
+  const congruence_class *splitter_cls = pair->cls;
+
+  /* If counted bits are greater than zero and less than the number of members
+     a group will be splitted.  */
+  unsigned popcount = bitmap_count_bits (b);
+
+  if (popcount > 0 && popcount < cls->members.length ())
+    {
+      congruence_class* newclasses[2] = { new congruence_class (class_id++), new congruence_class (class_id++) };
+
+      for (unsigned int i = 0; i < cls->members.length (); i++)
+	{
+	  int target = bitmap_bit_p (b, i);
+	  congruence_class *tc = newclasses[target];
+
+	  add_item_to_class (tc, cls->members[i]);
+	}
+
+#ifdef ENABLE_CHECKING
+      for (unsigned int i = 0; i < 2; i++)
+	gcc_checking_assert (newclasses[i]->members.length ());
+#endif
+
+      if (splitter_cls == cls)
+	optimizer->splitter_class_removed = true;
+
+      /* Remove old class from worklist if presented.  */
+      bool in_work_list = optimizer->worklist_contains (cls);
+
+      if (in_work_list)
+	optimizer->worklist_remove (cls);
+
+      for (hash_table <congruence_class_group_hash>::iterator it =
+	     optimizer->classes.begin (); it != optimizer->classes.end (); ++it)
+	{
+	  for (unsigned int i = 0; i < (*it).classes.length (); i++)
+	    if ((*it).classes[i] == cls)
+	      {
+		(*it).classes.ordered_remove (i);
+		break;
+	      }
+	}
+      /* New class will be inserted and integrated to work list.  */
+      for (unsigned int i = 0; i < 2; i++)
+	optimizer->add_class (newclasses[i]);
+
+      /* Two classes replace one, so that increment just by one.  */
+      optimizer->classes_count++;
+
+      /* If OLD class was presented in the worklist, we remove the class
+         are replace it will both newly created classes.  */
+      if (in_work_list)
+	for (unsigned int i = 0; i < 2; i++)
+	  optimizer->worklist_push (newclasses[i]);
+      else /* Just smaller class is inserted.  */
+	{
+	  unsigned int smaller_index = newclasses[0]->members.length () <
+				       newclasses[1]->members.length () ?
+				       0 : 1;
+	  optimizer->worklist_push (newclasses[smaller_index]);
+	}
+
+      if (dump_file && (dump_flags & TDF_DETAILS))
+	{
+	  fprintf (dump_file, "  congruence class splitted:\n");
+	  cls->dump (dump_file, 4);
+
+	  fprintf (dump_file, "  newly created groups:\n");
+	  for (unsigned int i = 0; i < 2; i++)
+	    newclasses[i]->dump (dump_file, 4);
+	}
+
+      delete cls;
+    }
+
+
+  return true;
+}
+
+/* Tests if a class CLS used as INDEXth splits any congruence classes.
+   Bitmap stack BMSTACK is used for bitmap allocation.  */
+
+void
+sem_item_optimizer::do_congruence_step_for_index (congruence_class *cls,
+    unsigned int index)
+{
+  /* Split map reset */
+  if (split_map != NULL)
+    delete split_map;
+
+  pointer_map <bitmap> *split_map = new pointer_map <bitmap> ();
+
+  for (unsigned int i = 0; i < cls->members.length (); i++)
+    {
+      sem_item *item = cls->members[i];
+
+      /* Iterate all usages that have INDEX as usage of the item.  */
+      for (unsigned int j = 0; j < item->usages.length (); j++)
+	{
+	  sem_usage_pair *usage = item->usages[j];
+
+	  if (usage->index != index)
+	    continue;
+
+	  bitmap *slot = split_map->contains (usage->item->cls);
+
+	  if(!slot)
+	    {
+	      slot = split_map->insert (usage->item->cls);
+	      *slot = BITMAP_ALLOC (&bmstack);
+	    }
+
+	  bitmap b = *slot;
+
+#if ENABLE_CHECKING
+	  gcc_checking_assert (usage->item->cls);
+	  gcc_checking_assert (usage->item->index_in_class <
+			       usage->item->cls->members.length ());
+#endif
+
+	  bitmap_set_bit (b, usage->item->index_in_class);
+	}
+    }
+
+  traverse_split_pair pair;
+  pair.optimizer = this;
+  pair.cls = cls;
+
+  splitter_class_removed = false;
+  split_map->traverse (&sem_item_optimizer::traverse_congruence_split, &pair);
+
+  /* Bitmap clean-up.  */
+  split_map->traverse (&sem_item_optimizer::release_split_map, NULL);
+}
+
+/* Every usage of a congruence class CLS is a candidate that can split the
+   collection of classes. Bitmap stack BMSTACK is used for bitmap
+   allocation.  */
+
+void
+sem_item_optimizer::do_congruence_step (congruence_class *cls)
+{
+  bitmap_iterator bi;
+  unsigned int i;
+
+  bitmap usage = BITMAP_ALLOC (&bmstack);
+
+  for (unsigned int i = 0; i < cls->members.length (); i++)
+    bitmap_ior_into (usage, cls->members[i]->usage_index_bitmap);
+
+  EXECUTE_IF_SET_IN_BITMAP (usage, 0, i, bi)
+  {
+    if (dump_file && (dump_flags & TDF_DETAILS))
+      fprintf (dump_file, "  processing congruece step for class: %u, index: %u\n",
+	       cls->id, i);
+
+    do_congruence_step_for_index (cls, i);
+
+    if (splitter_class_removed)
+      break;
+  }
+
+  BITMAP_FREE (usage);
+}
+
+/* Adds a newly created congruence class CLS to worklist.  */
+
+void
+sem_item_optimizer::worklist_push (congruence_class *cls)
+{
+  congruence_class **slot = worklist.find_slot (cls, INSERT);
+
+  if (*slot)
+    return;
+
+  *slot = cls;
+}
+
+/* Pops a class from worklist. */
+
+congruence_class *
+sem_item_optimizer::worklist_pop (void)
+{
+  gcc_assert (worklist.elements ());
+
+  congruence_class *cls = &(*worklist.begin ());
+  worklist.remove_elt (cls);
+
+  return cls;
+}
+
+/* Returns true if a congruence class CLS is presented in worklist.  */
+
+bool
+sem_item_optimizer::worklist_contains (const congruence_class *cls)
+{
+  return worklist.find (cls);
+}
+
+/* Removes given congruence class CLS from worklist.  */
+
+void
+sem_item_optimizer::worklist_remove (const congruence_class *cls)
+{
+  worklist.remove_elt (cls);
+}
+
+/* Iterative congruence reduction function.  */
+
+void
+sem_item_optimizer::process_cong_reduction (void)
+{
+  for (hash_table<congruence_class_group_hash>::iterator it = classes.begin ();
+       it != classes.end (); ++it)
+    for (unsigned i = 0; i < (*it).classes.length (); i++)
+      if ((*it).classes[i]->is_class_used ())
+	worklist_push ((*it).classes[i]);
+
+  if (dump_file)
+    fprintf (dump_file, "Worklist has been filled with: %lu\n",
+	     worklist.elements ());
+
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    fprintf (dump_file, "Congruence class reduction\n");
+
+  while (worklist.elements ())
+    {
+      congruence_class *cls = worklist_pop ();
+      do_congruence_step (cls);
+    }
+}
+
+/* Debug function prints all informations about congruence classes.  */
+
+void
+sem_item_optimizer::dump_cong_classes (void)
+{
+  if (!dump_file)
+    return;
+
+  fprintf (dump_file,
+	   "Congruence classes: %u (unique hash values: %lu), with total: %u items\n",
+	   classes_count, classes.elements(), items.length ());
+
+  /* Histogram calculation.  */
+  unsigned int max_index = 0;
+  unsigned int* histogram = XCNEWVEC (unsigned int, items.length ());
+
+  for (hash_table<congruence_class_group_hash>::iterator it = classes.begin ();
+       it != classes.end (); ++it)
+
+    for (unsigned i = 0; i < (*it).classes.length (); i++)
+      {
+	unsigned int c = (*it).classes[i]->members.length ();
+	histogram[c]++;
+
+	if (c > max_index)
+	  max_index = c;
+      }
+
+  fprintf (dump_file,
+	   "Class size histogram [num of members]: number of classe number of classess\n");
+
+  for (unsigned int i = 0; i <= max_index; i++)
+    if (histogram[i])
+      fprintf (dump_file, "[%u]: %u classes\n", i, histogram[i]);
+
+  fprintf (dump_file, "\n\n");
+
+
+  if (dump_flags & TDF_DETAILS)
+    for (hash_table<congruence_class_group_hash>::iterator it = classes.begin ();
+	 it != classes.end (); ++it)
+      {
+	fprintf (dump_file, "  group: with %u classes:\n", (*it).classes.length ());
+
+	for (unsigned i = 0; i < (*it).classes.length (); i++)
+	  {
+	    (*it).classes[i]->dump (dump_file, 4);
+
+	    if(i < (*it).classes.length () - 1)
+	      fprintf (dump_file, " ");
+	  }
+      }
+
+  free (histogram);
+}
+
+/* After reduction is done, we can declare all items in a group
+   to be equal. PREV_CLASS_COUNT is start number of classes
+   before reduction.  */
+
+void
+sem_item_optimizer::merge_classes (unsigned int prev_class_count)
+{
+  unsigned int item_count = items.length ();
+  unsigned int class_count = classes_count;
+  unsigned int equal_items = item_count - class_count;
+
+  if (dump_file)
+    {
+      fprintf (dump_file, "\nItem count: %u\n", item_count);
+      fprintf (dump_file, "Congruent classes before: %u, after: %u\n",
+	       prev_class_count, class_count);
+      fprintf (dump_file, "Average class size before: %.2f, after: %.2f\n",
+	       1.0f * item_count / prev_class_count,
+	       1.0f * item_count / class_count);
+      fprintf (dump_file, "Equal symbols: %u\n", equal_items);
+      fprintf (dump_file, "Fraction of visited symbols: %.2f%%\n\n",
+	       100.0f * equal_items / item_count);
+    }
+
+  for (hash_table<congruence_class_group_hash>::iterator it = classes.begin ();
+       it != classes.end (); ++it)
+    for (unsigned int i = 0; i < (*it).classes.length (); i++)
+      {
+	congruence_class *c = (*it).classes[i];
+
+	if (c->members.length () == 1)
+	  continue;
+
+	gcc_assert (c->members.length ());
+
+	sem_item *source = c->members[0];
+
+	for (unsigned int j = 1; j < c->members.length (); j++)
+	  {
+	    sem_item *alias = c->members[j];
+
+	    if (dump_file)
+	      {
+		fprintf (dump_file, "Semantic equality hit:%s->%s\n",
+			 source->name (), alias->name ());
+		fprintf (dump_file, "Assembler symbol names:%s->%s\n",
+			 source->asm_name (), alias->asm_name ());
+	      }
+
+	    if (dump_file && (dump_flags & TDF_DETAILS))
+	      {
+		source->dump_to_file (dump_file);
+		alias->dump_to_file (dump_file);
+	      }
+
+	    source->merge (alias);
+	  }
+      }
+}
+
+/* Dump function prints all class members to a FILE with an INDENT.  */
+
+void
+congruence_class::dump (FILE *file, unsigned int indent) const
+{
+  FPRINTF_SPACES (file, indent, "class with id: %u, hash: %u, items: %u\n",
+		  id, members[0]->get_hash (), members.length ());
+
+  FPUTS_SPACES (file, indent + 2, "");
+  for (unsigned i = 0; i < members.length (); i++)
+    fprintf (file, "%s(%p/%u)", members[i]->asm_name (), (void *) members[i]->decl,
+	     members[i]->node->order);
+
+  fprintf (file, "\n");
+}
+
+/* Returns true if there's a member that is used from another group.  */
+
+bool
+congruence_class::is_class_used (void)
+{
+  for (unsigned int i = 0; i < members.length (); i++)
+    if (members[i]->usages.length ())
+      return true;
+
+  return false;
+}
+
+/* Initialization and computation of symtab node hash, there data
+   are propagated later on.  */
+
+static sem_item_optimizer optimizer;
+
+/* Generate pass summary for IPA ICF pass.  */
+
+static void
+ipa_icf_generate_summary (void)
+{
+  optimizer.parse_funcs_and_vars ();
+}
+
+/* Write pass summary for IPA ICF pass.  */
+
+static void
+ipa_icf_write_summary (void)
+{
+  optimizer.write_summary ();
+}
+
+/* Read pass summary for IPA ICF pass.  */
+
+static void
+ipa_icf_read_summary (void)
+{
+  optimizer.read_summary ();
+  optimizer.register_hooks ();
+}
+
+/* Semantic equality exection function.  */
+
+static unsigned int
+ipa_icf_driver (void)
+{
+  optimizer.execute ();
+  optimizer.unregister_hooks ();
+
+  return 0;
+}
+
+const pass_data pass_data_ipa_icf =
+{
+  IPA_PASS,		    /* type */
+  "icf",		    /* name */
+  OPTGROUP_IPA,             /* optinfo_flags */
+  true,                     /* has_execute */
+  TV_IPA_ICF,		    /* tv_id */
+  0,                        /* properties_required */
+  0,                        /* properties_provided */
+  0,                        /* properties_destroyed */
+  0,                        /* todo_flags_start */
+  0,                        /* todo_flags_finish */
+};
+
+class pass_ipa_icf : public ipa_opt_pass_d
+{
+public:
+  pass_ipa_icf (gcc::context *ctxt)
+    : ipa_opt_pass_d (pass_data_ipa_icf, ctxt,
+		      ipa_icf_generate_summary, /* generate_summary */
+		      ipa_icf_write_summary, /* write_summary */
+		      ipa_icf_read_summary, /* read_summary */
+		      NULL, /*
+		      write_optimization_summary */
+		      NULL, /*
+		      read_optimization_summary */
+		      NULL, /* stmt_fixup */
+		      0, /* function_transform_todo_flags_start */
+		      NULL, /* function_transform */
+		      NULL) /* variable_transform */
+  {}
+
+  /* opt_pass methods: */
+  virtual bool gate (function *)
+  {
+    return flag_ipa_icf;
+  }
+
+  virtual unsigned int execute (function *)
+  {
+    return ipa_icf_driver();
+  }
+}; // class pass_ipa_icf
+
+} // anon namespace
+
+ipa_opt_pass_d *
+make_pass_ipa_icf (gcc::context *ctxt)
+{
+  return new pass_ipa_icf (ctxt);
+}
diff --git a/gcc/ipa-icf.h b/gcc/ipa-icf.h
new file mode 100644
index 0000000..dc5e971
--- /dev/null
+++ b/gcc/ipa-icf.h
@@ -0,0 +1,687 @@
+/* Interprocedural semantic function equality pass
+   Copyright (C) 2014 Free Software Foundation, Inc.
+
+   Contributed by Jan Hubicka <hubicka@ucw.cz> and Martin Liska <mliska@suse.cz>
+
+This file is part of GCC.
+
+GCC 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, or (at your option) any later
+version.
+
+GCC 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 GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+/* Prints string STRING to a FILE with a given number of SPACE_COUNT.  */
+#define FPUTS_SPACES(file, space_count, string) \
+  do \
+  { \
+    fprintf (file, "%*s" string, space_count, " "); \
+  } \
+  while (false);
+
+/* fprintf function wrapper that transforms given FORMAT to follow given
+   number for SPACE_COUNT and call fprintf for a FILE.  */
+#define FPRINTF_SPACES(file, space_count, format, ...) \
+  fprintf (file, "%*s" format, space_count, " ", ##__VA_ARGS__);
+
+/* Prints a MESSAGE to dump_file if exists.  */
+#define SE_DUMP_MESSAGE(message) \
+  do \
+  { \
+    if (dump_file && (dump_flags & TDF_DETAILS)) \
+      fprintf (dump_file, "  debug message: %s (%s:%u)\n", message, __func__, __LINE__); \
+  } \
+  while (false);
+
+/* Logs a MESSAGE to dump_file if exists and returns false.  */
+#define SE_EXIT_FALSE_WITH_MSG(message) \
+  do \
+  { \
+    if (dump_file && (dump_flags & TDF_DETAILS)) \
+      fprintf (dump_file, "  false returned: '%s' (%s:%u)\n", message, __func__, __LINE__); \
+    return false; \
+  } \
+  while (false);
+
+/* Return false and log that false value is returned.  */
+#define SE_EXIT_FALSE() \
+  SE_EXIT_FALSE_WITH_MSG("")
+
+/* Logs return value if RESULT is false.  */
+#define SE_EXIT_DEBUG(result) \
+  do \
+  { \
+    if (!(result) && dump_file && (dump_flags & TDF_DETAILS)) \
+      fprintf (dump_file, "  false returned (%s:%u)\n", __func__, __LINE__); \
+    return result; \
+  } \
+  while (false);
+
+/* Verbose logging function logging statements S1 and S2 of a CODE.  */
+#define SE_DIFF_STATEMENT(s1, s2, code) \
+  do \
+  { \
+    if (dump_file && (dump_flags & TDF_DETAILS)) \
+      { \
+        fprintf (dump_file, "  different statement for code: %s:\n", code); \
+        print_gimple_stmt (dump_file, s1, 3, TDF_DETAILS); \
+        print_gimple_stmt (dump_file, s2, 3, TDF_DETAILS); \
+      } \
+    return false; \
+  } \
+  while (false);
+
+namespace {
+
+/* Forward declaration for sem_func class.  */
+class sem_item;
+
+/* A class aggregating all connections and semantic equivalents
+   for a given pair of semantic function candidates.  */
+class func_checker
+{
+public:
+  func_checker ();
+
+  /* Initializes internal structures according to given number of
+     source and target SSA names. The number of source names is SSA_SOURCE,
+     respectively SSA_TARGET.  */
+  void initialize (unsigned ssa_source, unsigned sss_target);
+
+  /* Memory release routine.  */
+  void release (void);
+
+  /* Verifies that trees T1 and T2 do correspond.  */
+  bool compare_ssa_name (tree t1, tree t2);
+
+  /* Verification function for edges E1 and E2.  */
+  bool compare_edge (edge e1, edge e2);
+
+  /* Verification function for declaration trees T1 and T2 that
+     come from functions FUNC1 and FUNC2.  */
+  bool compare_decl (tree t1, tree t2, tree func1, tree func2);
+
+private:
+  /* Vector mapping source SSA names to target ones.  */
+  vec <int> source_ssa_names;
+
+  /* Vector mapping target SSA names to source ones.  */
+  vec <int> target_ssa_names;
+
+  /* Source to target edge map.  */
+  pointer_map <edge> *edge_map;
+
+  /* Source to target declaration map.  */
+  pointer_map <tree> *decl_map;
+
+  /* Flag that indicates if the checker is initialize.  */
+  bool initialized;
+};
+
+/* Congruence class encompasses a collection of either functions or
+   read-only variables. These items are considered to be equivalent
+   if not proved the oposite.  */
+class congruence_class
+{
+public:
+  /* Congruence class constructor for a new class with _ID.  */
+  congruence_class (unsigned int _id);
+
+  /* Dump function prints all class members to a FILE with an INDENT.  */
+  void dump (FILE *file, unsigned int indent = 0) const;
+
+  /* Returns true if there's a member that is used from another group.  */
+  bool is_class_used (void);
+
+  /* Vector of all group members.  */
+  vec <sem_item *> members;
+
+  /* Global unique class identifier.  */
+  unsigned int id;
+};
+
+/* Semantic item type enum.  */
+enum sem_item_type
+{
+  FUNC,
+  VAR
+};
+
+/* Semantic item usage pair.  */
+class sem_usage_pair
+{
+public:
+  /* Constructor for key value pair, where _ITEM is key and _INDEX is a target.  */
+  sem_usage_pair (sem_item *_item, unsigned int _index);
+
+  /* Target semantic item where an item is used.  */
+  sem_item *item;
+
+  /* Index of usage of such an item.  */
+  unsigned int index;
+};
+
+/* Basic block struct for sematic equality pass.  */
+typedef struct sem_bb
+{
+  /* Basic block the structure belongs to.  */
+  basic_block bb;
+
+  /* Number of non-debug statements in the basic block.  */
+  unsigned nondbg_stmt_count;
+
+  /* Number of edges connected to the block.  */
+  unsigned edge_count;
+} sem_bb_t;
+
+/* Semantic item is a base class that encapsulates all shared functionality
+   for both semantic function and variable items.  */
+class sem_item
+{
+public:
+  /* Semantic item constructor for a node of _TYPE, where STACK is used
+     for bitmap memory allocation.  */
+  sem_item (enum sem_item_type _type, bitmap_obstack *stack);
+
+  /* Semantic item constructor for a node of _TYPE, where STACK is used
+     for bitmap memory allocation. The item is based on symtab node _NODE
+     with computed _HASH.  */
+  sem_item (enum sem_item_type _type, struct symtab_node *_node, hashval_t _hash,
+	    bitmap_obstack *stack);
+
+  virtual ~sem_item ();
+
+  /* Dump function for debugging purpose.  */
+  DEBUG_FUNCTION void dump (void);
+
+  /* Initialize semantic item by info reachable during LTO WPA phase.  */
+  virtual void init_wpa (void) = 0;
+
+  /* Semantic item initialization function.  */
+  virtual void init (void) = 0;
+
+  /* Gets symbol name of the item.  */
+  virtual const char *name (void) = 0;
+
+  /* Gets assembler name of the item.  */
+  virtual const char *asm_name (void) = 0;
+
+  /* Initializes references to other semantic functions/variables.  */
+  virtual void init_refs () = 0;
+
+  /* Fast equality function based on knowledge known in WPA.  */
+  virtual bool equals_wpa (sem_item *item) = 0;
+
+  /* Returns true if the item equals to ITEM given as arguemnt.  */
+  virtual bool equals (sem_item *item) = 0;
+
+  /* References independent hash function.  */
+  virtual hashval_t get_hash (void) = 0;
+
+  /* Merges instance with an ALIAS_ITEM, where alias, thunk or redirection can
+     be applied.  */
+  virtual bool merge (sem_item *alias_item) = 0;
+
+  /* Dump symbol to FILE.  */
+  virtual void dump_to_file (FILE *file) = 0;
+
+  /* Compare two types if are same aliases in case of strict aliasing
+     is enabled.  */
+  static bool compare_for_aliasing (tree t1, tree t2);
+
+  /* Item type.  */
+  enum sem_item_type type;
+
+  /* Global unique function index.  */
+  unsigned int index;
+
+  /* Symtab node.  */
+  struct symtab_node *node;
+
+  /* Declaration tree node.  */
+  tree decl;
+
+  /* Semantic references used that generate congruence groups.  */
+  vec <sem_item *> refs;
+
+  /* Pointer to a congruence class the item belongs to.  */
+  congruence_class *cls;
+
+  /* Index of the item in a class belonging to.  */
+  unsigned int index_in_class;
+
+  /* List of semantic items where the instance is used.  */
+  vec <sem_usage_pair *> usages;
+
+  /* A bitmap with indices of all classes referencing this item.  */
+  bitmap usage_index_bitmap;
+
+  /* List of tree references (either FUNC_DECL or VAR_DECL).  */
+  vec <tree> tree_refs;
+
+  /* A set with tree references (either FUNC_DECL or VAR_DECL).  */
+  pointer_set_t *tree_refs_set;
+
+protected:
+  /* Cached, once calculated hash for the item.  */
+  hashval_t hash;
+
+private:
+  /* Initialize internal data structures. Bitmap STACK is used for
+     bitmap memory allocation process.  */
+  void setup (bitmap_obstack *stack);
+}; // class sem_item
+
+class sem_function: public sem_item
+{
+public:
+  /* Semantic function constructor that uses STACK as bitmap memory stack.  */
+  sem_function (bitmap_obstack *stack);
+
+  /*  Constructor based on callgraph node _NODE with computed hash _HASH.
+      Bitmap STACK is used for memory allocation.  */
+  sem_function (cgraph_node *_node, hashval_t _hash, bitmap_obstack *stack);
+
+  ~sem_function ();
+
+  virtual void init_wpa (void);
+  virtual void init (void);
+  virtual const char *name (void);
+  virtual const char *asm_name (void);
+  virtual hashval_t get_hash (void);
+  virtual bool equals_wpa (sem_item *item);
+  virtual bool equals (sem_item *item);
+  virtual void init_refs ();
+  virtual bool merge (sem_item *alias_item);
+  virtual void dump_to_file (FILE *file);
+
+  /* Parses function arguments and result type.  */
+  void parse_tree_args (void);
+
+  /* Returns cgraph_node.  */
+  struct cgraph_node *get_node (void);
+
+  /* For a given call graph NODE, the function constructs new
+     semantic function item.  */
+  static sem_function *parse (struct cgraph_node *node, bitmap_obstack *stack);
+
+  /* Exception handling region tree.  */
+  eh_region region_tree;
+
+  /* Result type tree node.  */
+  tree result_type;
+
+  /* Array of argument tree types.  */
+  vec <tree> arg_types;
+
+  /* Number of function arguments.  */
+  unsigned int arg_count;
+
+  /* Basic block count.  */
+  unsigned int bb_count;
+
+  /* Total amount of edges in the function.  */
+  unsigned int edge_count;
+
+  /* Array of sizes of all basic blocks.  */
+  unsigned int *bb_sizes;
+
+  /* Control flow graph checksum.  */
+  hashval_t cfg_checksum;
+
+  /* GIMPLE codes hash value.  */
+  hashval_t gcode_hash;
+
+  /* Total number of SSA names used in the function.  */
+  unsigned ssa_names_size;
+
+  /* Array of structures for all basic blocks.  */
+  sem_bb_t **bb_sorted;
+
+private:
+  /* Calculates hash value based on a BASIC_BLOCK.  */
+  hashval_t get_bb_hash (const sem_bb_t *basic_block);
+
+  /* Basic block equivalence comparison function that returns true if
+     basic blocks BB1 and BB2 (from functions FUNC1 and FUNC2) correspond.  */
+  bool compare_bb (sem_bb_t *bb1, sem_bb_t *bb2, tree func1, tree func2);
+
+  /* For given basic blocks BB1 and BB2 (from functions FUNC1 and FUNC),
+     true value is returned if phi nodes are sematically
+     equivalent in these blocks .  */
+  bool compare_phi_node (basic_block bb1, basic_block bb2, tree func1,
+			 tree func2);
+
+  /* For given basic blocks BB1 and BB2 (from functions FUNC1 and FUNC),
+     true value is returned if exception handling regions are equivalent
+     in these blocks.  */
+  bool compare_eh_region (eh_region r1, eh_region r2, tree func1, tree func2);
+
+  /* Iterates GSI statement iterator to the next non-debug statement.  */
+  void gsi_next_nondebug_stmt (gimple_stmt_iterator &gsi);
+
+  /* Iterates GSI statement iterator to the next non-virtual statement.  */
+  void gsi_next_nonvirtual_phi (gimple_stmt_iterator &it);
+
+  /* Verifies that trees T1 and T2 do correspond.  */
+  bool compare_function_decl (tree t1, tree t2);
+
+  /* Verifies that trees T1 and T2 do correspond.  */
+  bool compare_variable_decl (tree t1, tree t2, tree func1, tree func2);
+
+  /* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
+     call statements are semantically equivalent.  */
+  bool compare_gimple_call (gimple s1, gimple s2,
+			    tree func1, tree func2);
+
+  /* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
+     assignment statements are semantically equivalent.  */
+  bool compare_gimple_assign (gimple s1, gimple s2, tree func1, tree func2);
+
+  /* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
+     condition statements are semantically equivalent.  */
+  bool compare_gimple_cond (gimple s1, gimple s2, tree func1, tree func2);
+
+  /* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
+     label statements are semantically equivalent.  */
+  bool compare_gimple_label (gimple s1, gimple s2, tree func1, tree func2);
+
+  /* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
+     switch statements are semantically equivalent.  */
+  bool compare_gimple_switch (gimple s1, gimple s2, tree func1, tree func2);
+
+  /* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
+     return statements are semantically equivalent.  */
+  bool compare_gimple_return (gimple s1, gimple s2, tree func1, tree func2);
+
+  /* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
+     goto statements are semantically equivalent.  */
+  bool compare_gimple_goto (gimple s1, gimple s2, tree func1, tree func2);
+
+  /* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
+     resx statements are semantically equivalent.  */
+  bool compare_gimple_resx (gimple s1, gimple s2);
+
+  /* Verifies for given GIMPLEs S1 and S2 that ASM statements are equivalent.
+     For the beginning, the pass only supports equality for
+     '__asm__ __volatile__ ("", "", "", "memory")'.  */
+  bool compare_gimple_asm (gimple s1, gimple s2);
+
+  /* Verifies that tree labels T1 and T2 correspond in FUNC1 and FUNC2.  */
+  bool compare_tree_ssa_label (tree t1, tree t2, tree func1, tree func2);
+
+  /* Function compares two operands T1 and T2 and returns true if these
+     two trees from FUNC1 (respectively FUNC2) are semantically equivalent.  */
+  bool compare_operand (tree t1, tree t2, tree func1, tree func2);
+
+  /* If T1 and T2 are SSA names, dictionary comparison is processed. Otherwise,
+     declaration comparasion is executed.  */
+  bool compare_ssa_name (tree t1, tree t2, tree func1, tree func2);
+
+  /* Basic blocks dictionary BB_DICT returns true if SOURCE index BB
+     corresponds to TARGET.  */
+  bool bb_dict_test (int* bb_dict, int source, int target);
+
+  /* Iterates all tree types in T1 and T2 and returns true if all types
+     are compatible.  */
+  bool compare_type_list (tree t1, tree t2);
+
+  /* Processes function equality comparison.  */
+  bool equals_private (sem_item *item);
+
+  /* Initializes references to another sem_item for gimple STMT of type assign.  */
+  void init_refs_for_assign (gimple stmt);
+
+  /* Initializes references to another sem_item for tree T.  */
+  void init_refs_for_tree (tree t);
+
+  /* Returns true if tree T can be compared as a handled component.  */
+  static bool icf_handled_component_p (tree t);
+
+  /* Function checker stores binding between functions.   */
+  func_checker checker;
+
+  /* COMPARED_FUNC is a function that we compare to.  */
+  sem_function *compared_func;
+}; // class sem_function
+
+class sem_variable: public sem_item
+{
+public:
+  /* Semantic variable constructor that uses STACK as bitmap memory stack.  */
+  sem_variable (bitmap_obstack *stack);
+
+  /*  Constructor based on callgraph node _NODE with computed hash _HASH.
+      Bitmap STACK is used for memory allocation.  */
+
+  sem_variable (varpool_node *_node, hashval_t _hash, bitmap_obstack *stack);
+
+  virtual void init_wpa (void);
+  virtual void init (void);
+  virtual const char *name (void);
+  virtual const char *asm_name (void);
+  virtual void init_refs ();
+  virtual hashval_t get_hash (void);
+  virtual bool merge (sem_item *alias_item);
+  virtual void dump_to_file (FILE *file);
+  virtual bool equals_wpa (sem_item *item);
+  virtual bool equals (sem_item *item);
+
+  /* Returns varpool_node.  */
+  struct varpool_node *get_node (void);
+
+  /* Parser function that visits a varpool NODE.  */
+  static sem_variable *parse (struct varpool_node *node, bitmap_obstack *stack);
+
+  /* Variable constructor.  */
+  tree ctor;
+
+private:
+  /* Iterates though a constructor and identifies tree references
+     we are interested in semantic function equality.  */
+  void parse_tree_refs (tree t);
+
+  /* Compares trees T1 and T2 for semantic equality.  */
+  static bool equals (tree t1, tree t2);
+
+}; // class sem_variable
+
+class sem_item_optimizer;
+
+/* Congruence class set structure.  */
+struct congruence_class_var_hash: typed_noop_remove <congruence_class>
+{
+  typedef congruence_class value_type;
+  typedef congruence_class compare_type;
+  static inline hashval_t hash (const value_type *);
+  static inline int equal (const value_type *, const compare_type *);
+};
+
+typedef struct congruence_class_group
+{
+  hashval_t hash;
+  vec <congruence_class *> classes;
+} congruence_class_group_t;
+
+/* Congruence class set structure.  */
+struct congruence_class_group_hash: typed_noop_remove <congruence_class_group_t>
+{
+  typedef congruence_class_group value_type;
+  typedef congruence_class_group compare_type;
+  static inline hashval_t hash (const value_type *);
+  static inline int equal (const value_type *, const compare_type *);
+};
+
+struct traverse_split_pair
+{
+  sem_item_optimizer *optimizer;
+  class congruence_class *cls;
+};
+
+/* Semantic item optimizer includes all top-level logic
+   related to semantic equality comparison.  */
+class sem_item_optimizer
+{
+public:
+  sem_item_optimizer ();
+  ~sem_item_optimizer ();
+
+  /* Function responsible for visiting all potential functions and
+     read-only variables that can be merged.  */
+  void parse_funcs_and_vars (void);
+
+  /* Optimizer entry point.  */
+  void execute (void);
+
+  /* Dump function. */
+  void dump (void);
+
+  /* Verify congruence classes if checking is enabled.  */
+  void verify_classes (void);
+
+  /* Write IPA ICF summary for symbols.  */
+  void write_summary (void);
+
+  /* Read IPA IPA ICF summary for symbols.  */
+  void read_summary (void);
+
+  /* Callgraph removal hook called for a NODE with a custom DATA.  */
+  static void cgraph_removal_hook (struct cgraph_node *node, void *data);
+
+  /* Varpool removal hook called for a NODE with a custom DATA.  */
+  static void varpool_removal_hook (struct varpool_node *node, void *data);
+
+  /* Worklist of congruence classes that can potentially
+     refine classes of congruence.  */
+  hash_table <congruence_class_var_hash> worklist;
+
+  /* Remove symtab NODE triggered by symtab removal hooks.  */
+  void remove_symtab_node (struct symtab_node *node);
+
+  /* Register callgraph and varpool hooks.  */
+  void register_hooks (void);
+
+  /* Unregister callgraph and varpool hooks.  */
+  void unregister_hooks (void);
+
+  /* Adds a CLS to hashtable associated by hash value.  */
+  void add_class (congruence_class *cls);
+
+  /* Gets a congruence class group based on given HASH value.  */
+  congruence_class_group_t *get_group_by_hash (hashval_t hash);
+
+private:
+
+  /* Congruence classes are built by hash value.  */
+  void build_hash_based_classes (void);
+
+  /* Semantic items in classes having more than one element and initialized.
+     In case of WPA, we load function body.  */
+  void parse_nonsingleton_classes (void);
+
+  /* Equality function for semantic items is used to subdivide existing
+     classes. If IN_WPA, fast equality function is invoked.  */
+  void subdivide_classes_by_equality (bool in_wpa = false);
+
+  /* Debug function prints all informations about congruence classes.  */
+  void dump_cong_classes (void);
+
+  /* Iterative congruence reduction function.  */
+  void process_cong_reduction (void);
+
+  /* After reduction is done, we can declare all items in a group
+     to be equal. PREV_CLASS_COUNT is start number of classes
+     before reduction.  */
+  void merge_classes (unsigned int prev_class_count);
+
+  /* Adds a newly created congruence class CLS to worklist.  */
+  void worklist_push (congruence_class *cls);
+
+  /* Pops a class from worklist. */
+  congruence_class *worklist_pop ();
+
+  /* Returns true if a congruence class CLS is presented in worklist.  */
+  bool worklist_contains (const congruence_class *cls);
+
+  /* Removes given congruence class CLS from worklist.  */
+  void worklist_remove (const congruence_class *cls);
+
+  /* Every usage of a congruence class CLS is a candidate that can split the
+     collection of classes. Bitmap stack BMSTACK is used for bitmap
+     allocation.  */
+  void do_congruence_step (congruence_class *cls);
+
+  /* Tests if a class CLS used as INDEXth splits any congruence classes.
+     Bitmap stack BMSTACK is used for bitmap allocation.  */
+  void do_congruence_step_for_index (congruence_class *cls, unsigned int index);
+
+  /* Makes pairing between a congruence class CLS and semantic ITEM.  */
+  static void add_item_to_class (congruence_class *cls, sem_item *item);
+
+  /* Disposes split map traverse function. CLS_PTR is pointer to congruence
+     class, BSLOT is bitmap slot we want to release. DATA is mandatory,
+     but unused argument.  */
+  static bool release_split_map (const void *cls_ptr, bitmap *bslot, void *data);
+
+  /* Process split operation for a class given as pointer CLS_PTR,
+     where bitmap B splits congruence class members. DATA is used
+     as argument of split pair.  */
+  static bool traverse_congruence_split (const void *cls_ptr, bitmap *b,
+					 void *data);
+
+  /* Reads a section from LTO stream file FILE_DATA. Input block for DATA
+     contains LEN bytes.  */
+  void read_section (struct lto_file_decl_data *file_data, const char *data,
+		     size_t len);
+
+  /* Removes all callgraph and varpool nodes that are marked by symtab
+     as deleted.  */
+  void filter_removed_items (void);
+
+  /* Vector of semantic items.  */
+  vec <sem_item *> items;
+
+  /* A set containing all items removed by hooks.  */
+  pointer_set_t *removed_items_set;
+
+  /* Hashtable of congruence classes */
+  hash_table <congruence_class_group_hash> classes;
+
+  /* Count of congruence classes.  */
+  unsigned int classes_count;
+
+  /* Map data structure maps trees to semantic items.  */
+  pointer_map <sem_item *> decl_map;
+
+  /* Map data structure maps symtab nodes to semantic items.  */
+  pointer_map <sem_item *> symtab_node_map;
+
+  /* For all congruence classes, we indicate partial mapping
+     during reduction.  */
+  pointer_map <bitmap> *split_map;
+
+  /* Set to true if a splitter class is removed.  */
+  bool splitter_class_removed;
+
+  /* Global unique class id counter.  */
+  static unsigned int class_id;
+
+  /* Callgraph node removal hook holder.  */
+  struct cgraph_node_hook_list *cgraph_node_hooks;
+
+  /* Varpool node removal hook holder.  */
+  struct varpool_node_hook_list *varpool_node_hooks;
+
+  /* Bitmap stack.  */
+  bitmap_obstack bmstack;
+}; // class sem_item_optimizer
+
+}
diff --git a/gcc/lto-section-in.c b/gcc/lto-section-in.c
index d887763..f9587cf 100644
--- a/gcc/lto-section-in.c
+++ b/gcc/lto-section-in.c
@@ -60,7 +60,8 @@ const char *lto_section_name[LTO_N_SECTION_TYPES] =
   "opts",
   "cgraphopt",
   "inline",
-  "ipcp_trans"
+  "ipcp_trans",
+  "icf"
 };
 
 
diff --git a/gcc/lto-streamer.h b/gcc/lto-streamer.h
index 521d78d..3207d08 100644
--- a/gcc/lto-streamer.h
+++ b/gcc/lto-streamer.h
@@ -247,6 +247,7 @@ enum lto_section_type
   LTO_section_cgraph_opt_sum,
   LTO_section_inline_summary,
   LTO_section_ipcp_transform,
+  LTO_section_ipa_icf,
   LTO_N_SECTION_TYPES		/* Must be last.  */
 };
 
diff --git a/gcc/opts.c b/gcc/opts.c
index 2b1280a..b5a58a5 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -496,6 +496,7 @@ static const struct default_options default_options_table[] =
     { OPT_LEVELS_2_PLUS, OPT_fvect_cost_model_, NULL, VECT_COST_MODEL_CHEAP },
     { OPT_LEVELS_2_PLUS_SPEED_ONLY, OPT_foptimize_strlen, NULL, 1 },
     { OPT_LEVELS_2_PLUS, OPT_fhoist_adjacent_loads, NULL, 1 },
+    { OPT_LEVELS_2_PLUS, OPT_fipa_icf, NULL, 1 },
     { OPT_LEVELS_2_PLUS, OPT_fisolate_erroneous_paths_dereference, NULL, 1 },
     { OPT_LEVELS_2_PLUS, OPT_fuse_caller_save, NULL, 1 },
 
diff --git a/gcc/passes.def b/gcc/passes.def
index f9e0b2a..e629a1d 100644
--- a/gcc/passes.def
+++ b/gcc/passes.def
@@ -105,6 +105,7 @@ along with GCC; see the file COPYING3.  If not see
   NEXT_PASS (pass_ipa_whole_program_visibility);
   NEXT_PASS (pass_ipa_profile);
   NEXT_PASS (pass_ipa_devirt);
+  NEXT_PASS (pass_ipa_icf);
   NEXT_PASS (pass_ipa_cp);
   NEXT_PASS (pass_ipa_cdtor_merge);
   NEXT_PASS (pass_ipa_inline);
diff --git a/gcc/timevar.def b/gcc/timevar.def
index cbb64d5..c1d09eb 100644
--- a/gcc/timevar.def
+++ b/gcc/timevar.def
@@ -89,6 +89,7 @@ DEFTIMEVAR (TV_WHOPR_LTRANS          , "whopr ltrans")
 DEFTIMEVAR (TV_IPA_REFERENCE         , "ipa reference")
 DEFTIMEVAR (TV_IPA_PROFILE           , "ipa profile")
 DEFTIMEVAR (TV_IPA_PURE_CONST        , "ipa pure const")
+DEFTIMEVAR (TV_IPA_ICF		     , "ipa icf")
 DEFTIMEVAR (TV_IPA_PTA               , "ipa points-to")
 DEFTIMEVAR (TV_IPA_SRA               , "ipa SRA")
 DEFTIMEVAR (TV_IPA_FREE_LANG_DATA    , "ipa free lang data")
diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h
index 3888bb6..d55d7b8 100644
--- a/gcc/tree-pass.h
+++ b/gcc/tree-pass.h
@@ -464,6 +464,7 @@ extern simple_ipa_opt_pass *make_pass_ipa_free_lang_data (gcc::context *ctxt);
 extern simple_ipa_opt_pass *make_pass_ipa_free_inline_summary (gcc::context
 							       *ctxt);
 extern ipa_opt_pass_d *make_pass_ipa_cp (gcc::context *ctxt);
+extern ipa_opt_pass_d *make_pass_ipa_icf (gcc::context *ctxt);
 extern ipa_opt_pass_d *make_pass_ipa_devirt (gcc::context *ctxt);
 extern ipa_opt_pass_d *make_pass_ipa_reference (gcc::context *ctxt);
 extern ipa_opt_pass_d *make_pass_ipa_pure_const (gcc::context *ctxt);
-- 
1.8.4.5


^ permalink raw reply	[flat|nested] 70+ messages in thread

* [PATCH 1/5] New Identical Code Folding IPA pass
@ 2014-06-16 10:07 mliska
  2014-06-16 10:07 ` [PATCH 4/5] Existing tests fix mliska
                   ` (6 more replies)
  0 siblings, 7 replies; 70+ messages in thread
From: mliska @ 2014-06-16 10:07 UTC (permalink / raw)
  To: gcc-patches; +Cc: hubicka

Hello,
  I am sorry for wrong reply address in previously sent thread. After working for quite a long time, I would like to introduce new IPA pass. Goal of the pass is to merge semantically equivalent functions and read-only variables.
  If we prove that a function A is an equivalent to a function B, depending on circumstances, an alias, thunk or a function redirection is created.

  The optimization is inspired by Microsoft /OPT:ICF optimization (http://msdn.microsoft.com/en-us/library/bxwfs976.aspx) that merges COMDAT sections with each function reside in a separate section.
  Apart from that, GOLD linker implemented equivalent implementation for GNU toolchain: SAFE ICF (http://research.google.com/pubs/pub36912.html). Both implementations suffer from a collection of functions where an address was taken for comparison purpose. GOLD linker adds more conservative --icf=safe option.

  You may ask, why the GNU GCC does need such a new optimization. The compiler, having simply better knowledge of a compiled source file, is capable of reaching better results, especially if Link-Time optimization is enabled. Apart from that, GCC implementation adds support for read-only variables like construction vtables (mentioned in: http://hubicka.blogspot.cz/2014/02/devirtualization-in-c-part-3-building.html).

  The pass is capable of building GIMP, Inkscape and Firefox with enabled LTO. Majority of issues connected to testsuite has been fixed, there is a few of IPA tests I need to consult with Jan, how to fix it correctly.

  Statistics about the pass:
  Inkscape: 11.95 MB -> 11.44 MB (-4.27%)
  Firefox: 70.12 MB -> 70.12 MB (-3.07%)

  SPEC 2K6 statistics show how many equivalent functions (and variables) are proved by GOLD and GCC ICF:
  +----------------+----------+---------+------------+------------------+--------------+--------------+-------------+
  | SPEC           | GOLD ICF | IPA ICF | difference | difference perc. | intersection | Just in GOLD | Just in ICF |
  +----------------+----------+---------+------------+------------------+--------------+--------------+-------------+
  | 400.perlbench  |       26 |      53 |         27 |          203.85% |           26 |            0 |          27 |
  | 401.bzip2      |        0 |       1 |          1 |            0.00% |            0 |            0 |           1 |
  | 403.gcc        |       88 |     223 |        135 |          253.41% |           81 |            7 |         142 |
  | 410.bwaves     |        0 |       0 |          0 |            0.00% |            0 |            0 |           0 |
  | 416.gamess     |        9 |      23 |         14 |          255.56% |            9 |            0 |          14 |
  | 429.mcf        |        0 |       0 |          0 |            0.00% |            0 |            0 |           0 |
  | 433.milc       |        0 |       8 |          8 |            0.00% |            0 |            0 |           8 |
  | 434.zeusmp     |        0 |       1 |          1 |            0.00% |            0 |            0 |           1 |
  | 436.cactusADM  |       19 |      55 |         36 |          289.47% |           14 |            5 |          41 |
  | 437.leslie3d   |        0 |       0 |          0 |            0.00% |            0 |            0 |           0 |
  | 444.namd       |        0 |       0 |          0 |            0.00% |            0 |            0 |           0 |
  | 445.gobmk      |        0 |     332 |        332 |            0.00% |            0 |            0 |         332 |
  | 453.povray     |       73 |      93 |         20 |          127.40% |           60 |           13 |          33 |
  | 454.calculix   |        3 |       6 |          3 |          200.00% |            3 |            0 |           3 |
  | 456.hmmer      |        0 |       1 |          1 |            0.00% |            0 |            0 |           1 |
  | 458.sjeng      |        0 |       1 |          1 |            0.00% |            0 |            0 |           1 |
  | 459.GemsFDTD   |        0 |       0 |          0 |            0.00% |            0 |            0 |           0 |
  | 462.libquantum |        0 |       0 |          0 |            0.00% |            0 |            0 |           0 |
  | 464.h264ref    |        0 |       4 |          4 |            0.00% |            0 |            0 |           4 |
  | 470.lbm        |        0 |       0 |          0 |            0.00% |            0 |            0 |           0 |
  | 471.omnetpp    |      134 |     179 |         45 |          133.58% |          127 |            7 |          52 |
  | 473.astar      |        0 |       1 |          1 |            0.00% |            0 |            0 |           1 |
  | 481.wrf        |       51 |      75 |         24 |          147.06% |           50 |            1 |          25 |
  | 482.sphinx3    |        0 |       1 |          1 |            0.00% |            0 |            0 |           1 |
  +----------------+----------+---------+------------+------------------+--------------+--------------+-------------+
  | TOTAL          |      403 |    1057 |        654 |          262.28% |          370 |           33 |         687 |
  +----------------+----------+---------+------------+------------------+--------------+--------------+-------------+

  I've been testing the pass on a QEMU Gentoo virtual machine and I will bring stats as soon as possible.

  There are still unresolved issues connected to correct debugging. I am not familiar with DWARF extensions, but it looks
  there is a solution that can be applied to the pass: http://wiki.dwarfstd.org/index.php?title=ICF ?

  I tried to create equivalent functions foo and bar, merged by ICF GOLD. Function bar is removed from .text section, but gdb
  can put a breakpoint to the function, where content of function foo is displayed. I think this is acceptable behavior.

  I expect many comments related to pass, but I hope effort invested by me a Honza will be transformed to acceptation to trunk.

  Martin

(please ignore this diff)

diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 3ddd98c..4f9418e 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,4 @@
+
 2014-06-13  Thomas Preud'homme  <thomas.preudhomme@arm.com>
 
 	PR tree-optimization/61375
-- 
1.8.4.5


^ permalink raw reply	[flat|nested] 70+ messages in thread

* [PATCH 2/5] Existing call graph infrastructure enhancement
  2014-06-16 10:07 [PATCH 1/5] New Identical Code Folding IPA pass mliska
  2014-06-16 10:07 ` [PATCH 4/5] Existing tests fix mliska
  2014-06-16 10:07 ` [PATCH 5/5] New tests introduction mliska
@ 2014-06-16 10:07 ` mliska
  2014-06-17 20:00   ` Jeff Law
  2014-09-24 14:23   ` Martin Liška
  2014-06-16 10:07 ` [PATCH 3/5] IPA ICF pass mliska
                   ` (3 subsequent siblings)
  6 siblings, 2 replies; 70+ messages in thread
From: mliska @ 2014-06-16 10:07 UTC (permalink / raw)
  To: gcc-patches; +Cc: hubicka

Hi,
    this small patch prepares remaining needed infrastructure for the new pass.

Changelog:

2014-06-13  Martin Liska  <mliska@suse.cz>
	    Honza Hubicka  <hubicka@ucw.cz>

	* ipa-utils.h (polymorphic_type_binfo_p): Function marked external
	instead of static.
	* ipa-devirt.c (polymorphic_type_binfo_p): Likewise.
	* ipa-prop.h (count_formal_params): Likewise.
	* ipa-prop.c (count_formal_params): Likewise.
	* ipa-utils.c (ipa_merge_profiles): Be more tolerant if we merge
	profiles for semantically equivalent functions.
	* passes.c (do_per_function): If we load body of a function during WPA,
	this condition should behave same.
	* varpool.c (ctor_for_folding): More tolerant assert for variable
	aliases created during WPA.

diff --git a/gcc/ipa-devirt.c b/gcc/ipa-devirt.c
index d733461..18592d7 100644
--- a/gcc/ipa-devirt.c
+++ b/gcc/ipa-devirt.c
@@ -176,7 +176,7 @@ struct GTY(()) odr_type_d
    inheritance (because vtables are shared).  Look up the BINFO of type
    and check presence of its vtable.  */
 
-static inline bool
+bool
 polymorphic_type_binfo_p (tree binfo)
 {
   /* See if BINFO's type has an virtual table associtated with it.  */
diff --git a/gcc/ipa-prop.c b/gcc/ipa-prop.c
index b67deed..60bda71 100644
--- a/gcc/ipa-prop.c
+++ b/gcc/ipa-prop.c
@@ -210,7 +210,7 @@ ipa_populate_param_decls (struct cgraph_node *node,
 
 /* Return how many formal parameters FNDECL has.  */
 
-static inline int
+int
 count_formal_params (tree fndecl)
 {
   tree parm;
diff --git a/gcc/ipa-prop.h b/gcc/ipa-prop.h
index cb23698..87573ff 100644
--- a/gcc/ipa-prop.h
+++ b/gcc/ipa-prop.h
@@ -529,6 +529,7 @@ void ipa_free_all_edge_args (void);
 void ipa_free_all_structures_after_ipa_cp (void);
 void ipa_free_all_structures_after_iinln (void);
 void ipa_register_cgraph_hooks (void);
+int count_formal_params (tree fndecl);
 
 /* This function ensures the array of node param infos is big enough to
    accommodate a structure for all nodes and reallocates it if not.  */
diff --git a/gcc/ipa-utils.c b/gcc/ipa-utils.c
index 8e7c7cb..bc2b958 100644
--- a/gcc/ipa-utils.c
+++ b/gcc/ipa-utils.c
@@ -660,13 +660,8 @@ ipa_merge_profiles (struct cgraph_node *dst,
   if (dst->tp_first_run > src->tp_first_run && src->tp_first_run)
     dst->tp_first_run = src->tp_first_run;
 
-  if (src->profile_id)
-    {
-      if (!dst->profile_id)
-	dst->profile_id = src->profile_id;
-      else
-	gcc_assert (src->profile_id == dst->profile_id);
-    }
+  if (src->profile_id && !dst->profile_id)
+    dst->profile_id = src->profile_id;
 
   if (!dst->count)
     return;
diff --git a/gcc/ipa-utils.h b/gcc/ipa-utils.h
index a2c985a..996249a 100644
--- a/gcc/ipa-utils.h
+++ b/gcc/ipa-utils.h
@@ -72,6 +72,8 @@ struct odr_type_d;
 typedef odr_type_d *odr_type;
 void build_type_inheritance_graph (void);
 void update_type_inheritance_graph (void);
+bool polymorphic_type_binfo_p (tree binfo);
+
 vec <cgraph_node *>
 possible_polymorphic_call_targets (tree, HOST_WIDE_INT,
 				   ipa_polymorphic_call_context,
diff --git a/gcc/passes.c b/gcc/passes.c
index 4366251..9fdfe51 100644
--- a/gcc/passes.c
+++ b/gcc/passes.c
@@ -1506,7 +1506,7 @@ do_per_function (void (*callback) (function *, void *data), void *data)
     {
       struct cgraph_node *node;
       FOR_EACH_DEFINED_FUNCTION (node)
-	if (node->analyzed && gimple_has_body_p (node->decl)
+	if (node->analyzed && (gimple_has_body_p (node->decl) && !in_lto_p)
 	    && (!node->clone_of || node->decl != node->clone_of->decl))
 	  callback (DECL_STRUCT_FUNCTION (node->decl), data);
     }
diff --git a/gcc/varpool.c b/gcc/varpool.c
index ff67127..5cc558e 100644
--- a/gcc/varpool.c
+++ b/gcc/varpool.c
@@ -293,6 +293,7 @@ ctor_for_folding (tree decl)
   if (decl != real_decl)
     {
       gcc_assert (!DECL_INITIAL (decl)
+		  || (node->alias && varpool_alias_target (node) == real_node)
 		  || DECL_INITIAL (decl) == error_mark_node);
       if (lookup_attribute ("weakref", DECL_ATTRIBUTES (decl)))
 	{
-- 
1.8.4.5


^ permalink raw reply	[flat|nested] 70+ messages in thread

* [PATCH 5/5] New tests introduction
  2014-06-16 10:07 [PATCH 1/5] New Identical Code Folding IPA pass mliska
  2014-06-16 10:07 ` [PATCH 4/5] Existing tests fix mliska
@ 2014-06-16 10:07 ` mliska
  2014-06-17 19:53   ` Jeff Law
  2014-06-16 10:07 ` [PATCH 2/5] Existing call graph infrastructure enhancement mliska
                   ` (4 subsequent siblings)
  6 siblings, 1 reply; 70+ messages in thread
From: mliska @ 2014-06-16 10:07 UTC (permalink / raw)
  To: gcc-patches; +Cc: hubicka

Hi,
   this is a new collection of tests for IPA ICF pass.

Martin

Changelog:

2014-06-13  Martin Liska  <mliska@suse.cz>
	    Honza Hubicka  <hubicka@ucw.cz>

	* gcc/testsuite/g++.dg/ipa/ipa-se-1.C: New test.
	* gcc/testsuite/g++.dg/ipa/ipa-se-2.C: Likewise.
	* gcc/testsuite/g++.dg/ipa/ipa-se-3.C: Likewise.
	* gcc/testsuite/g++.dg/ipa/ipa-se-4.C: Likewise.
	* gcc/testsuite/g++.dg/ipa/ipa-se-5.C: Likewise.
	* gcc/testsuite/gcc.dg/ipa/ipa-se-1.c: Likewise.
	* gcc/testsuite/gcc.dg/ipa/ipa-se-10.c: Likewise.
	* gcc/testsuite/gcc.dg/ipa/ipa-se-11.c: Likewise.
	* gcc/testsuite/gcc.dg/ipa/ipa-se-12.c: Likewise.
	* gcc/testsuite/gcc.dg/ipa/ipa-se-13.c: Likewise.
	* gcc/testsuite/gcc.dg/ipa/ipa-se-14.c: Likewise.
	* gcc/testsuite/gcc.dg/ipa/ipa-se-15.c: Likewise.
	* gcc/testsuite/gcc.dg/ipa/ipa-se-16.c: Likewise.
	* gcc/testsuite/gcc.dg/ipa/ipa-se-17.c: Likewise.
	* gcc/testsuite/gcc.dg/ipa/ipa-se-18.c: Likewise.
	* gcc/testsuite/gcc.dg/ipa/ipa-se-19.c: Likewise.
	* gcc/testsuite/gcc.dg/ipa/ipa-se-2.c: Likewise.
	* gcc/testsuite/gcc.dg/ipa/ipa-se-20.c: Likewise.
	* gcc/testsuite/gcc.dg/ipa/ipa-se-21.c: Likewise.
	* gcc/testsuite/gcc.dg/ipa/ipa-se-22.c: Likewise.
	* gcc/testsuite/gcc.dg/ipa/ipa-se-23.c: Likewise.
	* gcc/testsuite/gcc.dg/ipa/ipa-se-24.c: Likewise.
	* gcc/testsuite/gcc.dg/ipa/ipa-se-25.c: Likewise.
	* gcc/testsuite/gcc.dg/ipa/ipa-se-26.c: Likewise.
	* gcc/testsuite/gcc.dg/ipa/ipa-se-27.c: Likewise.
	* gcc/testsuite/gcc.dg/ipa/ipa-se-28.c: Likewise.
	* gcc/testsuite/gcc.dg/ipa/ipa-se-3.c: Likewise.
	* gcc/testsuite/gcc.dg/ipa/ipa-se-4.c: Likewise.
	* gcc/testsuite/gcc.dg/ipa/ipa-se-5.c: Likewise.
	* gcc/testsuite/gcc.dg/ipa/ipa-se-6.c: Likewise.
	* gcc/testsuite/gcc.dg/ipa/ipa-se-7.c: Likewise.
	* gcc/testsuite/gcc.dg/ipa/ipa-se-8.c: Likewise.
	* gcc/testsuite/gcc.dg/ipa/ipa-se-9.c: Likewise.

diff --git a/gcc/testsuite/g++.dg/ipa/ipa-se-1.C b/gcc/testsuite/g++.dg/ipa/ipa-se-1.C
new file mode 100644
index 0000000..d27abf4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ipa/ipa-se-1.C
@@ -0,0 +1,43 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf"  } */
+
+class A
+{
+public:
+  __attribute__ ((noinline))
+  virtual int Foo2()
+  {
+    return v;
+  }
+
+  float f;
+  int v;
+};
+
+class B
+{
+public:
+  __attribute__ ((noinline))
+  int Bar2()
+  {
+    return v;
+  }
+
+  float f, aaa;
+  int v;
+};
+
+int main()
+{
+  A a;
+  B b;
+
+  a.Foo2();
+  b.Bar2();
+
+  return 12345;
+}
+
+/* { dg-final { scan-ipa-dump-not "Semantic equality hit:" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 0" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/g++.dg/ipa/ipa-se-2.C b/gcc/testsuite/g++.dg/ipa/ipa-se-2.C
new file mode 100644
index 0000000..48badd7
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ipa/ipa-se-2.C
@@ -0,0 +1,40 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf"  } */
+
+class A
+{
+public:
+  __attribute__ ((noinline))
+  int Foo2()
+  {
+    return 1;
+  }
+
+  int v;
+  float f;
+};
+
+class B
+{
+public:
+  __attribute__ ((noinline))
+  int Bar2()
+  {
+    return 1;
+  }
+
+  int v;
+  float f, aaa;
+};
+
+int main()
+{
+  A a;
+  B b;
+
+  return a.Foo2() + b.Bar2();
+}
+
+/* { dg-final { scan-ipa-dump "Semantic equality hit:" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 1" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/g++.dg/ipa/ipa-se-3.C b/gcc/testsuite/g++.dg/ipa/ipa-se-3.C
new file mode 100644
index 0000000..042f789
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ipa/ipa-se-3.C
@@ -0,0 +1,36 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf"  } */
+
+__attribute__ ((noinline))
+int zero()
+{
+  return 0;
+}
+
+__attribute__ ((noinline))
+int nula()
+{
+  return 0;
+}
+
+__attribute__ ((noinline))
+int foo()
+{
+  return zero();
+}
+
+__attribute__ ((noinline))
+int bar()
+{
+  return nula();
+}
+
+int main()
+{
+  return foo() + bar();
+}
+
+/* { dg-final { scan-ipa-dump "Semantic equality hit:.*bar.*->.*foo.*" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Semantic equality hit:.*nula.*->.*zero.*" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 2" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/g++.dg/ipa/ipa-se-4.C b/gcc/testsuite/g++.dg/ipa/ipa-se-4.C
new file mode 100644
index 0000000..4ac2cf9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ipa/ipa-se-4.C
@@ -0,0 +1,49 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf -fno-inline" } */
+
+namespace {
+struct A
+{
+  virtual void foo(void) {}
+};
+struct B: virtual A
+{
+  virtual void foo(void) {}
+};
+struct C: virtual A
+{
+  virtual void bar(void) {}
+};
+struct D: virtual A
+{
+  virtual void sparta(void) {}
+};
+struct E: B,C,D
+{
+  virtual void foo(void) {}
+  virtual void barbar(void) {}
+};
+} // anonymous namespace
+
+int main()
+{
+  struct A a;
+  struct B b;
+  struct C c;
+  struct D d;
+  struct E e;
+
+  a.foo();
+  b.foo();
+  c.bar();
+  d.foo();
+  d.sparta();
+  e.barbar();
+
+  return 123;
+}
+
+/* { dg-final { scan-ipa-dump "Varpool alias has been created" "icf"  } } */
+/* { dg-final { scan-ipa-dump-times "Callgraph alias has been created" 5 "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 6" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/g++.dg/ipa/ipa-se-5.C b/gcc/testsuite/g++.dg/ipa/ipa-se-5.C
new file mode 100644
index 0000000..855c32e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ipa/ipa-se-5.C
@@ -0,0 +1,24 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf" } */
+
+struct test
+{
+  int a;
+  float b;
+};
+
+extern const struct test myarray __attribute__ ((visibility("hidden")));
+extern const struct test myarray_alias __attribute__ ((visibility("hidden")));
+
+const struct test myarray = {1, 1.5f};
+
+extern const struct test myarray_alias __attribute__ ((alias ("myarray")));
+
+int main()
+{
+  return myarray.a - myarray_alias.a;
+}
+
+/* { dg-final { scan-ipa-dump "Varpool alias cannot be created (alias cycle)." "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 1" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-se-1.c b/gcc/testsuite/gcc.dg/ipa/ipa-se-1.c
new file mode 100644
index 0000000..aeee356
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-se-1.c
@@ -0,0 +1,61 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf"  } */
+
+#include <stdio.h>
+
+struct container
+{
+  int x;
+  int y;
+};
+
+static struct container max;
+static int array[3][3];
+static int array2[123];
+
+__attribute__ ((noinline))
+void foo(void)
+{
+  printf("Foo()");
+}
+
+__attribute__ ((noinline))
+int order(int x, int y)
+{
+  return x < y ? 2 : 4;
+}
+
+__attribute__ ((noinline))
+int order2(int y, int x)
+{
+  return x < y ? 2 : 4;
+}
+
+__attribute__ ((noinline))
+void x1(int x)
+{
+  int i;
+  for(i = 0; i < 20; ++i)
+    array2[i] = i;
+
+  array2[2] = 13;
+}
+
+__attribute__ ((noinline))
+void x2(int a)
+{
+  int i;
+  for(i = 0; i < 20; ++i)
+    array2[i] = i;
+
+  array2[2] = 13;
+}
+
+int main(int argc, char **argv)
+{
+  return 0;
+}
+
+/* { dg-final { scan-ipa-dump "Semantic equality hit:x2->x1" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 1" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-se-10.c b/gcc/testsuite/gcc.dg/ipa/ipa-se-10.c
new file mode 100644
index 0000000..b9bca60
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-se-10.c
@@ -0,0 +1,34 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf"  } */
+
+int ferda(int x, int y) __attribute__ ((pure));
+int funkce(int a, int b) __attribute__ ((pure));
+
+__attribute__ ((noinline))
+int ferda(int x, int y)
+{
+  if (x < y)
+    {
+      return x;
+    }
+  else
+    return y;
+}
+
+__attribute__ ((noinline))
+int funkce(int a, int b)
+{
+  if(a < b)
+    return a;
+  else
+    return b;
+}
+
+int main(int argc, char **argv)
+{
+  return 0;
+}
+
+/* { dg-final { scan-ipa-dump "Semantic equality hit:funkce->ferda" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 1" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-se-11.c b/gcc/testsuite/gcc.dg/ipa/ipa-se-11.c
new file mode 100644
index 0000000..2eb90da
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-se-11.c
@@ -0,0 +1,29 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf"  } */
+
+__attribute__ ((noinline))
+int fce(int a, int b)
+{
+  return a + b;
+}
+
+__attribute__ ((noinline))
+int f0(int a)
+{
+  return fce(a, 5) + fce(a, 7);
+}
+
+__attribute__ ((noinline))
+int f1(int a)
+{
+  return fce(a, 5) + fce(a, 7);
+}
+
+int main(int argc, char **argv)
+{
+  return f0(argc) * f1(argc);
+}
+
+/* { dg-final { scan-ipa-dump "Semantic equality hit:f1->f0" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 1" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-se-12.c b/gcc/testsuite/gcc.dg/ipa/ipa-se-12.c
new file mode 100644
index 0000000..d4b7c38
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-se-12.c
@@ -0,0 +1,78 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf"  } */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+int gcd(int x, int y) __attribute__ ((pure));
+
+__attribute__ ((noinline))
+int gcd(int x, int y)
+{
+  int swap;
+
+  if(x <= 0 || y <= 0)
+    return 0;
+
+  if(x < y)
+    {
+      swap = x;
+      x = y;
+      y = swap;
+    }
+
+  while(x != y)
+    {
+      x = x - y;
+
+      if(y > x)
+	{
+	  swap = x;
+	  x = y;
+	  y = swap;
+	}
+    }
+
+  return x;
+}
+
+int nsd(int x, int y) __attribute__ ((pure));
+
+__attribute__ ((noinline))
+int nsd(int x, int y)
+{
+  int swap;
+
+  if(x <= 0 || y <= 0)
+    return 0;
+
+  if(x < y)
+    {
+      swap = x;
+      x = y;
+      y = swap;
+    }
+
+  while(x != y)
+    {
+      x = x - y;
+
+      if(y > x)
+	{
+	  swap = x;
+	  x = y;
+	  y = swap;
+	}
+    }
+
+  return x;
+}
+
+int main(int argc, char **argv)
+{
+  return 0;
+}
+
+/* { dg-final { scan-ipa-dump "Semantic equality hit:nsd->gcd" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 1" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-se-13.c b/gcc/testsuite/gcc.dg/ipa/ipa-se-13.c
new file mode 100644
index 0000000..e409ee4
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-se-13.c
@@ -0,0 +1,194 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf"  } */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+int gcd(int x, int y) __attribute__ ((pure));
+
+__attribute__ ((noinline))
+int gcd(int x, int y)
+{
+  int swap;
+
+  if(x <= 0 || y <= 0)
+    return 0;
+
+  if(x < y)
+    {
+      swap = x;
+      x = y;
+      y = swap;
+    }
+
+  while(x != y)
+    {
+      x = x - y;
+
+      if(y > x)
+	{
+	  swap = x;
+	  x = y;
+	  y = swap;
+	}
+    }
+
+  return x;
+}
+
+int nsd(int x, int y) __attribute__ ((pure));
+
+__attribute__ ((noinline))
+int nsd(int x, int y)
+{
+  int swap;
+
+  if(x <= 0 || y <= 0)
+    return 0;
+
+  if(x < y)
+    {
+      swap = x;
+      x = y;
+      y = swap;
+    }
+
+  while(x != y)
+    {
+      x = x - y;
+
+      if(y > x)
+	{
+	  swap = x;
+	  x = y;
+	  y = swap;
+	}
+    }
+
+  return x;
+}
+
+int nsd_different_result(int x, int y) __attribute__ ((pure));
+
+__attribute__ ((noinline))
+int nsd_different_result(int x, int y)
+{
+  int pes;
+
+  if(x <= 0 || y <= 0)
+    return 1;
+
+  if(x < 10)
+    y = 12;
+  else if(x == 44)
+    y = 124;
+  else
+    y = 1111;
+
+  if(x < y)
+    {
+      pes = x;
+      x = y;
+      y = pes;
+    }
+
+  while(x != y)
+    {
+      x = x - y;
+
+      if(y > x)
+	{
+	  pes = x;
+	  x = y;
+	  y = pes;
+	}
+    }
+
+  return x;
+}
+
+int nsd_different_result2(int x, int y) __attribute__ ((pure));
+
+__attribute__ ((noinline))
+int nsd_different_result2(int x, int y)
+{
+  int pes;
+
+  if(x <= 0 || y <= 0)
+    return 1;
+
+  if(x < 10)
+    y = 12;
+  else if(x == 44)
+    y = 124;
+  else
+    y = 1111;
+
+  if(x < y)
+    {
+      pes = x;
+      x = y;
+      y = pes;
+    }
+
+  while(x != y)
+    {
+      x = x - y;
+
+      if(y > x)
+	{
+	  pes = x;
+	  x = y;
+	  y = pes;
+	}
+    }
+
+  return x;
+}
+
+__attribute__ ((noinline))
+int s1(int x)
+{
+  switch (x)
+    {
+    case 10:
+    case 11:
+      return 2;
+    case 12:
+      return 123;
+    default:
+      return x + 2;
+    }
+}
+
+__attribute__ ((noinline))
+int s2(int x)
+{
+  switch (x)
+    {
+    case 10:
+    case 11:
+      return 2;
+    case 12:
+      return 123;
+    default:
+      return x + 2;
+    }
+}
+int main(int argc, char **argv)
+{
+  if(argc < 3)
+    return 1;
+
+  int a = atoi(argv[1]);
+  int b = atoi(argv[2]);
+
+  printf("Test1: %d, %d, gdc: %d\n", a, b, gcd(a, b));
+  printf("Test2: %d, %d, gdc: %d\n", a, b, nsd(a, b));
+}
+
+/* { dg-final { scan-ipa-dump "Semantic equality hit:s2->s1" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Semantic equality hit:nsd_different_result2->nsd_different_result" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Semantic equality hit:nsd->gcd" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 3" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-se-14.c b/gcc/testsuite/gcc.dg/ipa/ipa-se-14.c
new file mode 100644
index 0000000..cae02fb
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-se-14.c
@@ -0,0 +1,47 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf"  } */
+
+#include <stdio.h>
+
+__attribute__ ((noinline))
+int foo(int a)
+{
+  void *l = &&error;
+
+  if(a == 4)
+    goto *l;
+
+  return 150;
+
+error:
+  return a;
+failure:
+  return a + 2;
+}
+
+__attribute__ ((noinline))
+int foo_wrong(int a)
+{
+  void *l = &&failure;
+
+  if(a == 4)
+    goto *l;
+
+  return 150;
+
+error:
+  return a;
+failure:
+  return a + 2;
+}
+
+int main(int argc, char **argv)
+{
+  printf("value: %d\n", foo(argc));
+
+  return 0;
+}
+
+/* { dg-final { scan-ipa-dump-not "Semantic equality hit:" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 0" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-se-15.c b/gcc/testsuite/gcc.dg/ipa/ipa-se-15.c
new file mode 100644
index 0000000..f6e7fb3
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-se-15.c
@@ -0,0 +1,47 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf"  } */
+
+#include <stdio.h>
+
+__attribute__ ((noinline))
+int bar(int a)
+{
+  void *l = &&error;
+
+  if(a == 4)
+    goto *l;
+
+  return 150;
+
+error:
+  return a;
+failure:
+  return a + 2;
+}
+
+__attribute__ ((noinline))
+int foo(int a)
+{
+  void *l = &&error;
+
+  if(a == 4)
+    goto *l;
+
+  return 150;
+
+error:
+  return a;
+failure:
+  return a + 2;
+}
+
+int main(int argc, char **argv)
+{
+  printf("value: %d\n", foo(argc));
+
+  return 0;
+}
+
+/* { dg-final { scan-ipa-dump "Semantic equality hit:foo->bar" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 1" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-se-16.c b/gcc/testsuite/gcc.dg/ipa/ipa-se-16.c
new file mode 100644
index 0000000..fb0b116
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-se-16.c
@@ -0,0 +1,27 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf"  } */
+
+#include <stdio.h>
+
+__attribute__ ((noinline))
+int foo()
+{
+  printf ("Hello world.\n");
+  return 0;
+}
+
+__attribute__ ((noinline))
+int bar()
+{
+  printf ("Hello world.\n");
+  return 0;
+}
+
+int main()
+{
+  return foo() + bar();
+}
+
+/* { dg-final { scan-ipa-dump "Semantic equality hit:bar->foo" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 1" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-se-17.c b/gcc/testsuite/gcc.dg/ipa/ipa-se-17.c
new file mode 100644
index 0000000..7c9172d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-se-17.c
@@ -0,0 +1,37 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf"  } */
+
+__attribute__ ((noinline))
+int foo(int x)
+{
+  int c = x;
+
+  if (x > 10)
+    c += 2;
+  else
+    c -= 3;
+
+  return x;
+}
+
+__attribute__ ((noinline))
+int bar(int y)
+{
+  int d = y;
+
+  if (y > 10)
+    d += 2;
+  else
+    d -= 3;
+
+  return d;
+}
+
+int main()
+{
+  return 0;
+}
+
+/* { dg-final { scan-ipa-dump-not "Semantic equality hit:" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 0" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-se-18.c b/gcc/testsuite/gcc.dg/ipa/ipa-se-18.c
new file mode 100644
index 0000000..9dc3979
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-se-18.c
@@ -0,0 +1,37 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf"  } */
+
+__attribute__ ((noinline))
+int foo(int x)
+{
+  int c = x;
+
+  if (x > 10)
+    c += 2;
+  else
+    c -= 3;
+
+  return c;
+}
+
+__attribute__ ((noinline))
+int bar(int y)
+{
+  int d = y;
+
+  if (y > 10)
+    d += 2;
+  else
+    d -= 3;
+
+  return d;
+}
+
+int main()
+{
+  return 0;
+}
+
+/* { dg-final { scan-ipa-dump "Semantic equality hit:bar->foo" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 1" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-se-19.c b/gcc/testsuite/gcc.dg/ipa/ipa-se-19.c
new file mode 100644
index 0000000..7a29cf3
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-se-19.c
@@ -0,0 +1,37 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf"  } */
+
+__attribute__ ((noinline))
+int foo(int x)
+{
+  int c = x;
+
+  if (x > 10)
+    c += 2;
+  else
+    c -= 3;
+
+  return c;
+}
+
+__attribute__ ((noinline))
+int bar(int y)
+{
+  int d = y;
+
+  if (y > 11)
+    d += 2;
+  else
+    d -= 3;
+
+  return d;
+}
+
+int main()
+{
+  return 0;
+}
+
+/* { dg-final { scan-ipa-dump-not "Semantic equality hit:" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 0" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-se-2.c b/gcc/testsuite/gcc.dg/ipa/ipa-se-2.c
new file mode 100644
index 0000000..385db0c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-se-2.c
@@ -0,0 +1,69 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf"  } */
+
+#include <stdio.h>
+
+struct container
+{
+  int x;
+  int y;
+};
+
+static struct container max;
+static int pole[3][3];
+static int pole2[123];
+
+static struct container superpole[10][10];
+
+void f1(struct container *c)
+{
+  struct container pes;
+  pes.x = 123;
+  pes.y = 123;
+
+  struct container *pesp = c;
+  pesp->x = 5;
+
+  pole[1][2] = 3;
+
+  superpole[4][3].x = 4;
+  max.x = 3;
+  void *x = &pole;
+
+  int **a = (int**)pole;
+  a[1][2] = 543;
+
+  if(x != 0)
+    pole[1][2] = 123;
+}
+
+void f2(struct container *c)
+{
+  struct container pes;
+  pes.x = 123;
+  pes.y = 123;
+
+  struct container *pesp = c;
+  pesp->x = 5;
+
+  pole[1][2] = 3;
+
+  superpole[4][3].x = 4;
+  max.x = 3;
+  void *x = &pole;
+
+  int **a = (int**)pole;
+  a[1][2] = 543;
+
+  if(x != 0)
+    pole[1][2] = 123;
+}
+
+int main(int argc, char **argv)
+{
+  return 0;
+}
+
+/* { dg-final { scan-ipa-dump "Semantic equality hit:f2->f1" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 1" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-se-20.c b/gcc/testsuite/gcc.dg/ipa/ipa-se-20.c
new file mode 100644
index 0000000..9912a9a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-se-20.c
@@ -0,0 +1,28 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf"  } */
+
+#include <math.h>
+
+__attribute__ ((noinline))
+float foo()
+{
+  return sin(12.4f);
+}
+
+__attribute__ ((noinline))
+float bar()
+{
+  return sin(12.4f);
+}
+
+int main()
+{
+  foo();
+  bar();
+
+  return 0;
+}
+
+/* { dg-final { scan-ipa-dump "Semantic equality hit:bar->foo" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 1" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-se-21.c b/gcc/testsuite/gcc.dg/ipa/ipa-se-21.c
new file mode 100644
index 0000000..7358e43
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-se-21.c
@@ -0,0 +1,27 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf"  } */
+
+#include <xmmintrin.h>
+
+__attribute__ ((noinline))
+void foo()
+{
+  float x = 1.2345f;
+  __m128 v =_mm_load1_ps(&x);
+}
+
+__attribute__ ((noinline))
+void bar()
+{
+  float x = 1.2345f;
+  __m128 v =_mm_load1_ps(&x);
+}
+
+int main()
+{
+  return 2;
+}
+
+/* { dg-final { scan-ipa-dump "Semantic equality hit:bar->foo" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 1" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-se-22.c b/gcc/testsuite/gcc.dg/ipa/ipa-se-22.c
new file mode 100644
index 0000000..9561026
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-se-22.c
@@ -0,0 +1,37 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf"  } */
+
+struct A
+{
+  int a, b, c;
+};
+
+struct B
+{
+  int x, y;
+};
+
+__attribute__ ((noinline))
+int foo(struct A *a)
+{
+  a->c = 1;
+
+  return 123;
+}
+
+__attribute__ ((noinline))
+int bar(struct B *b)
+{
+  b->y = 1;
+
+  return 123;
+}
+
+int main()
+{
+  return foo(0) + bar(0);
+}
+
+/* { dg-final { scan-ipa-dump-not "Semantic equality hit:" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 0" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-se-23.c b/gcc/testsuite/gcc.dg/ipa/ipa-se-23.c
new file mode 100644
index 0000000..7e81ae2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-se-23.c
@@ -0,0 +1,29 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf"  } */
+
+struct A
+{
+  int a;
+  int b;
+};
+
+__attribute__ ((noinline))
+int foo(struct A *a)
+{
+  return 123;
+}
+
+__attribute__ ((noinline))
+int bar(struct A *b)
+{
+  return 123;
+}
+
+int main()
+{
+  return foo(0) + bar(0);
+}
+
+/* { dg-final { scan-ipa-dump "Semantic equality hit:bar->foo" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 1" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-se-24.c b/gcc/testsuite/gcc.dg/ipa/ipa-se-24.c
new file mode 100644
index 0000000..3cd476f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-se-24.c
@@ -0,0 +1,36 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf"  } */
+
+struct A
+{
+  int a, b, c, d;
+};
+
+struct B
+{
+  int x, y, z;
+};
+
+__attribute__ ((noinline))
+int foo(struct A *a)
+{
+  a->c = 1;
+
+  return 123;
+}
+
+__attribute__ ((noinline))
+int bar(struct B *b)
+{
+  b->z = 1;
+
+  return 123;
+}
+
+int main()
+{
+  return foo(0) + bar(0);
+}
+
+/* { dg-final { scan-ipa-dump "Equal symbols: 0" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-se-25.c b/gcc/testsuite/gcc.dg/ipa/ipa-se-25.c
new file mode 100644
index 0000000..e6cf8ac
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-se-25.c
@@ -0,0 +1,48 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf"  } */
+
+__attribute__ ((noinline))
+int foo()
+{
+  return zip();
+}
+
+__attribute__ ((noinline))
+int bar()
+{
+  return zap();
+}
+
+__attribute__ ((noinline))
+int baz()
+{
+  return two();
+}
+
+__attribute__ ((noinline))
+int zip()
+{
+  return 0;
+}
+
+__attribute__ ((noinline))
+int zap()
+{
+  return 0;
+}
+
+__attribute__ ((noinline))
+int two()
+{
+  return 2;
+}
+
+int main()
+{
+  return foo() + bar();
+}
+
+/* { dg-final { scan-ipa-dump "Semantic equality hit:bar->foo" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Semantic equality hit:zap->zip" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 2" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-se-26.c b/gcc/testsuite/gcc.dg/ipa/ipa-se-26.c
new file mode 100644
index 0000000..0c5a5a6
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-se-26.c
@@ -0,0 +1,44 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf"  } */
+
+void destroy (void)
+{
+}
+
+void remove (void)
+{
+}
+
+
+struct callbacks
+{
+  void (*success) (void);
+  void (*error) (void);
+};
+
+struct callbacks my_callbacks;
+
+__attribute__ ((noinline))
+void foo()
+{
+  my_callbacks.success = destroy;
+}
+
+__attribute__ ((noinline))
+void bar()
+{
+  my_callbacks.success = remove;
+}
+
+int main()
+{
+  foo();
+  bar();
+
+  return 0;
+}
+
+/* { dg-final { scan-ipa-dump "Semantic equality hit:bar->foo" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Semantic equality hit:remove->destroy" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 2" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-se-27.c b/gcc/testsuite/gcc.dg/ipa/ipa-se-27.c
new file mode 100644
index 0000000..fab2e41
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-se-27.c
@@ -0,0 +1,30 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf -fno-inline"  } */
+
+void destroy (void)
+{
+  __asm__ __volatile__ ("" : : : "memory");
+}
+
+void remove (void)
+{
+  __asm__ __volatile__ ("" : : : "memory");
+}
+
+void remove2 (void)
+{
+  __asm__ __volatile__ ("" : : : );
+}
+
+int main()
+{
+  destroy ();
+  remove ();
+  remove2 ();
+
+  return 0;
+}
+
+/* { dg-final { scan-ipa-dump "Semantic equality hit:remove->destroy" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 1" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-se-28.c b/gcc/testsuite/gcc.dg/ipa/ipa-se-28.c
new file mode 100644
index 0000000..538e0ab
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-se-28.c
@@ -0,0 +1,24 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf-details -fno-inline"  } */
+
+__attribute__ ((noinline, constructor(200)))
+int foo()
+{
+  return 123;
+}
+
+__attribute__ ((noinline, constructor(400)))
+int bar()
+{
+  return 123;
+}
+
+int main()
+{
+  foo() + bar();
+
+  return 0;
+}
+/* { dg-final { scan-ipa-dump "Equal symbols: 0" "icf"  } } */
+/* { dg-final { scan-ipa-dump "attribute values are different" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-se-3.c b/gcc/testsuite/gcc.dg/ipa/ipa-se-3.c
new file mode 100644
index 0000000..e4e2eb6
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-se-3.c
@@ -0,0 +1,36 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf"  } */
+
+typedef int v4si __attribute__ ((vector_size (16)));
+
+__attribute__ ((noinline))
+int foo(void)
+{
+  v4si a = {1,2,3,4};
+  v4si b = {3,2,1,4};
+  v4si c;
+
+  return 54;
+}
+
+__attribute__ ((noinline))
+int bar(void)
+{
+  v4si a = {1,2,3,4};
+  v4si b = {3,2,1,4};
+  v4si c;
+
+  return 54;
+}
+
+int main()
+{
+  foo();
+  bar();
+
+  return 0;
+}
+
+/* { dg-final { scan-ipa-dump "Semantic equality hit:bar->foo" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 1" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-se-4.c b/gcc/testsuite/gcc.dg/ipa/ipa-se-4.c
new file mode 100644
index 0000000..9434fb0
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-se-4.c
@@ -0,0 +1,32 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf"  } */
+
+__attribute__ ((noinline))
+int foo(int a)
+{
+  return a * a;
+}
+
+__attribute__ ((noinline))
+int bar(int b)
+{
+  return b;
+}
+
+__attribute__ ((noinline))
+void caller(int x)
+{
+  return;
+}
+
+int main(int argc, char **argv)
+{
+  caller(foo(argc));
+  caller(bar(argc));
+
+  return 123;
+}
+
+/* { dg-final { scan-ipa-dump-not "Semantic equality hit:" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 0" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-se-5.c b/gcc/testsuite/gcc.dg/ipa/ipa-se-5.c
new file mode 100644
index 0000000..45fddf5
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-se-5.c
@@ -0,0 +1,55 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf"  } */
+
+#include <complex.h>
+
+static double test;
+
+__attribute__ ((noinline))
+double f1(void)
+{
+  double complex z1 = 1.0 + 3.0 * I;
+  double complex z2 = 1.0 - 4.0 * I;
+
+  unsigned a = 123;
+  unsigned b = 321;
+
+  if (a & b)
+    return 1.2f;
+
+  if(cimag(z1) > 1)
+    return 1.0f;
+
+  test = cimag(z1) + 2;
+
+  return cimag(z1 + z2);
+}
+
+__attribute__ ((noinline))
+double f2(void)
+{
+  double complex z1 = 1.0 + 3.0 * I;
+  double complex z2 = 1.0 - 4.0 * I;
+
+  unsigned a = 123;
+  unsigned b = 321;
+
+  if (a & b)
+    return 1.2f;
+
+  if(cimag(z1) > 1)
+    return 1.0f;
+
+  test = cimag(z1) + 2;
+
+  return cimag(z1 + z2);
+}
+
+int main()
+{
+  return 1;
+}
+
+/* { dg-final { scan-ipa-dump "Semantic equality hit:f2->f1" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 1" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-se-6.c b/gcc/testsuite/gcc.dg/ipa/ipa-se-6.c
new file mode 100644
index 0000000..6e6758e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-se-6.c
@@ -0,0 +1,36 @@
+/* { dg-do compile } */
+/* { dg-options "-O0 -fipa-icf -fdump-ipa-icf"  } */
+
+typedef int v4si __attribute__ ((vector_size (16)));
+
+__attribute__ ((noinline))
+int foo(void)
+{
+  v4si a = {1,2,3,4};
+  v4si b = {3,2,1,4};
+  v4si c;
+
+  return 54;
+}
+
+__attribute__ ((noinline))
+int bar(void)
+{
+  v4si a = {1,2,3,4};
+  v4si b = {3,2,5,4};
+  v4si c;
+
+  return 54;
+}
+
+int main()
+{
+  foo();
+  bar();
+
+  return 0;
+}
+
+/* { dg-final { scan-ipa-dump-not "Semantic equality hit:" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 0" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-se-7.c b/gcc/testsuite/gcc.dg/ipa/ipa-se-7.c
new file mode 100644
index 0000000..ec7961d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-se-7.c
@@ -0,0 +1,72 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf"  } */
+
+#include <complex.h>
+
+#if (__SIZEOF_INT__ == __SIZEOF_FLOAT__)
+typedef int intflt;
+#elif (__SIZEOF_LONG__ == __SIZEOF_FLOAT__)
+typedef long intflt;
+#else
+#error Add target support here for type that will union float size
+#endif
+
+
+static double test;
+
+struct struktura
+{
+  union
+  {
+    long i;
+    float f;
+  } u;
+};
+
+struct struktura sss;
+
+struct X
+{
+  int i;
+  union
+  {
+    intflt j;
+    intflt k;
+    float f;
+  } u;
+};
+
+__attribute__ ((noinline))
+intflt foo(intflt j)
+{
+  struct X a;
+
+  a.u.j = j;
+  a.u.f = a.u.f;
+  a.u.f = a.u.f;
+  a.u.j = a.u.j;
+  a.u.f = a.u.f;
+  return a.u.k;
+}
+
+__attribute__ ((noinline))
+intflt foo2(intflt j)
+{
+  struct X a;
+
+  a.u.j = j;
+  a.u.f = a.u.f;
+  a.u.f = a.u.f;
+  a.u.j = a.u.j;
+  a.u.f = a.u.f;
+  return a.u.k;
+}
+
+int main()
+{
+  return 1;
+}
+
+/* { dg-final { scan-ipa-dump "Semantic equality hit:foo2->foo" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 1" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-se-8.c b/gcc/testsuite/gcc.dg/ipa/ipa-se-8.c
new file mode 100644
index 0000000..d35df90
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-se-8.c
@@ -0,0 +1,45 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf"  } */
+
+#include <stdio.h>
+
+__attribute__ ((noinline))
+int fce1(int a, int b)
+{
+  int swap;
+
+  if(a < b)
+    {
+      swap = a;
+      a = b;
+      b = swap;
+    }
+
+  return a / b;
+}
+
+__attribute__ ((noinline))
+int fce2(int x, int y)
+{
+  int tmp;
+
+  if(x < y)
+    {
+      tmp = x;
+      x = y;
+      y = tmp;
+    }
+
+  return x / y;
+}
+
+
+int main(int argc, char **argv)
+{
+  printf("fce1: %d\n", fce1(argc, argc + 2));
+  printf("fce2: %d\n", fce2(argc, 2 * argc));
+}
+
+/* { dg-final { scan-ipa-dump "Semantic equality hit:fce2->fce1" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 1" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-se-9.c b/gcc/testsuite/gcc.dg/ipa/ipa-se-9.c
new file mode 100644
index 0000000..9d04dd1
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-se-9.c
@@ -0,0 +1,33 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf"  } */
+
+int funkce(int a, int b) __attribute__ ((pure));
+
+__attribute__ ((noinline))
+int ferda(int x, int y)
+{
+  if (x < y)
+    {
+      return x;
+    }
+  else
+    return y;
+}
+
+__attribute__ ((noinline))
+int funkce(int a, int b)
+{
+  if(a < b)
+    return a;
+  else
+    return b;
+}
+
+int main(int argc, char **argv)
+{
+  return 0;
+}
+
+/* { dg-final { scan-ipa-dump-not "Semantic equality hit:" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 0" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
-- 
1.8.4.5

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 1/5] New Identical Code Folding IPA pass
  2014-06-16 10:07 [PATCH 1/5] New Identical Code Folding IPA pass mliska
                   ` (3 preceding siblings ...)
  2014-06-16 10:07 ` [PATCH 3/5] IPA ICF pass mliska
@ 2014-06-17 19:49 ` Jeff Law
  2014-06-18 19:05   ` Jan Hubicka
  2014-06-17 20:09 ` Paolo Carlini
  2014-06-17 20:17 ` David Malcolm
  6 siblings, 1 reply; 70+ messages in thread
From: Jeff Law @ 2014-06-17 19:49 UTC (permalink / raw)
  To: mliska, gcc-patches; +Cc: hubicka

On 06/13/14 04:24, mliska wrote:
>
> You may ask, why the GNU GCC does need such a new optimization. The
> compiler, having simply better knowledge of a compiled source file,
> is capable of reaching better results, especially if Link-Time
> optimization is enabled. Apart from that, GCC implementation adds
> support for read-only variables like construction vtables (mentioned
> in:
> http://hubicka.blogspot.cz/2014/02/devirtualization-in-c-part-3-building.html).
Can you outline at a high level cases where GCC's knowledge allows it to 
reach a better result?  Is it because you're not requiring bit for bit 
identical code, but that the code merely be semantically equivalent?

The GCC driven ICF seems to pick up 2X more opportunities than the gold 
driven ICF.  But if I'm reading everything correctly, that includes ICF 
of both functions and variables.

Do you have any sense of how those improvements break down?  ie, is it 
mostly more function's you're finding as identical, and if so what is it 
about the GCC implementation that allows us to find more ICF 
opportunities.  If it's mostly variables, that's fine too.  I'm just 
trying to understand where the improvements are coming from.

Jeff

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 4/5] Existing tests fix
  2014-06-16 10:07 ` [PATCH 4/5] Existing tests fix mliska
@ 2014-06-17 19:52   ` Jeff Law
  2014-06-17 20:50     ` Rainer Orth
  2014-06-30 12:12     ` Martin Liška
  0 siblings, 2 replies; 70+ messages in thread
From: Jeff Law @ 2014-06-17 19:52 UTC (permalink / raw)
  To: mliska, gcc-patches; +Cc: hubicka

On 06/13/14 04:48, mliska wrote:
> Hi,
>    many tests rely on a precise number of scanned functions in a dump file. If IPA ICF decides to merge some function and(or) read-only variables, counts do not match.
>
> Martin
>
> Changelog:
>
> 2014-06-13  Martin Liska  <mliska@suse.cz>
> 	    Honza Hubicka  <hubicka@ucw.cz>
>
> 	* c-c++-common/rotate-1.c: Text
> 	* c-c++-common/rotate-2.c: New test.
> 	* c-c++-common/rotate-3.c: Likewise.
> 	* c-c++-common/rotate-4.c: Likewise.
> 	* g++.dg/cpp0x/rv-return.C: Likewise.
> 	* g++.dg/cpp0x/rv1n.C: Likewise.
> 	* g++.dg/cpp0x/rv1p.C: Likewise.
> 	* g++.dg/cpp0x/rv2n.C: Likewise.
> 	* g++.dg/cpp0x/rv3n.C: Likewise.
> 	* g++.dg/cpp0x/rv4n.C: Likewise.
> 	* g++.dg/cpp0x/rv5n.C: Likewise.
> 	* g++.dg/cpp0x/rv6n.C: Likewise.
> 	* g++.dg/cpp0x/rv7n.C: Likewise.
> 	* gcc.dg/ipa/ipacost-1.c: Likewise.
> 	* gcc.dg/ipa/ipacost-2.c: Likewise.
> 	* gcc.dg/ipa/ipcp-agg-6.c: Likewise.
> 	* gcc.dg/ipa/remref-2a.c: Likewise.
> 	* gcc.dg/ipa/remref-2b.c: Likewise.
> 	* gcc.dg/pr46309-2.c: Likewise.
> 	* gcc.dg/torture/ipa-pta-1.c: Likewise.
> 	* gcc.dg/tree-ssa/andor-3.c: Likewise.
> 	* gcc.dg/tree-ssa/andor-4.c: Likewise.
> 	* gcc.dg/tree-ssa/andor-5.c: Likewise.
> 	* gcc.dg/vect/no-vfa-pr29145.c: Likewise.
> 	* gcc.dg/vect/vect-cond-10.c: Likewise.
> 	* gcc.dg/vect/vect-cond-9.c: Likewise.
> 	* gcc.dg/vect/vect-widen-mult-const-s16.c: Likewise.
> 	* gcc.dg/vect/vect-widen-mult-const-u16.c: Likewise.
> 	* gcc.dg/vect/vect-widen-mult-half-u8.c: Likewise.
> 	* gcc.target/i386/bmi-1.c: Likewise.
> 	* gcc.target/i386/bmi-2.c: Likewise.
> 	* gcc.target/i386/pr56564-2.c: Likewise.
> 	* g++.dg/opt/pr30965.C: Likewise.
> 	* g++.dg/tree-ssa/pr19637.C: Likewise.
> 	* gcc.dg/guality/csttest.c: Likewise.
> 	* gcc.dg/ipa/iinline-4.c: Likewise.
> 	* gcc.dg/ipa/iinline-7.c: Likewise.
> 	* gcc.dg/ipa/ipa-pta-13.c: Likewise.
I know this is the least interesting part of your changes, but it's also 
simple and mechanical and thus trivial to review.  Approved, but 
obviously don't install until the rest of your patch has been approved.

Similar changes for recently added tests or cases where you might 
improve ICF requiring similar tweaks to existing tests are pre-approved 
as well.

jeff

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 5/5] New tests introduction
  2014-06-16 10:07 ` [PATCH 5/5] New tests introduction mliska
@ 2014-06-17 19:53   ` Jeff Law
  2014-06-30 12:14     ` Martin Liška
  0 siblings, 1 reply; 70+ messages in thread
From: Jeff Law @ 2014-06-17 19:53 UTC (permalink / raw)
  To: mliska, gcc-patches; +Cc: hubicka

On 06/13/14 05:16, mliska wrote:
> Hi,
>     this is a new collection of tests for IPA ICF pass.
>
> Martin
>
> Changelog:
>
> 2014-06-13  Martin Liska  <mliska@suse.cz>
> 	    Honza Hubicka  <hubicka@ucw.cz>
>
> 	* gcc/testsuite/g++.dg/ipa/ipa-se-1.C: New test.
> 	* gcc/testsuite/g++.dg/ipa/ipa-se-2.C: Likewise.
> 	* gcc/testsuite/g++.dg/ipa/ipa-se-3.C: Likewise.
> 	* gcc/testsuite/g++.dg/ipa/ipa-se-4.C: Likewise.
> 	* gcc/testsuite/g++.dg/ipa/ipa-se-5.C: Likewise.
> 	* gcc/testsuite/gcc.dg/ipa/ipa-se-1.c: Likewise.
> 	* gcc/testsuite/gcc.dg/ipa/ipa-se-10.c: Likewise.
> 	* gcc/testsuite/gcc.dg/ipa/ipa-se-11.c: Likewise.
> 	* gcc/testsuite/gcc.dg/ipa/ipa-se-12.c: Likewise.
> 	* gcc/testsuite/gcc.dg/ipa/ipa-se-13.c: Likewise.
> 	* gcc/testsuite/gcc.dg/ipa/ipa-se-14.c: Likewise.
> 	* gcc/testsuite/gcc.dg/ipa/ipa-se-15.c: Likewise.
> 	* gcc/testsuite/gcc.dg/ipa/ipa-se-16.c: Likewise.
> 	* gcc/testsuite/gcc.dg/ipa/ipa-se-17.c: Likewise.
> 	* gcc/testsuite/gcc.dg/ipa/ipa-se-18.c: Likewise.
> 	* gcc/testsuite/gcc.dg/ipa/ipa-se-19.c: Likewise.
> 	* gcc/testsuite/gcc.dg/ipa/ipa-se-2.c: Likewise.
> 	* gcc/testsuite/gcc.dg/ipa/ipa-se-20.c: Likewise.
> 	* gcc/testsuite/gcc.dg/ipa/ipa-se-21.c: Likewise.
> 	* gcc/testsuite/gcc.dg/ipa/ipa-se-22.c: Likewise.
> 	* gcc/testsuite/gcc.dg/ipa/ipa-se-23.c: Likewise.
> 	* gcc/testsuite/gcc.dg/ipa/ipa-se-24.c: Likewise.
> 	* gcc/testsuite/gcc.dg/ipa/ipa-se-25.c: Likewise.
> 	* gcc/testsuite/gcc.dg/ipa/ipa-se-26.c: Likewise.
> 	* gcc/testsuite/gcc.dg/ipa/ipa-se-27.c: Likewise.
> 	* gcc/testsuite/gcc.dg/ipa/ipa-se-28.c: Likewise.
> 	* gcc/testsuite/gcc.dg/ipa/ipa-se-3.c: Likewise.
> 	* gcc/testsuite/gcc.dg/ipa/ipa-se-4.c: Likewise.
> 	* gcc/testsuite/gcc.dg/ipa/ipa-se-5.c: Likewise.
> 	* gcc/testsuite/gcc.dg/ipa/ipa-se-6.c: Likewise.
> 	* gcc/testsuite/gcc.dg/ipa/ipa-se-7.c: Likewise.
> 	* gcc/testsuite/gcc.dg/ipa/ipa-se-8.c: Likewise.
> 	* gcc/testsuite/gcc.dg/ipa/ipa-se-9.c: Likewise.
Also approved, but please don't install entire the entire kit is approved.

I'd like to applaud you and Jan for including a nice baseline of tests.

jeff

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 2/5] Existing call graph infrastructure enhancement
  2014-06-16 10:07 ` [PATCH 2/5] Existing call graph infrastructure enhancement mliska
@ 2014-06-17 20:00   ` Jeff Law
  2014-06-30 11:49     ` Martin Liška
  2014-09-24 14:23   ` Martin Liška
  1 sibling, 1 reply; 70+ messages in thread
From: Jeff Law @ 2014-06-17 20:00 UTC (permalink / raw)
  To: mliska, gcc-patches; +Cc: hubicka

On 06/13/14 04:26, mliska wrote:
> Hi,
>      this small patch prepares remaining needed infrastructure for the new pass.
>
> Changelog:
>
> 2014-06-13  Martin Liska  <mliska@suse.cz>
> 	    Honza Hubicka  <hubicka@ucw.cz>
>
> 	* ipa-utils.h (polymorphic_type_binfo_p): Function marked external
> 	instead of static.
> 	* ipa-devirt.c (polymorphic_type_binfo_p): Likewise.
> 	* ipa-prop.h (count_formal_params): Likewise.
> 	* ipa-prop.c (count_formal_params): Likewise.
> 	* ipa-utils.c (ipa_merge_profiles): Be more tolerant if we merge
> 	profiles for semantically equivalent functions.
> 	* passes.c (do_per_function): If we load body of a function during WPA,
> 	this condition should behave same.
> 	* varpool.c (ctor_for_folding): More tolerant assert for variable
> 	aliases created during WPA.
Presumably we don't have any useful way to merge the cases where we have 
provides for SRC & DST in ipa_merge_profiles or even to guess which is 
more useful when presented with both?  Does it make sense to log this 
into a debugging file when we drop one?

I think this patch is fine.  If adding logging makes sense, then feel 
free to do so and consider that trivial change pre-approved.

Jeff

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 1/5] New Identical Code Folding IPA pass
  2014-06-16 10:07 [PATCH 1/5] New Identical Code Folding IPA pass mliska
                   ` (4 preceding siblings ...)
  2014-06-17 19:49 ` [PATCH 1/5] New Identical Code Folding IPA pass Jeff Law
@ 2014-06-17 20:09 ` Paolo Carlini
  2014-06-18  8:46   ` Martin Liška
  2014-06-17 20:17 ` David Malcolm
  6 siblings, 1 reply; 70+ messages in thread
From: Paolo Carlini @ 2014-06-17 20:09 UTC (permalink / raw)
  To: mliska; +Cc: gcc-patches, hubicka

Hi,

On 13/06/14 12:24, mliska wrote:
>    The optimization is inspired by Microsoft /OPT:ICF optimization (http://msdn.microsoft.com/en-us/library/bxwfs976.aspx) that merges COMDAT sections with each function reside in a separate section.
In terms of C++ testcases, I'm wondering if you already double checked 
that the new pass already does well on the typical examples on which, I 
was told, the Microsoft optimization is known to do well, eg, code 
instantiating std::vector for different pointer types, or even long and 
long long on x86_64-linux, things like that.

Thanks,
Paolo.

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 1/5] New Identical Code Folding IPA pass
  2014-06-16 10:07 [PATCH 1/5] New Identical Code Folding IPA pass mliska
                   ` (5 preceding siblings ...)
  2014-06-17 20:09 ` Paolo Carlini
@ 2014-06-17 20:17 ` David Malcolm
  2014-06-18  8:10   ` Martin Liška
  6 siblings, 1 reply; 70+ messages in thread
From: David Malcolm @ 2014-06-17 20:17 UTC (permalink / raw)
  To: mliska; +Cc: gcc-patches, hubicka

On Fri, 2014-06-13 at 12:24 +0200, mliska wrote:
[...snip...]
>   Statistics about the pass:
>   Inkscape: 11.95 MB -> 11.44 MB (-4.27%)
>   Firefox: 70.12 MB -> 70.12 MB (-3.07%)

FWIW, you wrote 70.12 MB here for both before and after for Firefox, but
give a -3.07% change, which seems like a typo.

A 3.07% reduction from 70.12 MB would be 67.97 MB; was this what the
pass achieved?

[...snip...]

Thanks (nice patch, btw)
Dave

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 4/5] Existing tests fix
  2014-06-17 19:52   ` Jeff Law
@ 2014-06-17 20:50     ` Rainer Orth
  2014-06-18  9:02       ` Martin Liška
  2014-06-30 12:12     ` Martin Liška
  1 sibling, 1 reply; 70+ messages in thread
From: Rainer Orth @ 2014-06-17 20:50 UTC (permalink / raw)
  To: Jeff Law; +Cc: mliska, gcc-patches, hubicka

Jeff Law <law@redhat.com> writes:

> On 06/13/14 04:48, mliska wrote:
>> Hi,
>>    many tests rely on a precise number of scanned functions in a dump file. If IPA ICF decides to merge some function and(or) read-only variables, counts do not match.
>>
>> Martin
>>
>> Changelog:
>>
>> 2014-06-13  Martin Liska  <mliska@suse.cz>
>> 	    Honza Hubicka  <hubicka@ucw.cz>
>>
>> 	* c-c++-common/rotate-1.c: Text

                                   ^ Huh?

>> 	* c-c++-common/rotate-2.c: New test.
>> 	* c-c++-common/rotate-3.c: Likewise.

	Rainer

-- 
-----------------------------------------------------------------------------
Rainer Orth, Center for Biotechnology, Bielefeld University

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 1/5] New Identical Code Folding IPA pass
  2014-06-17 20:17 ` David Malcolm
@ 2014-06-18  8:10   ` Martin Liška
  0 siblings, 0 replies; 70+ messages in thread
From: Martin Liška @ 2014-06-18  8:10 UTC (permalink / raw)
  To: David Malcolm; +Cc: gcc-patches, hubicka


On 06/17/2014 10:14 PM, David Malcolm wrote:
> On Fri, 2014-06-13 at 12:24 +0200, mliska wrote:
> [...snip...]
>>    Statistics about the pass:
>>    Inkscape: 11.95 MB -> 11.44 MB (-4.27%)
>>    Firefox: 70.12 MB -> 70.12 MB (-3.07%)
> FWIW, you wrote 70.12 MB here for both before and after for Firefox, but
> give a -3.07% change, which seems like a typo.
>
> A 3.07% reduction from 70.12 MB would be 67.97 MB; was this what the
> pass achieved?

Hi,
    it's typo, original size of FF is 72.34 MB. I hope -3.07% is the correctly evaluated achievement.

Thanks,
Martin

>
> [...snip...]
>
> Thanks (nice patch, btw)
> Dave
>

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 1/5] New Identical Code Folding IPA pass
  2014-06-17 20:09 ` Paolo Carlini
@ 2014-06-18  8:46   ` Martin Liška
  2014-06-18  8:49     ` Paolo Carlini
  0 siblings, 1 reply; 70+ messages in thread
From: Martin Liška @ 2014-06-18  8:46 UTC (permalink / raw)
  To: Paolo Carlini; +Cc: gcc-patches, hubicka


On 06/17/2014 10:09 PM, Paolo Carlini wrote:
> Hi,
>
> On 13/06/14 12:24, mliska wrote:
>>    The optimization is inspired by Microsoft /OPT:ICF optimization (http://msdn.microsoft.com/en-us/library/bxwfs976.aspx) that merges COMDAT sections with each function reside in a separate section.
> In terms of C++ testcases, I'm wondering if you already double checked that the new pass already does well on the typical examples on which, I was told, the Microsoft optimization is known to do well, eg, code instantiating std::vector for different pointer types, or even long and long long on x86_64-linux, things like that.

I've just added another C++ test case:

#include <vector>

using namespace std;

static vector<vector<int> *> a;
static vector<void *> b;

int main()
{
   return b.size() + a.size ();
}

where the pass identifies following equality:

Semantic equality hit:std::vector<_Tp, _Alloc>::size_type std::vector<_Tp, _Alloc>::size() const [with _Tp = std::vector<int>*; _Alloc = std::allocator<std::vector<int>*>; std::vector<_Tp, _Alloc>::size_type = long unsigned int]->std::vector<_Tp, _Alloc>::size_type std::vector<_Tp, _Alloc>::size() const [with _Tp = void*; _Alloc = std::allocator<void*>; std::vector<_Tp, _Alloc>::size_type = long unsigned int]
Semantic equality hit:static void std::_Destroy_aux<true>::__destroy(_ForwardIterator, _ForwardIterator) [with _ForwardIterator = void**]->static void std::_Destroy_aux<true>::__destroy(_ForwardIterator, _ForwardIterator) [with _ForwardIterator = std::vector<int>**]
Semantic equality hit:void std::_Destroy(_ForwardIterator, _ForwardIterator) [with _ForwardIterator = void**]->void std::_Destroy(_ForwardIterator, _ForwardIterator) [with _ForwardIterator = std::vector<int>**]
Semantic equality hit:void std::_Destroy(_ForwardIterator, _ForwardIterator, std::allocator<_T2>&) [with _ForwardIterator = void**; _Tp = void*]->void std::_Destroy(_ForwardIterator, _ForwardIterator, std::allocator<_T2>&) [with _ForwardIterator = std::vector<int>**; _Tp = std::vector<int>*]
Semantic equality hit:void __gnu_cxx::new_allocator<_Tp>::deallocate(__gnu_cxx::new_allocator<_Tp>::pointer, __gnu_cxx::new_allocator<_Tp>::size_type) [with _Tp = void*; __gnu_cxx::new_allocator<_Tp>::pointer = void**; __gnu_cxx::new_allocator<_Tp>::size_type = long unsigned int]->void __gnu_cxx::new_allocator<_Tp>::deallocate(__gnu_cxx::new_allocator<_Tp>::pointer, __gnu_cxx::new_allocator<_Tp>::size_type) [with _Tp = std::vector<int>*; __gnu_cxx::new_allocator<_Tp>::pointer = std::vector<int>**; __gnu_cxx::new_allocator<_Tp>::size_type = long unsigned int]
Semantic equality hit:static void __gnu_cxx::__alloc_traits<_Alloc>::deallocate(_Alloc&, __gnu_cxx::__alloc_traits<_Alloc>::pointer, __gnu_cxx::__alloc_traits<_Alloc>::size_type) [with _Alloc = std::allocator<void*>; __gnu_cxx::__alloc_traits<_Alloc>::pointer = void**; __gnu_cxx::__alloc_traits<_Alloc>::size_type = long unsigned int]->static void __gnu_cxx::__alloc_traits<_Alloc>::deallocate(_Alloc&, __gnu_cxx::__alloc_traits<_Alloc>::pointer, __gnu_cxx::__alloc_traits<_Alloc>::size_type) [with _Alloc = std::allocator<std::vector<int>*>; __gnu_cxx::__alloc_traits<_Alloc>::pointer = std::vector<int>**; __gnu_cxx::__alloc_traits<_Alloc>::size_type = long unsigned int]

As one would expect, there is a function 'size'.

Martin

>
> Thanks,
> Paolo.

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 1/5] New Identical Code Folding IPA pass
  2014-06-18  8:46   ` Martin Liška
@ 2014-06-18  8:49     ` Paolo Carlini
  0 siblings, 0 replies; 70+ messages in thread
From: Paolo Carlini @ 2014-06-18  8:49 UTC (permalink / raw)
  To: Martin Liška; +Cc: gcc-patches, hubicka

Hi,

On 18/06/14 10:46, Martin Liška wrote:
> As one would expect, there is a function 'size'.
Cool, thanks!

Paolo.

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 4/5] Existing tests fix
  2014-06-17 20:50     ` Rainer Orth
@ 2014-06-18  9:02       ` Martin Liška
  0 siblings, 0 replies; 70+ messages in thread
From: Martin Liška @ 2014-06-18  9:02 UTC (permalink / raw)
  To: gcc-patches


On 06/17/2014 10:50 PM, Rainer Orth wrote:
> Jeff Law <law@redhat.com> writes:
>
>> On 06/13/14 04:48, mliska wrote:
>>> Hi,
>>>     many tests rely on a precise number of scanned functions in a dump file. If IPA ICF decides to merge some function and(or) read-only variables, counts do not match.
>>>
>>> Martin
>>>
>>> Changelog:
>>>
>>> 2014-06-13  Martin Liska  <mliska@suse.cz>
>>> 	    Honza Hubicka  <hubicka@ucw.cz>
>>>
>>> 	* c-c++-common/rotate-1.c: Text
>                                     ^ Huh?

You are right, batch replacement mistake. There should be:

* c-c++-common/rotate-1.c: Update dg-options.
* c-c++-common/rotate-2.c: Likewise.
...


Martin

>
>>> 	* c-c++-common/rotate-2.c: New test.
>>> 	* c-c++-common/rotate-3.c: Likewise.
> 	Rainer
>

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 1/5] New Identical Code Folding IPA pass
  2014-06-17 19:49 ` [PATCH 1/5] New Identical Code Folding IPA pass Jeff Law
@ 2014-06-18 19:05   ` Jan Hubicka
  0 siblings, 0 replies; 70+ messages in thread
From: Jan Hubicka @ 2014-06-18 19:05 UTC (permalink / raw)
  To: Jeff Law; +Cc: mliska, gcc-patches, hubicka

> On 06/13/14 04:24, mliska wrote:
> >
> >You may ask, why the GNU GCC does need such a new optimization. The
> >compiler, having simply better knowledge of a compiled source file,
> >is capable of reaching better results, especially if Link-Time
> >optimization is enabled. Apart from that, GCC implementation adds
> >support for read-only variables like construction vtables (mentioned
> >in:
> >http://hubicka.blogspot.cz/2014/02/devirtualization-in-c-part-3-building.html).
> Can you outline at a high level cases where GCC's knowledge allows
> it to reach a better result?  Is it because you're not requiring bit
> for bit identical code, but that the code merely be semantically
> equivalent?
> 
> The GCC driven ICF seems to pick up 2X more opportunities than the
> gold driven ICF.  But if I'm reading everything correctly, that
> includes ICF of both functions and variables.

There are important differences between in-GCC ICF and gold's ICF. Basically

 - GCC ICF runs before most of context sensitive optimizations, so it does
   see code that is identical to start with, but would become different
   during optimization.

   For example if you have function a1...a1000 calling function b1....b1000
   where all bX are same, but all aX differs, then before inlining one
   can easily unify b and let inliner's heuristic decide whether it is good
   idea to duplicate body of b, while after inlining this is no longer
   possible.

   We don't do much in this respect, but we should try to unify accidental
   code duplication early in early passes to not let duplicates bubble
   until late optimizations where they may or may not be caught by
   i.e. tail merging

   This however also means that at least in current implementation it will
   result in somewhat more corruption of debug info (by replacing inline
   functions by different inline function with same body).

 - GCC ICF (doesn't in the current implementation) can do value numbering
   matching and match identical semantic with different implementation.
   It is the plan to get smarter here, I just wanted to have something working
   first and then play with more advanced tricks.

 - GCC ICF sees some things as different while they are not in final assembly.
   Types, alias classes and other details that are important for GCC but lost
   in codegen.  So here gold can do better work.

 - Theoretically, if tuned well, GCC ICF could improve compilation speed
   by avoiding need to optimize duplicates.

 - Gold's ICF depends on functions sections that are not free.

 - GCC ICF can be smarter about objects with address taken: we need analysis
   deciding when the address can be compared with a different address.
   This would be useful on other places, too.

Honza
> 
> Do you have any sense of how those improvements break down?  ie, is
> it mostly more function's you're finding as identical, and if so
> what is it about the GCC implementation that allows us to find more
> ICF opportunities.  If it's mostly variables, that's fine too.  I'm
> just trying to understand where the improvements are coming from.
> 
> Jeff

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 3/5] IPA ICF pass
  2014-06-16 10:07 ` [PATCH 3/5] IPA ICF pass mliska
@ 2014-06-20  7:32   ` Trevor Saunders
  2014-06-26 11:18     ` Martin Liška
  2014-07-05 21:44     ` Gerald Pfeifer
  2014-06-24 20:31   ` Jeff Law
  1 sibling, 2 replies; 70+ messages in thread
From: Trevor Saunders @ 2014-06-20  7:32 UTC (permalink / raw)
  To: mliska; +Cc: gcc-patches, hubicka

[-- Attachment #1: Type: text/plain, Size: 87007 bytes --]

On Fri, Jun 13, 2014 at 12:44:22PM +0200, mliska wrote:
> Hello,
>    this is core of IPA ICF patchset. It adds new pass and registers all needed stuff related to a newly introduced interprocedural optimization.
> 
> Algorithm description:
>   In LGEN, we visit all read-only variables and functions. For each symbol, a hash value based on e.g. number of arguments,
>   number of BB, GIMPLE CODES is computed (similar hash is computed for read-only variables). This kind of information is streamed
>   for LTO.
> 
>   In WPA, we build congruence classes for all symbols having a same hash value. For functions, these classes are subdivided in WPA by argument type comparison. Each reference (a call or a variable reference) to another semantic item candidate is marked and stored for further congruence class reduction (similar algorithm as Value Numbering:  www.cs.ucr.edu/~gupta/teaching/553-07/Papers/value.pdf).
> 
>   For every congruence class of functions with more than one semantic function, we load function body. Having this information, we can
>   process complete semantic function equality and subdivide such congruence class. Read-only variable class members are also deeply compared.
> 
>   After that, we process Value numbering algorithm to do a final subdivision. Finally, all items belonging to a congruence class with more than one
>   item are merged.
> 
> Martin
> 
> Changelog:
> 
> 2014-06-13  Martin Liska  <mliska@suse.cz>
> 	    Jan Hubicka  <hubicka@ucw.cz>
> 
> 	* Makefile.in: New pass object file added.
> 	* common.opt: New -fipa-icf flag introduced.
> 	* doc/invoke.texi: Documentation enhanced for the pass.
> 	* lto-section-in.c: New LTO section for a summary created by IPA-ICF.
> 	* lto-streamer.h: New section name introduced.
> 	* opts.c: Optimization is added to -O2.
> 	* passes.def: New pass added.
> 	* timevar.def: New time var for IPA-ICF.
> 	* tree-pass.h: Pass construction function.
> 	* ipa-icf.h: New pass header file added.
> 	* ipa-icf.c: New pass source file added.
> 
> diff --git a/gcc/Makefile.in b/gcc/Makefile.in
> index 5587b75..4b59418 100644
> --- a/gcc/Makefile.in
> +++ b/gcc/Makefile.in
> @@ -1276,6 +1276,7 @@ OBJS = \
>  	ipa-profile.o \
>  	ipa-prop.o \
>  	ipa-pure-const.o \
> +	ipa-icf.o \
>  	ipa-reference.o \
>  	ipa-ref.o \
>  	ipa-utils.o \
> diff --git a/gcc/common.opt b/gcc/common.opt
> index 7f05092..3661dcc 100644
> --- a/gcc/common.opt
> +++ b/gcc/common.opt
> @@ -1409,6 +1409,10 @@ fipa-pure-const
>  Common Report Var(flag_ipa_pure_const) Init(0) Optimization
>  Discover pure and const functions
>  
> +fipa-icf
> +Common Report Var(flag_ipa_icf) Optimization
> +Perform Identical Code Folding for functions and read-only variables
> +
>  fipa-reference
>  Common Report Var(flag_ipa_reference) Init(0) Optimization
>  Discover readonly and non addressable static variables
> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> index 3c02341..b2bbe69 100644
> --- a/gcc/doc/invoke.texi
> +++ b/gcc/doc/invoke.texi
> @@ -377,7 +377,7 @@ Objective-C and Objective-C++ Dialects}.
>  -fif-conversion2 -findirect-inlining @gol
>  -finline-functions -finline-functions-called-once -finline-limit=@var{n} @gol
>  -finline-small-functions -fipa-cp -fipa-cp-clone @gol
> --fipa-pta -fipa-profile -fipa-pure-const -fipa-reference @gol
> +-fipa-pta -fipa-profile -fipa-pure-const -fipa-reference -fipa-icf @gol
>  -fira-algorithm=@var{algorithm} @gol
>  -fira-region=@var{region} -fira-hoist-pressure @gol
>  -fira-loop-pressure -fno-ira-share-save-slots @gol
> @@ -6947,6 +6947,7 @@ also turns on the following optimization flags:
>  -finline-small-functions @gol
>  -findirect-inlining @gol
>  -fipa-sra @gol
> +-fipa-icf @gol
>  -fisolate-erroneous-paths-dereference @gol
>  -foptimize-sibling-calls @gol
>  -fpartial-inlining @gol
> @@ -7862,6 +7863,14 @@ it may significantly increase code size
>  (see @option{--param ipcp-unit-growth=@var{value}}).
>  This flag is enabled by default at @option{-O3}.
>  
> +@item -fipa-icf
> +@opindex fipa-icf
> +Perform Identical Code Folding for functions and read-only variables.
> +Behavior is similar to Gold Linker ICF optimization. Symbols proved
> +as semantically equivalent are redirected to corresponding symbol. The pass
> +sensitively decides for usage of alias, thunk or local redirection.
> +This flag is enabled by default at @option{-O2}.
> +
>  @item -fisolate-erroneous-paths-dereference
>  Detect paths which trigger erroneous or undefined behaviour due to
>  dereferencing a NULL pointer.  Isolate those paths from the main control
> diff --git a/gcc/ipa-icf.c b/gcc/ipa-icf.c
> new file mode 100644
> index 0000000..628d257
> --- /dev/null
> +++ b/gcc/ipa-icf.c
> @@ -0,0 +1,3247 @@
> +/* Interprocedural Identical Code Folding pass
> +   Copyright (C) 2014 Free Software Foundation, Inc.
> +
> +   Contributed by Jan Hubicka <hubicka@ucw.cz> and Martin Liska <mliska@suse.cz>
> +
> +This file is part of GCC.
> +
> +GCC 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, or (at your option) any later
> +version.
> +
> +GCC 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 GCC; see the file COPYING3.  If not see
> +<http://www.gnu.org/licenses/>.  */
> +
> +/* Interprocedural Identical Code Folding for functions and
> +   read-only variables.
> +
> +   The goal of this transformation is to discover functions and read-only
> +   variables which do have exactly the same semantics.
> +
> +   In case of functions,
> +   we could either create a virtual clone or do a simple function wrapper
> +   that will call equivalent function. If the function is just locally visible,
> +   all function calls can be redirected. For read-only variables, we create
> +   aliases if possible.

In theory you don't actually need the alias, you could change all the
references to point at the merged data right?  Can that automatically
happen for hidden visibility alias?

> +
> +   Optimization pass arranges as follows:
> +   1) All functions and read-only variables are visited and internal
> +      data structure, either sem_function or sem_variables is created.
> +   2) For every symbol from the previoues step, VAR_DECL and FUNCTION_DECL are

previous

> +      saved and matched to corresponding sem_items.
> +   3) These declaration are ignored for equality check and are solved
> +      by Value Numbering algorithm published by Alpert, Zadeck in 1992.
> +   4) We compute hash value for each symbol.
> +   5) Congruence classes are created based on hash value. If hash value are
> +      equal, equals function is called and symbols are deeply compared.
> +      We must prove that all SSA names, declarations and other items
> +      correspond.
> +   6) Value Numbering is executed for these classes. At the end of the process
> +      all symbol members in remaining classes can be mrged.
merged

> +   7) Merge operation creates alias in case of read-only variables. For
> +      callgraph node, we must decide if we can redirect local calls,
> +      create an alias or a thunk.
> +
> +*/
> +
> +#include "config.h"
> +#include "system.h"
> +#include "coretypes.h"
> +#include "tree.h"
> +#include "basic-block.h"
> +#include "tree-ssa-alias.h"
> +#include "internal-fn.h"
> +#include "gimple-expr.h"
> +#include "is-a.h"
> +#include "gimple.h"
> +#include "expr.h"
> +#include "gimple-iterator.h"
> +#include "gimple-ssa.h"
> +#include "tree-cfg.h"
> +#include "tree-phinodes.h"
> +#include "stringpool.h"
> +#include "tree-ssanames.h"
> +#include "tree-dfa.h"
> +#include "tree-pass.h"
> +#include "gimple-pretty-print.h"
> +#include "ipa-inline.h"
> +#include "cfgloop.h"
> +#include "except.h"
> +#include "hash-table.h"
> +#include "coverage.h"
> +#include "pointer-set.h"
> +#include "attribs.h"
> +#include "print-tree.h"
> +#include "lto-streamer.h"
> +#include "data-streamer.h"
> +#include "ipa-utils.h"
> +#include "ipa-icf.h"
> +
> +namespace {
> +
> +func_checker::func_checker (): initialized (false)
> +{
> +}

that style seems weird I'd expect

function_checker::function_checker () :
  initialized (false)
  {
  }

  but really why isn't that inline in the class, it seems really short.

> +
> +/* Itializes internal structures according to given number of

initializes

> +   source and target SSA names. The number of source names is SSA_SOURCE,
> +   respectively SSA_TARGET.  */
> +
> +void
> +func_checker::initialize (unsigned ssa_source, unsigned ssa_target)
> +{
> +  release ();
> +  initialized = true;
> +
> +  source_ssa_names.create (ssa_source);
> +  target_ssa_names.create (ssa_target);
> +
> +  for (unsigned int i = 0; i < ssa_source; i++)
> +    source_ssa_names.safe_push (-1);
> +
> +  for (unsigned int i = 0; i < ssa_target; i++)
> +    target_ssa_names.safe_push (-1);
> +
> +  edge_map = new pointer_map <edge> ();
> +
> +  decl_map = new pointer_map <tree> ();
> +}

So, if its infallible why do you need a seperate init function?

> +
> +/* Memory release routine.  */
> +
> +void
> +func_checker::release (void)
> +{
> +  if (!initialized)
> +    return;
> +
> +  delete edge_map;
> +  delete decl_map;
> +  source_ssa_names.release();
> +  target_ssa_names.release();
> +}

obligatory why isn't this a dtor?

> +
> +/* Verifies that trees T1 and T2 do correspond.  */
> +
> +bool
> +func_checker::compare_ssa_name (tree t1, tree t2)
> +{
> +  unsigned i1 = SSA_NAME_VERSION (t1);
> +  unsigned i2 = SSA_NAME_VERSION (t2);
> +
> +  if (source_ssa_names[i1] == -1)
> +    source_ssa_names[i1] = i2;
> +  else if (source_ssa_names[i1] != (int) i2)
> +    return false;
> +
> +  if(target_ssa_names[i2] == -1)
> +    target_ssa_names[i2] = i1;
> +  else if (target_ssa_names[i2] != (int) i1)
> +    return false;
> +
> +  return true;
> +}
> +
> +/* Verification function for edges E1 and E2.  */
> +
> +bool
> +func_checker::compare_edge (edge e1, edge e2)
> +{
> +  if (e1->flags != e2->flags)
> +    return false;
> +
> +  edge *slot = edge_map->contains (e1);
> +  if (slot)
> +    {
> +      SE_EXIT_DEBUG (*slot == e2);
> +    }
> +  else
> +    {
> +      slot = edge_map->insert (e1);
> +      *slot = e2;
> +    }

it'd be more efficient to just call insert and pass &existed to check if
the element was previously there.  It'd always be 1 hash lookup not
sometimes 2.

> +/* Verification function for declaration trees T1 and T2 that

its more comparison than verification right?

> +func_checker::compare_decl (tree t1, tree t2, tree func1, tree func2)
> +{
> +  if (!auto_var_in_fn_p (t1, func1) || !auto_var_in_fn_p (t2, func2))
> +    SE_EXIT_DEBUG (t1 == t2);
> +
> +  if (!types_compatible_p (TREE_TYPE (t1), TREE_TYPE (t2)))
> +    SE_EXIT_FALSE ();
> +
> +  tree *slot = decl_map->contains (t1);
> +  if (slot)
> +    {
> +      SE_EXIT_DEBUG (*slot == t2);
> +    }
> +  else
> +    {
> +      slot = decl_map->insert (t1);
> +      *slot = t2;
> +    }

same

> +congruence_class::congruence_class (unsigned int _id): id(_id)
> +{
> +  members.create (2);

hrm, we should probably add a specialization of auto_vec<T, 0> so you
can use that for things like this and lose the weird .create () stuff.

> +}
> +
> +/* Constructor for key value pair, where _ITEM is key and _INDEX is a target.  */
> +
> +sem_usage_pair::sem_usage_pair (sem_item *_item, unsigned int _index):
> +  item (_item), index (_index)

we've been naming members m_foo which should solve the shadowing problem
here as well as making code more readable.

> +sem_item::~sem_item ()
> +{
> +  if (tree_refs_set)
> +    pointer_set_destroy (tree_refs_set);
> +
> +  for (unsigned i = 0; i < usages.length (); i++)
> +    delete usages[i];

what about the vectors themselves? who owns them? I know this bit of
vec<> is horrible :(

> +/* Compare two types if are same aliases in case of strict aliasing
> +   is enabled.  */

nit, bad grammer

> +bool
> +sem_item::compare_for_aliasing (tree t1, tree t2)
> +{
> +  if (flag_strict_aliasing)
> +    {
> +      alias_set_type s1 = get_deref_alias_set (TREE_TYPE (t1));
> +      alias_set_type s2 = get_deref_alias_set (TREE_TYPE (t2));
> +
> +      return s1 == s2;
> +    }
> +
> +  return true;
> +}
> +
> +/* Semantic function constructor that uses STACK as bitmap memory stack.  */
> +
> +sem_function::sem_function (bitmap_obstack *stack): sem_item (FUNC, stack),
> +  compared_func (NULL)
> +{
> +  arg_types.create (0);
> +}
> +
> +/*  Constructor based on callgraph node _NODE with computed hash _HASH.
> +    Bitmap STACK is used for memory allocation.  */
> +sem_function::sem_function (cgraph_node *node, hashval_t hash,
> +			    bitmap_obstack *stack):
> +  sem_item (FUNC, node, hash, stack), bb_sorted (NULL), compared_func (NULL)
> +{
> +  arg_types.create (0);
> +}
> +
> +sem_function::~sem_function ()
> +{
> +  if (!bb_sorted)
> +    return;
> +
> +  for (unsigned i = 0; i < bb_count; i++)
> +    free (bb_sorted[i]);
> +
> +  free (bb_sizes);
> +  free (bb_sorted);
> +}

who owns arg_types? it doesn't seem to be us

> +sem_function::get_hash (void)
> +{
> +  if(!hash)
> +    {
> +      hash = 177454; /* Random number for function type.  */
> +
> +      hash = iterative_hash_object (arg_count, hash);
> +      hash = iterative_hash_object (bb_count, hash);
> +      hash = iterative_hash_object (edge_count, hash);
> +      hash = iterative_hash_object (cfg_checksum, hash);
> +      hash = iterative_hash_object (gcode_hash, hash);
> +
> +      for (unsigned i = 0; i < bb_count; i++)
> +	hash = iterative_hash_object (hash, get_bb_hash (bb_sorted[i]));
> +
> +      for (unsigned i = 0; i < bb_count; i++)
> +	hash = iterative_hash_object (bb_sizes[i], hash);
> +    }
> +
> +  return hash;
> +}

I have to say I've always wondered if our hash functions try too hard to
be perfect hashes at the expensive of speed, but of course its hard to
know without lots of data.

> +
> +/* Fast equality function based on knowledge known in WPA.  */
> +
> +bool
> +sem_function::equals_wpa (sem_item *item)
> +{
> +  if (item->type != FUNC)
> +    return false;

should this just be an assert? When we're creating the congruence
classes in the first place we can keep functions and variables in
different classes right?

> +/* Returns true if the item equals to ITEM given as arguemnt.  */

argument

> +sem_function::equals_private (sem_item *item)
> +{
> +  if (item->type != FUNC)
> +    return false;

assert?

> +
> +  basic_block bb1, bb2;
> +  edge e1, e2;
> +  edge_iterator ei1, ei2;
> +  int *bb_dict = NULL;
> +  bool result = true;
> +  tree arg1, arg2;
> +
> +  compared_func = static_cast<sem_function *> (item);
> +
> +  gcc_assert (decl != item->decl);
> +
> +  if (arg_count != compared_func->arg_count
> +      || bb_count != compared_func->bb_count
> +      || edge_count != compared_func->edge_count
> +      || cfg_checksum != compared_func->cfg_checksum)
> +    SE_EXIT_FALSE();
> +
> +  if (!equals_wpa (item))

isn't this redundant with a bunch of the checks above it? could you
remove them?

> +    return false;
> +
> +  /* Checking function arguments.  */
> +  tree decl1 = DECL_ATTRIBUTES (decl);
> +  tree decl2 = DECL_ATTRIBUTES (compared_func->decl);
> +
> +  while (decl1)
> +    {
> +      if (decl2 == NULL)
> +	SE_EXIT_FALSE();

are attributes always in the same order?

> +
> +      if (get_attribute_name (decl1) != get_attribute_name (decl2))
> +	SE_EXIT_FALSE();
> +
> +      tree attr_value1 = TREE_VALUE (decl1);
> +      tree attr_value2 = TREE_VALUE (decl2);
> +
> +      if (attr_value1 && attr_value2)
> +	{
> +	  bool ret = compare_operand (TREE_VALUE (attr_value1),
> +				      TREE_VALUE (attr_value2), decl,
> +				      compared_func->decl);
> +	  if (!ret)
> +	    SE_EXIT_FALSE_WITH_MSG ("attribute values are different")
> +	  }
> +      else if (!attr_value1 && !attr_value2)
> +	{}
> +      else
> +	SE_EXIT_FALSE ();
> +
> +      decl1 = TREE_CHAIN (decl1);
> +      decl2 = TREE_CHAIN (decl2);
> +    }
> +
> +  if (decl1 != decl2)
> +    SE_EXIT_FALSE();
> +
> +  checker.initialize (ssa_names_size, compared_func->ssa_names_size);
> +
> +  for (arg1 = DECL_ARGUMENTS (decl), arg2 = DECL_ARGUMENTS (compared_func->decl);
> +       arg1; arg1 = DECL_CHAIN (arg1), arg2 = DECL_CHAIN (arg2))
> +    checker.compare_decl (arg1, arg2, decl, compared_func->decl);
> +
> +  /* Exception handling regions comparison.  */
> +  if (!compare_eh_region (region_tree, compared_func->region_tree, decl,
> +			  compared_func->decl))
> +    SE_EXIT_FALSE();
> +
> +  /* Checking all basic blocks.  */
> +  for (unsigned i = 0; i < bb_count; ++i)
> +    if(!compare_bb (bb_sorted[i], compared_func->bb_sorted[i], decl,
> +		    compared_func->decl))
> +      SE_EXIT_FALSE();
> +
> +  SE_DUMP_MESSAGE ("All BBs are equal\n");
> +
> +  /* Basic block edges check.  */
> +  for (unsigned i = 0; i < bb_count; ++i)
> +    {
> +      bb_dict = XNEWVEC (int, bb_count + 2);
> +      memset (bb_dict, -1, (bb_count + 2) * sizeof (int));
> +
> +      bb1 = bb_sorted[i]->bb;
> +      bb2 = compared_func->bb_sorted[i]->bb;
> +
> +      ei2 = ei_start (bb2->preds);
> +
> +      for (ei1 = ei_start (bb1->preds); ei_cond (ei1, &e1); ei_next (&ei1))
> +	{
> +	  ei_cond (ei2, &e2);
> +
> +	  if (!bb_dict_test (bb_dict, e1->src->index, e2->src->index))
> +	    SE_EXIT_FALSE_WITH_MSG("edge comparison returns false");
> +
> +	  if (!bb_dict_test (bb_dict, e1->dest->index, e2->dest->index))
> +	    SE_EXIT_FALSE_WITH_MSG("BB comparison returns false");
> +
> +	  if (e1->flags != e2->flags)
> +	    SE_EXIT_FALSE_WITH_MSG("flags comparison returns false");

check this condition before the previous two since I guess its faster?

> +sem_function::init_refs_for_tree (tree t)
> +{
> +  switch (TREE_CODE (t))
> +    {
> +    case VAR_DECL:
> +    case FUNCTION_DECL:
> +      tree_refs.safe_push (t);
> +      break;
> +    case MEM_REF:
> +    case ADDR_EXPR:
> +    case OBJ_TYPE_REF:
> +      init_refs_for_tree (TREE_OPERAND (t, 0));
> +      break;
> +    case FIELD_DECL:
> +      init_refs_for_tree (DECL_FCONTEXT (t));
> +      break;
> +    default:
> +      break;
> +    }
> +}
> +
> +/* Initializes references to another sem_item for gimple STMT of type assign.  */
> +
> +void
> +sem_function::init_refs_for_assign (gimple stmt)
> +{
> +  if (gimple_num_ops (stmt) != 2)
> +    return;
> +
> +  tree rhs = gimple_op (stmt, 1);
> +
> +  init_refs_for_tree (rhs);
> +}
> +
> +/* Initializes references to other semantic functions/variables.  */
> +
> +void
> +sem_function::init_refs (void)
> +{
> +  for (unsigned i = 0; i < bb_count; i++)
> +    {
> +      basic_block bb = bb_sorted[i]->bb;
> +
> +      for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi);
> +	   gsi_next (&gsi))
> +	{
> +	  gimple stmt = gsi_stmt (gsi);
> +	  hashval_t code = (hashval_t) gimple_code (stmt);
> +
> +	  switch (code)
> +	    {
> +	    case GIMPLE_CALL:
> +	      {
> +		tree funcdecl = gimple_call_fndecl (stmt);
> +
> +		/* Function pointer variables are not support yet.  */

supported

> +		if (funcdecl)
> +		  tree_refs.safe_push (funcdecl);
> +
> +		break;
> +	      }
> +	    case GIMPLE_ASSIGN:
> +	      init_refs_for_assign (stmt);
> +	      break;
> +	    default:
> +	      break;
> +	    }
> +	}
> +    }
> +}
> +
> +/* Merges instance with an ALIAS_ITEM, where alias, thunk or redirection can
> +   be applied.  */
> +bool
> +sem_function::merge (sem_item *alias_item)
> +{
> +  gcc_assert (alias_item->type == FUNC);
> +
> +  sem_function *alias_func = static_cast<sem_function *> (alias_item);
> +
> +  struct cgraph_node *original = get_node ();
> +  struct cgraph_node *local_original = original;
> +  struct cgraph_node *alias = alias_func->get_node ();
> +  bool original_address_matters;
> +  bool alias_address_matters;
> +
> +  bool create_thunk = false;
> +  bool create_alias = false;
> +  bool redirect_callers = false;
> +  bool original_discardable = false;
> +
> +  /* Do not attempt to mix functions from different user sections;
> +     we do not know what user intends with those.  */
> +  if (((DECL_SECTION_NAME (original->decl) && !original->implicit_section)
> +       || (DECL_SECTION_NAME (alias->decl) && !alias->implicit_section))
> +      && DECL_SECTION_NAME (original->decl) != DECL_SECTION_NAME (alias->decl))
> +    {
> +      if (dump_file)
> +	fprintf (dump_file,
> +		 "Not unifying; original and alias are in different sections.\n\n");
> +      return false;
> +    }
> +
> +  /* See if original is in a section that can be discarded if the main
> +     symbol is not used.  */
> +  if (DECL_EXTERNAL (original->decl))
> +    original_discardable = true;
> +  if (original->resolution == LDPR_PREEMPTED_REG
> +      || original->resolution == LDPR_PREEMPTED_IR)
> +    original_discardable = true;
> +  if (symtab_can_be_discarded (original))
> +    original_discardable = true;

shouldn't this last check handle the previous ones?

> +
> +  /* See if original and/or alias address can be compared for equality.  */
> +  original_address_matters
> +    = (!DECL_VIRTUAL_P (original->decl)
> +       && (original->externally_visible
> +	   || address_taken_from_non_vtable_p (original)));
> +  alias_address_matters
> +    = (!DECL_VIRTUAL_P (alias->decl)
> +       && (alias->externally_visible
> +	   || address_taken_from_non_vtable_p (alias)));
> +
> +  /* If alias and original can be compared for address equality, we need
> +     to create a thunk.  Also we can not create extra aliases into discardable
> +     section (or we risk link failures when section is discarded).  */
> +  if ((original_address_matters
> +       && alias_address_matters)
> +      || original_discardable)
> +    {
> +      create_thunk = !stdarg_p (TREE_TYPE (alias->decl));
> +      create_alias = false;
> +      /* When both alias and original are not overwritable, we can save
> +         the extra thunk wrapper for direct calls.  */
> +      redirect_callers
> +	= (!original_discardable
> +	   && cgraph_function_body_availability (alias) > AVAIL_OVERWRITABLE
> +	   && cgraph_function_body_availability (original) > AVAIL_OVERWRITABLE);
> +    }
> +  else
> +    {
> +      create_alias = true;
> +      create_thunk = false;
> +      redirect_callers = false;
> +    }
> +
> +  if (create_alias && DECL_COMDAT_GROUP (alias->decl))
> +    {
> +      create_alias = false;
> +      create_thunk = true;
> +    }
> +
> +  /* We want thunk to always jump to the local function body
> +     unless the body is comdat and may be optimized out.  */
> +  if ((create_thunk || redirect_callers)
> +      && (!original_discardable
> +	  || (DECL_COMDAT_GROUP (original->decl)
> +	      && (DECL_COMDAT_GROUP (original->decl)
> +		  == DECL_COMDAT_GROUP (alias->decl)))))
> +    local_original
> +      = cgraph (symtab_nonoverwritable_alias (original));
> +
> +  if (redirect_callers)
> +    {
> +      /* If alias is non-overwritable then
> +         all direct calls are safe to be redirected to the original.  */
> +      bool redirected = false;
> +      while (alias->callers)
> +	{
> +	  struct cgraph_edge *e = alias->callers;
> +	  cgraph_redirect_edge_callee (e, local_original);
> +	  push_cfun (DECL_STRUCT_FUNCTION (e->caller->decl));
> +
> +	  if (e->call_stmt)
> +	    cgraph_redirect_edge_call_stmt_to_callee (e);
> +
> +	  pop_cfun ();
> +	  redirected = true;
> +	}
> +
> +      /* The alias function is removed just if symbol address

s/just//

> +         does not matters.  */

s/matters/matter/

> +sem_function *
> +sem_function::parse (struct cgraph_node *node, bitmap_obstack *stack)
> +{
> +  tree fndecl = node->decl;
> +  struct function *func = DECL_STRUCT_FUNCTION (fndecl);
> +
> +  if (!func || !cgraph_function_with_gimple_body_p (node))
> +    return NULL;
> +
> +  if (lookup_attribute_by_prefix ("omp ", DECL_ATTRIBUTES (node->decl)) != NULL)
> +    return NULL;
> +
> +  sem_function *f = new sem_function (node, 0, stack);
> +
> +  f->init ();
> +
> +  return f;
> +}
> +
> +/* Parses function arguments and result type.  */
> +
> +void
> +sem_function::parse_tree_args (void)
> +{
> +  tree result;
> +  arg_types.create (4);
> +  tree fnargs = DECL_ARGUMENTS (decl);
> +
> +  for (tree parm = fnargs; parm; parm = DECL_CHAIN (parm))
> +    arg_types.safe_push (TYPE_CANONICAL (DECL_ARG_TYPE (parm)));
> +
> +  /* Function result type.  */
> +  result = DECL_RESULT (decl);
> +  result_type = result ? TYPE_CANONICAL (TREE_TYPE (result)) : NULL;
> +
> +  /* During WPA, we can get arguments by following method.  */
> +  if (!fnargs)
> +    {
> +      tree type = TYPE_ARG_TYPES (TREE_TYPE (decl));
> +      for (tree parm = type; parm; parm = TREE_CHAIN (parm))
> +	arg_types.safe_push (TYPE_CANONICAL (TREE_VALUE (parm)));
> +
> +      result_type = TREE_TYPE (TREE_TYPE (decl));
> +    }
> +
> +  arg_count = arg_types.length ();

So, is it really worth the extra memory to save a memory access when you
need the number of arguments? especially considering you'll probably
want the vector elements next.

> +/* For given basic blocks BB1 and BB2 (from functions FUNC1 and FUNC),
> +   true value is returned if phi nodes are sematically

semantically

> +   equivalent in these blocks .  */

perhaps return true if .... ?

> +sem_function::gsi_next_nondebug_stmt (gimple_stmt_iterator &gsi)

 I'd say a pointer makes it clearler the callie will modify the argument.

> +{
> +  gimple s;
> +
> +  s = gsi_stmt (gsi);
> +
> +  while (gimple_code (s) == GIMPLE_DEBUG)
> +    {
> +      gsi_next (&gsi);
> +      gcc_assert (!gsi_end_p (gsi));
> +
> +      s = gsi_stmt (gsi);
> +    }
> +}

really, shouldn't this be part of the gsi interface not something you
hand roll here?

> +
> +/* Iterates GSI statement iterator to the next non-virtual statement.  */
> +
> +void
> +sem_function::gsi_next_nonvirtual_phi (gimple_stmt_iterator &it)

same

> +sem_function::icf_handled_component_p (tree t)
> +{
> +  enum tree_code tc = TREE_CODE (t);
> +
> +  return ((handled_component_p (t) && handled_component_p (t))

it must be too late, or are you calling the same function twice with the
same args?

> +	  || tc == ADDR_EXPR || tc == MEM_REF || tc == REALPART_EXPR
> +	  || tc == IMAGPART_EXPR || tc == OBJ_TYPE_REF);
> +}
> +/* Returns true if the item equals to ITEM given as arguemnt.  */

argument

Its nice to see someone trying this and better it works ;)  I wonder if
in the future it'll be worth looking at congruity of chunks of functions
and then sharing those too.

anyway I've had it for tonight hopefully I can finish looking at this
soon.

Trev

> +sem_item_optimizer::write_summary (void)
> +{
> +  unsigned int count = 0;
> +
> +  struct output_block *ob = create_output_block (LTO_section_ipa_icf);
> +  lto_symtab_encoder_t encoder = ob->decl_state->symtab_node_encoder;
> +  ob->cgraph_node = NULL;
> +
> +  /* Calculate number of symbols to be serialized.  */
> +  for (lto_symtab_encoder_iterator lsei = lsei_start_in_partition (encoder);
> +       !lsei_end_p (lsei);
> +       lsei_next_in_partition (&lsei))
> +    {
> +      struct symtab_node *node = lsei_node (lsei);
> +
> +      if (symtab_node_map.contains (node))
> +	count++;
> +    }
> +
> +  streamer_write_uhwi (ob, count);
> +
> +  /* Process all of the symbols.  */
> +  for (lto_symtab_encoder_iterator lsei = lsei_start_in_partition (encoder);
> +       !lsei_end_p (lsei);
> +       lsei_next_in_partition (&lsei))
> +    {
> +      struct symtab_node *node = lsei_node (lsei);
> +
> +      sem_item **item = symtab_node_map.contains (node);
> +
> +      if (item && *item)
> +	{
> +	  int node_ref = lto_symtab_encoder_encode (encoder, node);
> +	  streamer_write_uhwi_stream (ob->main_stream, node_ref);
> +
> +	  streamer_write_uhwi (ob, (*item)->get_hash ());
> +	}
> +    }
> +
> +  streamer_write_char_stream (ob->main_stream, 0);
> +  produce_asm (ob, NULL);
> +  destroy_output_block (ob);
> +}
> +
> +/* Reads a section from LTO stream file FILE_DATA. Input block for DATA
> +   contains LEN bytes.  */
> +
> +void
> +sem_item_optimizer::read_section (struct lto_file_decl_data *file_data,
> +				  const char *data, size_t len)
> +{
> +  const struct lto_function_header *header =
> +  (const struct lto_function_header *) data;
> +  const int cfg_offset = sizeof (struct lto_function_header);
> +  const int main_offset = cfg_offset + header->cfg_size;
> +  const int string_offset = main_offset + header->main_size;
> +  struct data_in *data_in;
> +  struct lto_input_block ib_main;
> +  unsigned int i;
> +  unsigned int count;
> +
> +  LTO_INIT_INPUT_BLOCK (ib_main, (const char *) data + main_offset, 0,
> +			header->main_size);
> +
> +  data_in =
> +    lto_data_in_create (file_data, (const char *) data + string_offset,
> +			header->string_size, vNULL);
> +
> +  count = streamer_read_uhwi (&ib_main);
> +
> +  for (i = 0; i < count; i++)
> +    {
> +      unsigned int index;
> +      struct symtab_node *node;
> +      lto_symtab_encoder_t encoder;
> +
> +      index = streamer_read_uhwi (&ib_main);
> +      encoder = file_data->symtab_node_encoder;
> +      node = lto_symtab_encoder_deref (encoder, index);
> +
> +      hashval_t hash = streamer_read_uhwi (&ib_main);
> +
> +      gcc_assert (node->definition);
> +
> +      if (dump_file)
> +	fprintf (dump_file, "Symbol added:%s (tree: %p, uid:%u)\n", node->asm_name (),
> +		 (void *) node->decl, node->order);
> +
> +      if (is_a_helper<cgraph_node *>::test (node))
> +	{
> +	  cgraph_node *cnode = cgraph (node);
> +
> +	  items.safe_push (new sem_function (cnode, hash, &bmstack));
> +	}
> +      else
> +	{
> +	  varpool_node *vnode = varpool (node);
> +
> +	  items.safe_push (new sem_variable (vnode, hash, &bmstack));
> +	}
> +    }
> +
> +  lto_free_section_data (file_data, LTO_section_ipa_icf, NULL, data,
> +			 len);
> +  lto_data_in_delete (data_in);
> +}
> +
> +/* Read IPA IPA ICF summary for symbols.  */
> +
> +void
> +sem_item_optimizer::read_summary (void)
> +{
> +  struct lto_file_decl_data **file_data_vec = lto_get_file_decl_data ();
> +  struct lto_file_decl_data *file_data;
> +  unsigned int j = 0;
> +
> +  while ((file_data = file_data_vec[j++]))
> +    {
> +      size_t len;
> +      const char *data = lto_get_section_data (file_data,
> +			 LTO_section_ipa_icf, NULL, &len);
> +
> +      if (data)
> +	read_section (file_data, data, len);
> +    }
> +}
> +
> +/* Register callgraph and varpool hooks.  */
> +
> +void
> +sem_item_optimizer::register_hooks (void)
> +{
> +  cgraph_node_hooks = cgraph_add_node_removal_hook (
> +			&sem_item_optimizer::cgraph_removal_hook, this);
> +
> +  varpool_node_hooks = varpool_add_node_removal_hook (
> +			 &sem_item_optimizer::varpool_removal_hook, this);
> +}
> +
> +/* Unregister callgraph and varpool hooks.  */
> +
> +void
> +sem_item_optimizer::unregister_hooks (void)
> +{
> +  if (cgraph_node_hooks)
> +    cgraph_remove_node_removal_hook (cgraph_node_hooks);
> +
> +  if (varpool_node_hooks)
> +    varpool_remove_node_removal_hook (varpool_node_hooks);
> +}
> +
> +/* Adds a CLS to hashtable associated by hash value.  */
> +
> +void
> +sem_item_optimizer::add_class (congruence_class *cls)
> +{
> +  gcc_assert (cls->members.length ());
> +
> +  congruence_class_group_t *group = get_group_by_hash (
> +				      cls->members[0]->get_hash ());
> +  group->classes.safe_push (cls);
> +}
> +
> +/* Gets a congruence class group based on given HASH value.  */
> +
> +congruence_class_group_t *
> +sem_item_optimizer::get_group_by_hash (hashval_t hash)
> +{
> +  congruence_class_group_t *item = XNEW (congruence_class_group_t);
> +  item->hash = hash;
> +
> +  congruence_class_group **slot = classes.find_slot (item, INSERT);
> +
> +  if (*slot)
> +    free (item);
> +  else
> +    {
> +      item->classes.create (1);
> +      *slot = item;
> +    }
> +
> +  return *slot;
> +}
> +
> +/* Callgraph removal hook called for a NODE with a custom DATA.  */
> +
> +void
> +sem_item_optimizer::cgraph_removal_hook (struct cgraph_node *node, void *data)
> +{
> +  sem_item_optimizer *optimizer = (sem_item_optimizer *) data;
> +  optimizer->remove_symtab_node (node);
> +}
> +
> +/* Varpool removal hook called for a NODE with a custom DATA.  */
> +
> +void
> +sem_item_optimizer::varpool_removal_hook (struct varpool_node *node, void *data)
> +{
> +  sem_item_optimizer *optimizer = (sem_item_optimizer *) data;
> +  optimizer->remove_symtab_node (node);
> +}
> +
> +/* Remove symtab NODE triggered by symtab removal hooks.  */
> +
> +void
> +sem_item_optimizer::remove_symtab_node (struct symtab_node *node)
> +{
> +  gcc_assert (!classes.elements());
> +
> +  pointer_set_insert (removed_items_set, node);
> +}
> +
> +/* Removes all callgraph and varpool nodes that are marked by symtab
> +   as deleted.  */
> +
> +void
> +sem_item_optimizer::filter_removed_items (void)
> +{
> +  vec <sem_item *> filtered;
> +  filtered.create (items.length());
> +
> +  for (unsigned int i = 0; i < items.length(); i++)
> +    {
> +      sem_item *item = items[i];
> +
> +      bool no_body_function = false;
> +
> +      if (item->type == FUNC)
> +	{
> +	  struct cgraph_node *cnode = static_cast <sem_function *>(item)->get_node ();
> +
> +	  no_body_function = in_lto_p && (cnode->alias || cnode->body_removed);
> +	}
> +
> +      if(!pointer_set_contains (removed_items_set, items[i]->node)
> +	  && !no_body_function)
> +	filtered.safe_push (items[i]);
> +    }
> +  items.release ();
> +
> +  for (unsigned int i = 0; i < filtered.length(); i++)
> +    items.safe_push (filtered[i]);
> +}
> +
> +/* Optimizer entry point.  */
> +
> +void
> +sem_item_optimizer::execute (void)
> +{
> +  filter_removed_items ();
> +  build_hash_based_classes ();
> +
> +  if (dump_file)
> +    fprintf (dump_file, "Dump after hash based groups\n");
> +  dump_cong_classes ();
> +
> +  for (unsigned int i = 0; i < items.length(); i++)
> +    items[i]->init_wpa ();
> +
> +  subdivide_classes_by_equality (true);
> +
> +  if (dump_file)
> +    fprintf (dump_file, "Dump after WPA based types groups\n");
> +  dump_cong_classes ();
> +
> +  parse_nonsingleton_classes ();
> +  subdivide_classes_by_equality ();
> +
> +  if (dump_file)
> +    fprintf (dump_file, "Dump after full equality comparison of groups\n");
> +
> +  dump_cong_classes ();
> +
> +  unsigned int prev_class_count = classes_count;
> +
> +  process_cong_reduction ();
> +  dump_cong_classes ();
> +  merge_classes (prev_class_count);
> +
> +  if (dump_file && (dump_flags & TDF_DETAILS))
> +    dump_symtab (dump_file);
> +}
> +
> +/* Function responsible for visiting all potential functions and
> +   read-only variables that can be merged.  */
> +
> +void
> +sem_item_optimizer::parse_funcs_and_vars (void)
> +{
> +  struct cgraph_node *cnode;
> +  sem_item **slot;
> +
> +  FOR_EACH_DEFINED_FUNCTION (cnode)
> +  {
> +    sem_function *f = sem_function::parse (cnode, &bmstack);
> +    if (f)
> +      {
> +	items.safe_push (f);
> +	slot = symtab_node_map.insert (cnode);
> +	*slot = f;
> +
> +	if (dump_file)
> +	  fprintf (dump_file, "Parsed function:%s\n", f->asm_name ());
> +
> +	if (dump_file && (dump_flags & TDF_DETAILS))
> +	  f->dump_to_file (dump_file);
> +      }
> +    else if (dump_file)
> +      fprintf (dump_file, "Not parsed function:%s\n", cnode->asm_name ());
> +  }
> +
> +  varpool_node *vnode;
> +
> +  FOR_EACH_DEFINED_VARIABLE (vnode)
> +  {
> +    sem_variable *v = sem_variable::parse (vnode, &bmstack);
> +
> +    if (v)
> +      {
> +	items.safe_push (v);
> +	slot = symtab_node_map.insert (vnode);
> +	*slot = v;
> +      }
> +  }
> +}
> +
> +/* Makes pairing between a congruence class CLS and semantic ITEM.  */
> +
> +void
> +sem_item_optimizer::add_item_to_class (congruence_class *cls, sem_item *item)
> +{
> +  item->index_in_class = cls->members.length ();
> +  cls->members.safe_push (item);
> +  item->cls = cls;
> +}
> +
> +/* Congruence classes are built by hash value.  */
> +
> +void
> +sem_item_optimizer::build_hash_based_classes (void)
> +{
> +  for (unsigned i = 0; i < items.length (); i++)
> +    {
> +      sem_item *item = items[i];
> +
> +      congruence_class_group_t *group = get_group_by_hash (item->get_hash ());
> +
> +      if (!group->classes.length ())
> +	{
> +	  classes_count++;
> +	  group->classes.safe_push (new congruence_class (class_id++));
> +	}
> +
> +      add_item_to_class (group->classes[0], item);
> +    }
> +}
> +
> +/* Semantic items in classes having more than one element and initialized.
> +   In case of WPA, we load function body.  */
> +
> +void
> +sem_item_optimizer::parse_nonsingleton_classes (void)
> +{
> +  for (hash_table <congruence_class_group_hash>::iterator it = classes.begin ();
> +       it != classes.end (); ++it)
> +    {
> +      for (unsigned i = 0; i < (*it).classes.length (); i++)
> +	{
> +	  congruence_class *c = (*it).classes [i];
> +
> +	  if (c->members.length() > 1)
> +	    for (unsigned j = 0; j < c->members.length (); j++)
> +	      {
> +		sem_item *item = c->members[j];
> +		sem_item **slot;
> +
> +		slot = decl_map.insert (item->decl);
> +		*slot = item;
> +
> +	      }
> +	}
> +    }
> +
> +  unsigned int init_called_count = 0;
> +
> +  for (hash_table <congruence_class_group_hash>::iterator it = classes.begin ();
> +       it != classes.end (); ++it)
> +    {
> +      /* We fill in all declarations for sem_items.  */
> +      for (unsigned i = 0; i < (*it).classes.length (); i++)
> +	{
> +	  congruence_class *c = (*it).classes [i];
> +
> +	  if (c->members.length() > 1)
> +	    for (unsigned j = 0; j < c->members.length (); j++)
> +	      {
> +		sem_item *item = c->members[j];
> +
> +		item->init ();
> +		item->init_refs ();
> +		init_called_count++;
> +
> +		for (unsigned j = 0; j < item->tree_refs.length (); j++)
> +		  {
> +		    sem_item **result = decl_map.contains (item->tree_refs[j]);
> +
> +		    if(result)
> +		      {
> +			sem_item *target = *result;
> +			item->refs.safe_push (target);
> +
> +			unsigned index = item->refs.length ();
> +			target->usages.safe_push (new sem_usage_pair(item, index));
> +			bitmap_set_bit (target->usage_index_bitmap, index);
> +			pointer_set_insert (item->tree_refs_set, item->tree_refs[j]);
> +		      }
> +		  }
> +	      }
> +	}
> +    }
> +
> +  if (dump_file)
> +    fprintf (dump_file, "Init called for %u items (%.2f%%).\n", init_called_count,
> +	     100.0f * init_called_count / items.length ());
> +}
> +
> +/* Equality function for semantic items is used to subdivide existing
> +   classes. If IN_WPA, fast equality function is invoked.  */
> +
> +void
> +sem_item_optimizer::subdivide_classes_by_equality (bool in_wpa)
> +{
> +  for (hash_table <congruence_class_group_hash>::iterator it = classes.begin ();
> +       it != classes.end (); ++it)
> +    {
> +      unsigned int class_count = (*it).classes.length ();
> +
> +      for (unsigned i = 0; i < class_count; i++)
> +	{
> +	  congruence_class *c = (*it).classes [i];
> +
> +	  if (c->members.length() > 1)
> +	    {
> +	      vec <sem_item *> new_vector;
> +	      new_vector.create (c->members.length ());
> +
> +	      sem_item *first = c->members[0];
> +	      new_vector.safe_push (first);
> +
> +	      unsigned class_split_first = (*it).classes.length ();
> +
> +	      for (unsigned j = 1; j < c->members.length (); j++)
> +		{
> +		  sem_item *item = c->members[j];
> +
> +		  bool equals = in_wpa ? first->equals_wpa (item) : first->equals (item);
> +
> +		  if (equals)
> +		    new_vector.safe_push (item);
> +		  else
> +		    {
> +		      bool integrated = false;
> +
> +		      for (unsigned k = class_split_first; k < (*it).classes.length (); k++)
> +			{
> +			  sem_item *x = (*it).classes[k]->members[0];
> +			  bool equals = in_wpa ? x->equals_wpa (item) : x->equals (item);
> +
> +			  if (equals)
> +			    {
> +			      integrated = true;
> +			      add_item_to_class ((*it).classes[k], item);
> +
> +			      break;
> +			    }
> +			}
> +
> +		      if (!integrated)
> +			{
> +			  congruence_class *c = new congruence_class (class_id++);
> +			  classes_count++;
> +			  add_item_to_class (c, item);
> +
> +			  (*it).classes.safe_push (c);
> +			}
> +		    }
> +		}
> +
> +	      // we replace newly created new_vector for the class we've just splitted
> +	      c->members.release ();
> +	      c->members.create (new_vector.length ());
> +
> +	      for (unsigned int j = 0; j < new_vector.length (); j++)
> +		add_item_to_class (c, new_vector[j]);
> +	    }
> +	}
> +    }
> +
> +  verify_classes ();
> +}
> +
> +/* Verify congruence classes if checking is enabled.  */
> +
> +void
> +sem_item_optimizer::verify_classes (void)
> +{
> +#if ENABLE_CHECKING
> +  for (hash_table <congruence_class_group_hash>::iterator it = classes.begin ();
> +       it != classes.end (); ++it)
> +    {
> +      for (unsigned int i = 0; i < (*it).classes.length (); i++)
> +	{
> +	  congruence_class *cls = (*it).classes[i];
> +
> +	  gcc_checking_assert (cls);
> +	  gcc_checking_assert (cls->members.length () > 0);
> +
> +	  for (unsigned int j = 0; j < cls->members.length (); j++)
> +	    {
> +	      sem_item *item = cls->members[j];
> +
> +	      gcc_checking_assert (item);
> +
> +	      for (unsigned k = 0; k < item->usages.length (); k++)
> +		{
> +		  sem_usage_pair *usage = item->usages[k];
> +		  gcc_checking_assert (usage->item->index_in_class <
> +				       usage->item->cls->members.length ());
> +		}
> +	    }
> +	}
> +    }
> +#endif
> +}
> +
> +/* Disposes split map traverse function. CLS_PTR is pointer to congruence
> +   class, BSLOT is bitmap slot we want to release. DATA is mandatory,
> +   but unused argument.  */
> +
> +bool
> +sem_item_optimizer::release_split_map (__attribute__((__unused__)) const void
> +				       *cls_ptr,
> +				       __attribute__((__unused__)) bitmap *bslot,
> +				       __attribute__((__unused__)) void *data)
> +{
> +  bitmap b = *bslot;
> +
> +  BITMAP_FREE (b);
> +
> +  return true;
> +}
> +
> +/* Process split operation for a class given as pointer CLS_PTR,
> +   where bitmap B splits congruence class members. DATA is used
> +   as argument of split pair.  */
> +
> +bool
> +sem_item_optimizer::traverse_congruence_split (const void *cls_ptr,
> +    bitmap *bslot, void *data)
> +{
> +  const congruence_class *cls = (const congruence_class *) cls_ptr;
> +  bitmap b = *bslot;
> +
> +  traverse_split_pair *pair = (traverse_split_pair *) data;
> +  sem_item_optimizer *optimizer = pair->optimizer;
> +  const congruence_class *splitter_cls = pair->cls;
> +
> +  /* If counted bits are greater than zero and less than the number of members
> +     a group will be splitted.  */
> +  unsigned popcount = bitmap_count_bits (b);
> +
> +  if (popcount > 0 && popcount < cls->members.length ())
> +    {
> +      congruence_class* newclasses[2] = { new congruence_class (class_id++), new congruence_class (class_id++) };
> +
> +      for (unsigned int i = 0; i < cls->members.length (); i++)
> +	{
> +	  int target = bitmap_bit_p (b, i);
> +	  congruence_class *tc = newclasses[target];
> +
> +	  add_item_to_class (tc, cls->members[i]);
> +	}
> +
> +#ifdef ENABLE_CHECKING
> +      for (unsigned int i = 0; i < 2; i++)
> +	gcc_checking_assert (newclasses[i]->members.length ());
> +#endif
> +
> +      if (splitter_cls == cls)
> +	optimizer->splitter_class_removed = true;
> +
> +      /* Remove old class from worklist if presented.  */
> +      bool in_work_list = optimizer->worklist_contains (cls);
> +
> +      if (in_work_list)
> +	optimizer->worklist_remove (cls);
> +
> +      for (hash_table <congruence_class_group_hash>::iterator it =
> +	     optimizer->classes.begin (); it != optimizer->classes.end (); ++it)
> +	{
> +	  for (unsigned int i = 0; i < (*it).classes.length (); i++)
> +	    if ((*it).classes[i] == cls)
> +	      {
> +		(*it).classes.ordered_remove (i);
> +		break;
> +	      }
> +	}
> +      /* New class will be inserted and integrated to work list.  */
> +      for (unsigned int i = 0; i < 2; i++)
> +	optimizer->add_class (newclasses[i]);
> +
> +      /* Two classes replace one, so that increment just by one.  */
> +      optimizer->classes_count++;
> +
> +      /* If OLD class was presented in the worklist, we remove the class
> +         are replace it will both newly created classes.  */
> +      if (in_work_list)
> +	for (unsigned int i = 0; i < 2; i++)
> +	  optimizer->worklist_push (newclasses[i]);
> +      else /* Just smaller class is inserted.  */
> +	{
> +	  unsigned int smaller_index = newclasses[0]->members.length () <
> +				       newclasses[1]->members.length () ?
> +				       0 : 1;
> +	  optimizer->worklist_push (newclasses[smaller_index]);
> +	}
> +
> +      if (dump_file && (dump_flags & TDF_DETAILS))
> +	{
> +	  fprintf (dump_file, "  congruence class splitted:\n");
> +	  cls->dump (dump_file, 4);
> +
> +	  fprintf (dump_file, "  newly created groups:\n");
> +	  for (unsigned int i = 0; i < 2; i++)
> +	    newclasses[i]->dump (dump_file, 4);
> +	}
> +
> +      delete cls;
> +    }
> +
> +
> +  return true;
> +}
> +
> +/* Tests if a class CLS used as INDEXth splits any congruence classes.
> +   Bitmap stack BMSTACK is used for bitmap allocation.  */
> +
> +void
> +sem_item_optimizer::do_congruence_step_for_index (congruence_class *cls,
> +    unsigned int index)
> +{
> +  /* Split map reset */
> +  if (split_map != NULL)
> +    delete split_map;
> +
> +  pointer_map <bitmap> *split_map = new pointer_map <bitmap> ();
> +
> +  for (unsigned int i = 0; i < cls->members.length (); i++)
> +    {
> +      sem_item *item = cls->members[i];
> +
> +      /* Iterate all usages that have INDEX as usage of the item.  */
> +      for (unsigned int j = 0; j < item->usages.length (); j++)
> +	{
> +	  sem_usage_pair *usage = item->usages[j];
> +
> +	  if (usage->index != index)
> +	    continue;
> +
> +	  bitmap *slot = split_map->contains (usage->item->cls);
> +
> +	  if(!slot)
> +	    {
> +	      slot = split_map->insert (usage->item->cls);
> +	      *slot = BITMAP_ALLOC (&bmstack);
> +	    }
> +
> +	  bitmap b = *slot;
> +
> +#if ENABLE_CHECKING
> +	  gcc_checking_assert (usage->item->cls);
> +	  gcc_checking_assert (usage->item->index_in_class <
> +			       usage->item->cls->members.length ());
> +#endif
> +
> +	  bitmap_set_bit (b, usage->item->index_in_class);
> +	}
> +    }
> +
> +  traverse_split_pair pair;
> +  pair.optimizer = this;
> +  pair.cls = cls;
> +
> +  splitter_class_removed = false;
> +  split_map->traverse (&sem_item_optimizer::traverse_congruence_split, &pair);
> +
> +  /* Bitmap clean-up.  */
> +  split_map->traverse (&sem_item_optimizer::release_split_map, NULL);
> +}
> +
> +/* Every usage of a congruence class CLS is a candidate that can split the
> +   collection of classes. Bitmap stack BMSTACK is used for bitmap
> +   allocation.  */
> +
> +void
> +sem_item_optimizer::do_congruence_step (congruence_class *cls)
> +{
> +  bitmap_iterator bi;
> +  unsigned int i;
> +
> +  bitmap usage = BITMAP_ALLOC (&bmstack);
> +
> +  for (unsigned int i = 0; i < cls->members.length (); i++)
> +    bitmap_ior_into (usage, cls->members[i]->usage_index_bitmap);
> +
> +  EXECUTE_IF_SET_IN_BITMAP (usage, 0, i, bi)
> +  {
> +    if (dump_file && (dump_flags & TDF_DETAILS))
> +      fprintf (dump_file, "  processing congruece step for class: %u, index: %u\n",
> +	       cls->id, i);
> +
> +    do_congruence_step_for_index (cls, i);
> +
> +    if (splitter_class_removed)
> +      break;
> +  }
> +
> +  BITMAP_FREE (usage);
> +}
> +
> +/* Adds a newly created congruence class CLS to worklist.  */
> +
> +void
> +sem_item_optimizer::worklist_push (congruence_class *cls)
> +{
> +  congruence_class **slot = worklist.find_slot (cls, INSERT);
> +
> +  if (*slot)
> +    return;
> +
> +  *slot = cls;
> +}
> +
> +/* Pops a class from worklist. */
> +
> +congruence_class *
> +sem_item_optimizer::worklist_pop (void)
> +{
> +  gcc_assert (worklist.elements ());
> +
> +  congruence_class *cls = &(*worklist.begin ());
> +  worklist.remove_elt (cls);
> +
> +  return cls;
> +}
> +
> +/* Returns true if a congruence class CLS is presented in worklist.  */
> +
> +bool
> +sem_item_optimizer::worklist_contains (const congruence_class *cls)
> +{
> +  return worklist.find (cls);
> +}
> +
> +/* Removes given congruence class CLS from worklist.  */
> +
> +void
> +sem_item_optimizer::worklist_remove (const congruence_class *cls)
> +{
> +  worklist.remove_elt (cls);
> +}
> +
> +/* Iterative congruence reduction function.  */
> +
> +void
> +sem_item_optimizer::process_cong_reduction (void)
> +{
> +  for (hash_table<congruence_class_group_hash>::iterator it = classes.begin ();
> +       it != classes.end (); ++it)
> +    for (unsigned i = 0; i < (*it).classes.length (); i++)
> +      if ((*it).classes[i]->is_class_used ())
> +	worklist_push ((*it).classes[i]);
> +
> +  if (dump_file)
> +    fprintf (dump_file, "Worklist has been filled with: %lu\n",
> +	     worklist.elements ());
> +
> +  if (dump_file && (dump_flags & TDF_DETAILS))
> +    fprintf (dump_file, "Congruence class reduction\n");
> +
> +  while (worklist.elements ())
> +    {
> +      congruence_class *cls = worklist_pop ();
> +      do_congruence_step (cls);
> +    }
> +}
> +
> +/* Debug function prints all informations about congruence classes.  */
> +
> +void
> +sem_item_optimizer::dump_cong_classes (void)
> +{
> +  if (!dump_file)
> +    return;
> +
> +  fprintf (dump_file,
> +	   "Congruence classes: %u (unique hash values: %lu), with total: %u items\n",
> +	   classes_count, classes.elements(), items.length ());
> +
> +  /* Histogram calculation.  */
> +  unsigned int max_index = 0;
> +  unsigned int* histogram = XCNEWVEC (unsigned int, items.length ());
> +
> +  for (hash_table<congruence_class_group_hash>::iterator it = classes.begin ();
> +       it != classes.end (); ++it)
> +
> +    for (unsigned i = 0; i < (*it).classes.length (); i++)
> +      {
> +	unsigned int c = (*it).classes[i]->members.length ();
> +	histogram[c]++;
> +
> +	if (c > max_index)
> +	  max_index = c;
> +      }
> +
> +  fprintf (dump_file,
> +	   "Class size histogram [num of members]: number of classe number of classess\n");
> +
> +  for (unsigned int i = 0; i <= max_index; i++)
> +    if (histogram[i])
> +      fprintf (dump_file, "[%u]: %u classes\n", i, histogram[i]);
> +
> +  fprintf (dump_file, "\n\n");
> +
> +
> +  if (dump_flags & TDF_DETAILS)
> +    for (hash_table<congruence_class_group_hash>::iterator it = classes.begin ();
> +	 it != classes.end (); ++it)
> +      {
> +	fprintf (dump_file, "  group: with %u classes:\n", (*it).classes.length ());
> +
> +	for (unsigned i = 0; i < (*it).classes.length (); i++)
> +	  {
> +	    (*it).classes[i]->dump (dump_file, 4);
> +
> +	    if(i < (*it).classes.length () - 1)
> +	      fprintf (dump_file, " ");
> +	  }
> +      }
> +
> +  free (histogram);
> +}
> +
> +/* After reduction is done, we can declare all items in a group
> +   to be equal. PREV_CLASS_COUNT is start number of classes
> +   before reduction.  */
> +
> +void
> +sem_item_optimizer::merge_classes (unsigned int prev_class_count)
> +{
> +  unsigned int item_count = items.length ();
> +  unsigned int class_count = classes_count;
> +  unsigned int equal_items = item_count - class_count;
> +
> +  if (dump_file)
> +    {
> +      fprintf (dump_file, "\nItem count: %u\n", item_count);
> +      fprintf (dump_file, "Congruent classes before: %u, after: %u\n",
> +	       prev_class_count, class_count);
> +      fprintf (dump_file, "Average class size before: %.2f, after: %.2f\n",
> +	       1.0f * item_count / prev_class_count,
> +	       1.0f * item_count / class_count);
> +      fprintf (dump_file, "Equal symbols: %u\n", equal_items);
> +      fprintf (dump_file, "Fraction of visited symbols: %.2f%%\n\n",
> +	       100.0f * equal_items / item_count);
> +    }
> +
> +  for (hash_table<congruence_class_group_hash>::iterator it = classes.begin ();
> +       it != classes.end (); ++it)
> +    for (unsigned int i = 0; i < (*it).classes.length (); i++)
> +      {
> +	congruence_class *c = (*it).classes[i];
> +
> +	if (c->members.length () == 1)
> +	  continue;
> +
> +	gcc_assert (c->members.length ());
> +
> +	sem_item *source = c->members[0];
> +
> +	for (unsigned int j = 1; j < c->members.length (); j++)
> +	  {
> +	    sem_item *alias = c->members[j];
> +
> +	    if (dump_file)
> +	      {
> +		fprintf (dump_file, "Semantic equality hit:%s->%s\n",
> +			 source->name (), alias->name ());
> +		fprintf (dump_file, "Assembler symbol names:%s->%s\n",
> +			 source->asm_name (), alias->asm_name ());
> +	      }
> +
> +	    if (dump_file && (dump_flags & TDF_DETAILS))
> +	      {
> +		source->dump_to_file (dump_file);
> +		alias->dump_to_file (dump_file);
> +	      }
> +
> +	    source->merge (alias);
> +	  }
> +      }
> +}
> +
> +/* Dump function prints all class members to a FILE with an INDENT.  */
> +
> +void
> +congruence_class::dump (FILE *file, unsigned int indent) const
> +{
> +  FPRINTF_SPACES (file, indent, "class with id: %u, hash: %u, items: %u\n",
> +		  id, members[0]->get_hash (), members.length ());
> +
> +  FPUTS_SPACES (file, indent + 2, "");
> +  for (unsigned i = 0; i < members.length (); i++)
> +    fprintf (file, "%s(%p/%u)", members[i]->asm_name (), (void *) members[i]->decl,
> +	     members[i]->node->order);
> +
> +  fprintf (file, "\n");
> +}
> +
> +/* Returns true if there's a member that is used from another group.  */
> +
> +bool
> +congruence_class::is_class_used (void)
> +{
> +  for (unsigned int i = 0; i < members.length (); i++)
> +    if (members[i]->usages.length ())
> +      return true;
> +
> +  return false;
> +}
> +
> +/* Initialization and computation of symtab node hash, there data
> +   are propagated later on.  */
> +
> +static sem_item_optimizer optimizer;
> +
> +/* Generate pass summary for IPA ICF pass.  */
> +
> +static void
> +ipa_icf_generate_summary (void)
> +{
> +  optimizer.parse_funcs_and_vars ();
> +}
> +
> +/* Write pass summary for IPA ICF pass.  */
> +
> +static void
> +ipa_icf_write_summary (void)
> +{
> +  optimizer.write_summary ();
> +}
> +
> +/* Read pass summary for IPA ICF pass.  */
> +
> +static void
> +ipa_icf_read_summary (void)
> +{
> +  optimizer.read_summary ();
> +  optimizer.register_hooks ();
> +}
> +
> +/* Semantic equality exection function.  */
> +
> +static unsigned int
> +ipa_icf_driver (void)
> +{
> +  optimizer.execute ();
> +  optimizer.unregister_hooks ();
> +
> +  return 0;
> +}
> +
> +const pass_data pass_data_ipa_icf =
> +{
> +  IPA_PASS,		    /* type */
> +  "icf",		    /* name */
> +  OPTGROUP_IPA,             /* optinfo_flags */
> +  true,                     /* has_execute */
> +  TV_IPA_ICF,		    /* tv_id */
> +  0,                        /* properties_required */
> +  0,                        /* properties_provided */
> +  0,                        /* properties_destroyed */
> +  0,                        /* todo_flags_start */
> +  0,                        /* todo_flags_finish */
> +};
> +
> +class pass_ipa_icf : public ipa_opt_pass_d
> +{
> +public:
> +  pass_ipa_icf (gcc::context *ctxt)
> +    : ipa_opt_pass_d (pass_data_ipa_icf, ctxt,
> +		      ipa_icf_generate_summary, /* generate_summary */
> +		      ipa_icf_write_summary, /* write_summary */
> +		      ipa_icf_read_summary, /* read_summary */
> +		      NULL, /*
> +		      write_optimization_summary */
> +		      NULL, /*
> +		      read_optimization_summary */
> +		      NULL, /* stmt_fixup */
> +		      0, /* function_transform_todo_flags_start */
> +		      NULL, /* function_transform */
> +		      NULL) /* variable_transform */
> +  {}
> +
> +  /* opt_pass methods: */
> +  virtual bool gate (function *)
> +  {
> +    return flag_ipa_icf;
> +  }
> +
> +  virtual unsigned int execute (function *)
> +  {
> +    return ipa_icf_driver();
> +  }
> +}; // class pass_ipa_icf
> +
> +} // anon namespace
> +
> +ipa_opt_pass_d *
> +make_pass_ipa_icf (gcc::context *ctxt)
> +{
> +  return new pass_ipa_icf (ctxt);
> +}
> diff --git a/gcc/ipa-icf.h b/gcc/ipa-icf.h
> new file mode 100644
> index 0000000..dc5e971
> --- /dev/null
> +++ b/gcc/ipa-icf.h
> @@ -0,0 +1,687 @@
> +/* Interprocedural semantic function equality pass
> +   Copyright (C) 2014 Free Software Foundation, Inc.
> +
> +   Contributed by Jan Hubicka <hubicka@ucw.cz> and Martin Liska <mliska@suse.cz>
> +
> +This file is part of GCC.
> +
> +GCC 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, or (at your option) any later
> +version.
> +
> +GCC 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 GCC; see the file COPYING3.  If not see
> +<http://www.gnu.org/licenses/>.  */
> +
> +/* Prints string STRING to a FILE with a given number of SPACE_COUNT.  */
> +#define FPUTS_SPACES(file, space_count, string) \
> +  do \
> +  { \
> +    fprintf (file, "%*s" string, space_count, " "); \
> +  } \
> +  while (false);
> +
> +/* fprintf function wrapper that transforms given FORMAT to follow given
> +   number for SPACE_COUNT and call fprintf for a FILE.  */
> +#define FPRINTF_SPACES(file, space_count, format, ...) \
> +  fprintf (file, "%*s" format, space_count, " ", ##__VA_ARGS__);
> +
> +/* Prints a MESSAGE to dump_file if exists.  */
> +#define SE_DUMP_MESSAGE(message) \
> +  do \
> +  { \
> +    if (dump_file && (dump_flags & TDF_DETAILS)) \
> +      fprintf (dump_file, "  debug message: %s (%s:%u)\n", message, __func__, __LINE__); \
> +  } \
> +  while (false);
> +
> +/* Logs a MESSAGE to dump_file if exists and returns false.  */
> +#define SE_EXIT_FALSE_WITH_MSG(message) \
> +  do \
> +  { \
> +    if (dump_file && (dump_flags & TDF_DETAILS)) \
> +      fprintf (dump_file, "  false returned: '%s' (%s:%u)\n", message, __func__, __LINE__); \
> +    return false; \
> +  } \
> +  while (false);
> +
> +/* Return false and log that false value is returned.  */
> +#define SE_EXIT_FALSE() \
> +  SE_EXIT_FALSE_WITH_MSG("")
> +
> +/* Logs return value if RESULT is false.  */
> +#define SE_EXIT_DEBUG(result) \
> +  do \
> +  { \
> +    if (!(result) && dump_file && (dump_flags & TDF_DETAILS)) \
> +      fprintf (dump_file, "  false returned (%s:%u)\n", __func__, __LINE__); \
> +    return result; \
> +  } \
> +  while (false);
> +
> +/* Verbose logging function logging statements S1 and S2 of a CODE.  */
> +#define SE_DIFF_STATEMENT(s1, s2, code) \
> +  do \
> +  { \
> +    if (dump_file && (dump_flags & TDF_DETAILS)) \
> +      { \
> +        fprintf (dump_file, "  different statement for code: %s:\n", code); \
> +        print_gimple_stmt (dump_file, s1, 3, TDF_DETAILS); \
> +        print_gimple_stmt (dump_file, s2, 3, TDF_DETAILS); \
> +      } \
> +    return false; \
> +  } \
> +  while (false);
> +
> +namespace {
> +
> +/* Forward declaration for sem_func class.  */
> +class sem_item;
> +
> +/* A class aggregating all connections and semantic equivalents
> +   for a given pair of semantic function candidates.  */
> +class func_checker
> +{
> +public:
> +  func_checker ();
> +
> +  /* Initializes internal structures according to given number of
> +     source and target SSA names. The number of source names is SSA_SOURCE,
> +     respectively SSA_TARGET.  */
> +  void initialize (unsigned ssa_source, unsigned sss_target);
> +
> +  /* Memory release routine.  */
> +  void release (void);
> +
> +  /* Verifies that trees T1 and T2 do correspond.  */
> +  bool compare_ssa_name (tree t1, tree t2);
> +
> +  /* Verification function for edges E1 and E2.  */
> +  bool compare_edge (edge e1, edge e2);
> +
> +  /* Verification function for declaration trees T1 and T2 that
> +     come from functions FUNC1 and FUNC2.  */
> +  bool compare_decl (tree t1, tree t2, tree func1, tree func2);
> +
> +private:
> +  /* Vector mapping source SSA names to target ones.  */
> +  vec <int> source_ssa_names;
> +
> +  /* Vector mapping target SSA names to source ones.  */
> +  vec <int> target_ssa_names;
> +
> +  /* Source to target edge map.  */
> +  pointer_map <edge> *edge_map;
> +
> +  /* Source to target declaration map.  */
> +  pointer_map <tree> *decl_map;
> +
> +  /* Flag that indicates if the checker is initialize.  */
> +  bool initialized;
> +};
> +
> +/* Congruence class encompasses a collection of either functions or
> +   read-only variables. These items are considered to be equivalent
> +   if not proved the oposite.  */
> +class congruence_class
> +{
> +public:
> +  /* Congruence class constructor for a new class with _ID.  */
> +  congruence_class (unsigned int _id);
> +
> +  /* Dump function prints all class members to a FILE with an INDENT.  */
> +  void dump (FILE *file, unsigned int indent = 0) const;
> +
> +  /* Returns true if there's a member that is used from another group.  */
> +  bool is_class_used (void);
> +
> +  /* Vector of all group members.  */
> +  vec <sem_item *> members;
> +
> +  /* Global unique class identifier.  */
> +  unsigned int id;
> +};
> +
> +/* Semantic item type enum.  */
> +enum sem_item_type
> +{
> +  FUNC,
> +  VAR
> +};
> +
> +/* Semantic item usage pair.  */
> +class sem_usage_pair
> +{
> +public:
> +  /* Constructor for key value pair, where _ITEM is key and _INDEX is a target.  */
> +  sem_usage_pair (sem_item *_item, unsigned int _index);
> +
> +  /* Target semantic item where an item is used.  */
> +  sem_item *item;
> +
> +  /* Index of usage of such an item.  */
> +  unsigned int index;
> +};
> +
> +/* Basic block struct for sematic equality pass.  */
> +typedef struct sem_bb
> +{
> +  /* Basic block the structure belongs to.  */
> +  basic_block bb;
> +
> +  /* Number of non-debug statements in the basic block.  */
> +  unsigned nondbg_stmt_count;
> +
> +  /* Number of edges connected to the block.  */
> +  unsigned edge_count;
> +} sem_bb_t;
> +
> +/* Semantic item is a base class that encapsulates all shared functionality
> +   for both semantic function and variable items.  */
> +class sem_item
> +{
> +public:
> +  /* Semantic item constructor for a node of _TYPE, where STACK is used
> +     for bitmap memory allocation.  */
> +  sem_item (enum sem_item_type _type, bitmap_obstack *stack);
> +
> +  /* Semantic item constructor for a node of _TYPE, where STACK is used
> +     for bitmap memory allocation. The item is based on symtab node _NODE
> +     with computed _HASH.  */
> +  sem_item (enum sem_item_type _type, struct symtab_node *_node, hashval_t _hash,
> +	    bitmap_obstack *stack);
> +
> +  virtual ~sem_item ();
> +
> +  /* Dump function for debugging purpose.  */
> +  DEBUG_FUNCTION void dump (void);
> +
> +  /* Initialize semantic item by info reachable during LTO WPA phase.  */
> +  virtual void init_wpa (void) = 0;
> +
> +  /* Semantic item initialization function.  */
> +  virtual void init (void) = 0;
> +
> +  /* Gets symbol name of the item.  */
> +  virtual const char *name (void) = 0;
> +
> +  /* Gets assembler name of the item.  */
> +  virtual const char *asm_name (void) = 0;
> +
> +  /* Initializes references to other semantic functions/variables.  */
> +  virtual void init_refs () = 0;
> +
> +  /* Fast equality function based on knowledge known in WPA.  */
> +  virtual bool equals_wpa (sem_item *item) = 0;
> +
> +  /* Returns true if the item equals to ITEM given as arguemnt.  */
> +  virtual bool equals (sem_item *item) = 0;
> +
> +  /* References independent hash function.  */
> +  virtual hashval_t get_hash (void) = 0;
> +
> +  /* Merges instance with an ALIAS_ITEM, where alias, thunk or redirection can
> +     be applied.  */
> +  virtual bool merge (sem_item *alias_item) = 0;
> +
> +  /* Dump symbol to FILE.  */
> +  virtual void dump_to_file (FILE *file) = 0;
> +
> +  /* Compare two types if are same aliases in case of strict aliasing
> +     is enabled.  */
> +  static bool compare_for_aliasing (tree t1, tree t2);
> +
> +  /* Item type.  */
> +  enum sem_item_type type;
> +
> +  /* Global unique function index.  */
> +  unsigned int index;
> +
> +  /* Symtab node.  */
> +  struct symtab_node *node;
> +
> +  /* Declaration tree node.  */
> +  tree decl;
> +
> +  /* Semantic references used that generate congruence groups.  */
> +  vec <sem_item *> refs;
> +
> +  /* Pointer to a congruence class the item belongs to.  */
> +  congruence_class *cls;
> +
> +  /* Index of the item in a class belonging to.  */
> +  unsigned int index_in_class;
> +
> +  /* List of semantic items where the instance is used.  */
> +  vec <sem_usage_pair *> usages;
> +
> +  /* A bitmap with indices of all classes referencing this item.  */
> +  bitmap usage_index_bitmap;
> +
> +  /* List of tree references (either FUNC_DECL or VAR_DECL).  */
> +  vec <tree> tree_refs;
> +
> +  /* A set with tree references (either FUNC_DECL or VAR_DECL).  */
> +  pointer_set_t *tree_refs_set;
> +
> +protected:
> +  /* Cached, once calculated hash for the item.  */
> +  hashval_t hash;
> +
> +private:
> +  /* Initialize internal data structures. Bitmap STACK is used for
> +     bitmap memory allocation process.  */
> +  void setup (bitmap_obstack *stack);
> +}; // class sem_item
> +
> +class sem_function: public sem_item
> +{
> +public:
> +  /* Semantic function constructor that uses STACK as bitmap memory stack.  */
> +  sem_function (bitmap_obstack *stack);
> +
> +  /*  Constructor based on callgraph node _NODE with computed hash _HASH.
> +      Bitmap STACK is used for memory allocation.  */
> +  sem_function (cgraph_node *_node, hashval_t _hash, bitmap_obstack *stack);
> +
> +  ~sem_function ();
> +
> +  virtual void init_wpa (void);
> +  virtual void init (void);
> +  virtual const char *name (void);
> +  virtual const char *asm_name (void);
> +  virtual hashval_t get_hash (void);
> +  virtual bool equals_wpa (sem_item *item);
> +  virtual bool equals (sem_item *item);
> +  virtual void init_refs ();
> +  virtual bool merge (sem_item *alias_item);
> +  virtual void dump_to_file (FILE *file);
> +
> +  /* Parses function arguments and result type.  */
> +  void parse_tree_args (void);
> +
> +  /* Returns cgraph_node.  */
> +  struct cgraph_node *get_node (void);
> +
> +  /* For a given call graph NODE, the function constructs new
> +     semantic function item.  */
> +  static sem_function *parse (struct cgraph_node *node, bitmap_obstack *stack);
> +
> +  /* Exception handling region tree.  */
> +  eh_region region_tree;
> +
> +  /* Result type tree node.  */
> +  tree result_type;
> +
> +  /* Array of argument tree types.  */
> +  vec <tree> arg_types;
> +
> +  /* Number of function arguments.  */
> +  unsigned int arg_count;
> +
> +  /* Basic block count.  */
> +  unsigned int bb_count;
> +
> +  /* Total amount of edges in the function.  */
> +  unsigned int edge_count;
> +
> +  /* Array of sizes of all basic blocks.  */
> +  unsigned int *bb_sizes;
> +
> +  /* Control flow graph checksum.  */
> +  hashval_t cfg_checksum;
> +
> +  /* GIMPLE codes hash value.  */
> +  hashval_t gcode_hash;
> +
> +  /* Total number of SSA names used in the function.  */
> +  unsigned ssa_names_size;
> +
> +  /* Array of structures for all basic blocks.  */
> +  sem_bb_t **bb_sorted;
> +
> +private:
> +  /* Calculates hash value based on a BASIC_BLOCK.  */
> +  hashval_t get_bb_hash (const sem_bb_t *basic_block);
> +
> +  /* Basic block equivalence comparison function that returns true if
> +     basic blocks BB1 and BB2 (from functions FUNC1 and FUNC2) correspond.  */
> +  bool compare_bb (sem_bb_t *bb1, sem_bb_t *bb2, tree func1, tree func2);
> +
> +  /* For given basic blocks BB1 and BB2 (from functions FUNC1 and FUNC),
> +     true value is returned if phi nodes are sematically
> +     equivalent in these blocks .  */
> +  bool compare_phi_node (basic_block bb1, basic_block bb2, tree func1,
> +			 tree func2);
> +
> +  /* For given basic blocks BB1 and BB2 (from functions FUNC1 and FUNC),
> +     true value is returned if exception handling regions are equivalent
> +     in these blocks.  */
> +  bool compare_eh_region (eh_region r1, eh_region r2, tree func1, tree func2);
> +
> +  /* Iterates GSI statement iterator to the next non-debug statement.  */
> +  void gsi_next_nondebug_stmt (gimple_stmt_iterator &gsi);
> +
> +  /* Iterates GSI statement iterator to the next non-virtual statement.  */
> +  void gsi_next_nonvirtual_phi (gimple_stmt_iterator &it);
> +
> +  /* Verifies that trees T1 and T2 do correspond.  */
> +  bool compare_function_decl (tree t1, tree t2);
> +
> +  /* Verifies that trees T1 and T2 do correspond.  */
> +  bool compare_variable_decl (tree t1, tree t2, tree func1, tree func2);
> +
> +  /* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
> +     call statements are semantically equivalent.  */
> +  bool compare_gimple_call (gimple s1, gimple s2,
> +			    tree func1, tree func2);
> +
> +  /* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
> +     assignment statements are semantically equivalent.  */
> +  bool compare_gimple_assign (gimple s1, gimple s2, tree func1, tree func2);
> +
> +  /* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
> +     condition statements are semantically equivalent.  */
> +  bool compare_gimple_cond (gimple s1, gimple s2, tree func1, tree func2);
> +
> +  /* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
> +     label statements are semantically equivalent.  */
> +  bool compare_gimple_label (gimple s1, gimple s2, tree func1, tree func2);
> +
> +  /* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
> +     switch statements are semantically equivalent.  */
> +  bool compare_gimple_switch (gimple s1, gimple s2, tree func1, tree func2);
> +
> +  /* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
> +     return statements are semantically equivalent.  */
> +  bool compare_gimple_return (gimple s1, gimple s2, tree func1, tree func2);
> +
> +  /* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
> +     goto statements are semantically equivalent.  */
> +  bool compare_gimple_goto (gimple s1, gimple s2, tree func1, tree func2);
> +
> +  /* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
> +     resx statements are semantically equivalent.  */
> +  bool compare_gimple_resx (gimple s1, gimple s2);
> +
> +  /* Verifies for given GIMPLEs S1 and S2 that ASM statements are equivalent.
> +     For the beginning, the pass only supports equality for
> +     '__asm__ __volatile__ ("", "", "", "memory")'.  */
> +  bool compare_gimple_asm (gimple s1, gimple s2);
> +
> +  /* Verifies that tree labels T1 and T2 correspond in FUNC1 and FUNC2.  */
> +  bool compare_tree_ssa_label (tree t1, tree t2, tree func1, tree func2);
> +
> +  /* Function compares two operands T1 and T2 and returns true if these
> +     two trees from FUNC1 (respectively FUNC2) are semantically equivalent.  */
> +  bool compare_operand (tree t1, tree t2, tree func1, tree func2);
> +
> +  /* If T1 and T2 are SSA names, dictionary comparison is processed. Otherwise,
> +     declaration comparasion is executed.  */
> +  bool compare_ssa_name (tree t1, tree t2, tree func1, tree func2);
> +
> +  /* Basic blocks dictionary BB_DICT returns true if SOURCE index BB
> +     corresponds to TARGET.  */
> +  bool bb_dict_test (int* bb_dict, int source, int target);
> +
> +  /* Iterates all tree types in T1 and T2 and returns true if all types
> +     are compatible.  */
> +  bool compare_type_list (tree t1, tree t2);
> +
> +  /* Processes function equality comparison.  */
> +  bool equals_private (sem_item *item);
> +
> +  /* Initializes references to another sem_item for gimple STMT of type assign.  */
> +  void init_refs_for_assign (gimple stmt);
> +
> +  /* Initializes references to another sem_item for tree T.  */
> +  void init_refs_for_tree (tree t);
> +
> +  /* Returns true if tree T can be compared as a handled component.  */
> +  static bool icf_handled_component_p (tree t);
> +
> +  /* Function checker stores binding between functions.   */
> +  func_checker checker;
> +
> +  /* COMPARED_FUNC is a function that we compare to.  */
> +  sem_function *compared_func;
> +}; // class sem_function
> +
> +class sem_variable: public sem_item
> +{
> +public:
> +  /* Semantic variable constructor that uses STACK as bitmap memory stack.  */
> +  sem_variable (bitmap_obstack *stack);
> +
> +  /*  Constructor based on callgraph node _NODE with computed hash _HASH.
> +      Bitmap STACK is used for memory allocation.  */
> +
> +  sem_variable (varpool_node *_node, hashval_t _hash, bitmap_obstack *stack);
> +
> +  virtual void init_wpa (void);
> +  virtual void init (void);
> +  virtual const char *name (void);
> +  virtual const char *asm_name (void);
> +  virtual void init_refs ();
> +  virtual hashval_t get_hash (void);
> +  virtual bool merge (sem_item *alias_item);
> +  virtual void dump_to_file (FILE *file);
> +  virtual bool equals_wpa (sem_item *item);
> +  virtual bool equals (sem_item *item);
> +
> +  /* Returns varpool_node.  */
> +  struct varpool_node *get_node (void);
> +
> +  /* Parser function that visits a varpool NODE.  */
> +  static sem_variable *parse (struct varpool_node *node, bitmap_obstack *stack);
> +
> +  /* Variable constructor.  */
> +  tree ctor;
> +
> +private:
> +  /* Iterates though a constructor and identifies tree references
> +     we are interested in semantic function equality.  */
> +  void parse_tree_refs (tree t);
> +
> +  /* Compares trees T1 and T2 for semantic equality.  */
> +  static bool equals (tree t1, tree t2);
> +
> +}; // class sem_variable
> +
> +class sem_item_optimizer;
> +
> +/* Congruence class set structure.  */
> +struct congruence_class_var_hash: typed_noop_remove <congruence_class>
> +{
> +  typedef congruence_class value_type;
> +  typedef congruence_class compare_type;
> +  static inline hashval_t hash (const value_type *);
> +  static inline int equal (const value_type *, const compare_type *);
> +};
> +
> +typedef struct congruence_class_group
> +{
> +  hashval_t hash;
> +  vec <congruence_class *> classes;
> +} congruence_class_group_t;
> +
> +/* Congruence class set structure.  */
> +struct congruence_class_group_hash: typed_noop_remove <congruence_class_group_t>
> +{
> +  typedef congruence_class_group value_type;
> +  typedef congruence_class_group compare_type;
> +  static inline hashval_t hash (const value_type *);
> +  static inline int equal (const value_type *, const compare_type *);
> +};
> +
> +struct traverse_split_pair
> +{
> +  sem_item_optimizer *optimizer;
> +  class congruence_class *cls;
> +};
> +
> +/* Semantic item optimizer includes all top-level logic
> +   related to semantic equality comparison.  */
> +class sem_item_optimizer
> +{
> +public:
> +  sem_item_optimizer ();
> +  ~sem_item_optimizer ();
> +
> +  /* Function responsible for visiting all potential functions and
> +     read-only variables that can be merged.  */
> +  void parse_funcs_and_vars (void);
> +
> +  /* Optimizer entry point.  */
> +  void execute (void);
> +
> +  /* Dump function. */
> +  void dump (void);
> +
> +  /* Verify congruence classes if checking is enabled.  */
> +  void verify_classes (void);
> +
> +  /* Write IPA ICF summary for symbols.  */
> +  void write_summary (void);
> +
> +  /* Read IPA IPA ICF summary for symbols.  */
> +  void read_summary (void);
> +
> +  /* Callgraph removal hook called for a NODE with a custom DATA.  */
> +  static void cgraph_removal_hook (struct cgraph_node *node, void *data);
> +
> +  /* Varpool removal hook called for a NODE with a custom DATA.  */
> +  static void varpool_removal_hook (struct varpool_node *node, void *data);
> +
> +  /* Worklist of congruence classes that can potentially
> +     refine classes of congruence.  */
> +  hash_table <congruence_class_var_hash> worklist;
> +
> +  /* Remove symtab NODE triggered by symtab removal hooks.  */
> +  void remove_symtab_node (struct symtab_node *node);
> +
> +  /* Register callgraph and varpool hooks.  */
> +  void register_hooks (void);
> +
> +  /* Unregister callgraph and varpool hooks.  */
> +  void unregister_hooks (void);
> +
> +  /* Adds a CLS to hashtable associated by hash value.  */
> +  void add_class (congruence_class *cls);
> +
> +  /* Gets a congruence class group based on given HASH value.  */
> +  congruence_class_group_t *get_group_by_hash (hashval_t hash);
> +
> +private:
> +
> +  /* Congruence classes are built by hash value.  */
> +  void build_hash_based_classes (void);
> +
> +  /* Semantic items in classes having more than one element and initialized.
> +     In case of WPA, we load function body.  */
> +  void parse_nonsingleton_classes (void);
> +
> +  /* Equality function for semantic items is used to subdivide existing
> +     classes. If IN_WPA, fast equality function is invoked.  */
> +  void subdivide_classes_by_equality (bool in_wpa = false);
> +
> +  /* Debug function prints all informations about congruence classes.  */
> +  void dump_cong_classes (void);
> +
> +  /* Iterative congruence reduction function.  */
> +  void process_cong_reduction (void);
> +
> +  /* After reduction is done, we can declare all items in a group
> +     to be equal. PREV_CLASS_COUNT is start number of classes
> +     before reduction.  */
> +  void merge_classes (unsigned int prev_class_count);
> +
> +  /* Adds a newly created congruence class CLS to worklist.  */
> +  void worklist_push (congruence_class *cls);
> +
> +  /* Pops a class from worklist. */
> +  congruence_class *worklist_pop ();
> +
> +  /* Returns true if a congruence class CLS is presented in worklist.  */
> +  bool worklist_contains (const congruence_class *cls);
> +
> +  /* Removes given congruence class CLS from worklist.  */
> +  void worklist_remove (const congruence_class *cls);
> +
> +  /* Every usage of a congruence class CLS is a candidate that can split the
> +     collection of classes. Bitmap stack BMSTACK is used for bitmap
> +     allocation.  */
> +  void do_congruence_step (congruence_class *cls);
> +
> +  /* Tests if a class CLS used as INDEXth splits any congruence classes.
> +     Bitmap stack BMSTACK is used for bitmap allocation.  */
> +  void do_congruence_step_for_index (congruence_class *cls, unsigned int index);
> +
> +  /* Makes pairing between a congruence class CLS and semantic ITEM.  */
> +  static void add_item_to_class (congruence_class *cls, sem_item *item);
> +
> +  /* Disposes split map traverse function. CLS_PTR is pointer to congruence
> +     class, BSLOT is bitmap slot we want to release. DATA is mandatory,
> +     but unused argument.  */
> +  static bool release_split_map (const void *cls_ptr, bitmap *bslot, void *data);
> +
> +  /* Process split operation for a class given as pointer CLS_PTR,
> +     where bitmap B splits congruence class members. DATA is used
> +     as argument of split pair.  */
> +  static bool traverse_congruence_split (const void *cls_ptr, bitmap *b,
> +					 void *data);
> +
> +  /* Reads a section from LTO stream file FILE_DATA. Input block for DATA
> +     contains LEN bytes.  */
> +  void read_section (struct lto_file_decl_data *file_data, const char *data,
> +		     size_t len);
> +
> +  /* Removes all callgraph and varpool nodes that are marked by symtab
> +     as deleted.  */
> +  void filter_removed_items (void);
> +
> +  /* Vector of semantic items.  */
> +  vec <sem_item *> items;
> +
> +  /* A set containing all items removed by hooks.  */
> +  pointer_set_t *removed_items_set;
> +
> +  /* Hashtable of congruence classes */
> +  hash_table <congruence_class_group_hash> classes;
> +
> +  /* Count of congruence classes.  */
> +  unsigned int classes_count;
> +
> +  /* Map data structure maps trees to semantic items.  */
> +  pointer_map <sem_item *> decl_map;
> +
> +  /* Map data structure maps symtab nodes to semantic items.  */
> +  pointer_map <sem_item *> symtab_node_map;
> +
> +  /* For all congruence classes, we indicate partial mapping
> +     during reduction.  */
> +  pointer_map <bitmap> *split_map;
> +
> +  /* Set to true if a splitter class is removed.  */
> +  bool splitter_class_removed;
> +
> +  /* Global unique class id counter.  */
> +  static unsigned int class_id;
> +
> +  /* Callgraph node removal hook holder.  */
> +  struct cgraph_node_hook_list *cgraph_node_hooks;
> +
> +  /* Varpool node removal hook holder.  */
> +  struct varpool_node_hook_list *varpool_node_hooks;
> +
> +  /* Bitmap stack.  */
> +  bitmap_obstack bmstack;
> +}; // class sem_item_optimizer
> +
> +}
> diff --git a/gcc/lto-section-in.c b/gcc/lto-section-in.c
> index d887763..f9587cf 100644
> --- a/gcc/lto-section-in.c
> +++ b/gcc/lto-section-in.c
> @@ -60,7 +60,8 @@ const char *lto_section_name[LTO_N_SECTION_TYPES] =
>    "opts",
>    "cgraphopt",
>    "inline",
> -  "ipcp_trans"
> +  "ipcp_trans",
> +  "icf"
>  };
>  
>  
> diff --git a/gcc/lto-streamer.h b/gcc/lto-streamer.h
> index 521d78d..3207d08 100644
> --- a/gcc/lto-streamer.h
> +++ b/gcc/lto-streamer.h
> @@ -247,6 +247,7 @@ enum lto_section_type
>    LTO_section_cgraph_opt_sum,
>    LTO_section_inline_summary,
>    LTO_section_ipcp_transform,
> +  LTO_section_ipa_icf,
>    LTO_N_SECTION_TYPES		/* Must be last.  */
>  };
>  
> diff --git a/gcc/opts.c b/gcc/opts.c
> index 2b1280a..b5a58a5 100644
> --- a/gcc/opts.c
> +++ b/gcc/opts.c
> @@ -496,6 +496,7 @@ static const struct default_options default_options_table[] =
>      { OPT_LEVELS_2_PLUS, OPT_fvect_cost_model_, NULL, VECT_COST_MODEL_CHEAP },
>      { OPT_LEVELS_2_PLUS_SPEED_ONLY, OPT_foptimize_strlen, NULL, 1 },
>      { OPT_LEVELS_2_PLUS, OPT_fhoist_adjacent_loads, NULL, 1 },
> +    { OPT_LEVELS_2_PLUS, OPT_fipa_icf, NULL, 1 },
>      { OPT_LEVELS_2_PLUS, OPT_fisolate_erroneous_paths_dereference, NULL, 1 },
>      { OPT_LEVELS_2_PLUS, OPT_fuse_caller_save, NULL, 1 },
>  
> diff --git a/gcc/passes.def b/gcc/passes.def
> index f9e0b2a..e629a1d 100644
> --- a/gcc/passes.def
> +++ b/gcc/passes.def
> @@ -105,6 +105,7 @@ along with GCC; see the file COPYING3.  If not see
>    NEXT_PASS (pass_ipa_whole_program_visibility);
>    NEXT_PASS (pass_ipa_profile);
>    NEXT_PASS (pass_ipa_devirt);
> +  NEXT_PASS (pass_ipa_icf);
>    NEXT_PASS (pass_ipa_cp);
>    NEXT_PASS (pass_ipa_cdtor_merge);
>    NEXT_PASS (pass_ipa_inline);
> diff --git a/gcc/timevar.def b/gcc/timevar.def
> index cbb64d5..c1d09eb 100644
> --- a/gcc/timevar.def
> +++ b/gcc/timevar.def
> @@ -89,6 +89,7 @@ DEFTIMEVAR (TV_WHOPR_LTRANS          , "whopr ltrans")
>  DEFTIMEVAR (TV_IPA_REFERENCE         , "ipa reference")
>  DEFTIMEVAR (TV_IPA_PROFILE           , "ipa profile")
>  DEFTIMEVAR (TV_IPA_PURE_CONST        , "ipa pure const")
> +DEFTIMEVAR (TV_IPA_ICF		     , "ipa icf")
>  DEFTIMEVAR (TV_IPA_PTA               , "ipa points-to")
>  DEFTIMEVAR (TV_IPA_SRA               , "ipa SRA")
>  DEFTIMEVAR (TV_IPA_FREE_LANG_DATA    , "ipa free lang data")
> diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h
> index 3888bb6..d55d7b8 100644
> --- a/gcc/tree-pass.h
> +++ b/gcc/tree-pass.h
> @@ -464,6 +464,7 @@ extern simple_ipa_opt_pass *make_pass_ipa_free_lang_data (gcc::context *ctxt);
>  extern simple_ipa_opt_pass *make_pass_ipa_free_inline_summary (gcc::context
>  							       *ctxt);
>  extern ipa_opt_pass_d *make_pass_ipa_cp (gcc::context *ctxt);
> +extern ipa_opt_pass_d *make_pass_ipa_icf (gcc::context *ctxt);
>  extern ipa_opt_pass_d *make_pass_ipa_devirt (gcc::context *ctxt);
>  extern ipa_opt_pass_d *make_pass_ipa_reference (gcc::context *ctxt);
>  extern ipa_opt_pass_d *make_pass_ipa_pure_const (gcc::context *ctxt);
> -- 
> 1.8.4.5
> 
> 

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 3/5] IPA ICF pass
  2014-06-16 10:07 ` [PATCH 3/5] IPA ICF pass mliska
  2014-06-20  7:32   ` Trevor Saunders
@ 2014-06-24 20:31   ` Jeff Law
  2014-06-26 16:02     ` Martin Liška
  2014-06-26 18:46     ` Jan Hubicka
  1 sibling, 2 replies; 70+ messages in thread
From: Jeff Law @ 2014-06-24 20:31 UTC (permalink / raw)
  To: mliska, gcc-patches; +Cc: hubicka

On 06/13/14 04:44, mliska wrote:
> Hello,
>     this is core of IPA ICF patchset. It adds new pass and registers all needed stuff related to a newly introduced interprocedural optimization.
>
> Algorithm description:
>    In LGEN, we visit all read-only variables and functions. For each symbol, a hash value based on e.g. number of arguments,
>    number of BB, GIMPLE CODES is computed (similar hash is computed for read-only variables). This kind of information is streamed
>    for LTO.
>
>    In WPA, we build congruence classes for all symbols having a same hash value. For functions, these classes are subdivided in WPA by argument type comparison. Each reference (a call or a variable reference) to another semantic item candidate is marked and stored for further congruence class reduction (similar algorithm as Value Numbering:  www.cs.ucr.edu/~gupta/teaching/553-07/Papers/value.pdf).
>
>    For every congruence class of functions with more than one semantic function, we load function body. Having this information, we can
>    process complete semantic function equality and subdivide such congruence class. Read-only variable class members are also deeply compared.
>
>    After that, we process Value numbering algorithm to do a final subdivision. Finally, all items belonging to a congruence class with more than one
>    item are merged.
>
> Martin
>
> Changelog:
>
> 2014-06-13  Martin Liska  <mliska@suse.cz>
> 	    Jan Hubicka  <hubicka@ucw.cz>
>
> 	* Makefile.in: New pass object file added.
> 	* common.opt: New -fipa-icf flag introduced.
> 	* doc/invoke.texi: Documentation enhanced for the pass.
> 	* lto-section-in.c: New LTO section for a summary created by IPA-ICF.
> 	* lto-streamer.h: New section name introduced.
> 	* opts.c: Optimization is added to -O2.
> 	* passes.def: New pass added.
> 	* timevar.def: New time var for IPA-ICF.
> 	* tree-pass.h: Pass construction function.
> 	* ipa-icf.h: New pass header file added.
> 	* ipa-icf.c: New pass source file added.
You'll note many of my comments are "do you need to ...".  You may in 
fact be handling that stuff correctly, they're just things I'd like you 
to verify are properly handled.  If they're properly handled just say so :-)

At a high level, I think this needs to be broken down a bit more.  We've 
got two high level concepts in ipa-icf.  One is all the equivalence 
testing the other is using that information for the icf optimization.

Splitting out the equivalence testing seems like a good thing to do as 
there's other contexts where it would be useful.

Overall I think you're on the right path and we just need to iterate a 
bit on this part of the patchset.

>
> @@ -7862,6 +7863,14 @@ it may significantly increase code size
>   (see @option{--param ipcp-unit-growth=@var{value}}).
>   This flag is enabled by default at @option{-O3}.
>
> +@item -fipa-icf
> +@opindex fipa-icf
> +Perform Identical Code Folding for functions and read-only variables.
> +Behavior is similar to Gold Linker ICF optimization. Symbols proved
> +as semantically equivalent are redirected to corresponding symbol. The pass
> +sensitively decides for usage of alias, thunk or local redirection.
> +This flag is enabled by default at @option{-O2}.
So you've added this at -O2, what is the general compile-time impact? 
Would it make more sense to instead have it be part of -O3, particularly 
since ICF is rarely going to improve performance (sans icache issues).


> +
> +/* Interprocedural Identical Code Folding for functions and
> +   read-only variables.
> +
> +   The goal of this transformation is to discover functions and read-only
> +   variables which do have exactly the same semantics.
> +
> +   In case of functions,
> +   we could either create a virtual clone or do a simple function wrapper
> +   that will call equivalent function. If the function is just locally visible,
> +   all function calls can be redirected. For read-only variables, we create
> +   aliases if possible.
> +
> +   Optimization pass arranges as follows:
> +   1) All functions and read-only variables are visited and internal
> +      data structure, either sem_function or sem_variables is created.
> +   2) For every symbol from the previoues step, VAR_DECL and FUNCTION_DECL are
> +      saved and matched to corresponding sem_items.
s/previoues/previous/

> +   3) These declaration are ignored for equality check and are solved
> +      by Value Numbering algorithm published by Alpert, Zadeck in 1992.
> +   4) We compute hash value for each symbol.
> +   5) Congruence classes are created based on hash value. If hash value are
> +      equal, equals function is called and symbols are deeply compared.
> +      We must prove that all SSA names, declarations and other items
> +      correspond.
> +   6) Value Numbering is executed for these classes. At the end of the process
> +      all symbol members in remaining classes can be mrged.
s/mrged/merged.



> +   7) Merge operation creates alias in case of read-only variables. For
> +      callgraph node, we must decide if we can redirect local calls,
> +      create an alias or a thunk.
Presumably that's the order in which we try to resolve identical 
functions (first by redirection if it's local, then an alias, then a thunk)?

Is the code conditionalized so that it works properly on targets where 
we can't create aliases?  Do we have any such targets that can be easily 
tested these days?  Has this been tested on anything other than x86-64 
linux?  AIX immediately comes to mind as an interesting testing target.



>
> +
> +namespace {
> +
> +func_checker::func_checker (): initialized (false)
> +{
> +}
> +
> +/* Itializes internal structures according to given number of
s/Itializes/Initializes/


> +   source and target SSA names. The number of source names is SSA_SOURCE,
> +   respectively SSA_TARGET.  */
> +
> +void
> +func_checker::initialize (unsigned ssa_source, unsigned ssa_target)
> +{
> +  release ();
> +  initialized = true;
> +
> +  source_ssa_names.create (ssa_source);
> +  target_ssa_names.create (ssa_target);
> +
> +  for (unsigned int i = 0; i < ssa_source; i++)
> +    source_ssa_names.safe_push (-1);
> +
> +  for (unsigned int i = 0; i < ssa_target; i++)
> +    target_ssa_names.safe_push (-1);
> +
> +  edge_map = new pointer_map <edge> ();
> +
> +  decl_map = new pointer_map <tree> ();
> +}
> +
> +/* Memory release routine.  */
> +
> +void
> +func_checker::release (void)
> +{
> +  if (!initialized)
> +    return;
> +
> +  delete edge_map;
> +  delete decl_map;
> +  source_ssa_names.release();
> +  target_ssa_names.release();
> +}
So I'm a big fan of RAII style of writing code.  While I primarily like 
it for locking, I also find it's a good model for memory management. 
Is there any reason these two functions aren't a suitable ctor/dtor?


> +
> +/* Verifies that trees T1 and T2 do correspond.  */
> +
> +bool
> +func_checker::compare_ssa_name (tree t1, tree t2)
> +{
> +  unsigned i1 = SSA_NAME_VERSION (t1);
> +  unsigned i2 = SSA_NAME_VERSION (t2);
> +
> +  if (source_ssa_names[i1] == -1)
> +    source_ssa_names[i1] = i2;
> +  else if (source_ssa_names[i1] != (int) i2)
> +    return false;
> +
> +  if(target_ssa_names[i2] == -1)
> +    target_ssa_names[i2] = i1;
> +  else if (target_ssa_names[i2] != (int) i1)
> +    return false;
> +
> +  return true;
> +}
Isn't this really checking for equivalence? "do correspond" seems 
awkward here.

> +
> +/* Verification function for edges E1 and E2.  */
> +
> +bool
> +func_checker::compare_edge (edge e1, edge e2)
> +{
> +  if (e1->flags != e2->flags)
> +    return false;
Presumably there's no flags we can safely ignore.  So absolute equality 
seems reasonable here.


> +/* Congruence class constructor for a new class with _ID.  */
> +
> +congruence_class::congruence_class (unsigned int _id): id(_id)
> +{
> +  members.create (2);
> +}
Is there a dtor which releases this object?
> +
> +void
> +sem_item::setup (bitmap_obstack *stack)
> +{
> +  gcc_checking_assert (node);
> +
> +  refs.create (0);
> +  tree_refs.create (0);
> +  usages.create (0);
> +  tree_refs_set = pointer_set_create ();
> +  usage_index_bitmap = BITMAP_ALLOC (stack);
> +}
> +
> +sem_item::~sem_item ()
> +{
> +  if (tree_refs_set)
> +    pointer_set_destroy (tree_refs_set);
> +
> +  for (unsigned i = 0; i < usages.length (); i++)
> +    delete usages[i];
> +}
Do you need to release refs, tree_refs or the bitmap?

> +
> +/* Compare two types if are same aliases in case of strict aliasing
> +   is enabled.  */
> +bool
> +sem_item::compare_for_aliasing (tree t1, tree t2)
> +{
> +  if (flag_strict_aliasing)
> +    {
> +      alias_set_type s1 = get_deref_alias_set (TREE_TYPE (t1));
> +      alias_set_type s2 = get_deref_alias_set (TREE_TYPE (t2));
> +
> +      return s1 == s2;
> +    }
> +
> +  return true;
> +}
Is returning TRUE really the conservatively correct thing to do in the 
absence of aliasing information?  Isn't that case really "I don't know" 
in which case the proper return value is FALSE?





> +
> +/* Semantic function constructor that uses STACK as bitmap memory stack.  */
> +
> +sem_function::sem_function (bitmap_obstack *stack): sem_item (FUNC, stack),
> +  compared_func (NULL)
> +{
> +  arg_types.create (0);
Does this need to be released?

> +
> +/* Gets symbol name of the item.  */
> +
> +const char *
> +sem_function::name (void)
> +{
> +  return node->name ();
> +}
> +
> +/* Gets assembler name of the item.  */
> +
> +const char *
> +sem_function::asm_name (void)
> +{
> +  return node->asm_name ();
> +}
Are these trivial enough that they should be inlined in the class 
definition?  As much as I dislike that style of programming, these seem 
like classic candidates unless I'm missing something.


> +/* References independent hash function.  */
> +
> +hashval_t
> +sem_function::get_hash (void)
> +{
> +  if(!hash)
> +    {
> +      hash = 177454; /* Random number for function type.  */
> +
> +      hash = iterative_hash_object (arg_count, hash);
> +      hash = iterative_hash_object (bb_count, hash);
> +      hash = iterative_hash_object (edge_count, hash);
> +      hash = iterative_hash_object (cfg_checksum, hash);
Does CFG_CHECKSUM encompass the bb/edge counts?

+}
> +
> +/* Fast equality function based on knowledge known in WPA.  */
> +
> +bool
> +sem_function::equals_wpa (sem_item *item)
> +{
> +  if (item->type != FUNC)
> +    return false;
Can this ever happen?  Either remove to turn into an assert.

> +/* Processes function equality comparison.  */
> +
> +bool
> +sem_function::equals_private (sem_item *item)
> +{
> +  if (item->type != FUNC)
> +    return false;
> +
> +  basic_block bb1, bb2;
> +  edge e1, e2;
> +  edge_iterator ei1, ei2;
> +  int *bb_dict = NULL;
> +  bool result = true;
> +  tree arg1, arg2;
> +
> +  compared_func = static_cast<sem_function *> (item);
> +
> +  gcc_assert (decl != item->decl);
> +
> +  if (arg_count != compared_func->arg_count
> +      || bb_count != compared_func->bb_count
> +      || edge_count != compared_func->edge_count
> +      || cfg_checksum != compared_func->cfg_checksum)
Again, check if testing the bb/edge counts aren't necessary as we check 
the cfg checksum.

> +    SE_EXIT_FALSE();
> +
> +  if (!equals_wpa (item))
> +    return false;
> +
> +  /* Checking function arguments.  */
> +  tree decl1 = DECL_ATTRIBUTES (decl);
> +  tree decl2 = DECL_ATTRIBUTES (compared_func->decl);
So are there any attributes we can safely ignore?  Probably not. 
However, we ought to handle the case where the attributes appear in 
different orders.

Also makes me wonder does the comparison of arguments/return value 
verify their attributes as well?


> +
> +/* Returns cgraph_node.  */
> +
> +struct cgraph_node *
> +sem_function::get_node (void)
> +{
> +  return cgraph (node);
> +}
> +
> +/* Initialize semantic item by info reachable during LTO WPA phase.  */
> +
> +void
> +sem_function::init_wpa (void)
> +{
> +  parse_tree_args ();
> +}
inline? Worth or not worth the headache?


> +
> +bool
> +sem_function::compare_bb (sem_bb_t *bb1, sem_bb_t *bb2, tree func1, tree func2)
So this routine walks down the gimple statements and compares them for 
equality.  Would it make sense to have the equality testing in gimple? 
  That way if someone adds a new gimple code the places they need to 
check/update are at least somewhat more localized?


> +
> +      for (i = 0; i < size1; ++i)
> +	{
> +	  t1 = gimple_phi_arg (phi1, i)->def;
> +	  t2 = gimple_phi_arg (phi2, i)->def;
> +
> +	  if (!compare_operand (t1, t2, func1, func2))
> +	    SE_EXIT_FALSE ();
> +
> +	  e1 = gimple_phi_arg_edge (phi1, i);
> +	  e2 = gimple_phi_arg_edge (phi2, i);
> +
> +	  if (!checker.compare_edge (e1, e2))
> +	    SE_EXIT_FALSE ();
> +	}
I don't think we guarantee any particular order on the PHI args.  ISTM 
you'd want to sort them or something so as not to reject a possible 
duplicate simply because of ordering issues.

Related, I'm not sure bb indexes are even guaranteed to have any stable 
ordering.  So ISTM you'd want to do something like a DFS walk to set a 
index for each block, then sort the PHI arguments based on DFS index to 
get a stable, consistent check here.


> +/* For given basic blocks BB1 and BB2 (from functions FUNC1 and FUNC),
> +   true value is returned if exception handling regions are equivalent
> +   in these blocks.  */
> +
> +bool
> +sem_function::compare_eh_region (eh_region r1, eh_region r2, tree func1,
> +				 tree func2)
Similarly, are EH region indexes stable enough to check here?

> +
> +void
> +sem_function::gsi_next_nondebug_stmt (gimple_stmt_iterator &gsi)
> +{
> +  gimple s;
> +
> +  s = gsi_stmt (gsi);
> +
> +  while (gimple_code (s) == GIMPLE_DEBUG)
> +    {
> +      gsi_next (&gsi);
> +      gcc_assert (!gsi_end_p (gsi));
> +
> +      s = gsi_stmt (gsi);
> +    }
> +}
Should this be elsewhere?  Seems like the concept of asking for the next 
non-debug statement happens often.


> +
> +/* Iterates GSI statement iterator to the next non-virtual statement.  */
> +
> +void
> +sem_function::gsi_next_nonvirtual_phi (gimple_stmt_iterator &it)
> +{
> +  gimple phi;
> +
> +  if (gsi_end_p (it))
> +    return;
> +
> +  phi = gsi_stmt (it);
> +  gcc_assert (phi != NULL);
> +
> +  while (virtual_operand_p (gimple_phi_result (phi)))
> +    {
> +      gsi_next (&it);
> +
> +      if (gsi_end_p (it))
> +	return;
> +
> +      phi = gsi_stmt (it);
> +    }
> +}
Similarly.


> +
> +/* Verifies that trees T1 and T2 do correspond.  */
Again, don't like the "do correspond" terminology.  Seems like we're 
testing for equivalence.

A lot of the sem_function::compare_XXX feel like they should be 
elsewhere, possibly their own module.  I could easily see reusing them 
for other purposes.

There's a number of algorithms that want to do the same kind of block 
hashing & equality testing that you're doing here in ICF.  Maybe pull 
them into their own file/api?

> +/* Gets symbol name of the item.  */
> +
> +const char *
> +sem_variable::name (void)
> +{
> +  return node->name ();
> +}
> +
> +/* Gets assembler name of the item.  */
> +
> +const char *
> +sem_variable::asm_name (void)
> +{
> +  return node->asm_name ();
> +}
Inline the accessors?

I'm starting to gloss over things....  It feels like we've got too much 
stuff in this one file.  Breaking things out would help (like for 
example the hashing/equality bits).  I'd like to see things broken out a 
bit and reposted for further reviewing.

Jeff

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 3/5] IPA ICF pass
  2014-06-20  7:32   ` Trevor Saunders
@ 2014-06-26 11:18     ` Martin Liška
  2014-07-05 21:44     ` Gerald Pfeifer
  1 sibling, 0 replies; 70+ messages in thread
From: Martin Liška @ 2014-06-26 11:18 UTC (permalink / raw)
  To: Trevor Saunders; +Cc: gcc-patches, hubicka

On 06/20/2014 09:31 AM, Trevor Saunders wrote:
> On Fri, Jun 13, 2014 at 12:44:22PM +0200, mliska wrote:
>> Hello,
>>     this is core of IPA ICF patchset. It adds new pass and registers all needed stuff related to a newly introduced interprocedural optimization.
>>
>> Algorithm description:
>>    In LGEN, we visit all read-only variables and functions. For each symbol, a hash value based on e.g. number of arguments,
>>    number of BB, GIMPLE CODES is computed (similar hash is computed for read-only variables). This kind of information is streamed
>>    for LTO.
>>
>>    In WPA, we build congruence classes for all symbols having a same hash value. For functions, these classes are subdivided in WPA by argument type comparison. Each reference (a call or a variable reference) to another semantic item candidate is marked and stored for further congruence class reduction (similar algorithm as Value Numbering:  www.cs.ucr.edu/~gupta/teaching/553-07/Papers/value.pdf).
>>
>>    For every congruence class of functions with more than one semantic function, we load function body. Having this information, we can
>>    process complete semantic function equality and subdivide such congruence class. Read-only variable class members are also deeply compared.
>>
>>    After that, we process Value numbering algorithm to do a final subdivision. Finally, all items belonging to a congruence class with more than one
>>    item are merged.
>>
>> Martin
>>
>> Changelog:
>>
>> 2014-06-13  Martin Liska  <mliska@suse.cz>
>> 	    Jan Hubicka  <hubicka@ucw.cz>
>>
>> 	* Makefile.in: New pass object file added.
>> 	* common.opt: New -fipa-icf flag introduced.
>> 	* doc/invoke.texi: Documentation enhanced for the pass.
>> 	* lto-section-in.c: New LTO section for a summary created by IPA-ICF.
>> 	* lto-streamer.h: New section name introduced.
>> 	* opts.c: Optimization is added to -O2.
>> 	* passes.def: New pass added.
>> 	* timevar.def: New time var for IPA-ICF.
>> 	* tree-pass.h: Pass construction function.
>> 	* ipa-icf.h: New pass header file added.
>> 	* ipa-icf.c: New pass source file added.
>>
>> diff --git a/gcc/Makefile.in b/gcc/Makefile.in
>> index 5587b75..4b59418 100644
>> --- a/gcc/Makefile.in
>> +++ b/gcc/Makefile.in
>> @@ -1276,6 +1276,7 @@ OBJS = \
>>   	ipa-profile.o \
>>   	ipa-prop.o \
>>   	ipa-pure-const.o \
>> +	ipa-icf.o \
>>   	ipa-reference.o \
>>   	ipa-ref.o \
>>   	ipa-utils.o \
>> diff --git a/gcc/common.opt b/gcc/common.opt
>> index 7f05092..3661dcc 100644
>> --- a/gcc/common.opt
>> +++ b/gcc/common.opt
>> @@ -1409,6 +1409,10 @@ fipa-pure-const
>>   Common Report Var(flag_ipa_pure_const) Init(0) Optimization
>>   Discover pure and const functions
>>   
>> +fipa-icf
>> +Common Report Var(flag_ipa_icf) Optimization
>> +Perform Identical Code Folding for functions and read-only variables
>> +
>>   fipa-reference
>>   Common Report Var(flag_ipa_reference) Init(0) Optimization
>>   Discover readonly and non addressable static variables
>> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
>> index 3c02341..b2bbe69 100644
>> --- a/gcc/doc/invoke.texi
>> +++ b/gcc/doc/invoke.texi
>> @@ -377,7 +377,7 @@ Objective-C and Objective-C++ Dialects}.
>>   -fif-conversion2 -findirect-inlining @gol
>>   -finline-functions -finline-functions-called-once -finline-limit=@var{n} @gol
>>   -finline-small-functions -fipa-cp -fipa-cp-clone @gol
>> --fipa-pta -fipa-profile -fipa-pure-const -fipa-reference @gol
>> +-fipa-pta -fipa-profile -fipa-pure-const -fipa-reference -fipa-icf @gol
>>   -fira-algorithm=@var{algorithm} @gol
>>   -fira-region=@var{region} -fira-hoist-pressure @gol
>>   -fira-loop-pressure -fno-ira-share-save-slots @gol
>> @@ -6947,6 +6947,7 @@ also turns on the following optimization flags:
>>   -finline-small-functions @gol
>>   -findirect-inlining @gol
>>   -fipa-sra @gol
>> +-fipa-icf @gol
>>   -fisolate-erroneous-paths-dereference @gol
>>   -foptimize-sibling-calls @gol
>>   -fpartial-inlining @gol
>> @@ -7862,6 +7863,14 @@ it may significantly increase code size
>>   (see @option{--param ipcp-unit-growth=@var{value}}).
>>   This flag is enabled by default at @option{-O3}.
>>   
>> +@item -fipa-icf
>> +@opindex fipa-icf
>> +Perform Identical Code Folding for functions and read-only variables.
>> +Behavior is similar to Gold Linker ICF optimization. Symbols proved
>> +as semantically equivalent are redirected to corresponding symbol. The pass
>> +sensitively decides for usage of alias, thunk or local redirection.
>> +This flag is enabled by default at @option{-O2}.
>> +
>>   @item -fisolate-erroneous-paths-dereference
>>   Detect paths which trigger erroneous or undefined behaviour due to
>>   dereferencing a NULL pointer.  Isolate those paths from the main control
>> diff --git a/gcc/ipa-icf.c b/gcc/ipa-icf.c
>> new file mode 100644
>> index 0000000..628d257
>> --- /dev/null
>> +++ b/gcc/ipa-icf.c
>> @@ -0,0 +1,3247 @@
>> +/* Interprocedural Identical Code Folding pass
>> +   Copyright (C) 2014 Free Software Foundation, Inc.
>> +
>> +   Contributed by Jan Hubicka <hubicka@ucw.cz> and Martin Liska <mliska@suse.cz>
>> +
>> +This file is part of GCC.
>> +
>> +GCC 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, or (at your option) any later
>> +version.
>> +
>> +GCC 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 GCC; see the file COPYING3.  If not see
>> +<http://www.gnu.org/licenses/>.  */
>> +
>> +/* Interprocedural Identical Code Folding for functions and
>> +   read-only variables.
>> +
>> +   The goal of this transformation is to discover functions and read-only
>> +   variables which do have exactly the same semantics.
>> +
>> +   In case of functions,
>> +   we could either create a virtual clone or do a simple function wrapper
>> +   that will call equivalent function. If the function is just locally visible,
>> +   all function calls can be redirected. For read-only variables, we create
>> +   aliases if possible.

Hello Trevor,
    thank you for your time spent with review.
> In theory you don't actually need the alias, you could change all the
> references to point at the merged data right?  Can that automatically
> happen for hidden visibility alias?
>
>> +
>> +   Optimization pass arranges as follows:
>> +   1) All functions and read-only variables are visited and internal
>> +      data structure, either sem_function or sem_variables is created.
>> +   2) For every symbol from the previoues step, VAR_DECL and FUNCTION_DECL are
> previous
>
>> +      saved and matched to corresponding sem_items.
>> +   3) These declaration are ignored for equality check and are solved
>> +      by Value Numbering algorithm published by Alpert, Zadeck in 1992.
>> +   4) We compute hash value for each symbol.
>> +   5) Congruence classes are created based on hash value. If hash value are
>> +      equal, equals function is called and symbols are deeply compared.
>> +      We must prove that all SSA names, declarations and other items
>> +      correspond.
>> +   6) Value Numbering is executed for these classes. At the end of the process
>> +      all symbol members in remaining classes can be mrged.
> merged
>
>> +   7) Merge operation creates alias in case of read-only variables. For
>> +      callgraph node, we must decide if we can redirect local calls,
>> +      create an alias or a thunk.
>> +
>> +*/
>> +
>> +#include "config.h"
>> +#include "system.h"
>> +#include "coretypes.h"
>> +#include "tree.h"
>> +#include "basic-block.h"
>> +#include "tree-ssa-alias.h"
>> +#include "internal-fn.h"
>> +#include "gimple-expr.h"
>> +#include "is-a.h"
>> +#include "gimple.h"
>> +#include "expr.h"
>> +#include "gimple-iterator.h"
>> +#include "gimple-ssa.h"
>> +#include "tree-cfg.h"
>> +#include "tree-phinodes.h"
>> +#include "stringpool.h"
>> +#include "tree-ssanames.h"
>> +#include "tree-dfa.h"
>> +#include "tree-pass.h"
>> +#include "gimple-pretty-print.h"
>> +#include "ipa-inline.h"
>> +#include "cfgloop.h"
>> +#include "except.h"
>> +#include "hash-table.h"
>> +#include "coverage.h"
>> +#include "pointer-set.h"
>> +#include "attribs.h"
>> +#include "print-tree.h"
>> +#include "lto-streamer.h"
>> +#include "data-streamer.h"
>> +#include "ipa-utils.h"
>> +#include "ipa-icf.h"
>> +
>> +namespace {
>> +
>> +func_checker::func_checker (): initialized (false)
>> +{
>> +}
> that style seems weird I'd expect
>
> function_checker::function_checker () :
>    initialized (false)
>    {
>    }
>
>    but really why isn't that inline in the class, it seems really short.
>
>> +
>> +/* Itializes internal structures according to given number of
> initializes

You are right, I'll put it to header file.
>
>> +   source and target SSA names. The number of source names is SSA_SOURCE,
>> +   respectively SSA_TARGET.  */
>> +
>> +void
>> +func_checker::initialize (unsigned ssa_source, unsigned ssa_target)
>> +{
>> +  release ();
>> +  initialized = true;
>> +
>> +  source_ssa_names.create (ssa_source);
>> +  target_ssa_names.create (ssa_target);
>> +
>> +  for (unsigned int i = 0; i < ssa_source; i++)
>> +    source_ssa_names.safe_push (-1);
>> +
>> +  for (unsigned int i = 0; i < ssa_target; i++)
>> +    target_ssa_names.safe_push (-1);
>> +
>> +  edge_map = new pointer_map <edge> ();
>> +
>> +  decl_map = new pointer_map <tree> ();
>> +}
> So, if its infallible why do you need a seperate init function?
Sure.
>
>> +
>> +/* Memory release routine.  */
>> +
>> +void
>> +func_checker::release (void)
>> +{
>> +  if (!initialized)
>> +    return;
>> +
>> +  delete edge_map;
>> +  delete decl_map;
>> +  source_ssa_names.release();
>> +  target_ssa_names.release();
>> +}
> obligatory why isn't this a dtor?

Yes.
>
>> +
>> +/* Verifies that trees T1 and T2 do correspond.  */
>> +
>> +bool
>> +func_checker::compare_ssa_name (tree t1, tree t2)
>> +{
>> +  unsigned i1 = SSA_NAME_VERSION (t1);
>> +  unsigned i2 = SSA_NAME_VERSION (t2);
>> +
>> +  if (source_ssa_names[i1] == -1)
>> +    source_ssa_names[i1] = i2;
>> +  else if (source_ssa_names[i1] != (int) i2)
>> +    return false;
>> +
>> +  if(target_ssa_names[i2] == -1)
>> +    target_ssa_names[i2] = i1;
>> +  else if (target_ssa_names[i2] != (int) i1)
>> +    return false;
>> +
>> +  return true;
>> +}
>> +
>> +/* Verification function for edges E1 and E2.  */
>> +
>> +bool
>> +func_checker::compare_edge (edge e1, edge e2)
>> +{
>> +  if (e1->flags != e2->flags)
>> +    return false;
>> +
>> +  edge *slot = edge_map->contains (e1);
>> +  if (slot)
>> +    {
>> +      SE_EXIT_DEBUG (*slot == e2);
>> +    }
>> +  else
>> +    {
>> +      slot = edge_map->insert (e1);
>> +      *slot = e2;
>> +    }
> it'd be more efficient to just call insert and pass &existed to check if
> the element was previously there.  It'd always be 1 hash lookup not
> sometimes 2.
Thanks, good point.
>
>> +/* Verification function for declaration trees T1 and T2 that
> its more comparison than verification right?
>
>> +func_checker::compare_decl (tree t1, tree t2, tree func1, tree func2)
>> +{
>> +  if (!auto_var_in_fn_p (t1, func1) || !auto_var_in_fn_p (t2, func2))
>> +    SE_EXIT_DEBUG (t1 == t2);
>> +
>> +  if (!types_compatible_p (TREE_TYPE (t1), TREE_TYPE (t2)))
>> +    SE_EXIT_FALSE ();
>> +
>> +  tree *slot = decl_map->contains (t1);
>> +  if (slot)
>> +    {
>> +      SE_EXIT_DEBUG (*slot == t2);
>> +    }
>> +  else
>> +    {
>> +      slot = decl_map->insert (t1);
>> +      *slot = t2;
>> +    }
> same
>
>> +congruence_class::congruence_class (unsigned int _id): id(_id)
>> +{
>> +  members.create (2);
> hrm, we should probably add a specialization of auto_vec<T, 0> so you
> can use that for things like this and lose the weird .create () stuff.
It would make sense such a specialization.

>
>> +}
>> +
>> +/* Constructor for key value pair, where _ITEM is key and _INDEX is a target.  */
>> +
>> +sem_usage_pair::sem_usage_pair (sem_item *_item, unsigned int _index):
>> +  item (_item), index (_index)
> we've been naming members m_foo which should solve the shadowing problem
> here as well as making code more readable.
I globally renamed all my private members to start with m_.
>
>> +sem_item::~sem_item ()
>> +{
>> +  if (tree_refs_set)
>> +    pointer_set_destroy (tree_refs_set);
>> +
>> +  for (unsigned i = 0; i < usages.length (); i++)
>> +    delete usages[i];
> what about the vectors themselves? who owns them? I know this bit of
> vec<> is horrible :(
Yeah, the leak is reported by -fmem-report too.
>
>> +/* Compare two types if are same aliases in case of strict aliasing
>> +   is enabled.  */
> nit, bad grammer
>
>> +bool
>> +sem_item::compare_for_aliasing (tree t1, tree t2)
>> +{
>> +  if (flag_strict_aliasing)
>> +    {
>> +      alias_set_type s1 = get_deref_alias_set (TREE_TYPE (t1));
>> +      alias_set_type s2 = get_deref_alias_set (TREE_TYPE (t2));
>> +
>> +      return s1 == s2;
>> +    }
>> +
>> +  return true;
>> +}
>> +
>> +/* Semantic function constructor that uses STACK as bitmap memory stack.  */
>> +
>> +sem_function::sem_function (bitmap_obstack *stack): sem_item (FUNC, stack),
>> +  compared_func (NULL)
>> +{
>> +  arg_types.create (0);
>> +}
>> +
>> +/*  Constructor based on callgraph node _NODE with computed hash _HASH.
>> +    Bitmap STACK is used for memory allocation.  */
>> +sem_function::sem_function (cgraph_node *node, hashval_t hash,
>> +			    bitmap_obstack *stack):
>> +  sem_item (FUNC, node, hash, stack), bb_sorted (NULL), compared_func (NULL)
>> +{
>> +  arg_types.create (0);
>> +}
>> +
>> +sem_function::~sem_function ()
>> +{
>> +  if (!bb_sorted)
>> +    return;
>> +
>> +  for (unsigned i = 0; i < bb_count; i++)
>> +    free (bb_sorted[i]);
>> +
>> +  free (bb_sizes);
>> +  free (bb_sorted);
>> +}
> who owns arg_types? it doesn't seem to be us
Fixed.
>
>> +sem_function::get_hash (void)
>> +{
>> +  if(!hash)
>> +    {
>> +      hash = 177454; /* Random number for function type.  */
>> +
>> +      hash = iterative_hash_object (arg_count, hash);
>> +      hash = iterative_hash_object (bb_count, hash);
>> +      hash = iterative_hash_object (edge_count, hash);
>> +      hash = iterative_hash_object (cfg_checksum, hash);
>> +      hash = iterative_hash_object (gcode_hash, hash);
>> +
>> +      for (unsigned i = 0; i < bb_count; i++)
>> +	hash = iterative_hash_object (hash, get_bb_hash (bb_sorted[i]));
>> +
>> +      for (unsigned i = 0; i < bb_count; i++)
>> +	hash = iterative_hash_object (bb_sizes[i], hash);
>> +    }
>> +
>> +  return hash;
>> +}
> I have to say I've always wondered if our hash functions try too hard to
> be perfect hashes at the expensive of speed, but of course its hard to
> know without lots of data.
>
>> +
>> +/* Fast equality function based on knowledge known in WPA.  */
>> +
>> +bool
>> +sem_function::equals_wpa (sem_item *item)
>> +{
>> +  if (item->type != FUNC)
>> +    return false;
> should this just be an assert? When we're creating the congruence
> classes in the first place we can keep functions and variables in
> different classes right?
You are right, I guarantee that a function and a variable cannot be in a same group.
>
>> +/* Returns true if the item equals to ITEM given as arguemnt.  */
> argument
>
>> +sem_function::equals_private (sem_item *item)
>> +{
>> +  if (item->type != FUNC)
>> +    return false;
> assert?
>
>> +
>> +  basic_block bb1, bb2;
>> +  edge e1, e2;
>> +  edge_iterator ei1, ei2;
>> +  int *bb_dict = NULL;
>> +  bool result = true;
>> +  tree arg1, arg2;
>> +
>> +  compared_func = static_cast<sem_function *> (item);
>> +
>> +  gcc_assert (decl != item->decl);
>> +
>> +  if (arg_count != compared_func->arg_count
>> +      || bb_count != compared_func->bb_count
>> +      || edge_count != compared_func->edge_count
>> +      || cfg_checksum != compared_func->cfg_checksum)
>> +    SE_EXIT_FALSE();
>> +
>> +  if (!equals_wpa (item))
> isn't this redundant with a bunch of the checks above it? could you
> remove them?
>
>> +    return false;
>> +
>> +  /* Checking function arguments.  */
>> +  tree decl1 = DECL_ATTRIBUTES (decl);
>> +  tree decl2 = DECL_ATTRIBUTES (compared_func->decl);
>> +
>> +  while (decl1)
>> +    {
>> +      if (decl2 == NULL)
>> +	SE_EXIT_FALSE();
> are attributes always in the same order?
They can be in a different order, I will enhance this part.

>
>> +
>> +      if (get_attribute_name (decl1) != get_attribute_name (decl2))
>> +	SE_EXIT_FALSE();
>> +
>> +      tree attr_value1 = TREE_VALUE (decl1);
>> +      tree attr_value2 = TREE_VALUE (decl2);
>> +
>> +      if (attr_value1 && attr_value2)
>> +	{
>> +	  bool ret = compare_operand (TREE_VALUE (attr_value1),
>> +				      TREE_VALUE (attr_value2), decl,
>> +				      compared_func->decl);
>> +	  if (!ret)
>> +	    SE_EXIT_FALSE_WITH_MSG ("attribute values are different")
>> +	  }
>> +      else if (!attr_value1 && !attr_value2)
>> +	{}
>> +      else
>> +	SE_EXIT_FALSE ();
>> +
>> +      decl1 = TREE_CHAIN (decl1);
>> +      decl2 = TREE_CHAIN (decl2);
>> +    }
>> +
>> +  if (decl1 != decl2)
>> +    SE_EXIT_FALSE();
>> +
>> +  checker.initialize (ssa_names_size, compared_func->ssa_names_size);
>> +
>> +  for (arg1 = DECL_ARGUMENTS (decl), arg2 = DECL_ARGUMENTS (compared_func->decl);
>> +       arg1; arg1 = DECL_CHAIN (arg1), arg2 = DECL_CHAIN (arg2))
>> +    checker.compare_decl (arg1, arg2, decl, compared_func->decl);
>> +
>> +  /* Exception handling regions comparison.  */
>> +  if (!compare_eh_region (region_tree, compared_func->region_tree, decl,
>> +			  compared_func->decl))
>> +    SE_EXIT_FALSE();
>> +
>> +  /* Checking all basic blocks.  */
>> +  for (unsigned i = 0; i < bb_count; ++i)
>> +    if(!compare_bb (bb_sorted[i], compared_func->bb_sorted[i], decl,
>> +		    compared_func->decl))
>> +      SE_EXIT_FALSE();
>> +
>> +  SE_DUMP_MESSAGE ("All BBs are equal\n");
>> +
>> +  /* Basic block edges check.  */
>> +  for (unsigned i = 0; i < bb_count; ++i)
>> +    {
>> +      bb_dict = XNEWVEC (int, bb_count + 2);
>> +      memset (bb_dict, -1, (bb_count + 2) * sizeof (int));
>> +
>> +      bb1 = bb_sorted[i]->bb;
>> +      bb2 = compared_func->bb_sorted[i]->bb;
>> +
>> +      ei2 = ei_start (bb2->preds);
>> +
>> +      for (ei1 = ei_start (bb1->preds); ei_cond (ei1, &e1); ei_next (&ei1))
>> +	{
>> +	  ei_cond (ei2, &e2);
>> +
>> +	  if (!bb_dict_test (bb_dict, e1->src->index, e2->src->index))
>> +	    SE_EXIT_FALSE_WITH_MSG("edge comparison returns false");
>> +
>> +	  if (!bb_dict_test (bb_dict, e1->dest->index, e2->dest->index))
>> +	    SE_EXIT_FALSE_WITH_MSG("BB comparison returns false");
>> +
>> +	  if (e1->flags != e2->flags)
>> +	    SE_EXIT_FALSE_WITH_MSG("flags comparison returns false");
> check this condition before the previous two since I guess its faster?
Sure.

>
>> +sem_function::init_refs_for_tree (tree t)
>> +{
>> +  switch (TREE_CODE (t))
>> +    {
>> +    case VAR_DECL:
>> +    case FUNCTION_DECL:
>> +      tree_refs.safe_push (t);
>> +      break;
>> +    case MEM_REF:
>> +    case ADDR_EXPR:
>> +    case OBJ_TYPE_REF:
>> +      init_refs_for_tree (TREE_OPERAND (t, 0));
>> +      break;
>> +    case FIELD_DECL:
>> +      init_refs_for_tree (DECL_FCONTEXT (t));
>> +      break;
>> +    default:
>> +      break;
>> +    }
>> +}
>> +
>> +/* Initializes references to another sem_item for gimple STMT of type assign.  */
>> +
>> +void
>> +sem_function::init_refs_for_assign (gimple stmt)
>> +{
>> +  if (gimple_num_ops (stmt) != 2)
>> +    return;
>> +
>> +  tree rhs = gimple_op (stmt, 1);
>> +
>> +  init_refs_for_tree (rhs);
>> +}
>> +
>> +/* Initializes references to other semantic functions/variables.  */
>> +
>> +void
>> +sem_function::init_refs (void)
>> +{
>> +  for (unsigned i = 0; i < bb_count; i++)
>> +    {
>> +      basic_block bb = bb_sorted[i]->bb;
>> +
>> +      for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi);
>> +	   gsi_next (&gsi))
>> +	{
>> +	  gimple stmt = gsi_stmt (gsi);
>> +	  hashval_t code = (hashval_t) gimple_code (stmt);
>> +
>> +	  switch (code)
>> +	    {
>> +	    case GIMPLE_CALL:
>> +	      {
>> +		tree funcdecl = gimple_call_fndecl (stmt);
>> +
>> +		/* Function pointer variables are not support yet.  */
> supported
>
>> +		if (funcdecl)
>> +		  tree_refs.safe_push (funcdecl);
>> +
>> +		break;
>> +	      }
>> +	    case GIMPLE_ASSIGN:
>> +	      init_refs_for_assign (stmt);
>> +	      break;
>> +	    default:
>> +	      break;
>> +	    }
>> +	}
>> +    }
>> +}
>> +
>> +/* Merges instance with an ALIAS_ITEM, where alias, thunk or redirection can
>> +   be applied.  */
>> +bool
>> +sem_function::merge (sem_item *alias_item)
>> +{
>> +  gcc_assert (alias_item->type == FUNC);
>> +
>> +  sem_function *alias_func = static_cast<sem_function *> (alias_item);
>> +
>> +  struct cgraph_node *original = get_node ();
>> +  struct cgraph_node *local_original = original;
>> +  struct cgraph_node *alias = alias_func->get_node ();
>> +  bool original_address_matters;
>> +  bool alias_address_matters;
>> +
>> +  bool create_thunk = false;
>> +  bool create_alias = false;
>> +  bool redirect_callers = false;
>> +  bool original_discardable = false;
>> +
>> +  /* Do not attempt to mix functions from different user sections;
>> +     we do not know what user intends with those.  */
>> +  if (((DECL_SECTION_NAME (original->decl) && !original->implicit_section)
>> +       || (DECL_SECTION_NAME (alias->decl) && !alias->implicit_section))
>> +      && DECL_SECTION_NAME (original->decl) != DECL_SECTION_NAME (alias->decl))
>> +    {
>> +      if (dump_file)
>> +	fprintf (dump_file,
>> +		 "Not unifying; original and alias are in different sections.\n\n");
>> +      return false;
>> +    }
>> +
>> +  /* See if original is in a section that can be discarded if the main
>> +     symbol is not used.  */
>> +  if (DECL_EXTERNAL (original->decl))
>> +    original_discardable = true;
>> +  if (original->resolution == LDPR_PREEMPTED_REG
>> +      || original->resolution == LDPR_PREEMPTED_IR)
>> +    original_discardable = true;
>> +  if (symtab_can_be_discarded (original))
>> +    original_discardable = true;
> shouldn't this last check handle the previous ones?
>
>> +
>> +  /* See if original and/or alias address can be compared for equality.  */
>> +  original_address_matters
>> +    = (!DECL_VIRTUAL_P (original->decl)
>> +       && (original->externally_visible
>> +	   || address_taken_from_non_vtable_p (original)));
>> +  alias_address_matters
>> +    = (!DECL_VIRTUAL_P (alias->decl)
>> +       && (alias->externally_visible
>> +	   || address_taken_from_non_vtable_p (alias)));
>> +
>> +  /* If alias and original can be compared for address equality, we need
>> +     to create a thunk.  Also we can not create extra aliases into discardable
>> +     section (or we risk link failures when section is discarded).  */
>> +  if ((original_address_matters
>> +       && alias_address_matters)
>> +      || original_discardable)
>> +    {
>> +      create_thunk = !stdarg_p (TREE_TYPE (alias->decl));
>> +      create_alias = false;
>> +      /* When both alias and original are not overwritable, we can save
>> +         the extra thunk wrapper for direct calls.  */
>> +      redirect_callers
>> +	= (!original_discardable
>> +	   && cgraph_function_body_availability (alias) > AVAIL_OVERWRITABLE
>> +	   && cgraph_function_body_availability (original) > AVAIL_OVERWRITABLE);
>> +    }
>> +  else
>> +    {
>> +      create_alias = true;
>> +      create_thunk = false;
>> +      redirect_callers = false;
>> +    }
>> +
>> +  if (create_alias && DECL_COMDAT_GROUP (alias->decl))
>> +    {
>> +      create_alias = false;
>> +      create_thunk = true;
>> +    }
>> +
>> +  /* We want thunk to always jump to the local function body
>> +     unless the body is comdat and may be optimized out.  */
>> +  if ((create_thunk || redirect_callers)
>> +      && (!original_discardable
>> +	  || (DECL_COMDAT_GROUP (original->decl)
>> +	      && (DECL_COMDAT_GROUP (original->decl)
>> +		  == DECL_COMDAT_GROUP (alias->decl)))))
>> +    local_original
>> +      = cgraph (symtab_nonoverwritable_alias (original));
>> +
>> +  if (redirect_callers)
>> +    {
>> +      /* If alias is non-overwritable then
>> +         all direct calls are safe to be redirected to the original.  */
>> +      bool redirected = false;
>> +      while (alias->callers)
>> +	{
>> +	  struct cgraph_edge *e = alias->callers;
>> +	  cgraph_redirect_edge_callee (e, local_original);
>> +	  push_cfun (DECL_STRUCT_FUNCTION (e->caller->decl));
>> +
>> +	  if (e->call_stmt)
>> +	    cgraph_redirect_edge_call_stmt_to_callee (e);
>> +
>> +	  pop_cfun ();
>> +	  redirected = true;
>> +	}
>> +
>> +      /* The alias function is removed just if symbol address
> s/just//
>
>> +         does not matters.  */
> s/matters/matter/
>
>> +sem_function *
>> +sem_function::parse (struct cgraph_node *node, bitmap_obstack *stack)
>> +{
>> +  tree fndecl = node->decl;
>> +  struct function *func = DECL_STRUCT_FUNCTION (fndecl);
>> +
>> +  if (!func || !cgraph_function_with_gimple_body_p (node))
>> +    return NULL;
>> +
>> +  if (lookup_attribute_by_prefix ("omp ", DECL_ATTRIBUTES (node->decl)) != NULL)
>> +    return NULL;
>> +
>> +  sem_function *f = new sem_function (node, 0, stack);
>> +
>> +  f->init ();
>> +
>> +  return f;
>> +}
>> +
>> +/* Parses function arguments and result type.  */
>> +
>> +void
>> +sem_function::parse_tree_args (void)
>> +{
>> +  tree result;
>> +  arg_types.create (4);
>> +  tree fnargs = DECL_ARGUMENTS (decl);
>> +
>> +  for (tree parm = fnargs; parm; parm = DECL_CHAIN (parm))
>> +    arg_types.safe_push (TYPE_CANONICAL (DECL_ARG_TYPE (parm)));
>> +
>> +  /* Function result type.  */
>> +  result = DECL_RESULT (decl);
>> +  result_type = result ? TYPE_CANONICAL (TREE_TYPE (result)) : NULL;
>> +
>> +  /* During WPA, we can get arguments by following method.  */
>> +  if (!fnargs)
>> +    {
>> +      tree type = TYPE_ARG_TYPES (TREE_TYPE (decl));
>> +      for (tree parm = type; parm; parm = TREE_CHAIN (parm))
>> +	arg_types.safe_push (TYPE_CANONICAL (TREE_VALUE (parm)));
>> +
>> +      result_type = TREE_TYPE (TREE_TYPE (decl));
>> +    }
>> +
>> +  arg_count = arg_types.length ();
> So, is it really worth the extra memory to save a memory access when you
> need the number of arguments? especially considering you'll probably
> want the vector elements next.
Actually in LGEN phase, I just need the number of arguments (arg_count). In WPA I would just use arg_types vector and corresponding length () method.

>
>> +/* For given basic blocks BB1 and BB2 (from functions FUNC1 and FUNC),
>> +   true value is returned if phi nodes are sematically
> semantically
>
>> +   equivalent in these blocks .  */
> perhaps return true if .... ?
>
>> +sem_function::gsi_next_nondebug_stmt (gimple_stmt_iterator &gsi)
>   I'd say a pointer makes it clearler the callie will modify the argument.
>
>> +{
>> +  gimple s;
>> +
>> +  s = gsi_stmt (gsi);
>> +
>> +  while (gimple_code (s) == GIMPLE_DEBUG)
>> +    {
>> +      gsi_next (&gsi);
>> +      gcc_assert (!gsi_end_p (gsi));
>> +
>> +      s = gsi_stmt (gsi);
>> +    }
>> +}
> really, shouldn't this be part of the gsi interface not something you
> hand roll here?
You are right, similar function can be seen in gimple-iterator.h. I'll use it.

>
>> +
>> +/* Iterates GSI statement iterator to the next non-virtual statement.  */
>> +
>> +void
>> +sem_function::gsi_next_nonvirtual_phi (gimple_stmt_iterator &it)
> same
This function will be moved to gimple-iterator.h
>
>> +sem_function::icf_handled_component_p (tree t)
>> +{
>> +  enum tree_code tc = TREE_CODE (t);
>> +
>> +  return ((handled_component_p (t) && handled_component_p (t))
> it must be too late, or are you calling the same function twice with the
> same args?
>
>> +	  || tc == ADDR_EXPR || tc == MEM_REF || tc == REALPART_EXPR
>> +	  || tc == IMAGPART_EXPR || tc == OBJ_TYPE_REF);
>> +}
>> +/* Returns true if the item equals to ITEM given as arguemnt.  */
> argument
>
> Its nice to see someone trying this and better it works ;)  I wonder if
> in the future it'll be worth looking at congruity of chunks of functions
> and then sharing those too.
>
> anyway I've had it for tonight hopefully I can finish looking at this
> soon.
>
> Trev

I'm going to apply suggestions observed by Jeff and after that I'm going to send a new version of patchset.

Thanks,
Martin
>
>> +sem_item_optimizer::write_summary (void)
>> +{
>> +  unsigned int count = 0;
>> +
>> +  struct output_block *ob = create_output_block (LTO_section_ipa_icf);
>> +  lto_symtab_encoder_t encoder = ob->decl_state->symtab_node_encoder;
>> +  ob->cgraph_node = NULL;
>> +
>> +  /* Calculate number of symbols to be serialized.  */
>> +  for (lto_symtab_encoder_iterator lsei = lsei_start_in_partition (encoder);
>> +       !lsei_end_p (lsei);
>> +       lsei_next_in_partition (&lsei))
>> +    {
>> +      struct symtab_node *node = lsei_node (lsei);
>> +
>> +      if (symtab_node_map.contains (node))
>> +	count++;
>> +    }
>> +
>> +  streamer_write_uhwi (ob, count);
>> +
>> +  /* Process all of the symbols.  */
>> +  for (lto_symtab_encoder_iterator lsei = lsei_start_in_partition (encoder);
>> +       !lsei_end_p (lsei);
>> +       lsei_next_in_partition (&lsei))
>> +    {
>> +      struct symtab_node *node = lsei_node (lsei);
>> +
>> +      sem_item **item = symtab_node_map.contains (node);
>> +
>> +      if (item && *item)
>> +	{
>> +	  int node_ref = lto_symtab_encoder_encode (encoder, node);
>> +	  streamer_write_uhwi_stream (ob->main_stream, node_ref);
>> +
>> +	  streamer_write_uhwi (ob, (*item)->get_hash ());
>> +	}
>> +    }
>> +
>> +  streamer_write_char_stream (ob->main_stream, 0);
>> +  produce_asm (ob, NULL);
>> +  destroy_output_block (ob);
>> +}
>> +
>> +/* Reads a section from LTO stream file FILE_DATA. Input block for DATA
>> +   contains LEN bytes.  */
>> +
>> +void
>> +sem_item_optimizer::read_section (struct lto_file_decl_data *file_data,
>> +				  const char *data, size_t len)
>> +{
>> +  const struct lto_function_header *header =
>> +  (const struct lto_function_header *) data;
>> +  const int cfg_offset = sizeof (struct lto_function_header);
>> +  const int main_offset = cfg_offset + header->cfg_size;
>> +  const int string_offset = main_offset + header->main_size;
>> +  struct data_in *data_in;
>> +  struct lto_input_block ib_main;
>> +  unsigned int i;
>> +  unsigned int count;
>> +
>> +  LTO_INIT_INPUT_BLOCK (ib_main, (const char *) data + main_offset, 0,
>> +			header->main_size);
>> +
>> +  data_in =
>> +    lto_data_in_create (file_data, (const char *) data + string_offset,
>> +			header->string_size, vNULL);
>> +
>> +  count = streamer_read_uhwi (&ib_main);
>> +
>> +  for (i = 0; i < count; i++)
>> +    {
>> +      unsigned int index;
>> +      struct symtab_node *node;
>> +      lto_symtab_encoder_t encoder;
>> +
>> +      index = streamer_read_uhwi (&ib_main);
>> +      encoder = file_data->symtab_node_encoder;
>> +      node = lto_symtab_encoder_deref (encoder, index);
>> +
>> +      hashval_t hash = streamer_read_uhwi (&ib_main);
>> +
>> +      gcc_assert (node->definition);
>> +
>> +      if (dump_file)
>> +	fprintf (dump_file, "Symbol added:%s (tree: %p, uid:%u)\n", node->asm_name (),
>> +		 (void *) node->decl, node->order);
>> +
>> +      if (is_a_helper<cgraph_node *>::test (node))
>> +	{
>> +	  cgraph_node *cnode = cgraph (node);
>> +
>> +	  items.safe_push (new sem_function (cnode, hash, &bmstack));
>> +	}
>> +      else
>> +	{
>> +	  varpool_node *vnode = varpool (node);
>> +
>> +	  items.safe_push (new sem_variable (vnode, hash, &bmstack));
>> +	}
>> +    }
>> +
>> +  lto_free_section_data (file_data, LTO_section_ipa_icf, NULL, data,
>> +			 len);
>> +  lto_data_in_delete (data_in);
>> +}
>> +
>> +/* Read IPA IPA ICF summary for symbols.  */
>> +
>> +void
>> +sem_item_optimizer::read_summary (void)
>> +{
>> +  struct lto_file_decl_data **file_data_vec = lto_get_file_decl_data ();
>> +  struct lto_file_decl_data *file_data;
>> +  unsigned int j = 0;
>> +
>> +  while ((file_data = file_data_vec[j++]))
>> +    {
>> +      size_t len;
>> +      const char *data = lto_get_section_data (file_data,
>> +			 LTO_section_ipa_icf, NULL, &len);
>> +
>> +      if (data)
>> +	read_section (file_data, data, len);
>> +    }
>> +}
>> +
>> +/* Register callgraph and varpool hooks.  */
>> +
>> +void
>> +sem_item_optimizer::register_hooks (void)
>> +{
>> +  cgraph_node_hooks = cgraph_add_node_removal_hook (
>> +			&sem_item_optimizer::cgraph_removal_hook, this);
>> +
>> +  varpool_node_hooks = varpool_add_node_removal_hook (
>> +			 &sem_item_optimizer::varpool_removal_hook, this);
>> +}
>> +
>> +/* Unregister callgraph and varpool hooks.  */
>> +
>> +void
>> +sem_item_optimizer::unregister_hooks (void)
>> +{
>> +  if (cgraph_node_hooks)
>> +    cgraph_remove_node_removal_hook (cgraph_node_hooks);
>> +
>> +  if (varpool_node_hooks)
>> +    varpool_remove_node_removal_hook (varpool_node_hooks);
>> +}
>> +
>> +/* Adds a CLS to hashtable associated by hash value.  */
>> +
>> +void
>> +sem_item_optimizer::add_class (congruence_class *cls)
>> +{
>> +  gcc_assert (cls->members.length ());
>> +
>> +  congruence_class_group_t *group = get_group_by_hash (
>> +				      cls->members[0]->get_hash ());
>> +  group->classes.safe_push (cls);
>> +}
>> +
>> +/* Gets a congruence class group based on given HASH value.  */
>> +
>> +congruence_class_group_t *
>> +sem_item_optimizer::get_group_by_hash (hashval_t hash)
>> +{
>> +  congruence_class_group_t *item = XNEW (congruence_class_group_t);
>> +  item->hash = hash;
>> +
>> +  congruence_class_group **slot = classes.find_slot (item, INSERT);
>> +
>> +  if (*slot)
>> +    free (item);
>> +  else
>> +    {
>> +      item->classes.create (1);
>> +      *slot = item;
>> +    }
>> +
>> +  return *slot;
>> +}
>> +
>> +/* Callgraph removal hook called for a NODE with a custom DATA.  */
>> +
>> +void
>> +sem_item_optimizer::cgraph_removal_hook (struct cgraph_node *node, void *data)
>> +{
>> +  sem_item_optimizer *optimizer = (sem_item_optimizer *) data;
>> +  optimizer->remove_symtab_node (node);
>> +}
>> +
>> +/* Varpool removal hook called for a NODE with a custom DATA.  */
>> +
>> +void
>> +sem_item_optimizer::varpool_removal_hook (struct varpool_node *node, void *data)
>> +{
>> +  sem_item_optimizer *optimizer = (sem_item_optimizer *) data;
>> +  optimizer->remove_symtab_node (node);
>> +}
>> +
>> +/* Remove symtab NODE triggered by symtab removal hooks.  */
>> +
>> +void
>> +sem_item_optimizer::remove_symtab_node (struct symtab_node *node)
>> +{
>> +  gcc_assert (!classes.elements());
>> +
>> +  pointer_set_insert (removed_items_set, node);
>> +}
>> +
>> +/* Removes all callgraph and varpool nodes that are marked by symtab
>> +   as deleted.  */
>> +
>> +void
>> +sem_item_optimizer::filter_removed_items (void)
>> +{
>> +  vec <sem_item *> filtered;
>> +  filtered.create (items.length());
>> +
>> +  for (unsigned int i = 0; i < items.length(); i++)
>> +    {
>> +      sem_item *item = items[i];
>> +
>> +      bool no_body_function = false;
>> +
>> +      if (item->type == FUNC)
>> +	{
>> +	  struct cgraph_node *cnode = static_cast <sem_function *>(item)->get_node ();
>> +
>> +	  no_body_function = in_lto_p && (cnode->alias || cnode->body_removed);
>> +	}
>> +
>> +      if(!pointer_set_contains (removed_items_set, items[i]->node)
>> +	  && !no_body_function)
>> +	filtered.safe_push (items[i]);
>> +    }
>> +  items.release ();
>> +
>> +  for (unsigned int i = 0; i < filtered.length(); i++)
>> +    items.safe_push (filtered[i]);
>> +}
>> +
>> +/* Optimizer entry point.  */
>> +
>> +void
>> +sem_item_optimizer::execute (void)
>> +{
>> +  filter_removed_items ();
>> +  build_hash_based_classes ();
>> +
>> +  if (dump_file)
>> +    fprintf (dump_file, "Dump after hash based groups\n");
>> +  dump_cong_classes ();
>> +
>> +  for (unsigned int i = 0; i < items.length(); i++)
>> +    items[i]->init_wpa ();
>> +
>> +  subdivide_classes_by_equality (true);
>> +
>> +  if (dump_file)
>> +    fprintf (dump_file, "Dump after WPA based types groups\n");
>> +  dump_cong_classes ();
>> +
>> +  parse_nonsingleton_classes ();
>> +  subdivide_classes_by_equality ();
>> +
>> +  if (dump_file)
>> +    fprintf (dump_file, "Dump after full equality comparison of groups\n");
>> +
>> +  dump_cong_classes ();
>> +
>> +  unsigned int prev_class_count = classes_count;
>> +
>> +  process_cong_reduction ();
>> +  dump_cong_classes ();
>> +  merge_classes (prev_class_count);
>> +
>> +  if (dump_file && (dump_flags & TDF_DETAILS))
>> +    dump_symtab (dump_file);
>> +}
>> +
>> +/* Function responsible for visiting all potential functions and
>> +   read-only variables that can be merged.  */
>> +
>> +void
>> +sem_item_optimizer::parse_funcs_and_vars (void)
>> +{
>> +  struct cgraph_node *cnode;
>> +  sem_item **slot;
>> +
>> +  FOR_EACH_DEFINED_FUNCTION (cnode)
>> +  {
>> +    sem_function *f = sem_function::parse (cnode, &bmstack);
>> +    if (f)
>> +      {
>> +	items.safe_push (f);
>> +	slot = symtab_node_map.insert (cnode);
>> +	*slot = f;
>> +
>> +	if (dump_file)
>> +	  fprintf (dump_file, "Parsed function:%s\n", f->asm_name ());
>> +
>> +	if (dump_file && (dump_flags & TDF_DETAILS))
>> +	  f->dump_to_file (dump_file);
>> +      }
>> +    else if (dump_file)
>> +      fprintf (dump_file, "Not parsed function:%s\n", cnode->asm_name ());
>> +  }
>> +
>> +  varpool_node *vnode;
>> +
>> +  FOR_EACH_DEFINED_VARIABLE (vnode)
>> +  {
>> +    sem_variable *v = sem_variable::parse (vnode, &bmstack);
>> +
>> +    if (v)
>> +      {
>> +	items.safe_push (v);
>> +	slot = symtab_node_map.insert (vnode);
>> +	*slot = v;
>> +      }
>> +  }
>> +}
>> +
>> +/* Makes pairing between a congruence class CLS and semantic ITEM.  */
>> +
>> +void
>> +sem_item_optimizer::add_item_to_class (congruence_class *cls, sem_item *item)
>> +{
>> +  item->index_in_class = cls->members.length ();
>> +  cls->members.safe_push (item);
>> +  item->cls = cls;
>> +}
>> +
>> +/* Congruence classes are built by hash value.  */
>> +
>> +void
>> +sem_item_optimizer::build_hash_based_classes (void)
>> +{
>> +  for (unsigned i = 0; i < items.length (); i++)
>> +    {
>> +      sem_item *item = items[i];
>> +
>> +      congruence_class_group_t *group = get_group_by_hash (item->get_hash ());
>> +
>> +      if (!group->classes.length ())
>> +	{
>> +	  classes_count++;
>> +	  group->classes.safe_push (new congruence_class (class_id++));
>> +	}
>> +
>> +      add_item_to_class (group->classes[0], item);
>> +    }
>> +}
>> +
>> +/* Semantic items in classes having more than one element and initialized.
>> +   In case of WPA, we load function body.  */
>> +
>> +void
>> +sem_item_optimizer::parse_nonsingleton_classes (void)
>> +{
>> +  for (hash_table <congruence_class_group_hash>::iterator it = classes.begin ();
>> +       it != classes.end (); ++it)
>> +    {
>> +      for (unsigned i = 0; i < (*it).classes.length (); i++)
>> +	{
>> +	  congruence_class *c = (*it).classes [i];
>> +
>> +	  if (c->members.length() > 1)
>> +	    for (unsigned j = 0; j < c->members.length (); j++)
>> +	      {
>> +		sem_item *item = c->members[j];
>> +		sem_item **slot;
>> +
>> +		slot = decl_map.insert (item->decl);
>> +		*slot = item;
>> +
>> +	      }
>> +	}
>> +    }
>> +
>> +  unsigned int init_called_count = 0;
>> +
>> +  for (hash_table <congruence_class_group_hash>::iterator it = classes.begin ();
>> +       it != classes.end (); ++it)
>> +    {
>> +      /* We fill in all declarations for sem_items.  */
>> +      for (unsigned i = 0; i < (*it).classes.length (); i++)
>> +	{
>> +	  congruence_class *c = (*it).classes [i];
>> +
>> +	  if (c->members.length() > 1)
>> +	    for (unsigned j = 0; j < c->members.length (); j++)
>> +	      {
>> +		sem_item *item = c->members[j];
>> +
>> +		item->init ();
>> +		item->init_refs ();
>> +		init_called_count++;
>> +
>> +		for (unsigned j = 0; j < item->tree_refs.length (); j++)
>> +		  {
>> +		    sem_item **result = decl_map.contains (item->tree_refs[j]);
>> +
>> +		    if(result)
>> +		      {
>> +			sem_item *target = *result;
>> +			item->refs.safe_push (target);
>> +
>> +			unsigned index = item->refs.length ();
>> +			target->usages.safe_push (new sem_usage_pair(item, index));
>> +			bitmap_set_bit (target->usage_index_bitmap, index);
>> +			pointer_set_insert (item->tree_refs_set, item->tree_refs[j]);
>> +		      }
>> +		  }
>> +	      }
>> +	}
>> +    }
>> +
>> +  if (dump_file)
>> +    fprintf (dump_file, "Init called for %u items (%.2f%%).\n", init_called_count,
>> +	     100.0f * init_called_count / items.length ());
>> +}
>> +
>> +/* Equality function for semantic items is used to subdivide existing
>> +   classes. If IN_WPA, fast equality function is invoked.  */
>> +
>> +void
>> +sem_item_optimizer::subdivide_classes_by_equality (bool in_wpa)
>> +{
>> +  for (hash_table <congruence_class_group_hash>::iterator it = classes.begin ();
>> +       it != classes.end (); ++it)
>> +    {
>> +      unsigned int class_count = (*it).classes.length ();
>> +
>> +      for (unsigned i = 0; i < class_count; i++)
>> +	{
>> +	  congruence_class *c = (*it).classes [i];
>> +
>> +	  if (c->members.length() > 1)
>> +	    {
>> +	      vec <sem_item *> new_vector;
>> +	      new_vector.create (c->members.length ());
>> +
>> +	      sem_item *first = c->members[0];
>> +	      new_vector.safe_push (first);
>> +
>> +	      unsigned class_split_first = (*it).classes.length ();
>> +
>> +	      for (unsigned j = 1; j < c->members.length (); j++)
>> +		{
>> +		  sem_item *item = c->members[j];
>> +
>> +		  bool equals = in_wpa ? first->equals_wpa (item) : first->equals (item);
>> +
>> +		  if (equals)
>> +		    new_vector.safe_push (item);
>> +		  else
>> +		    {
>> +		      bool integrated = false;
>> +
>> +		      for (unsigned k = class_split_first; k < (*it).classes.length (); k++)
>> +			{
>> +			  sem_item *x = (*it).classes[k]->members[0];
>> +			  bool equals = in_wpa ? x->equals_wpa (item) : x->equals (item);
>> +
>> +			  if (equals)
>> +			    {
>> +			      integrated = true;
>> +			      add_item_to_class ((*it).classes[k], item);
>> +
>> +			      break;
>> +			    }
>> +			}
>> +
>> +		      if (!integrated)
>> +			{
>> +			  congruence_class *c = new congruence_class (class_id++);
>> +			  classes_count++;
>> +			  add_item_to_class (c, item);
>> +
>> +			  (*it).classes.safe_push (c);
>> +			}
>> +		    }
>> +		}
>> +
>> +	      // we replace newly created new_vector for the class we've just splitted
>> +	      c->members.release ();
>> +	      c->members.create (new_vector.length ());
>> +
>> +	      for (unsigned int j = 0; j < new_vector.length (); j++)
>> +		add_item_to_class (c, new_vector[j]);
>> +	    }
>> +	}
>> +    }
>> +
>> +  verify_classes ();
>> +}
>> +
>> +/* Verify congruence classes if checking is enabled.  */
>> +
>> +void
>> +sem_item_optimizer::verify_classes (void)
>> +{
>> +#if ENABLE_CHECKING
>> +  for (hash_table <congruence_class_group_hash>::iterator it = classes.begin ();
>> +       it != classes.end (); ++it)
>> +    {
>> +      for (unsigned int i = 0; i < (*it).classes.length (); i++)
>> +	{
>> +	  congruence_class *cls = (*it).classes[i];
>> +
>> +	  gcc_checking_assert (cls);
>> +	  gcc_checking_assert (cls->members.length () > 0);
>> +
>> +	  for (unsigned int j = 0; j < cls->members.length (); j++)
>> +	    {
>> +	      sem_item *item = cls->members[j];
>> +
>> +	      gcc_checking_assert (item);
>> +
>> +	      for (unsigned k = 0; k < item->usages.length (); k++)
>> +		{
>> +		  sem_usage_pair *usage = item->usages[k];
>> +		  gcc_checking_assert (usage->item->index_in_class <
>> +				       usage->item->cls->members.length ());
>> +		}
>> +	    }
>> +	}
>> +    }
>> +#endif
>> +}
>> +
>> +/* Disposes split map traverse function. CLS_PTR is pointer to congruence
>> +   class, BSLOT is bitmap slot we want to release. DATA is mandatory,
>> +   but unused argument.  */
>> +
>> +bool
>> +sem_item_optimizer::release_split_map (__attribute__((__unused__)) const void
>> +				       *cls_ptr,
>> +				       __attribute__((__unused__)) bitmap *bslot,
>> +				       __attribute__((__unused__)) void *data)
>> +{
>> +  bitmap b = *bslot;
>> +
>> +  BITMAP_FREE (b);
>> +
>> +  return true;
>> +}
>> +
>> +/* Process split operation for a class given as pointer CLS_PTR,
>> +   where bitmap B splits congruence class members. DATA is used
>> +   as argument of split pair.  */
>> +
>> +bool
>> +sem_item_optimizer::traverse_congruence_split (const void *cls_ptr,
>> +    bitmap *bslot, void *data)
>> +{
>> +  const congruence_class *cls = (const congruence_class *) cls_ptr;
>> +  bitmap b = *bslot;
>> +
>> +  traverse_split_pair *pair = (traverse_split_pair *) data;
>> +  sem_item_optimizer *optimizer = pair->optimizer;
>> +  const congruence_class *splitter_cls = pair->cls;
>> +
>> +  /* If counted bits are greater than zero and less than the number of members
>> +     a group will be splitted.  */
>> +  unsigned popcount = bitmap_count_bits (b);
>> +
>> +  if (popcount > 0 && popcount < cls->members.length ())
>> +    {
>> +      congruence_class* newclasses[2] = { new congruence_class (class_id++), new congruence_class (class_id++) };
>> +
>> +      for (unsigned int i = 0; i < cls->members.length (); i++)
>> +	{
>> +	  int target = bitmap_bit_p (b, i);
>> +	  congruence_class *tc = newclasses[target];
>> +
>> +	  add_item_to_class (tc, cls->members[i]);
>> +	}
>> +
>> +#ifdef ENABLE_CHECKING
>> +      for (unsigned int i = 0; i < 2; i++)
>> +	gcc_checking_assert (newclasses[i]->members.length ());
>> +#endif
>> +
>> +      if (splitter_cls == cls)
>> +	optimizer->splitter_class_removed = true;
>> +
>> +      /* Remove old class from worklist if presented.  */
>> +      bool in_work_list = optimizer->worklist_contains (cls);
>> +
>> +      if (in_work_list)
>> +	optimizer->worklist_remove (cls);
>> +
>> +      for (hash_table <congruence_class_group_hash>::iterator it =
>> +	     optimizer->classes.begin (); it != optimizer->classes.end (); ++it)
>> +	{
>> +	  for (unsigned int i = 0; i < (*it).classes.length (); i++)
>> +	    if ((*it).classes[i] == cls)
>> +	      {
>> +		(*it).classes.ordered_remove (i);
>> +		break;
>> +	      }
>> +	}
>> +      /* New class will be inserted and integrated to work list.  */
>> +      for (unsigned int i = 0; i < 2; i++)
>> +	optimizer->add_class (newclasses[i]);
>> +
>> +      /* Two classes replace one, so that increment just by one.  */
>> +      optimizer->classes_count++;
>> +
>> +      /* If OLD class was presented in the worklist, we remove the class
>> +         are replace it will both newly created classes.  */
>> +      if (in_work_list)
>> +	for (unsigned int i = 0; i < 2; i++)
>> +	  optimizer->worklist_push (newclasses[i]);
>> +      else /* Just smaller class is inserted.  */
>> +	{
>> +	  unsigned int smaller_index = newclasses[0]->members.length () <
>> +				       newclasses[1]->members.length () ?
>> +				       0 : 1;
>> +	  optimizer->worklist_push (newclasses[smaller_index]);
>> +	}
>> +
>> +      if (dump_file && (dump_flags & TDF_DETAILS))
>> +	{
>> +	  fprintf (dump_file, "  congruence class splitted:\n");
>> +	  cls->dump (dump_file, 4);
>> +
>> +	  fprintf (dump_file, "  newly created groups:\n");
>> +	  for (unsigned int i = 0; i < 2; i++)
>> +	    newclasses[i]->dump (dump_file, 4);
>> +	}
>> +
>> +      delete cls;
>> +    }
>> +
>> +
>> +  return true;
>> +}
>> +
>> +/* Tests if a class CLS used as INDEXth splits any congruence classes.
>> +   Bitmap stack BMSTACK is used for bitmap allocation.  */
>> +
>> +void
>> +sem_item_optimizer::do_congruence_step_for_index (congruence_class *cls,
>> +    unsigned int index)
>> +{
>> +  /* Split map reset */
>> +  if (split_map != NULL)
>> +    delete split_map;
>> +
>> +  pointer_map <bitmap> *split_map = new pointer_map <bitmap> ();
>> +
>> +  for (unsigned int i = 0; i < cls->members.length (); i++)
>> +    {
>> +      sem_item *item = cls->members[i];
>> +
>> +      /* Iterate all usages that have INDEX as usage of the item.  */
>> +      for (unsigned int j = 0; j < item->usages.length (); j++)
>> +	{
>> +	  sem_usage_pair *usage = item->usages[j];
>> +
>> +	  if (usage->index != index)
>> +	    continue;
>> +
>> +	  bitmap *slot = split_map->contains (usage->item->cls);
>> +
>> +	  if(!slot)
>> +	    {
>> +	      slot = split_map->insert (usage->item->cls);
>> +	      *slot = BITMAP_ALLOC (&bmstack);
>> +	    }
>> +
>> +	  bitmap b = *slot;
>> +
>> +#if ENABLE_CHECKING
>> +	  gcc_checking_assert (usage->item->cls);
>> +	  gcc_checking_assert (usage->item->index_in_class <
>> +			       usage->item->cls->members.length ());
>> +#endif
>> +
>> +	  bitmap_set_bit (b, usage->item->index_in_class);
>> +	}
>> +    }
>> +
>> +  traverse_split_pair pair;
>> +  pair.optimizer = this;
>> +  pair.cls = cls;
>> +
>> +  splitter_class_removed = false;
>> +  split_map->traverse (&sem_item_optimizer::traverse_congruence_split, &pair);
>> +
>> +  /* Bitmap clean-up.  */
>> +  split_map->traverse (&sem_item_optimizer::release_split_map, NULL);
>> +}
>> +
>> +/* Every usage of a congruence class CLS is a candidate that can split the
>> +   collection of classes. Bitmap stack BMSTACK is used for bitmap
>> +   allocation.  */
>> +
>> +void
>> +sem_item_optimizer::do_congruence_step (congruence_class *cls)
>> +{
>> +  bitmap_iterator bi;
>> +  unsigned int i;
>> +
>> +  bitmap usage = BITMAP_ALLOC (&bmstack);
>> +
>> +  for (unsigned int i = 0; i < cls->members.length (); i++)
>> +    bitmap_ior_into (usage, cls->members[i]->usage_index_bitmap);
>> +
>> +  EXECUTE_IF_SET_IN_BITMAP (usage, 0, i, bi)
>> +  {
>> +    if (dump_file && (dump_flags & TDF_DETAILS))
>> +      fprintf (dump_file, "  processing congruece step for class: %u, index: %u\n",
>> +	       cls->id, i);
>> +
>> +    do_congruence_step_for_index (cls, i);
>> +
>> +    if (splitter_class_removed)
>> +      break;
>> +  }
>> +
>> +  BITMAP_FREE (usage);
>> +}
>> +
>> +/* Adds a newly created congruence class CLS to worklist.  */
>> +
>> +void
>> +sem_item_optimizer::worklist_push (congruence_class *cls)
>> +{
>> +  congruence_class **slot = worklist.find_slot (cls, INSERT);
>> +
>> +  if (*slot)
>> +    return;
>> +
>> +  *slot = cls;
>> +}
>> +
>> +/* Pops a class from worklist. */
>> +
>> +congruence_class *
>> +sem_item_optimizer::worklist_pop (void)
>> +{
>> +  gcc_assert (worklist.elements ());
>> +
>> +  congruence_class *cls = &(*worklist.begin ());
>> +  worklist.remove_elt (cls);
>> +
>> +  return cls;
>> +}
>> +
>> +/* Returns true if a congruence class CLS is presented in worklist.  */
>> +
>> +bool
>> +sem_item_optimizer::worklist_contains (const congruence_class *cls)
>> +{
>> +  return worklist.find (cls);
>> +}
>> +
>> +/* Removes given congruence class CLS from worklist.  */
>> +
>> +void
>> +sem_item_optimizer::worklist_remove (const congruence_class *cls)
>> +{
>> +  worklist.remove_elt (cls);
>> +}
>> +
>> +/* Iterative congruence reduction function.  */
>> +
>> +void
>> +sem_item_optimizer::process_cong_reduction (void)
>> +{
>> +  for (hash_table<congruence_class_group_hash>::iterator it = classes.begin ();
>> +       it != classes.end (); ++it)
>> +    for (unsigned i = 0; i < (*it).classes.length (); i++)
>> +      if ((*it).classes[i]->is_class_used ())
>> +	worklist_push ((*it).classes[i]);
>> +
>> +  if (dump_file)
>> +    fprintf (dump_file, "Worklist has been filled with: %lu\n",
>> +	     worklist.elements ());
>> +
>> +  if (dump_file && (dump_flags & TDF_DETAILS))
>> +    fprintf (dump_file, "Congruence class reduction\n");
>> +
>> +  while (worklist.elements ())
>> +    {
>> +      congruence_class *cls = worklist_pop ();
>> +      do_congruence_step (cls);
>> +    }
>> +}
>> +
>> +/* Debug function prints all informations about congruence classes.  */
>> +
>> +void
>> +sem_item_optimizer::dump_cong_classes (void)
>> +{
>> +  if (!dump_file)
>> +    return;
>> +
>> +  fprintf (dump_file,
>> +	   "Congruence classes: %u (unique hash values: %lu), with total: %u items\n",
>> +	   classes_count, classes.elements(), items.length ());
>> +
>> +  /* Histogram calculation.  */
>> +  unsigned int max_index = 0;
>> +  unsigned int* histogram = XCNEWVEC (unsigned int, items.length ());
>> +
>> +  for (hash_table<congruence_class_group_hash>::iterator it = classes.begin ();
>> +       it != classes.end (); ++it)
>> +
>> +    for (unsigned i = 0; i < (*it).classes.length (); i++)
>> +      {
>> +	unsigned int c = (*it).classes[i]->members.length ();
>> +	histogram[c]++;
>> +
>> +	if (c > max_index)
>> +	  max_index = c;
>> +      }
>> +
>> +  fprintf (dump_file,
>> +	   "Class size histogram [num of members]: number of classe number of classess\n");
>> +
>> +  for (unsigned int i = 0; i <= max_index; i++)
>> +    if (histogram[i])
>> +      fprintf (dump_file, "[%u]: %u classes\n", i, histogram[i]);
>> +
>> +  fprintf (dump_file, "\n\n");
>> +
>> +
>> +  if (dump_flags & TDF_DETAILS)
>> +    for (hash_table<congruence_class_group_hash>::iterator it = classes.begin ();
>> +	 it != classes.end (); ++it)
>> +      {
>> +	fprintf (dump_file, "  group: with %u classes:\n", (*it).classes.length ());
>> +
>> +	for (unsigned i = 0; i < (*it).classes.length (); i++)
>> +	  {
>> +	    (*it).classes[i]->dump (dump_file, 4);
>> +
>> +	    if(i < (*it).classes.length () - 1)
>> +	      fprintf (dump_file, " ");
>> +	  }
>> +      }
>> +
>> +  free (histogram);
>> +}
>> +
>> +/* After reduction is done, we can declare all items in a group
>> +   to be equal. PREV_CLASS_COUNT is start number of classes
>> +   before reduction.  */
>> +
>> +void
>> +sem_item_optimizer::merge_classes (unsigned int prev_class_count)
>> +{
>> +  unsigned int item_count = items.length ();
>> +  unsigned int class_count = classes_count;
>> +  unsigned int equal_items = item_count - class_count;
>> +
>> +  if (dump_file)
>> +    {
>> +      fprintf (dump_file, "\nItem count: %u\n", item_count);
>> +      fprintf (dump_file, "Congruent classes before: %u, after: %u\n",
>> +	       prev_class_count, class_count);
>> +      fprintf (dump_file, "Average class size before: %.2f, after: %.2f\n",
>> +	       1.0f * item_count / prev_class_count,
>> +	       1.0f * item_count / class_count);
>> +      fprintf (dump_file, "Equal symbols: %u\n", equal_items);
>> +      fprintf (dump_file, "Fraction of visited symbols: %.2f%%\n\n",
>> +	       100.0f * equal_items / item_count);
>> +    }
>> +
>> +  for (hash_table<congruence_class_group_hash>::iterator it = classes.begin ();
>> +       it != classes.end (); ++it)
>> +    for (unsigned int i = 0; i < (*it).classes.length (); i++)
>> +      {
>> +	congruence_class *c = (*it).classes[i];
>> +
>> +	if (c->members.length () == 1)
>> +	  continue;
>> +
>> +	gcc_assert (c->members.length ());
>> +
>> +	sem_item *source = c->members[0];
>> +
>> +	for (unsigned int j = 1; j < c->members.length (); j++)
>> +	  {
>> +	    sem_item *alias = c->members[j];
>> +
>> +	    if (dump_file)
>> +	      {
>> +		fprintf (dump_file, "Semantic equality hit:%s->%s\n",
>> +			 source->name (), alias->name ());
>> +		fprintf (dump_file, "Assembler symbol names:%s->%s\n",
>> +			 source->asm_name (), alias->asm_name ());
>> +	      }
>> +
>> +	    if (dump_file && (dump_flags & TDF_DETAILS))
>> +	      {
>> +		source->dump_to_file (dump_file);
>> +		alias->dump_to_file (dump_file);
>> +	      }
>> +
>> +	    source->merge (alias);
>> +	  }
>> +      }
>> +}
>> +
>> +/* Dump function prints all class members to a FILE with an INDENT.  */
>> +
>> +void
>> +congruence_class::dump (FILE *file, unsigned int indent) const
>> +{
>> +  FPRINTF_SPACES (file, indent, "class with id: %u, hash: %u, items: %u\n",
>> +		  id, members[0]->get_hash (), members.length ());
>> +
>> +  FPUTS_SPACES (file, indent + 2, "");
>> +  for (unsigned i = 0; i < members.length (); i++)
>> +    fprintf (file, "%s(%p/%u)", members[i]->asm_name (), (void *) members[i]->decl,
>> +	     members[i]->node->order);
>> +
>> +  fprintf (file, "\n");
>> +}
>> +
>> +/* Returns true if there's a member that is used from another group.  */
>> +
>> +bool
>> +congruence_class::is_class_used (void)
>> +{
>> +  for (unsigned int i = 0; i < members.length (); i++)
>> +    if (members[i]->usages.length ())
>> +      return true;
>> +
>> +  return false;
>> +}
>> +
>> +/* Initialization and computation of symtab node hash, there data
>> +   are propagated later on.  */
>> +
>> +static sem_item_optimizer optimizer;
>> +
>> +/* Generate pass summary for IPA ICF pass.  */
>> +
>> +static void
>> +ipa_icf_generate_summary (void)
>> +{
>> +  optimizer.parse_funcs_and_vars ();
>> +}
>> +
>> +/* Write pass summary for IPA ICF pass.  */
>> +
>> +static void
>> +ipa_icf_write_summary (void)
>> +{
>> +  optimizer.write_summary ();
>> +}
>> +
>> +/* Read pass summary for IPA ICF pass.  */
>> +
>> +static void
>> +ipa_icf_read_summary (void)
>> +{
>> +  optimizer.read_summary ();
>> +  optimizer.register_hooks ();
>> +}
>> +
>> +/* Semantic equality exection function.  */
>> +
>> +static unsigned int
>> +ipa_icf_driver (void)
>> +{
>> +  optimizer.execute ();
>> +  optimizer.unregister_hooks ();
>> +
>> +  return 0;
>> +}
>> +
>> +const pass_data pass_data_ipa_icf =
>> +{
>> +  IPA_PASS,		    /* type */
>> +  "icf",		    /* name */
>> +  OPTGROUP_IPA,             /* optinfo_flags */
>> +  true,                     /* has_execute */
>> +  TV_IPA_ICF,		    /* tv_id */
>> +  0,                        /* properties_required */
>> +  0,                        /* properties_provided */
>> +  0,                        /* properties_destroyed */
>> +  0,                        /* todo_flags_start */
>> +  0,                        /* todo_flags_finish */
>> +};
>> +
>> +class pass_ipa_icf : public ipa_opt_pass_d
>> +{
>> +public:
>> +  pass_ipa_icf (gcc::context *ctxt)
>> +    : ipa_opt_pass_d (pass_data_ipa_icf, ctxt,
>> +		      ipa_icf_generate_summary, /* generate_summary */
>> +		      ipa_icf_write_summary, /* write_summary */
>> +		      ipa_icf_read_summary, /* read_summary */
>> +		      NULL, /*
>> +		      write_optimization_summary */
>> +		      NULL, /*
>> +		      read_optimization_summary */
>> +		      NULL, /* stmt_fixup */
>> +		      0, /* function_transform_todo_flags_start */
>> +		      NULL, /* function_transform */
>> +		      NULL) /* variable_transform */
>> +  {}
>> +
>> +  /* opt_pass methods: */
>> +  virtual bool gate (function *)
>> +  {
>> +    return flag_ipa_icf;
>> +  }
>> +
>> +  virtual unsigned int execute (function *)
>> +  {
>> +    return ipa_icf_driver();
>> +  }
>> +}; // class pass_ipa_icf
>> +
>> +} // anon namespace
>> +
>> +ipa_opt_pass_d *
>> +make_pass_ipa_icf (gcc::context *ctxt)
>> +{
>> +  return new pass_ipa_icf (ctxt);
>> +}
>> diff --git a/gcc/ipa-icf.h b/gcc/ipa-icf.h
>> new file mode 100644
>> index 0000000..dc5e971
>> --- /dev/null
>> +++ b/gcc/ipa-icf.h
>> @@ -0,0 +1,687 @@
>> +/* Interprocedural semantic function equality pass
>> +   Copyright (C) 2014 Free Software Foundation, Inc.
>> +
>> +   Contributed by Jan Hubicka <hubicka@ucw.cz> and Martin Liska <mliska@suse.cz>
>> +
>> +This file is part of GCC.
>> +
>> +GCC 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, or (at your option) any later
>> +version.
>> +
>> +GCC 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 GCC; see the file COPYING3.  If not see
>> +<http://www.gnu.org/licenses/>.  */
>> +
>> +/* Prints string STRING to a FILE with a given number of SPACE_COUNT.  */
>> +#define FPUTS_SPACES(file, space_count, string) \
>> +  do \
>> +  { \
>> +    fprintf (file, "%*s" string, space_count, " "); \
>> +  } \
>> +  while (false);
>> +
>> +/* fprintf function wrapper that transforms given FORMAT to follow given
>> +   number for SPACE_COUNT and call fprintf for a FILE.  */
>> +#define FPRINTF_SPACES(file, space_count, format, ...) \
>> +  fprintf (file, "%*s" format, space_count, " ", ##__VA_ARGS__);
>> +
>> +/* Prints a MESSAGE to dump_file if exists.  */
>> +#define SE_DUMP_MESSAGE(message) \
>> +  do \
>> +  { \
>> +    if (dump_file && (dump_flags & TDF_DETAILS)) \
>> +      fprintf (dump_file, "  debug message: %s (%s:%u)\n", message, __func__, __LINE__); \
>> +  } \
>> +  while (false);
>> +
>> +/* Logs a MESSAGE to dump_file if exists and returns false.  */
>> +#define SE_EXIT_FALSE_WITH_MSG(message) \
>> +  do \
>> +  { \
>> +    if (dump_file && (dump_flags & TDF_DETAILS)) \
>> +      fprintf (dump_file, "  false returned: '%s' (%s:%u)\n", message, __func__, __LINE__); \
>> +    return false; \
>> +  } \
>> +  while (false);
>> +
>> +/* Return false and log that false value is returned.  */
>> +#define SE_EXIT_FALSE() \
>> +  SE_EXIT_FALSE_WITH_MSG("")
>> +
>> +/* Logs return value if RESULT is false.  */
>> +#define SE_EXIT_DEBUG(result) \
>> +  do \
>> +  { \
>> +    if (!(result) && dump_file && (dump_flags & TDF_DETAILS)) \
>> +      fprintf (dump_file, "  false returned (%s:%u)\n", __func__, __LINE__); \
>> +    return result; \
>> +  } \
>> +  while (false);
>> +
>> +/* Verbose logging function logging statements S1 and S2 of a CODE.  */
>> +#define SE_DIFF_STATEMENT(s1, s2, code) \
>> +  do \
>> +  { \
>> +    if (dump_file && (dump_flags & TDF_DETAILS)) \
>> +      { \
>> +        fprintf (dump_file, "  different statement for code: %s:\n", code); \
>> +        print_gimple_stmt (dump_file, s1, 3, TDF_DETAILS); \
>> +        print_gimple_stmt (dump_file, s2, 3, TDF_DETAILS); \
>> +      } \
>> +    return false; \
>> +  } \
>> +  while (false);
>> +
>> +namespace {
>> +
>> +/* Forward declaration for sem_func class.  */
>> +class sem_item;
>> +
>> +/* A class aggregating all connections and semantic equivalents
>> +   for a given pair of semantic function candidates.  */
>> +class func_checker
>> +{
>> +public:
>> +  func_checker ();
>> +
>> +  /* Initializes internal structures according to given number of
>> +     source and target SSA names. The number of source names is SSA_SOURCE,
>> +     respectively SSA_TARGET.  */
>> +  void initialize (unsigned ssa_source, unsigned sss_target);
>> +
>> +  /* Memory release routine.  */
>> +  void release (void);
>> +
>> +  /* Verifies that trees T1 and T2 do correspond.  */
>> +  bool compare_ssa_name (tree t1, tree t2);
>> +
>> +  /* Verification function for edges E1 and E2.  */
>> +  bool compare_edge (edge e1, edge e2);
>> +
>> +  /* Verification function for declaration trees T1 and T2 that
>> +     come from functions FUNC1 and FUNC2.  */
>> +  bool compare_decl (tree t1, tree t2, tree func1, tree func2);
>> +
>> +private:
>> +  /* Vector mapping source SSA names to target ones.  */
>> +  vec <int> source_ssa_names;
>> +
>> +  /* Vector mapping target SSA names to source ones.  */
>> +  vec <int> target_ssa_names;
>> +
>> +  /* Source to target edge map.  */
>> +  pointer_map <edge> *edge_map;
>> +
>> +  /* Source to target declaration map.  */
>> +  pointer_map <tree> *decl_map;
>> +
>> +  /* Flag that indicates if the checker is initialize.  */
>> +  bool initialized;
>> +};
>> +
>> +/* Congruence class encompasses a collection of either functions or
>> +   read-only variables. These items are considered to be equivalent
>> +   if not proved the oposite.  */
>> +class congruence_class
>> +{
>> +public:
>> +  /* Congruence class constructor for a new class with _ID.  */
>> +  congruence_class (unsigned int _id);
>> +
>> +  /* Dump function prints all class members to a FILE with an INDENT.  */
>> +  void dump (FILE *file, unsigned int indent = 0) const;
>> +
>> +  /* Returns true if there's a member that is used from another group.  */
>> +  bool is_class_used (void);
>> +
>> +  /* Vector of all group members.  */
>> +  vec <sem_item *> members;
>> +
>> +  /* Global unique class identifier.  */
>> +  unsigned int id;
>> +};
>> +
>> +/* Semantic item type enum.  */
>> +enum sem_item_type
>> +{
>> +  FUNC,
>> +  VAR
>> +};
>> +
>> +/* Semantic item usage pair.  */
>> +class sem_usage_pair
>> +{
>> +public:
>> +  /* Constructor for key value pair, where _ITEM is key and _INDEX is a target.  */
>> +  sem_usage_pair (sem_item *_item, unsigned int _index);
>> +
>> +  /* Target semantic item where an item is used.  */
>> +  sem_item *item;
>> +
>> +  /* Index of usage of such an item.  */
>> +  unsigned int index;
>> +};
>> +
>> +/* Basic block struct for sematic equality pass.  */
>> +typedef struct sem_bb
>> +{
>> +  /* Basic block the structure belongs to.  */
>> +  basic_block bb;
>> +
>> +  /* Number of non-debug statements in the basic block.  */
>> +  unsigned nondbg_stmt_count;
>> +
>> +  /* Number of edges connected to the block.  */
>> +  unsigned edge_count;
>> +} sem_bb_t;
>> +
>> +/* Semantic item is a base class that encapsulates all shared functionality
>> +   for both semantic function and variable items.  */
>> +class sem_item
>> +{
>> +public:
>> +  /* Semantic item constructor for a node of _TYPE, where STACK is used
>> +     for bitmap memory allocation.  */
>> +  sem_item (enum sem_item_type _type, bitmap_obstack *stack);
>> +
>> +  /* Semantic item constructor for a node of _TYPE, where STACK is used
>> +     for bitmap memory allocation. The item is based on symtab node _NODE
>> +     with computed _HASH.  */
>> +  sem_item (enum sem_item_type _type, struct symtab_node *_node, hashval_t _hash,
>> +	    bitmap_obstack *stack);
>> +
>> +  virtual ~sem_item ();
>> +
>> +  /* Dump function for debugging purpose.  */
>> +  DEBUG_FUNCTION void dump (void);
>> +
>> +  /* Initialize semantic item by info reachable during LTO WPA phase.  */
>> +  virtual void init_wpa (void) = 0;
>> +
>> +  /* Semantic item initialization function.  */
>> +  virtual void init (void) = 0;
>> +
>> +  /* Gets symbol name of the item.  */
>> +  virtual const char *name (void) = 0;
>> +
>> +  /* Gets assembler name of the item.  */
>> +  virtual const char *asm_name (void) = 0;
>> +
>> +  /* Initializes references to other semantic functions/variables.  */
>> +  virtual void init_refs () = 0;
>> +
>> +  /* Fast equality function based on knowledge known in WPA.  */
>> +  virtual bool equals_wpa (sem_item *item) = 0;
>> +
>> +  /* Returns true if the item equals to ITEM given as arguemnt.  */
>> +  virtual bool equals (sem_item *item) = 0;
>> +
>> +  /* References independent hash function.  */
>> +  virtual hashval_t get_hash (void) = 0;
>> +
>> +  /* Merges instance with an ALIAS_ITEM, where alias, thunk or redirection can
>> +     be applied.  */
>> +  virtual bool merge (sem_item *alias_item) = 0;
>> +
>> +  /* Dump symbol to FILE.  */
>> +  virtual void dump_to_file (FILE *file) = 0;
>> +
>> +  /* Compare two types if are same aliases in case of strict aliasing
>> +     is enabled.  */
>> +  static bool compare_for_aliasing (tree t1, tree t2);
>> +
>> +  /* Item type.  */
>> +  enum sem_item_type type;
>> +
>> +  /* Global unique function index.  */
>> +  unsigned int index;
>> +
>> +  /* Symtab node.  */
>> +  struct symtab_node *node;
>> +
>> +  /* Declaration tree node.  */
>> +  tree decl;
>> +
>> +  /* Semantic references used that generate congruence groups.  */
>> +  vec <sem_item *> refs;
>> +
>> +  /* Pointer to a congruence class the item belongs to.  */
>> +  congruence_class *cls;
>> +
>> +  /* Index of the item in a class belonging to.  */
>> +  unsigned int index_in_class;
>> +
>> +  /* List of semantic items where the instance is used.  */
>> +  vec <sem_usage_pair *> usages;
>> +
>> +  /* A bitmap with indices of all classes referencing this item.  */
>> +  bitmap usage_index_bitmap;
>> +
>> +  /* List of tree references (either FUNC_DECL or VAR_DECL).  */
>> +  vec <tree> tree_refs;
>> +
>> +  /* A set with tree references (either FUNC_DECL or VAR_DECL).  */
>> +  pointer_set_t *tree_refs_set;
>> +
>> +protected:
>> +  /* Cached, once calculated hash for the item.  */
>> +  hashval_t hash;
>> +
>> +private:
>> +  /* Initialize internal data structures. Bitmap STACK is used for
>> +     bitmap memory allocation process.  */
>> +  void setup (bitmap_obstack *stack);
>> +}; // class sem_item
>> +
>> +class sem_function: public sem_item
>> +{
>> +public:
>> +  /* Semantic function constructor that uses STACK as bitmap memory stack.  */
>> +  sem_function (bitmap_obstack *stack);
>> +
>> +  /*  Constructor based on callgraph node _NODE with computed hash _HASH.
>> +      Bitmap STACK is used for memory allocation.  */
>> +  sem_function (cgraph_node *_node, hashval_t _hash, bitmap_obstack *stack);
>> +
>> +  ~sem_function ();
>> +
>> +  virtual void init_wpa (void);
>> +  virtual void init (void);
>> +  virtual const char *name (void);
>> +  virtual const char *asm_name (void);
>> +  virtual hashval_t get_hash (void);
>> +  virtual bool equals_wpa (sem_item *item);
>> +  virtual bool equals (sem_item *item);
>> +  virtual void init_refs ();
>> +  virtual bool merge (sem_item *alias_item);
>> +  virtual void dump_to_file (FILE *file);
>> +
>> +  /* Parses function arguments and result type.  */
>> +  void parse_tree_args (void);
>> +
>> +  /* Returns cgraph_node.  */
>> +  struct cgraph_node *get_node (void);
>> +
>> +  /* For a given call graph NODE, the function constructs new
>> +     semantic function item.  */
>> +  static sem_function *parse (struct cgraph_node *node, bitmap_obstack *stack);
>> +
>> +  /* Exception handling region tree.  */
>> +  eh_region region_tree;
>> +
>> +  /* Result type tree node.  */
>> +  tree result_type;
>> +
>> +  /* Array of argument tree types.  */
>> +  vec <tree> arg_types;
>> +
>> +  /* Number of function arguments.  */
>> +  unsigned int arg_count;
>> +
>> +  /* Basic block count.  */
>> +  unsigned int bb_count;
>> +
>> +  /* Total amount of edges in the function.  */
>> +  unsigned int edge_count;
>> +
>> +  /* Array of sizes of all basic blocks.  */
>> +  unsigned int *bb_sizes;
>> +
>> +  /* Control flow graph checksum.  */
>> +  hashval_t cfg_checksum;
>> +
>> +  /* GIMPLE codes hash value.  */
>> +  hashval_t gcode_hash;
>> +
>> +  /* Total number of SSA names used in the function.  */
>> +  unsigned ssa_names_size;
>> +
>> +  /* Array of structures for all basic blocks.  */
>> +  sem_bb_t **bb_sorted;
>> +
>> +private:
>> +  /* Calculates hash value based on a BASIC_BLOCK.  */
>> +  hashval_t get_bb_hash (const sem_bb_t *basic_block);
>> +
>> +  /* Basic block equivalence comparison function that returns true if
>> +     basic blocks BB1 and BB2 (from functions FUNC1 and FUNC2) correspond.  */
>> +  bool compare_bb (sem_bb_t *bb1, sem_bb_t *bb2, tree func1, tree func2);
>> +
>> +  /* For given basic blocks BB1 and BB2 (from functions FUNC1 and FUNC),
>> +     true value is returned if phi nodes are sematically
>> +     equivalent in these blocks .  */
>> +  bool compare_phi_node (basic_block bb1, basic_block bb2, tree func1,
>> +			 tree func2);
>> +
>> +  /* For given basic blocks BB1 and BB2 (from functions FUNC1 and FUNC),
>> +     true value is returned if exception handling regions are equivalent
>> +     in these blocks.  */
>> +  bool compare_eh_region (eh_region r1, eh_region r2, tree func1, tree func2);
>> +
>> +  /* Iterates GSI statement iterator to the next non-debug statement.  */
>> +  void gsi_next_nondebug_stmt (gimple_stmt_iterator &gsi);
>> +
>> +  /* Iterates GSI statement iterator to the next non-virtual statement.  */
>> +  void gsi_next_nonvirtual_phi (gimple_stmt_iterator &it);
>> +
>> +  /* Verifies that trees T1 and T2 do correspond.  */
>> +  bool compare_function_decl (tree t1, tree t2);
>> +
>> +  /* Verifies that trees T1 and T2 do correspond.  */
>> +  bool compare_variable_decl (tree t1, tree t2, tree func1, tree func2);
>> +
>> +  /* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
>> +     call statements are semantically equivalent.  */
>> +  bool compare_gimple_call (gimple s1, gimple s2,
>> +			    tree func1, tree func2);
>> +
>> +  /* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
>> +     assignment statements are semantically equivalent.  */
>> +  bool compare_gimple_assign (gimple s1, gimple s2, tree func1, tree func2);
>> +
>> +  /* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
>> +     condition statements are semantically equivalent.  */
>> +  bool compare_gimple_cond (gimple s1, gimple s2, tree func1, tree func2);
>> +
>> +  /* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
>> +     label statements are semantically equivalent.  */
>> +  bool compare_gimple_label (gimple s1, gimple s2, tree func1, tree func2);
>> +
>> +  /* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
>> +     switch statements are semantically equivalent.  */
>> +  bool compare_gimple_switch (gimple s1, gimple s2, tree func1, tree func2);
>> +
>> +  /* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
>> +     return statements are semantically equivalent.  */
>> +  bool compare_gimple_return (gimple s1, gimple s2, tree func1, tree func2);
>> +
>> +  /* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
>> +     goto statements are semantically equivalent.  */
>> +  bool compare_gimple_goto (gimple s1, gimple s2, tree func1, tree func2);
>> +
>> +  /* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
>> +     resx statements are semantically equivalent.  */
>> +  bool compare_gimple_resx (gimple s1, gimple s2);
>> +
>> +  /* Verifies for given GIMPLEs S1 and S2 that ASM statements are equivalent.
>> +     For the beginning, the pass only supports equality for
>> +     '__asm__ __volatile__ ("", "", "", "memory")'.  */
>> +  bool compare_gimple_asm (gimple s1, gimple s2);
>> +
>> +  /* Verifies that tree labels T1 and T2 correspond in FUNC1 and FUNC2.  */
>> +  bool compare_tree_ssa_label (tree t1, tree t2, tree func1, tree func2);
>> +
>> +  /* Function compares two operands T1 and T2 and returns true if these
>> +     two trees from FUNC1 (respectively FUNC2) are semantically equivalent.  */
>> +  bool compare_operand (tree t1, tree t2, tree func1, tree func2);
>> +
>> +  /* If T1 and T2 are SSA names, dictionary comparison is processed. Otherwise,
>> +     declaration comparasion is executed.  */
>> +  bool compare_ssa_name (tree t1, tree t2, tree func1, tree func2);
>> +
>> +  /* Basic blocks dictionary BB_DICT returns true if SOURCE index BB
>> +     corresponds to TARGET.  */
>> +  bool bb_dict_test (int* bb_dict, int source, int target);
>> +
>> +  /* Iterates all tree types in T1 and T2 and returns true if all types
>> +     are compatible.  */
>> +  bool compare_type_list (tree t1, tree t2);
>> +
>> +  /* Processes function equality comparison.  */
>> +  bool equals_private (sem_item *item);
>> +
>> +  /* Initializes references to another sem_item for gimple STMT of type assign.  */
>> +  void init_refs_for_assign (gimple stmt);
>> +
>> +  /* Initializes references to another sem_item for tree T.  */
>> +  void init_refs_for_tree (tree t);
>> +
>> +  /* Returns true if tree T can be compared as a handled component.  */
>> +  static bool icf_handled_component_p (tree t);
>> +
>> +  /* Function checker stores binding between functions.   */
>> +  func_checker checker;
>> +
>> +  /* COMPARED_FUNC is a function that we compare to.  */
>> +  sem_function *compared_func;
>> +}; // class sem_function
>> +
>> +class sem_variable: public sem_item
>> +{
>> +public:
>> +  /* Semantic variable constructor that uses STACK as bitmap memory stack.  */
>> +  sem_variable (bitmap_obstack *stack);
>> +
>> +  /*  Constructor based on callgraph node _NODE with computed hash _HASH.
>> +      Bitmap STACK is used for memory allocation.  */
>> +
>> +  sem_variable (varpool_node *_node, hashval_t _hash, bitmap_obstack *stack);
>> +
>> +  virtual void init_wpa (void);
>> +  virtual void init (void);
>> +  virtual const char *name (void);
>> +  virtual const char *asm_name (void);
>> +  virtual void init_refs ();
>> +  virtual hashval_t get_hash (void);
>> +  virtual bool merge (sem_item *alias_item);
>> +  virtual void dump_to_file (FILE *file);
>> +  virtual bool equals_wpa (sem_item *item);
>> +  virtual bool equals (sem_item *item);
>> +
>> +  /* Returns varpool_node.  */
>> +  struct varpool_node *get_node (void);
>> +
>> +  /* Parser function that visits a varpool NODE.  */
>> +  static sem_variable *parse (struct varpool_node *node, bitmap_obstack *stack);
>> +
>> +  /* Variable constructor.  */
>> +  tree ctor;
>> +
>> +private:
>> +  /* Iterates though a constructor and identifies tree references
>> +     we are interested in semantic function equality.  */
>> +  void parse_tree_refs (tree t);
>> +
>> +  /* Compares trees T1 and T2 for semantic equality.  */
>> +  static bool equals (tree t1, tree t2);
>> +
>> +}; // class sem_variable
>> +
>> +class sem_item_optimizer;
>> +
>> +/* Congruence class set structure.  */
>> +struct congruence_class_var_hash: typed_noop_remove <congruence_class>
>> +{
>> +  typedef congruence_class value_type;
>> +  typedef congruence_class compare_type;
>> +  static inline hashval_t hash (const value_type *);
>> +  static inline int equal (const value_type *, const compare_type *);
>> +};
>> +
>> +typedef struct congruence_class_group
>> +{
>> +  hashval_t hash;
>> +  vec <congruence_class *> classes;
>> +} congruence_class_group_t;
>> +
>> +/* Congruence class set structure.  */
>> +struct congruence_class_group_hash: typed_noop_remove <congruence_class_group_t>
>> +{
>> +  typedef congruence_class_group value_type;
>> +  typedef congruence_class_group compare_type;
>> +  static inline hashval_t hash (const value_type *);
>> +  static inline int equal (const value_type *, const compare_type *);
>> +};
>> +
>> +struct traverse_split_pair
>> +{
>> +  sem_item_optimizer *optimizer;
>> +  class congruence_class *cls;
>> +};
>> +
>> +/* Semantic item optimizer includes all top-level logic
>> +   related to semantic equality comparison.  */
>> +class sem_item_optimizer
>> +{
>> +public:
>> +  sem_item_optimizer ();
>> +  ~sem_item_optimizer ();
>> +
>> +  /* Function responsible for visiting all potential functions and
>> +     read-only variables that can be merged.  */
>> +  void parse_funcs_and_vars (void);
>> +
>> +  /* Optimizer entry point.  */
>> +  void execute (void);
>> +
>> +  /* Dump function. */
>> +  void dump (void);
>> +
>> +  /* Verify congruence classes if checking is enabled.  */
>> +  void verify_classes (void);
>> +
>> +  /* Write IPA ICF summary for symbols.  */
>> +  void write_summary (void);
>> +
>> +  /* Read IPA IPA ICF summary for symbols.  */
>> +  void read_summary (void);
>> +
>> +  /* Callgraph removal hook called for a NODE with a custom DATA.  */
>> +  static void cgraph_removal_hook (struct cgraph_node *node, void *data);
>> +
>> +  /* Varpool removal hook called for a NODE with a custom DATA.  */
>> +  static void varpool_removal_hook (struct varpool_node *node, void *data);
>> +
>> +  /* Worklist of congruence classes that can potentially
>> +     refine classes of congruence.  */
>> +  hash_table <congruence_class_var_hash> worklist;
>> +
>> +  /* Remove symtab NODE triggered by symtab removal hooks.  */
>> +  void remove_symtab_node (struct symtab_node *node);
>> +
>> +  /* Register callgraph and varpool hooks.  */
>> +  void register_hooks (void);
>> +
>> +  /* Unregister callgraph and varpool hooks.  */
>> +  void unregister_hooks (void);
>> +
>> +  /* Adds a CLS to hashtable associated by hash value.  */
>> +  void add_class (congruence_class *cls);
>> +
>> +  /* Gets a congruence class group based on given HASH value.  */
>> +  congruence_class_group_t *get_group_by_hash (hashval_t hash);
>> +
>> +private:
>> +
>> +  /* Congruence classes are built by hash value.  */
>> +  void build_hash_based_classes (void);
>> +
>> +  /* Semantic items in classes having more than one element and initialized.
>> +     In case of WPA, we load function body.  */
>> +  void parse_nonsingleton_classes (void);
>> +
>> +  /* Equality function for semantic items is used to subdivide existing
>> +     classes. If IN_WPA, fast equality function is invoked.  */
>> +  void subdivide_classes_by_equality (bool in_wpa = false);
>> +
>> +  /* Debug function prints all informations about congruence classes.  */
>> +  void dump_cong_classes (void);
>> +
>> +  /* Iterative congruence reduction function.  */
>> +  void process_cong_reduction (void);
>> +
>> +  /* After reduction is done, we can declare all items in a group
>> +     to be equal. PREV_CLASS_COUNT is start number of classes
>> +     before reduction.  */
>> +  void merge_classes (unsigned int prev_class_count);
>> +
>> +  /* Adds a newly created congruence class CLS to worklist.  */
>> +  void worklist_push (congruence_class *cls);
>> +
>> +  /* Pops a class from worklist. */
>> +  congruence_class *worklist_pop ();
>> +
>> +  /* Returns true if a congruence class CLS is presented in worklist.  */
>> +  bool worklist_contains (const congruence_class *cls);
>> +
>> +  /* Removes given congruence class CLS from worklist.  */
>> +  void worklist_remove (const congruence_class *cls);
>> +
>> +  /* Every usage of a congruence class CLS is a candidate that can split the
>> +     collection of classes. Bitmap stack BMSTACK is used for bitmap
>> +     allocation.  */
>> +  void do_congruence_step (congruence_class *cls);
>> +
>> +  /* Tests if a class CLS used as INDEXth splits any congruence classes.
>> +     Bitmap stack BMSTACK is used for bitmap allocation.  */
>> +  void do_congruence_step_for_index (congruence_class *cls, unsigned int index);
>> +
>> +  /* Makes pairing between a congruence class CLS and semantic ITEM.  */
>> +  static void add_item_to_class (congruence_class *cls, sem_item *item);
>> +
>> +  /* Disposes split map traverse function. CLS_PTR is pointer to congruence
>> +     class, BSLOT is bitmap slot we want to release. DATA is mandatory,
>> +     but unused argument.  */
>> +  static bool release_split_map (const void *cls_ptr, bitmap *bslot, void *data);
>> +
>> +  /* Process split operation for a class given as pointer CLS_PTR,
>> +     where bitmap B splits congruence class members. DATA is used
>> +     as argument of split pair.  */
>> +  static bool traverse_congruence_split (const void *cls_ptr, bitmap *b,
>> +					 void *data);
>> +
>> +  /* Reads a section from LTO stream file FILE_DATA. Input block for DATA
>> +     contains LEN bytes.  */
>> +  void read_section (struct lto_file_decl_data *file_data, const char *data,
>> +		     size_t len);
>> +
>> +  /* Removes all callgraph and varpool nodes that are marked by symtab
>> +     as deleted.  */
>> +  void filter_removed_items (void);
>> +
>> +  /* Vector of semantic items.  */
>> +  vec <sem_item *> items;
>> +
>> +  /* A set containing all items removed by hooks.  */
>> +  pointer_set_t *removed_items_set;
>> +
>> +  /* Hashtable of congruence classes */
>> +  hash_table <congruence_class_group_hash> classes;
>> +
>> +  /* Count of congruence classes.  */
>> +  unsigned int classes_count;
>> +
>> +  /* Map data structure maps trees to semantic items.  */
>> +  pointer_map <sem_item *> decl_map;
>> +
>> +  /* Map data structure maps symtab nodes to semantic items.  */
>> +  pointer_map <sem_item *> symtab_node_map;
>> +
>> +  /* For all congruence classes, we indicate partial mapping
>> +     during reduction.  */
>> +  pointer_map <bitmap> *split_map;
>> +
>> +  /* Set to true if a splitter class is removed.  */
>> +  bool splitter_class_removed;
>> +
>> +  /* Global unique class id counter.  */
>> +  static unsigned int class_id;
>> +
>> +  /* Callgraph node removal hook holder.  */
>> +  struct cgraph_node_hook_list *cgraph_node_hooks;
>> +
>> +  /* Varpool node removal hook holder.  */
>> +  struct varpool_node_hook_list *varpool_node_hooks;
>> +
>> +  /* Bitmap stack.  */
>> +  bitmap_obstack bmstack;
>> +}; // class sem_item_optimizer
>> +
>> +}
>> diff --git a/gcc/lto-section-in.c b/gcc/lto-section-in.c
>> index d887763..f9587cf 100644
>> --- a/gcc/lto-section-in.c
>> +++ b/gcc/lto-section-in.c
>> @@ -60,7 +60,8 @@ const char *lto_section_name[LTO_N_SECTION_TYPES] =
>>     "opts",
>>     "cgraphopt",
>>     "inline",
>> -  "ipcp_trans"
>> +  "ipcp_trans",
>> +  "icf"
>>   };
>>   
>>   
>> diff --git a/gcc/lto-streamer.h b/gcc/lto-streamer.h
>> index 521d78d..3207d08 100644
>> --- a/gcc/lto-streamer.h
>> +++ b/gcc/lto-streamer.h
>> @@ -247,6 +247,7 @@ enum lto_section_type
>>     LTO_section_cgraph_opt_sum,
>>     LTO_section_inline_summary,
>>     LTO_section_ipcp_transform,
>> +  LTO_section_ipa_icf,
>>     LTO_N_SECTION_TYPES		/* Must be last.  */
>>   };
>>   
>> diff --git a/gcc/opts.c b/gcc/opts.c
>> index 2b1280a..b5a58a5 100644
>> --- a/gcc/opts.c
>> +++ b/gcc/opts.c
>> @@ -496,6 +496,7 @@ static const struct default_options default_options_table[] =
>>       { OPT_LEVELS_2_PLUS, OPT_fvect_cost_model_, NULL, VECT_COST_MODEL_CHEAP },
>>       { OPT_LEVELS_2_PLUS_SPEED_ONLY, OPT_foptimize_strlen, NULL, 1 },
>>       { OPT_LEVELS_2_PLUS, OPT_fhoist_adjacent_loads, NULL, 1 },
>> +    { OPT_LEVELS_2_PLUS, OPT_fipa_icf, NULL, 1 },
>>       { OPT_LEVELS_2_PLUS, OPT_fisolate_erroneous_paths_dereference, NULL, 1 },
>>       { OPT_LEVELS_2_PLUS, OPT_fuse_caller_save, NULL, 1 },
>>   
>> diff --git a/gcc/passes.def b/gcc/passes.def
>> index f9e0b2a..e629a1d 100644
>> --- a/gcc/passes.def
>> +++ b/gcc/passes.def
>> @@ -105,6 +105,7 @@ along with GCC; see the file COPYING3.  If not see
>>     NEXT_PASS (pass_ipa_whole_program_visibility);
>>     NEXT_PASS (pass_ipa_profile);
>>     NEXT_PASS (pass_ipa_devirt);
>> +  NEXT_PASS (pass_ipa_icf);
>>     NEXT_PASS (pass_ipa_cp);
>>     NEXT_PASS (pass_ipa_cdtor_merge);
>>     NEXT_PASS (pass_ipa_inline);
>> diff --git a/gcc/timevar.def b/gcc/timevar.def
>> index cbb64d5..c1d09eb 100644
>> --- a/gcc/timevar.def
>> +++ b/gcc/timevar.def
>> @@ -89,6 +89,7 @@ DEFTIMEVAR (TV_WHOPR_LTRANS          , "whopr ltrans")
>>   DEFTIMEVAR (TV_IPA_REFERENCE         , "ipa reference")
>>   DEFTIMEVAR (TV_IPA_PROFILE           , "ipa profile")
>>   DEFTIMEVAR (TV_IPA_PURE_CONST        , "ipa pure const")
>> +DEFTIMEVAR (TV_IPA_ICF		     , "ipa icf")
>>   DEFTIMEVAR (TV_IPA_PTA               , "ipa points-to")
>>   DEFTIMEVAR (TV_IPA_SRA               , "ipa SRA")
>>   DEFTIMEVAR (TV_IPA_FREE_LANG_DATA    , "ipa free lang data")
>> diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h
>> index 3888bb6..d55d7b8 100644
>> --- a/gcc/tree-pass.h
>> +++ b/gcc/tree-pass.h
>> @@ -464,6 +464,7 @@ extern simple_ipa_opt_pass *make_pass_ipa_free_lang_data (gcc::context *ctxt);
>>   extern simple_ipa_opt_pass *make_pass_ipa_free_inline_summary (gcc::context
>>   							       *ctxt);
>>   extern ipa_opt_pass_d *make_pass_ipa_cp (gcc::context *ctxt);
>> +extern ipa_opt_pass_d *make_pass_ipa_icf (gcc::context *ctxt);
>>   extern ipa_opt_pass_d *make_pass_ipa_devirt (gcc::context *ctxt);
>>   extern ipa_opt_pass_d *make_pass_ipa_reference (gcc::context *ctxt);
>>   extern ipa_opt_pass_d *make_pass_ipa_pure_const (gcc::context *ctxt);
>> -- 
>> 1.8.4.5
>>
>>

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 3/5] IPA ICF pass
  2014-06-24 20:31   ` Jeff Law
@ 2014-06-26 16:02     ` Martin Liška
  2014-06-26 18:46     ` Jan Hubicka
  1 sibling, 0 replies; 70+ messages in thread
From: Martin Liška @ 2014-06-26 16:02 UTC (permalink / raw)
  To: Jeff Law, gcc-patches; +Cc: hubicka


On 06/24/2014 10:31 PM, Jeff Law wrote:
> On 06/13/14 04:44, mliska wrote:
>> Hello,
>>     this is core of IPA ICF patchset. It adds new pass and registers all needed stuff related to a newly introduced interprocedural optimization.
>>
>> Algorithm description:
>>    In LGEN, we visit all read-only variables and functions. For each symbol, a hash value based on e.g. number of arguments,
>>    number of BB, GIMPLE CODES is computed (similar hash is computed for read-only variables). This kind of information is streamed
>>    for LTO.
>>
>>    In WPA, we build congruence classes for all symbols having a same hash value. For functions, these classes are subdivided in WPA by argument type comparison. Each reference (a call or a variable reference) to another semantic item candidate is marked and stored for further congruence class reduction (similar algorithm as Value Numbering: www.cs.ucr.edu/~gupta/teaching/553-07/Papers/value.pdf).
>>
>>    For every congruence class of functions with more than one semantic function, we load function body. Having this information, we can
>>    process complete semantic function equality and subdivide such congruence class. Read-only variable class members are also deeply compared.
>>
>>    After that, we process Value numbering algorithm to do a final subdivision. Finally, all items belonging to a congruence class with more than one
>>    item are merged.
>>
>> Martin
>>
>> Changelog:
>>
>> 2014-06-13  Martin Liska  <mliska@suse.cz>
>>         Jan Hubicka  <hubicka@ucw.cz>
>>
>>     * Makefile.in: New pass object file added.
>>     * common.opt: New -fipa-icf flag introduced.
>>     * doc/invoke.texi: Documentation enhanced for the pass.
>>     * lto-section-in.c: New LTO section for a summary created by IPA-ICF.
>>     * lto-streamer.h: New section name introduced.
>>     * opts.c: Optimization is added to -O2.
>>     * passes.def: New pass added.
>>     * timevar.def: New time var for IPA-ICF.
>>     * tree-pass.h: Pass construction function.
>>     * ipa-icf.h: New pass header file added.
>>     * ipa-icf.c: New pass source file added.
Hi Jeff,
    I must agree that the implementation of the patch is quite big. Suggested split makes sense, I'll do it.
> You'll note many of my comments are "do you need to ...".  You may in fact be handling that stuff correctly, they're just things I'd like you to verify are properly handled.  If they're properly handled just say so :-)
>
> At a high level, I think this needs to be broken down a bit more. We've got two high level concepts in ipa-icf.  One is all the equivalence testing the other is using that information for the icf optimization.
>
> Splitting out the equivalence testing seems like a good thing to do as there's other contexts where it would be useful.
>
> Overall I think you're on the right path and we just need to iterate a bit on this part of the patchset.
>
>>
>> @@ -7862,6 +7863,14 @@ it may significantly increase code size
>>   (see @option{--param ipcp-unit-growth=@var{value}}).
>>   This flag is enabled by default at @option{-O3}.
>>
>> +@item -fipa-icf
>> +@opindex fipa-icf
>> +Perform Identical Code Folding for functions and read-only variables.
>> +Behavior is similar to Gold Linker ICF optimization. Symbols proved
>> +as semantically equivalent are redirected to corresponding symbol. The pass
>> +sensitively decides for usage of alias, thunk or local redirection.
>> +This flag is enabled by default at @option{-O2}.
> So you've added this at -O2, what is the general compile-time impact? Would it make more sense to instead have it be part of -O3, particularly since ICF is rarely going to improve performance (sans icache issues).
This was Honza's idea to put the optimization for -O2, I'll measure compile-time impact.
>
>
>> +
>> +/* Interprocedural Identical Code Folding for functions and
>> +   read-only variables.
>> +
>> +   The goal of this transformation is to discover functions and read-only
>> +   variables which do have exactly the same semantics.
>> +
>> +   In case of functions,
>> +   we could either create a virtual clone or do a simple function wrapper
>> +   that will call equivalent function. If the function is just locally visible,
>> +   all function calls can be redirected. For read-only variables, we create
>> +   aliases if possible.
>> +
>> +   Optimization pass arranges as follows:
>> +   1) All functions and read-only variables are visited and internal
>> +      data structure, either sem_function or sem_variables is created.
>> +   2) For every symbol from the previoues step, VAR_DECL and FUNCTION_DECL are
>> +      saved and matched to corresponding sem_items.
> s/previoues/previous/
>
>> +   3) These declaration are ignored for equality check and are solved
>> +      by Value Numbering algorithm published by Alpert, Zadeck in 1992.
>> +   4) We compute hash value for each symbol.
>> +   5) Congruence classes are created based on hash value. If hash value are
>> +      equal, equals function is called and symbols are deeply compared.
>> +      We must prove that all SSA names, declarations and other items
>> +      correspond.
>> +   6) Value Numbering is executed for these classes. At the end of the process
>> +      all symbol members in remaining classes can be mrged.
> s/mrged/merged.
>
>
>
>> +   7) Merge operation creates alias in case of read-only variables. For
>> +      callgraph node, we must decide if we can redirect local calls,
>> +      create an alias or a thunk.
> Presumably that's the order in which we try to resolve identical functions (first by redirection if it's local, then an alias, then a thunk)?
>
> Is the code conditionalized so that it works properly on targets where we can't create aliases?  Do we have any such targets that can be easily tested these days?  Has this been tested on anything other than x86-64 linux?  AIX immediately comes to mind as an interesting testing target.
I hope Honza has many experiences with AIX in recent days and can help me ;)
>
>
>
>>
>> +
>> +namespace {
>> +
>> +func_checker::func_checker (): initialized (false)
>> +{
>> +}
>> +
>> +/* Itializes internal structures according to given number of
> s/Itializes/Initializes/
>
>
>> +   source and target SSA names. The number of source names is SSA_SOURCE,
>> +   respectively SSA_TARGET.  */
>> +
>> +void
>> +func_checker::initialize (unsigned ssa_source, unsigned ssa_target)
>> +{
>> +  release ();
>> +  initialized = true;
>> +
>> +  source_ssa_names.create (ssa_source);
>> +  target_ssa_names.create (ssa_target);
>> +
>> +  for (unsigned int i = 0; i < ssa_source; i++)
>> +    source_ssa_names.safe_push (-1);
>> +
>> +  for (unsigned int i = 0; i < ssa_target; i++)
>> +    target_ssa_names.safe_push (-1);
>> +
>> +  edge_map = new pointer_map <edge> ();
>> +
>> +  decl_map = new pointer_map <tree> ();
>> +}
>> +
>> +/* Memory release routine.  */
>> +
>> +void
>> +func_checker::release (void)
>> +{
>> +  if (!initialized)
>> +    return;
>> +
>> +  delete edge_map;
>> +  delete decl_map;
>> +  source_ssa_names.release();
>> +  target_ssa_names.release();
>> +}
> So I'm a big fan of RAII style of writing code.  While I primarily like it for locking, I also find it's a good model for memory management. Is there any reason these two functions aren't a suitable ctor/dtor?

Yeah, Trevor pointed the same observation. Fixed.
>
>
>> +
>> +/* Verifies that trees T1 and T2 do correspond.  */
>> +
>> +bool
>> +func_checker::compare_ssa_name (tree t1, tree t2)
>> +{
>> +  unsigned i1 = SSA_NAME_VERSION (t1);
>> +  unsigned i2 = SSA_NAME_VERSION (t2);
>> +
>> +  if (source_ssa_names[i1] == -1)
>> +    source_ssa_names[i1] = i2;
>> +  else if (source_ssa_names[i1] != (int) i2)
>> +    return false;
>> +
>> +  if(target_ssa_names[i2] == -1)
>> +    target_ssa_names[i2] = i1;
>> +  else if (target_ssa_names[i2] != (int) i1)
>> +    return false;
>> +
>> +  return true;
>> +}
> Isn't this really checking for equivalence? "do correspond" seems awkward here.
Comment updated.
>> +
>> +/* Verification function for edges E1 and E2.  */
>> +
>> +bool
>> +func_checker::compare_edge (edge e1, edge e2)
>> +{
>> +  if (e1->flags != e2->flags)
>> +    return false;
> Presumably there's no flags we can safely ignore.  So absolute equality seems reasonable here.
>
>
>> +/* Congruence class constructor for a new class with _ID.  */
>> +
>> +congruence_class::congruence_class (unsigned int _id): id(_id)
>> +{
>> +  members.create (2);
>> +}
> Is there a dtor which releases this object?
Fixed.
>> +
>> +void
>> +sem_item::setup (bitmap_obstack *stack)
>> +{
>> +  gcc_checking_assert (node);
>> +
>> +  refs.create (0);
>> +  tree_refs.create (0);
>> +  usages.create (0);
>> +  tree_refs_set = pointer_set_create ();
>> +  usage_index_bitmap = BITMAP_ALLOC (stack);
>> +}
>> +
>> +sem_item::~sem_item ()
>> +{
>> +  if (tree_refs_set)
>> +    pointer_set_destroy (tree_refs_set);
>> +
>> +  for (unsigned i = 0; i < usages.length (); i++)
>> +    delete usages[i];
>> +}
> Do you need to release refs, tree_refs or the bitmap?

Fixed.
>
>> +
>> +/* Compare two types if are same aliases in case of strict aliasing
>> +   is enabled.  */
>> +bool
>> +sem_item::compare_for_aliasing (tree t1, tree t2)
>> +{
>> +  if (flag_strict_aliasing)
>> +    {
>> +      alias_set_type s1 = get_deref_alias_set (TREE_TYPE (t1));
>> +      alias_set_type s2 = get_deref_alias_set (TREE_TYPE (t2));
>> +
>> +      return s1 == s2;
>> +    }
>> +
>> +  return true;
>> +}
> Is returning TRUE really the conservatively correct thing to do in the absence of aliasing information?  Isn't that case really "I don't know" in which case the proper return value is FALSE?
The function is just fraction of comparison machinery for types.
>
>
>
>
>
>> +
>> +/* Semantic function constructor that uses STACK as bitmap memory stack.  */
>> +
>> +sem_function::sem_function (bitmap_obstack *stack): sem_item (FUNC, stack),
>> +  compared_func (NULL)
>> +{
>> +  arg_types.create (0);
> Does this need to be released?
Fixed.
>
>> +
>> +/* Gets symbol name of the item.  */
>> +
>> +const char *
>> +sem_function::name (void)
>> +{
>> +  return node->name ();
>> +}
>> +
>> +/* Gets assembler name of the item.  */
>> +
>> +const char *
>> +sem_function::asm_name (void)
>> +{
>> +  return node->asm_name ();
>> +}
> Are these trivial enough that they should be inlined in the class definition?  As much as I dislike that style of programming, these seem like classic candidates unless I'm missing something.
Good idea, small functions will be moved to header file.
>
>
>> +/* References independent hash function. */
>> +
>> +hashval_t
>> +sem_function::get_hash (void)
>> +{
>> +  if(!hash)
>> +    {
>> +      hash = 177454; /* Random number for function type.  */
>> +
>> +      hash = iterative_hash_object (arg_count, hash);
>> +      hash = iterative_hash_object (bb_count, hash);
>> +      hash = iterative_hash_object (edge_count, hash);
>> +      hash = iterative_hash_object (cfg_checksum, hash);
> Does CFG_CHECKSUM encompass the bb/edge counts?

Good idea, that can be skipped because cfg_checksum encompasses bb_count and edge destination indices.
>
> +}
>> +
>> +/* Fast equality function based on knowledge known in WPA.  */
>> +
>> +bool
>> +sem_function::equals_wpa (sem_item *item)
>> +{
>> +  if (item->type != FUNC)
>> +    return false;
> Can this ever happen?  Either remove to turn into an assert.
gcc_assert introduced here.
>
>> +/* Processes function equality comparison.  */
>> +
>> +bool
>> +sem_function::equals_private (sem_item *item)
>> +{
>> +  if (item->type != FUNC)
>> +    return false;
>> +
>> +  basic_block bb1, bb2;
>> +  edge e1, e2;
>> +  edge_iterator ei1, ei2;
>> +  int *bb_dict = NULL;
>> +  bool result = true;
>> +  tree arg1, arg2;
>> +
>> +  compared_func = static_cast<sem_function *> (item);
>> +
>> +  gcc_assert (decl != item->decl);
>> +
>> +  if (arg_count != compared_func->arg_count
>> +      || bb_count != compared_func->bb_count
>> +      || edge_count != compared_func->edge_count
>> +      || cfg_checksum != compared_func->cfg_checksum)
> Again, check if testing the bb/edge counts aren't necessary as we check the cfg checksum.
There could potentially happen hash collision. For being sure, let's compare these counts.

>
>> +    SE_EXIT_FALSE();
>> +
>> +  if (!equals_wpa (item))
>> +    return false;
>> +
>> +  /* Checking function arguments.  */
>> +  tree decl1 = DECL_ATTRIBUTES (decl);
>> +  tree decl2 = DECL_ATTRIBUTES (compared_func->decl);
> So are there any attributes we can safely ignore?  Probably not. However, we ought to handle the case where the attributes appear in different orders.
>
> Also makes me wonder does the comparison of arguments/return value verify their attributes as well?
Attribute values comparison will be added, as well as support for a different order.

>
>
>> +
>> +/* Returns cgraph_node.  */
>> +
>> +struct cgraph_node *
>> +sem_function::get_node (void)
>> +{
>> +  return cgraph (node);
>> +}
>> +
>> +/* Initialize semantic item by info reachable during LTO WPA phase.  */
>> +
>> +void
>> +sem_function::init_wpa (void)
>> +{
>> +  parse_tree_args ();
>> +}
> inline? Worth or not worth the headache?
Agree.
>
>
>> +
>> +bool
>> +sem_function::compare_bb (sem_bb_t *bb1, sem_bb_t *bb2, tree func1, tree func2)
> So this routine walks down the gimple statements and compares them for equality.  Would it make sense to have the equality testing in gimple?  That way if someone adds a new gimple code the places they need to check/update are at least somewhat more localized?

I know it's not ideal, but moving this part to gimple.c will add many lines of code. I would prefer, as you suggested, to break this part to a separate comparison responsible part.
>
>
>> +
>> +      for (i = 0; i < size1; ++i)
>> +    {
>> +      t1 = gimple_phi_arg (phi1, i)->def;
>> +      t2 = gimple_phi_arg (phi2, i)->def;
>> +
>> +      if (!compare_operand (t1, t2, func1, func2))
>> +        SE_EXIT_FALSE ();
>> +
>> +      e1 = gimple_phi_arg_edge (phi1, i);
>> +      e2 = gimple_phi_arg_edge (phi2, i);
>> +
>> +      if (!checker.compare_edge (e1, e2))
>> +        SE_EXIT_FALSE ();
>> +    }
> I don't think we guarantee any particular order on the PHI args. ISTM you'd want to sort them or something so as not to reject a possible duplicate simply because of ordering issues.
>
> Related, I'm not sure bb indexes are even guaranteed to have any stable ordering.  So ISTM you'd want to do something like a DFS walk to set a index for each block, then sort the PHI arguments based on DFS index to get a stable, consistent check here.
As I can see comment for coverage_compute_cfg_checksum (struct function *fn):
    The checksum is calculated carefully so that
    source code changes that doesn't affect the control flow graph
    won't change the checksum.

The function iterates BBs and edges in the same manner. IPA ICF uses for iteration same approach, but I am not sure if there are any order assurances?

>
>
>> +/* For given basic blocks BB1 and BB2 (from functions FUNC1 and FUNC),
>> +   true value is returned if exception handling regions are equivalent
>> +   in these blocks.  */
>> +
>> +bool
>> +sem_function::compare_eh_region (eh_region r1, eh_region r2, tree func1,
>> +                 tree func2)
> Similarly, are EH region indexes stable enough to check here?
>
>> +
>> +void
>> +sem_function::gsi_next_nondebug_stmt (gimple_stmt_iterator &gsi)
>> +{
>> +  gimple s;
>> +
>> +  s = gsi_stmt (gsi);
>> +
>> +  while (gimple_code (s) == GIMPLE_DEBUG)
>> +    {
>> +      gsi_next (&gsi);
>> +      gcc_assert (!gsi_end_p (gsi));
>> +
>> +      s = gsi_stmt (gsi);
>> +    }
>> +}
> Should this be elsewhere?  Seems like the concept of asking for the next non-debug statement happens often.

Fixed.
>
>
>> +
>> +/* Iterates GSI statement iterator to the next non-virtual statement.  */
>> +
>> +void
>> +sem_function::gsi_next_nonvirtual_phi (gimple_stmt_iterator &it)
>> +{
>> +  gimple phi;
>> +
>> +  if (gsi_end_p (it))
>> +    return;
>> +
>> +  phi = gsi_stmt (it);
>> +  gcc_assert (phi != NULL);
>> +
>> +  while (virtual_operand_p (gimple_phi_result (phi)))
>> +    {
>> +      gsi_next (&it);
>> +
>> +      if (gsi_end_p (it))
>> +    return;
>> +
>> +      phi = gsi_stmt (it);
>> +    }
>> +}
> Similarly.
Fixed too.
>
>
>> +
>> +/* Verifies that trees T1 and T2 do correspond.  */
> Again, don't like the "do correspond" terminology.  Seems like we're testing for equivalence.
>
> A lot of the sem_function::compare_XXX feel like they should be elsewhere, possibly their own module.  I could easily see reusing them for other purposes.
Good idea.

>
> There's a number of algorithms that want to do the same kind of block hashing & equality testing that you're doing here in ICF.  Maybe pull them into their own file/api?

I am open to separate algorithm independent part to a new API.

>
>> +/* Gets symbol name of the item.  */
>> +
>> +const char *
>> +sem_variable::name (void)
>> +{
>> +  return node->name ();
>> +}
>> +
>> +/* Gets assembler name of the item.  */
>> +
>> +const char *
>> +sem_variable::asm_name (void)
>> +{
>> +  return node->asm_name ();
>> +}
> Inline the accessors?
>
> I'm starting to gloss over things....  It feels like we've got too much stuff in this one file.  Breaking things out would help (like for example the hashing/equality bits).  I'd like to see things broken out a bit and reposted for further reviewing.
Thank you for your review,
tomorrow, I will send updated patchset for another circle of review.

Martin
>
> Jeff
>

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 3/5] IPA ICF pass
  2014-06-24 20:31   ` Jeff Law
  2014-06-26 16:02     ` Martin Liška
@ 2014-06-26 18:46     ` Jan Hubicka
  2014-06-30 12:05       ` Martin Liška
  2014-06-30 19:06       ` Jeff Law
  1 sibling, 2 replies; 70+ messages in thread
From: Jan Hubicka @ 2014-06-26 18:46 UTC (permalink / raw)
  To: Jeff Law; +Cc: mliska, gcc-patches, hubicka

Jeff,
thanks for review! I did some passes over the patch before it got to the ML, I am
happy to have independent opinion. 
> >+@item -fipa-icf
> >+@opindex fipa-icf
> >+Perform Identical Code Folding for functions and read-only variables.
> >+Behavior is similar to Gold Linker ICF optimization. Symbols proved
> >+as semantically equivalent are redirected to corresponding symbol. The pass
> >+sensitively decides for usage of alias, thunk or local redirection.
> >+This flag is enabled by default at @option{-O2}.
> So you've added this at -O2, what is the general compile-time
> impact? Would it make more sense to instead have it be part of -O3,
> particularly since ICF is rarely going to improve performance (sans
> icache issues).

I think code size optimization not sacrifying any (or too much of) performance are
generally very welcome at -O2.  Compared to LLVM and Microsoft compilers we are
on code bloat side at -O2.

http://hubicka.blogspot.ca/2014/04/linktime-optimization-in-gcc-2-firefox.html
has some numbers for -O2 GGC/LLVM.

I believe this is result of tunning for relatively small benchamrks (SPECS) and
I hope we could revisit -O2 for code size considerations for 4.10 somewhat.  If
tuned well, ICF has no reason to be expnesive wrt compile time. So lets shoot
for that.  The considerable donwside of enabling ICF IMO should be only
disturbing effect on debug info.
> >+  return true;
> >+}
> Isn't this really checking for equivalence? "do correspond" seems
> awkward here.

The function turns the names equivalent on first invocation for a given name
and later checks that this tentative equivalence holds.

Not sure what is best name for it (originaly it was verify that did not sound
right to me either)
> 
> >+
> >+/* Verification function for edges E1 and E2.  */
> >+
> >+bool
> >+func_checker::compare_edge (edge e1, edge e2)
> >+{
> >+  if (e1->flags != e2->flags)
> >+    return false;
> Presumably there's no flags we can safely ignore.  So absolute
> equality seems reasonable here.

Yep
> >+/* Compare two types if are same aliases in case of strict aliasing
> >+   is enabled.  */
> >+bool
> >+sem_item::compare_for_aliasing (tree t1, tree t2)
> >+{
> >+  if (flag_strict_aliasing)
> >+    {
> >+      alias_set_type s1 = get_deref_alias_set (TREE_TYPE (t1));
> >+      alias_set_type s2 = get_deref_alias_set (TREE_TYPE (t2));
> >+
> >+      return s1 == s2;
> >+    }
> >+
> >+  return true;
> >+}
> Is returning TRUE really the conservatively correct thing to do in
> the absence of aliasing information?  Isn't that case really "I
> don't know" in which case the proper return value is FALSE?

I think with -fno-strict-aliasing the set should be 0 (Richi?) and thus we can
compare for equality.  We probably can be on agressive side and let 0 alias
set prevail the non-0.  But that can be done incrementally.

We also need to match type inheritance equality for polymorphic types. I will
add function for that into ipa-devirt.

> >+/* References independent hash function.  */
> >+
> >+hashval_t
> >+sem_function::get_hash (void)
> >+{
> >+  if(!hash)
> >+    {
> >+      hash = 177454; /* Random number for function type.  */
> >+
> >+      hash = iterative_hash_object (arg_count, hash);
> >+      hash = iterative_hash_object (bb_count, hash);
> >+      hash = iterative_hash_object (edge_count, hash);
> >+      hash = iterative_hash_object (cfg_checksum, hash);
> Does CFG_CHECKSUM encompass the bb/edge counts?

It is one used by profiling code to match profiles, so it should.
> >+    SE_EXIT_FALSE();
> >+
> >+  if (!equals_wpa (item))
> >+    return false;
> >+
> >+  /* Checking function arguments.  */
> >+  tree decl1 = DECL_ATTRIBUTES (decl);
> >+  tree decl2 = DECL_ATTRIBUTES (compared_func->decl);
> So are there any attributes we can safely ignore?  Probably not.
> However, we ought to handle the case where the attributes appear in
> different orders.

There are few, like we can ignore "weak" or "visibility" attribute because we do
produce alias with proper visibility anyway.  My plan is to start removing those
attributes from declarations once they are turned into suitable representation
in symbol table (or for attributes like const/noreturn/pure where we have explicit
decl flags).  This will make our life bit easier later, too.

We probably then can whitelist some attributes, but I would deal with this later.
> >+/* Returns cgraph_node.  */
> >+
> >+struct cgraph_node *
> >+sem_function::get_node (void)
> >+{
> >+  return cgraph (node);
> >+}
> >+
> >+/* Initialize semantic item by info reachable during LTO WPA phase.  */
> >+
> >+void
> >+sem_function::init_wpa (void)
> >+{
> >+  parse_tree_args ();
> >+}
> inline? Worth or not worth the headache?

We ought to autoinline simple wrappers even at -Os (for size)
(I am not agains explicit inline keywords here tough)
> 
> 
> >+
> >+bool
> >+sem_function::compare_bb (sem_bb_t *bb1, sem_bb_t *bb2, tree func1, tree func2)
> So this routine walks down the gimple statements and compares them
> for equality.  Would it make sense to have the equality testing in
> gimple?  That way if someone adds a new gimple code the places they
> need to check/update are at least somewhat more localized?

There are few places that does equality testing (tailmerge) AFAIK.  They are all somewhat
different - I think we can export this equality machinery with reosnable API and try to turn
those to use it, but it may be good incremental project IMO.
> 
> 
> >+
> >+      for (i = 0; i < size1; ++i)
> >+	{
> >+	  t1 = gimple_phi_arg (phi1, i)->def;
> >+	  t2 = gimple_phi_arg (phi2, i)->def;
> >+
> >+	  if (!compare_operand (t1, t2, func1, func2))
> >+	    SE_EXIT_FALSE ();
> >+
> >+	  e1 = gimple_phi_arg_edge (phi1, i);
> >+	  e2 = gimple_phi_arg_edge (phi2, i);
> >+
> >+	  if (!checker.compare_edge (e1, e2))
> >+	    SE_EXIT_FALSE ();
> >+	}
> I don't think we guarantee any particular order on the PHI args.
> ISTM you'd want to sort them or something so as not to reject a
> possible duplicate simply because of ordering issues.
> 
> Related, I'm not sure bb indexes are even guaranteed to have any
> stable ordering.  So ISTM you'd want to do something like a DFS walk
> to set a index for each block, then sort the PHI arguments based on
> DFS index to get a stable, consistent check here.

Yep, there are no resonable orders on it.  If function starts same in source code they ought
to end up same here.  Plan was to first match for exact equality and then play with 
smarter tricks here.
> 
> I'm starting to gloss over things....  It feels like we've got too
> much stuff in this one file.  Breaking things out would help (like
> for example the hashing/equality bits).  I'd like to see things
> broken out a bit and reposted for further reviewing.

Yep, the pass has grown up to be rather long. The gimple equality testing is the
main body of work, so perhaps doing this in separate file is good idea.

Honza
> 
> Jeff

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 2/5] Existing call graph infrastructure enhancement
  2014-06-17 20:00   ` Jeff Law
@ 2014-06-30 11:49     ` Martin Liška
  2014-06-30 18:54       ` Jeff Law
  0 siblings, 1 reply; 70+ messages in thread
From: Martin Liška @ 2014-06-30 11:49 UTC (permalink / raw)
  To: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 2328 bytes --]


On 06/17/2014 10:00 PM, Jeff Law wrote:
> On 06/13/14 04:26, mliska wrote:
>> Hi,
>>      this small patch prepares remaining needed infrastructure for the new pass.
>>
>> Changelog:
>>
>> 2014-06-13  Martin Liska  <mliska@suse.cz>
>>         Honza Hubicka  <hubicka@ucw.cz>
>>
>>     * ipa-utils.h (polymorphic_type_binfo_p): Function marked external
>>     instead of static.
>>     * ipa-devirt.c (polymorphic_type_binfo_p): Likewise.
>>     * ipa-prop.h (count_formal_params): Likewise.
>>     * ipa-prop.c (count_formal_params): Likewise.
>>     * ipa-utils.c (ipa_merge_profiles): Be more tolerant if we merge
>>     profiles for semantically equivalent functions.
>>     * passes.c (do_per_function): If we load body of a function during WPA,
>>     this condition should behave same.
>>     * varpool.c (ctor_for_folding): More tolerant assert for variable
>>     aliases created during WPA.
> Presumably we don't have any useful way to merge the cases where we have provides for SRC & DST in ipa_merge_profiles or even to guess which is more useful when presented with both?  Does it make sense to log this into a debugging file when we drop one?
Hello,
    this merge function was written by Honza, what do you think Honza about this note?

> I think this patch is fine.  If adding logging makes sense, then feel free to do so and consider that trivial change pre-approved.
I made a small change to this patch, where I moved 'gsi_next_nonvirtual_phi' from the pass to gimple-iterator.h.

Ready for trunk with this change?
Thanks,
Martin

gcc/ChangeLog

2014-06-30  Martin Liska  <mliska@suse.cz>
         Honza Hubicka  <hubicka@ucw.cz>

     * gimple-iterator.h (gsi_next_nonvirtual_phi): New function.
     * ipa-utils.h (polymorphic_type_binfo_p): Function marked external
     instead of static.
     * ipa-devirt.c (polymorphic_type_binfo_p): Likewise.
     * ipa-prop.h (count_formal_params): Likewise.
     * ipa-prop.c (count_formal_params): Likewise.
     * ipa-utils.c (ipa_merge_profiles): Be more tolerant if we merge
     profiles for semantically equivalent functions.
     * passes.c (do_per_function): If we load body of a function during WPA,
     this condition should behave same.
     * varpool.c (ctor_for_folding): More tolerant assert for variable
     aliases created during WPA.

>
> Jeff
>


[-- Attachment #2: 0001-Existing-call-graph-infrastructure-enhancement.patch --]
[-- Type: text/x-patch, Size: 3983 bytes --]

diff --git a/gcc/gimple-iterator.h b/gcc/gimple-iterator.h
index 909d58b..47168b9 100644
--- a/gcc/gimple-iterator.h
+++ b/gcc/gimple-iterator.h
@@ -281,6 +281,30 @@ gsi_last_nondebug_bb (basic_block bb)
   return i;
 }
 
+/* Iterates I statement iterator to the next non-virtual statement.  */
+
+static inline void
+gsi_next_nonvirtual_phi (gimple_stmt_iterator *i)
+{
+  gimple phi;
+
+  if (gsi_end_p (*i))
+    return;
+
+  phi = gsi_stmt (*i);
+  gcc_assert (phi != NULL);
+
+  while (virtual_operand_p (gimple_phi_result (phi)))
+    {
+      gsi_next (i);
+
+      if (gsi_end_p (*i))
+	return;
+
+      phi = gsi_stmt (*i);
+    }
+}
+
 /* Return the basic block associated with this iterator.  */
 
 static inline basic_block
diff --git a/gcc/ipa-devirt.c b/gcc/ipa-devirt.c
index d6b85bf..2f62323 100644
--- a/gcc/ipa-devirt.c
+++ b/gcc/ipa-devirt.c
@@ -176,7 +176,7 @@ struct GTY(()) odr_type_d
    inheritance (because vtables are shared).  Look up the BINFO of type
    and check presence of its vtable.  */
 
-static inline bool
+bool
 polymorphic_type_binfo_p (tree binfo)
 {
   /* See if BINFO's type has an virtual table associtated with it.  */
diff --git a/gcc/ipa-prop.c b/gcc/ipa-prop.c
index 68efc77..bb2fbf3 100644
--- a/gcc/ipa-prop.c
+++ b/gcc/ipa-prop.c
@@ -210,7 +210,7 @@ ipa_populate_param_decls (struct cgraph_node *node,
 
 /* Return how many formal parameters FNDECL has.  */
 
-static inline int
+int
 count_formal_params (tree fndecl)
 {
   tree parm;
diff --git a/gcc/ipa-prop.h b/gcc/ipa-prop.h
index 8886e93..bc6249e 100644
--- a/gcc/ipa-prop.h
+++ b/gcc/ipa-prop.h
@@ -529,6 +529,7 @@ void ipa_free_all_edge_args (void);
 void ipa_free_all_structures_after_ipa_cp (void);
 void ipa_free_all_structures_after_iinln (void);
 void ipa_register_cgraph_hooks (void);
+int count_formal_params (tree fndecl);
 
 /* This function ensures the array of node param infos is big enough to
    accommodate a structure for all nodes and reallocates it if not.  */
diff --git a/gcc/ipa-utils.c b/gcc/ipa-utils.c
index c191210..d58b170 100644
--- a/gcc/ipa-utils.c
+++ b/gcc/ipa-utils.c
@@ -660,13 +660,8 @@ ipa_merge_profiles (struct cgraph_node *dst,
   if (dst->tp_first_run > src->tp_first_run && src->tp_first_run)
     dst->tp_first_run = src->tp_first_run;
 
-  if (src->profile_id)
-    {
-      if (!dst->profile_id)
-	dst->profile_id = src->profile_id;
-      else
-	gcc_assert (src->profile_id == dst->profile_id);
-    }
+  if (src->profile_id && !dst->profile_id)
+    dst->profile_id = src->profile_id;
 
   if (!dst->count)
     return;
diff --git a/gcc/ipa-utils.h b/gcc/ipa-utils.h
index a2c985a..996249a 100644
--- a/gcc/ipa-utils.h
+++ b/gcc/ipa-utils.h
@@ -72,6 +72,8 @@ struct odr_type_d;
 typedef odr_type_d *odr_type;
 void build_type_inheritance_graph (void);
 void update_type_inheritance_graph (void);
+bool polymorphic_type_binfo_p (tree binfo);
+
 vec <cgraph_node *>
 possible_polymorphic_call_targets (tree, HOST_WIDE_INT,
 				   ipa_polymorphic_call_context,
diff --git a/gcc/passes.c b/gcc/passes.c
index 91b644e..cd88823 100644
--- a/gcc/passes.c
+++ b/gcc/passes.c
@@ -1506,7 +1506,7 @@ do_per_function (void (*callback) (function *, void *data), void *data)
     {
       struct cgraph_node *node;
       FOR_EACH_DEFINED_FUNCTION (node)
-	if (node->analyzed && gimple_has_body_p (node->decl)
+	if (node->analyzed && (gimple_has_body_p (node->decl) && !in_lto_p)
 	    && (!node->clone_of || node->decl != node->clone_of->decl))
 	  callback (DECL_STRUCT_FUNCTION (node->decl), data);
     }
diff --git a/gcc/varpool.c b/gcc/varpool.c
index 04ac870..59f4f8a 100644
--- a/gcc/varpool.c
+++ b/gcc/varpool.c
@@ -301,6 +301,7 @@ ctor_for_folding (tree decl)
   if (decl != real_decl)
     {
       gcc_assert (!DECL_INITIAL (decl)
+		  || (node->alias && varpool_alias_target (node) == real_node)
 		  || DECL_INITIAL (decl) == error_mark_node);
       if (lookup_attribute ("weakref", DECL_ATTRIBUTES (decl)))
 	{
-- 
1.8.4.5


^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 3/5] IPA ICF pass
  2014-06-26 18:46     ` Jan Hubicka
@ 2014-06-30 12:05       ` Martin Liška
  2014-07-01 23:45         ` Trevor Saunders
  2014-06-30 19:06       ` Jeff Law
  1 sibling, 1 reply; 70+ messages in thread
From: Martin Liška @ 2014-06-30 12:05 UTC (permalink / raw)
  To: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 8539 bytes --]


On 06/26/2014 08:46 PM, Jan Hubicka wrote:
> Jeff,
> thanks for review! I did some passes over the patch before it got to the ML, I am
> happy to have independent opinion.
>>> +@item -fipa-icf
>>> +@opindex fipa-icf
>>> +Perform Identical Code Folding for functions and read-only variables.
>>> +Behavior is similar to Gold Linker ICF optimization. Symbols proved
>>> +as semantically equivalent are redirected to corresponding symbol. The pass
>>> +sensitively decides for usage of alias, thunk or local redirection.
>>> +This flag is enabled by default at @option{-O2}.
>> So you've added this at -O2, what is the general compile-time
>> impact? Would it make more sense to instead have it be part of -O3,
>> particularly since ICF is rarely going to improve performance (sans
>> icache issues).
> I think code size optimization not sacrifying any (or too much of) performance are
> generally very welcome at -O2.  Compared to LLVM and Microsoft compilers we are
> on code bloat side at -O2.
>
> http://hubicka.blogspot.ca/2014/04/linktime-optimization-in-gcc-2-firefox.html
> has some numbers for -O2 GGC/LLVM.
>
> I believe this is result of tunning for relatively small benchamrks (SPECS) and
> I hope we could revisit -O2 for code size considerations for 4.10 somewhat.  If
> tuned well, ICF has no reason to be expnesive wrt compile time. So lets shoot
> for that.  The considerable donwside of enabling ICF IMO should be only
> disturbing effect on debug info.
>>> +  return true;
>>> +}
>> Isn't this really checking for equivalence? "do correspond" seems
>> awkward here.
> The function turns the names equivalent on first invocation for a given name
> and later checks that this tentative equivalence holds.
>
> Not sure what is best name for it (originaly it was verify that did not sound
> right to me either)
>>> +
>>> +/* Verification function for edges E1 and E2.  */
>>> +
>>> +bool
>>> +func_checker::compare_edge (edge e1, edge e2)
>>> +{
>>> +  if (e1->flags != e2->flags)
>>> +    return false;
>> Presumably there's no flags we can safely ignore.  So absolute
>> equality seems reasonable here.
> Yep
>>> +/* Compare two types if are same aliases in case of strict aliasing
>>> +   is enabled.  */
>>> +bool
>>> +sem_item::compare_for_aliasing (tree t1, tree t2)
>>> +{
>>> +  if (flag_strict_aliasing)
>>> +    {
>>> +      alias_set_type s1 = get_deref_alias_set (TREE_TYPE (t1));
>>> +      alias_set_type s2 = get_deref_alias_set (TREE_TYPE (t2));
>>> +
>>> +      return s1 == s2;
>>> +    }
>>> +
>>> +  return true;
>>> +}
>> Is returning TRUE really the conservatively correct thing to do in
>> the absence of aliasing information?  Isn't that case really "I
>> don't know" in which case the proper return value is FALSE?
> I think with -fno-strict-aliasing the set should be 0 (Richi?) and thus we can
> compare for equality.  We probably can be on agressive side and let 0 alias
> set prevail the non-0.  But that can be done incrementally.
>
> We also need to match type inheritance equality for polymorphic types. I will
> add function for that into ipa-devirt.

Hi,
    I would welcome help with type comparison function sensitive to type inheritance.

>
>>> +/* References independent hash function.  */
>>> +
>>> +hashval_t
>>> +sem_function::get_hash (void)
>>> +{
>>> +  if(!hash)
>>> +    {
>>> +      hash = 177454; /* Random number for function type.  */
>>> +
>>> +      hash = iterative_hash_object (arg_count, hash);
>>> +      hash = iterative_hash_object (bb_count, hash);
>>> +      hash = iterative_hash_object (edge_count, hash);
>>> +      hash = iterative_hash_object (cfg_checksum, hash);
>> Does CFG_CHECKSUM encompass the bb/edge counts?
> It is one used by profiling code to match profiles, so it should.
>>> +    SE_EXIT_FALSE();
>>> +
>>> +  if (!equals_wpa (item))
>>> +    return false;
>>> +
>>> +  /* Checking function arguments.  */
>>> +  tree decl1 = DECL_ATTRIBUTES (decl);
>>> +  tree decl2 = DECL_ATTRIBUTES (compared_func->decl);
>> So are there any attributes we can safely ignore?  Probably not.
>> However, we ought to handle the case where the attributes appear in
>> different orders.
> There are few, like we can ignore "weak" or "visibility" attribute because we do
> produce alias with proper visibility anyway.  My plan is to start removing those
> attributes from declarations once they are turned into suitable representation
> in symbol table (or for attributes like const/noreturn/pure where we have explicit
> decl flags).  This will make our life bit easier later, too.
>
> We probably then can whitelist some attributes, but I would deal with this later.
>>> +/* Returns cgraph_node.  */
>>> +
>>> +struct cgraph_node *
>>> +sem_function::get_node (void)
>>> +{
>>> +  return cgraph (node);
>>> +}
>>> +
>>> +/* Initialize semantic item by info reachable during LTO WPA phase.  */
>>> +
>>> +void
>>> +sem_function::init_wpa (void)
>>> +{
>>> +  parse_tree_args ();
>>> +}
>> inline? Worth or not worth the headache?
> We ought to autoinline simple wrappers even at -Os (for size)
> (I am not agains explicit inline keywords here tough)
>>
>>> +
>>> +bool
>>> +sem_function::compare_bb (sem_bb_t *bb1, sem_bb_t *bb2, tree func1, tree func2)
>> So this routine walks down the gimple statements and compares them
>> for equality.  Would it make sense to have the equality testing in
>> gimple?  That way if someone adds a new gimple code the places they
>> need to check/update are at least somewhat more localized?
> There are few places that does equality testing (tailmerge) AFAIK.  They are all somewhat
> different - I think we can export this equality machinery with reosnable API and try to turn
> those to use it, but it may be good incremental project IMO.
I definitely agree with aforementioned approach. The pass has few places where we can approve it to catch more-complex semantically equivalent functions and variables. These corner cases can improve the pass, but I would expect just small gain.

Majority of observations was added to this new version of the pass. I moved GIMPLE related comparison machinery to a separate file (ipa-icf-gimple.c). I hope it is acceptable for better pass understanding. Further enhancement would require introduction of a new API and as Honza said that could be good incremental project.

Thank you,
Martin

gcc/ChangeLog

2014-06-13  Martin Liska  <mliska@suse.cz>
         Jan Hubicka  <hubicka@ucw.cz>

     * Makefile.in: New pass object file added.
     * common.opt: New -fipa-icf flag introduced.
     * doc/invoke.texi: Documentation enhanced for the pass.
     * lto-section-in.c: New LTO section for a summary created by IPA-ICF.
     * lto-streamer.h: New section name introduced.
     * opts.c: Optimization is added to -O2.
     * passes.def: New pass added.
     * timevar.def: New time var for IPA-ICF.
     * tree-pass.h: Pass construction function.
     * ipa-icf.h: New pass header file added.
     * ipa-icf.c: New pass source file added.
     * ipa-icf-gimple.c: Likewise.

>>
>>> +
>>> +      for (i = 0; i < size1; ++i)
>>> +	{
>>> +	  t1 = gimple_phi_arg (phi1, i)->def;
>>> +	  t2 = gimple_phi_arg (phi2, i)->def;
>>> +
>>> +	  if (!compare_operand (t1, t2, func1, func2))
>>> +	    SE_EXIT_FALSE ();
>>> +
>>> +	  e1 = gimple_phi_arg_edge (phi1, i);
>>> +	  e2 = gimple_phi_arg_edge (phi2, i);
>>> +
>>> +	  if (!checker.compare_edge (e1, e2))
>>> +	    SE_EXIT_FALSE ();
>>> +	}
>> I don't think we guarantee any particular order on the PHI args.
>> ISTM you'd want to sort them or something so as not to reject a
>> possible duplicate simply because of ordering issues.
>>
>> Related, I'm not sure bb indexes are even guaranteed to have any
>> stable ordering.  So ISTM you'd want to do something like a DFS walk
>> to set a index for each block, then sort the PHI arguments based on
>> DFS index to get a stable, consistent check here.
> Yep, there are no resonable orders on it.  If function starts same in source code they ought
> to end up same here.  Plan was to first match for exact equality and then play with
> smarter tricks here.
>> I'm starting to gloss over things....  It feels like we've got too
>> much stuff in this one file.  Breaking things out would help (like
>> for example the hashing/equality bits).  I'd like to see things
>> broken out a bit and reposted for further reviewing.
> Yep, the pass has grown up to be rather long. The gimple equality testing is the
> main body of work, so perhaps doing this in separate file is good idea.
>
> Honza
>> Jeff


[-- Attachment #2: 0002-IPA-ICF-pass.patch --]
[-- Type: text/x-patch, Size: 114253 bytes --]

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 3d15eff..915038d 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1276,6 +1276,8 @@ OBJS = \
 	ipa-profile.o \
 	ipa-prop.o \
 	ipa-pure-const.o \
+	ipa-icf.o \
+	ipa-icf-gimple.o \
 	ipa-reference.o \
 	ipa-ref.o \
 	ipa-utils.o \
diff --git a/gcc/common.opt b/gcc/common.opt
index d515dca..253d9ea 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -1409,6 +1409,18 @@ fipa-pure-const
 Common Report Var(flag_ipa_pure_const) Init(0) Optimization
 Discover pure and const functions
 
+fipa-icf
+Common Report Var(flag_ipa_icf) Optimization
+Perform Identical Code Folding for functions and read-only variables
+
+fipa-icf-functions
+Common Report Var(flag_ipa_icf_functions) Optimization
+Perform Identical Code Folding for functions
+
+fipa-icf-variables
+Common Report Var(flag_ipa_icf_variables) Optimization
+Perform Identical Code Folding for variables
+
 fipa-reference
 Common Report Var(flag_ipa_reference) Init(0) Optimization
 Discover readonly and non addressable static variables
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 4df5f81..3f38c60 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -377,7 +377,7 @@ Objective-C and Objective-C++ Dialects}.
 -fif-conversion2 -findirect-inlining @gol
 -finline-functions -finline-functions-called-once -finline-limit=@var{n} @gol
 -finline-small-functions -fipa-cp -fipa-cp-clone @gol
--fipa-pta -fipa-profile -fipa-pure-const -fipa-reference @gol
+-fipa-pta -fipa-profile -fipa-pure-const -fipa-reference -fipa-icf @gol
 -fira-algorithm=@var{algorithm} @gol
 -fira-region=@var{region} -fira-hoist-pressure @gol
 -fira-loop-pressure -fno-ira-share-save-slots @gol
@@ -6956,6 +6956,7 @@ also turns on the following optimization flags:
 -finline-small-functions @gol
 -findirect-inlining @gol
 -fipa-sra @gol
+-fipa-icf @gol
 -fisolate-erroneous-paths-dereference @gol
 -foptimize-sibling-calls @gol
 -fpartial-inlining @gol
@@ -7887,6 +7888,26 @@ it may significantly increase code size
 (see @option{--param ipcp-unit-growth=@var{value}}).
 This flag is enabled by default at @option{-O3}.
 
+@item -fipa-icf
+@opindex fipa-icf
+Perform Identical Code Folding for functions and read-only variables.
+Behavior is similar to Gold Linker ICF optimization. Symbols proved
+as semantically equivalent are redirected to corresponding symbol. The pass
+sensitively decides for usage of alias, thunk or local redirection.
+This flag is enabled by default at @option{-O2}.
+
+The following options are enabled: @code{-fipa-icf-functions}, @code{-fipa-icf-variables}.
+
+@item -fipa-icf-functions
+@opindex fipa-icf-functions
+Perform Identical Code Folding for just for functions.
+This flag is enabled by @option{fipa-icf}.
+
+@item -fipa-icf-variables
+@opindex fipa-icf-variables
+Perform Identical Code Folding for just for read-only variables.
+This flag is enabled by @option{fipa-icf}.
+
 @item -fisolate-erroneous-paths-dereference
 Detect paths which trigger erroneous or undefined behaviour due to
 dereferencing a NULL pointer.  Isolate those paths from the main control
diff --git a/gcc/ipa-icf-gimple.c b/gcc/ipa-icf-gimple.c
new file mode 100644
index 0000000..01c723c
--- /dev/null
+++ b/gcc/ipa-icf-gimple.c
@@ -0,0 +1,384 @@
+/* Interprocedural Identical Code Folding pass
+   Copyright (C) 2014 Free Software Foundation, Inc.
+
+   Contributed by Jan Hubicka <hubicka@ucw.cz> and Martin Liska <mliska@suse.cz>
+
+This file is part of GCC.
+
+GCC 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, or (at your option) any later
+version.
+
+GCC 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 GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tree.h"
+#include "basic-block.h"
+#include "tree-ssa-alias.h"
+#include "internal-fn.h"
+#include "gimple-expr.h"
+#include "is-a.h"
+#include "gimple.h"
+#include "expr.h"
+#include "gimple-iterator.h"
+#include "gimple-ssa.h"
+#include "tree-cfg.h"
+#include "stringpool.h"
+#include "tree-dfa.h"
+#include "tree-pass.h"
+#include "gimple-pretty-print.h"
+#include "cfgloop.h"
+#include "except.h"
+#include "data-streamer.h"
+#include "ipa-utils.h"
+#include "ipa-icf.h"
+
+namespace ipa_icf {
+
+/* Basic block equivalence comparison function that returns true if
+   basic blocks BB1 and BB2 (from functions FUNC1 and FUNC2) correspond.  */
+
+bool
+sem_function::compare_bb (sem_bb_t *bb1, sem_bb_t *bb2, tree func1, tree func2)
+{
+  unsigned i;
+  gimple_stmt_iterator gsi1, gsi2;
+  gimple s1, s2;
+
+  if (bb1->nondbg_stmt_count != bb2->nondbg_stmt_count
+      || bb1->edge_count != bb2->edge_count)
+    SE_EXIT_FALSE ();
+
+  gsi1 = gsi_start_bb (bb1->bb);
+  gsi2 = gsi_start_bb (bb2->bb);
+
+  for (i = 0; i < bb1->nondbg_stmt_count; i++)
+    {
+      if (is_gimple_debug (gsi_stmt (gsi1)))
+	gsi_next_nondebug (&gsi1);
+
+      if (is_gimple_debug (gsi_stmt (gsi2)))
+	gsi_next_nondebug (&gsi2);
+
+      s1 = gsi_stmt (gsi1);
+      s2 = gsi_stmt (gsi2);
+
+      if (gimple_code (s1) != gimple_code (s2))
+	SE_EXIT_FALSE_WITH_MSG ("gimple codes are different");
+
+      switch (gimple_code (s1))
+	{
+	case GIMPLE_CALL:
+	  if (!compare_gimple_call (s1, s2, func1, func2))
+	    SE_DIFF_STATEMENT (s1, s2, "GIMPLE_CALL");
+	  break;
+	case GIMPLE_ASSIGN:
+	  if (!compare_gimple_assign (s1, s2, func1, func2))
+	    SE_DIFF_STATEMENT (s1, s2, "GIMPLE_ASSIGN");
+	  break;
+	case GIMPLE_COND:
+	  if (!compare_gimple_cond (s1, s2, func1, func2))
+	    SE_DIFF_STATEMENT (s1, s2, "GIMPLE_COND");
+	  break;
+	case GIMPLE_SWITCH:
+	  if (!compare_gimple_switch (s1, s2, func1, func2))
+	    SE_DIFF_STATEMENT (s1, s2, "GIMPLE_SWITCH");
+	  break;
+	case GIMPLE_DEBUG:
+	case GIMPLE_EH_DISPATCH:
+	  break;
+	case GIMPLE_RESX:
+	  if (!compare_gimple_resx (s1, s2))
+	    SE_DIFF_STATEMENT (s1, s2, "GIMPLE_RESX");
+	  break;
+	case GIMPLE_LABEL:
+	  if (!compare_gimple_label (s1, s2, func1, func2))
+	    SE_DIFF_STATEMENT (s1, s2, "GIMPLE_LABEL");
+	  break;
+	case GIMPLE_RETURN:
+	  if (!compare_gimple_return (s1, s2, func1, func2))
+	    SE_DIFF_STATEMENT (s1, s2, "GIMPLE_RETURN");
+	  break;
+	case GIMPLE_GOTO:
+	  if (!compare_gimple_goto (s1, s2, func1, func2))
+	    SE_DIFF_STATEMENT (s1, s2, "GIMPLE_GOTO");
+	  break;
+	case GIMPLE_ASM:
+	  if (!compare_gimple_asm (s1, s2))
+	    SE_DIFF_STATEMENT (s1, s2, "GIMPLE_ASM");
+	  break;
+	case GIMPLE_PREDICT:
+	case GIMPLE_NOP:
+	  return true;
+	default:
+	  SE_EXIT_FALSE_WITH_MSG ("Unknown GIMPLE code reached")
+	}
+
+      gsi_next (&gsi1);
+      gsi_next (&gsi2);
+    }
+
+  return true;
+}
+
+
+/* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
+   call statements are semantically equivalent.  */
+
+bool
+sem_function::compare_gimple_call (gimple s1, gimple s2, tree func1, tree func2)
+{
+  unsigned i;
+  tree t1, t2;
+
+  if (gimple_call_num_args (s1) != gimple_call_num_args (s2))
+    return false;
+
+  t1 = gimple_call_fndecl (s1);
+  t2 = gimple_call_fndecl (s2);
+
+  /* Function pointer variables are not supported yet.  */
+  if (t1 == NULL || t2 == NULL)
+    {
+      if (!compare_operand (t1, t2, func1, func2))
+	SE_EXIT_FALSE();
+    }
+  else if (!compare_function_decl (t1, t2))
+    return false;
+
+  /* Checking of argument.  */
+  for (i = 0; i < gimple_call_num_args (s1); ++i)
+    {
+      t1 = gimple_call_arg (s1, i);
+      t2 = gimple_call_arg (s2, i);
+
+      if (!compare_operand (t1, t2, func1, func2))
+	return false;
+    }
+
+  /* Return value checking.  */
+  t1 = gimple_get_lhs (s1);
+  t2 = gimple_get_lhs (s2);
+
+  return compare_operand (t1, t2, func1, func2);
+}
+
+/* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
+   assignment statements are semantically equivalent.  */
+
+bool
+sem_function::compare_gimple_assign (gimple s1, gimple s2, tree func1,
+				     tree func2)
+{
+  tree arg1, arg2;
+  enum tree_code code1, code2;
+  unsigned i;
+
+  code1 = gimple_expr_code (s1);
+  code2 = gimple_expr_code (s2);
+
+  if (code1 != code2)
+    return false;
+
+  code1 = gimple_assign_rhs_code (s1);
+  code2 = gimple_assign_rhs_code (s2);
+
+  if (code1 != code2)
+    return false;
+
+  for (i = 0; i < gimple_num_ops (s1); i++)
+    {
+      arg1 = gimple_op (s1, i);
+      arg2 = gimple_op (s2, i);
+
+      if (!compare_operand (arg1, arg2, func1, func2))
+	return false;
+    }
+
+  return true;
+}
+
+/* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
+   condition statements are semantically equivalent.  */
+
+bool
+sem_function::compare_gimple_cond (gimple s1, gimple s2, tree func1, tree func2)
+{
+  tree t1, t2;
+  enum tree_code code1, code2;
+
+  code1 = gimple_expr_code (s1);
+  code2 = gimple_expr_code (s2);
+
+  if (code1 != code2)
+    return false;
+
+  t1 = gimple_cond_lhs (s1);
+  t2 = gimple_cond_lhs (s2);
+
+  if (!compare_operand (t1, t2, func1, func2))
+    return false;
+
+  t1 = gimple_cond_rhs (s1);
+  t2 = gimple_cond_rhs (s2);
+
+  return compare_operand (t1, t2, func1, func2);
+}
+
+/* Verifies that tree labels T1 and T2 correspond in FUNC1 and FUNC2.  */
+
+bool
+sem_function::compare_tree_ssa_label (tree t1, tree t2, tree func1, tree func2)
+{
+  return compare_operand (t1, t2, func1, func2);
+}
+
+/* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
+   label statements are semantically equivalent.  */
+
+bool
+sem_function::compare_gimple_label (gimple g1, gimple g2, tree func1,
+				    tree func2)
+{
+  tree t1 = gimple_label_label (g1);
+  tree t2 = gimple_label_label (g2);
+
+  return compare_tree_ssa_label (t1, t2, func1, func2);
+}
+
+/* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
+   switch statements are semantically equivalent.  */
+
+bool
+sem_function::compare_gimple_switch (gimple g1, gimple g2, tree func1,
+				     tree func2)
+{
+  unsigned lsize1, lsize2, i;
+  tree t1, t2, low1, low2, high1, high2;
+
+  lsize1 = gimple_switch_num_labels (g1);
+  lsize2 = gimple_switch_num_labels (g2);
+
+  if (lsize1 != lsize2)
+    return false;
+
+  t1 = gimple_switch_index (g1);
+  t2 = gimple_switch_index (g2);
+
+  if (TREE_CODE (t1) != SSA_NAME || TREE_CODE(t2) != SSA_NAME)
+    return false;
+
+  if (!compare_operand (t1, t2, func1, func2))
+    return false;
+
+  for (i = 0; i < lsize1; i++)
+    {
+      low1 = CASE_LOW (gimple_switch_label (g1, i));
+      low2 = CASE_LOW (gimple_switch_label (g2, i));
+
+      if ((low1 != NULL) != (low2 != NULL)
+	  || (low1 && low2 && TREE_INT_CST_LOW (low1) != TREE_INT_CST_LOW (low2)))
+	return false;
+
+      high1 = CASE_HIGH (gimple_switch_label (g1, i));
+      high2 = CASE_HIGH (gimple_switch_label (g2, i));
+
+      if ((high1 != NULL) != (high2 != NULL)
+	  || (high1 && high2
+	      && TREE_INT_CST_LOW (high1) != TREE_INT_CST_LOW (high2)))
+	return false;
+    }
+
+  return true;
+}
+
+/* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
+   return statements are semantically equivalent.  */
+
+bool
+sem_function::compare_gimple_return (gimple g1, gimple g2, tree func1,
+				     tree func2)
+{
+  tree t1, t2;
+
+  t1 = gimple_return_retval (g1);
+  t2 = gimple_return_retval (g2);
+
+  /* Void return type.  */
+  if (t1 == NULL && t2 == NULL)
+    return true;
+  else
+    return compare_operand (t1, t2, func1, func2);
+}
+
+/* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
+   goto statements are semantically equivalent.  */
+
+bool
+sem_function::compare_gimple_goto (gimple g1, gimple g2, tree func1, tree func2)
+{
+  tree dest1, dest2;
+
+  dest1 = gimple_goto_dest (g1);
+  dest2 = gimple_goto_dest (g2);
+
+  if (TREE_CODE (dest1) != TREE_CODE (dest2) || TREE_CODE (dest1) != SSA_NAME)
+    return false;
+
+  return compare_operand (dest1, dest2, func1, func2);
+}
+
+/* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
+   resx statements are semantically equivalent.  */
+
+bool
+sem_function::compare_gimple_resx (gimple g1, gimple g2)
+{
+  return gimple_resx_region (g1) == gimple_resx_region (g2);
+}
+
+/* Verifies for given GIMPLEs S1 and S2 that ASM statements are equivalent.
+   For the beginning, the pass only supports equality for
+   '__asm__ __volatile__ ("", "", "", "memory")'.  */
+
+bool
+sem_function::compare_gimple_asm (gimple g1, gimple g2)
+{
+  if (gimple_asm_volatile_p (g1) != gimple_asm_volatile_p (g2))
+    return false;
+
+  if (gimple_asm_ninputs (g1) || gimple_asm_ninputs (g2))
+    return false;
+
+  if (gimple_asm_noutputs (g1) || gimple_asm_noutputs (g2))
+    return false;
+
+  if (gimple_asm_nlabels (g1) || gimple_asm_nlabels (g2))
+    return false;
+
+  if (gimple_asm_nclobbers (g1) != gimple_asm_nclobbers (g2))
+    return false;
+
+  for (unsigned i = 0; i < gimple_asm_nclobbers (g1); i++)
+    {
+      tree clobber1 = TREE_VALUE (gimple_asm_clobber_op (g1, i));
+      tree clobber2 = TREE_VALUE (gimple_asm_clobber_op (g2, i));
+
+      if (!operand_equal_p (clobber1, clobber2, OEP_ONLY_CONST))
+	return false;
+    }
+
+  return true;
+}
+
+} // ipa_icf namespace
diff --git a/gcc/ipa-icf.c b/gcc/ipa-icf.c
new file mode 100644
index 0000000..8a13dca
--- /dev/null
+++ b/gcc/ipa-icf.c
@@ -0,0 +1,2762 @@
+/* Interprocedural Identical Code Folding pass
+   Copyright (C) 2014 Free Software Foundation, Inc.
+
+   Contributed by Jan Hubicka <hubicka@ucw.cz> and Martin Liska <mliska@suse.cz>
+
+This file is part of GCC.
+
+GCC 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, or (at your option) any later
+version.
+
+GCC 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 GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+/* Interprocedural Identical Code Folding for functions and
+   read-only variables.
+
+   The goal of this transformation is to discover functions and read-only
+   variables which do have exactly the same semantics.
+
+   In case of functions,
+   we could either create a virtual clone or do a simple function wrapper
+   that will call equivalent function. If the function is just locally visible,
+   all function calls can be redirected. For read-only variables, we create
+   aliases if possible.
+
+   Optimization pass arranges as follows:
+   1) All functions and read-only variables are visited and internal
+      data structure, either sem_function or sem_variables is created.
+   2) For every symbol from the previous step, VAR_DECL and FUNCTION_DECL are
+      saved and matched to corresponding sem_items.
+   3) These declaration are ignored for equality check and are solved
+      by Value Numbering algorithm published by Alpert, Zadeck in 1992.
+   4) We compute hash value for each symbol.
+   5) Congruence classes are created based on hash value. If hash value are
+      equal, equals function is called and symbols are deeply compared.
+      We must prove that all SSA names, declarations and other items
+      correspond.
+   6) Value Numbering is executed for these classes. At the end of the process
+      all symbol members in remaining classes can be merged.
+   7) Merge operation creates alias in case of read-only variables. For
+      callgraph node, we must decide if we can redirect local calls,
+      create an alias or a thunk.
+
+*/
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tree.h"
+#include "basic-block.h"
+#include "tree-ssa-alias.h"
+#include "internal-fn.h"
+#include "gimple-expr.h"
+#include "is-a.h"
+#include "gimple.h"
+#include "expr.h"
+#include "gimple-iterator.h"
+#include "gimple-ssa.h"
+#include "tree-cfg.h"
+#include "tree-phinodes.h"
+#include "stringpool.h"
+#include "tree-ssanames.h"
+#include "tree-dfa.h"
+#include "tree-pass.h"
+#include "gimple-pretty-print.h"
+#include "ipa-inline.h"
+#include "cfgloop.h"
+#include "except.h"
+#include "hash-table.h"
+#include "coverage.h"
+#include "pointer-set.h"
+#include "attribs.h"
+#include "print-tree.h"
+#include "lto-streamer.h"
+#include "data-streamer.h"
+#include "ipa-utils.h"
+#include "ipa-icf.h"
+
+namespace ipa_icf {
+
+/* Itializes internal structures according to given number of
+   source and target SSA names. The number of source names is SSA_SOURCE,
+   respectively SSA_TARGET.  */
+
+func_checker::func_checker (unsigned ssa_source, unsigned ssa_target)
+{
+  m_source_ssa_names.create (ssa_source);
+  m_target_ssa_names.create (ssa_target);
+
+  for (unsigned int i = 0; i < ssa_source; i++)
+    m_source_ssa_names.safe_push (-1);
+
+  for (unsigned int i = 0; i < ssa_target; i++)
+    m_target_ssa_names.safe_push (-1);
+
+  m_edge_map = new hash_map <edge, edge> ();
+  m_decl_map = new hash_map <tree, tree> ();
+}
+
+/* Memory release routine.  */
+
+func_checker::~func_checker ()
+{
+  delete m_edge_map;
+  delete m_decl_map;
+  m_source_ssa_names.release();
+  m_target_ssa_names.release();
+}
+
+/* Verifies that trees T1 and T2 are equivalent from perspective of ICF.  */
+
+bool
+func_checker::compare_ssa_name (tree t1, tree t2)
+{
+  unsigned i1 = SSA_NAME_VERSION (t1);
+  unsigned i2 = SSA_NAME_VERSION (t2);
+
+  if (m_source_ssa_names[i1] == -1)
+    m_source_ssa_names[i1] = i2;
+  else if (m_source_ssa_names[i1] != (int) i2)
+    return false;
+
+  if(m_target_ssa_names[i2] == -1)
+    m_target_ssa_names[i2] = i1;
+  else if (m_target_ssa_names[i2] != (int) i1)
+    return false;
+
+  return true;
+}
+
+/* Verification function for edges E1 and E2.  */
+
+bool
+func_checker::compare_edge (edge e1, edge e2)
+{
+  if (e1->flags != e2->flags)
+    return false;
+
+  bool existed_p;
+
+  edge &slot = m_edge_map->get_or_insert (e1, &existed_p);
+  if (existed_p)
+    {
+      SE_EXIT_DEBUG (slot == e2);
+    }
+  else
+    slot = e2;
+
+  return true;
+}
+
+/* Verification function for declaration trees T1 and T2 that
+   come from functions FUNC1 and FUNC2.  */
+
+bool
+func_checker::compare_decl (tree t1, tree t2, tree func1, tree func2)
+{
+  if (!auto_var_in_fn_p (t1, func1) || !auto_var_in_fn_p (t2, func2))
+    SE_EXIT_DEBUG (t1 == t2);
+
+  if (!types_compatible_p (TREE_TYPE (t1), TREE_TYPE (t2)))
+    SE_EXIT_FALSE ();
+
+  bool existed_p;
+
+  tree &slot = m_decl_map->get_or_insert (t1, &existed_p);
+  if (existed_p)
+    {
+      SE_EXIT_DEBUG (slot == t2);
+    }
+  else
+    slot = t2;
+
+  return true;
+}
+
+/* Constructor for key value pair, where _ITEM is key and _INDEX is a target.  */
+
+sem_usage_pair::sem_usage_pair (sem_item *_item, unsigned int _index):
+  item (_item), index (_index)
+{
+}
+
+/* Semantic item constructor for a node of _TYPE, where STACK is used
+   for bitmap memory allocation.  */
+
+sem_item::sem_item (enum sem_item_type _type,
+		    bitmap_obstack *stack): type(_type), hash(0)
+{
+  setup (stack);
+}
+
+/* Semantic item constructor for a node of _TYPE, where STACK is used
+   for bitmap memory allocation. The item is based on symtab node _NODE
+   with computed _HASH.  */
+
+sem_item::sem_item (enum sem_item_type _type, struct symtab_node *_node,
+		    hashval_t _hash, bitmap_obstack *stack): type(_type),
+  node (_node), hash (_hash)
+{
+  decl = node->decl;
+  setup (stack);
+}
+
+/* Initialize internal data structures. Bitmap STACK is used for
+   bitmap memory allocation process.  */
+
+void
+sem_item::setup (bitmap_obstack *stack)
+{
+  gcc_checking_assert (node);
+
+  refs.create (0);
+  tree_refs.create (0);
+  usages.create (0);
+  tree_refs_set = pointer_set_create ();
+  usage_index_bitmap = BITMAP_ALLOC (stack);
+}
+
+sem_item::~sem_item ()
+{
+  if (tree_refs_set)
+    pointer_set_destroy (tree_refs_set);
+
+  for (unsigned i = 0; i < usages.length (); i++)
+    delete usages[i];
+
+  refs.release ();
+  tree_refs.release ();
+  usages.release ();
+
+  BITMAP_FREE (usage_index_bitmap);
+}
+
+/* Dump function for debugging purpose.  */
+
+DEBUG_FUNCTION void
+sem_item::dump (void)
+{
+  if (dump_file)
+    {
+      fprintf (dump_file, "[%s] %s (%u) (tree:%p)\n", type == FUNC ? "func" : "var",
+	       name(), node->order, (void *) node->decl);
+      fprintf (dump_file, "  hash: %u\n", get_hash ());
+      fprintf (dump_file, "  references: ");
+
+      for (unsigned i = 0; i < refs.length (); i++)
+	fprintf (dump_file, "%s%s ", refs[i]->name (),
+		 i < refs.length() - 1 ? "," : "");
+
+      fprintf (dump_file, "\n");
+    }
+}
+
+/* If strict aliasing is enabled, function compares if given types are
+   in the same alias set.  */
+bool
+sem_item::compare_for_aliasing (tree t1, tree t2)
+{
+  if (flag_strict_aliasing)
+    {
+      alias_set_type s1 = get_deref_alias_set (TREE_TYPE (t1));
+      alias_set_type s2 = get_deref_alias_set (TREE_TYPE (t2));
+
+      return s1 == s2;
+    }
+
+  return true;
+}
+
+/* Semantic function constructor that uses STACK as bitmap memory stack.  */
+
+sem_function::sem_function (bitmap_obstack *stack): sem_item (FUNC, stack),
+  m_checker (NULL), m_compared_func (NULL)
+{
+  arg_types.create (0);
+  bb_sizes.create (0);
+  bb_sorted.create (0);
+}
+
+/*  Constructor based on callgraph node _NODE with computed hash _HASH.
+    Bitmap STACK is used for memory allocation.  */
+sem_function::sem_function (cgraph_node *node, hashval_t hash,
+			    bitmap_obstack *stack):
+  sem_item (FUNC, node, hash, stack),
+  m_checker (NULL), m_compared_func (NULL)
+{
+  arg_types.create (0);
+  bb_sizes.create (0);
+  bb_sorted.create (0);
+}
+
+sem_function::~sem_function ()
+{
+  for (unsigned i = 0; i < bb_sorted.length (); i++)
+    free (bb_sorted[i]);
+
+  arg_types.release ();
+  bb_sizes.release ();
+  bb_sorted.release ();
+}
+
+/* Calculates hash value based on a BASIC_BLOCK.  */
+
+hashval_t
+sem_function::get_bb_hash (const sem_bb_t *basic_block)
+{
+  hashval_t hash = basic_block->nondbg_stmt_count;
+  hash = iterative_hash_object (basic_block->edge_count, hash);
+
+  return hash;
+}
+
+/* References independent hash function.  */
+
+hashval_t
+sem_function::get_hash (void)
+{
+  if(!hash)
+    {
+      hash = 177454; /* Random number for function type.  */
+
+      hash = iterative_hash_object (arg_count, hash);
+      hash = iterative_hash_object (cfg_checksum, hash);
+      hash = iterative_hash_object (gcode_hash, hash);
+
+      for (unsigned i = 0; i < bb_sorted.length (); i++)
+	hash = iterative_hash_object (hash, get_bb_hash (bb_sorted[i]));
+
+      for (unsigned i = 0; i < bb_sizes.length (); i++)
+	hash = iterative_hash_object (bb_sizes[i], hash);
+    }
+
+  return hash;
+}
+
+/* Fast equality function based on knowledge known in WPA.  */
+
+bool
+sem_function::equals_wpa (sem_item *item)
+{
+  gcc_assert (item->type == FUNC);
+
+  m_compared_func = static_cast<sem_function *> (item);
+
+  if (arg_types.length () != m_compared_func->arg_types.length ())
+    SE_EXIT_FALSE_WITH_MSG ("different number of arguments");
+
+  /* Checking types of arguments.  */
+  for (unsigned i = 0; i < arg_types.length (); i++)
+    {
+      /* This guard is here for function pointer with attributes (pr59927.c).  */
+      if (!arg_types[i] || !m_compared_func->arg_types[i])
+	SE_EXIT_FALSE_WITH_MSG ("NULL arg type");
+
+      if (!types_compatible_p (arg_types[i], m_compared_func->arg_types[i]))
+	SE_EXIT_FALSE_WITH_MSG("argument type is different");
+    }
+
+  /* Result type checking.  */
+  if (!types_compatible_p (result_type, m_compared_func->result_type))
+    SE_EXIT_FALSE_WITH_MSG("result types are different");
+
+  return true;
+}
+
+/* Returns true if the item equals to ITEM given as argument.  */
+
+bool
+sem_function::equals (sem_item *item)
+{
+  gcc_assert (item->type == FUNC);
+  bool eq = equals_private (item);
+
+  if (m_checker != NULL)
+    {
+      delete m_checker;
+      m_checker = NULL;
+    }
+
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    fprintf (dump_file,
+	     "Equals called for:%s:%s (%u:%u) (%s:%s) with result: %s\n\n",
+	     name(), item->name (), node->order, item->node->order, asm_name (),
+	     item->asm_name (), eq ? "true" : "false");
+
+  return eq;
+}
+
+/* Processes function equality comparison.  */
+
+bool
+sem_function::equals_private (sem_item *item)
+{
+  if (item->type != FUNC)
+    return false;
+
+  basic_block bb1, bb2;
+  edge e1, e2;
+  edge_iterator ei1, ei2;
+  int *bb_dict = NULL;
+  bool result = true;
+  tree arg1, arg2;
+
+  m_compared_func = static_cast<sem_function *> (item);
+
+  gcc_assert (decl != item->decl);
+
+  if (bb_sorted.length () != m_compared_func->bb_sorted.length ()
+      || edge_count != m_compared_func->edge_count
+      || cfg_checksum != m_compared_func->cfg_checksum)
+    SE_EXIT_FALSE();
+
+  if (!equals_wpa (item))
+    return false;
+
+  /* Checking function arguments.  */
+  tree decl1 = DECL_ATTRIBUTES (decl);
+  tree decl2 = DECL_ATTRIBUTES (m_compared_func->decl);
+
+  while (decl1)
+    {
+      if (decl2 == NULL)
+	SE_EXIT_FALSE();
+
+      if (get_attribute_name (decl1) != get_attribute_name (decl2))
+	SE_EXIT_FALSE();
+
+      tree attr_value1 = TREE_VALUE (decl1);
+      tree attr_value2 = TREE_VALUE (decl2);
+
+      if (attr_value1 && attr_value2)
+	{
+	  bool ret = compare_operand (TREE_VALUE (attr_value1),
+				      TREE_VALUE (attr_value2), decl,
+				      m_compared_func->decl);
+	  if (!ret)
+	    SE_EXIT_FALSE_WITH_MSG ("attribute values are different")
+	  }
+      else if (!attr_value1 && !attr_value2)
+	{}
+      else
+	SE_EXIT_FALSE ();
+
+      decl1 = TREE_CHAIN (decl1);
+      decl2 = TREE_CHAIN (decl2);
+    }
+
+  if (decl1 != decl2)
+    SE_EXIT_FALSE();
+
+  m_checker = new func_checker (ssa_names_size, m_compared_func->ssa_names_size);
+
+  for (arg1 = DECL_ARGUMENTS (decl),
+       arg2 = DECL_ARGUMENTS (m_compared_func->decl);
+       arg1; arg1 = DECL_CHAIN (arg1), arg2 = DECL_CHAIN (arg2))
+    m_checker->compare_decl (arg1, arg2, decl, m_compared_func->decl);
+
+  /* Exception handling regions comparison.  */
+  if (!compare_eh_region (region_tree, m_compared_func->region_tree, decl,
+			  m_compared_func->decl))
+    SE_EXIT_FALSE();
+
+  /* Checking all basic blocks.  */
+  for (unsigned i = 0; i < bb_sorted.length (); ++i)
+    if(!compare_bb (bb_sorted[i], m_compared_func->bb_sorted[i], decl,
+		    m_compared_func->decl))
+      SE_EXIT_FALSE();
+
+  SE_DUMP_MESSAGE ("All BBs are equal\n");
+
+  /* Basic block edges check.  */
+  for (unsigned i = 0; i < bb_sorted.length (); ++i)
+    {
+      bb_dict = XNEWVEC (int, bb_sorted.length () + 2);
+      memset (bb_dict, -1, (bb_sorted.length () + 2) * sizeof (int));
+
+      bb1 = bb_sorted[i]->bb;
+      bb2 = m_compared_func->bb_sorted[i]->bb;
+
+      ei2 = ei_start (bb2->preds);
+
+      for (ei1 = ei_start (bb1->preds); ei_cond (ei1, &e1); ei_next (&ei1))
+	{
+	  ei_cond (ei2, &e2);
+
+	  if (e1->flags != e2->flags)
+	    SE_EXIT_FALSE_WITH_MSG("flags comparison returns false");
+
+	  if (!bb_dict_test (bb_dict, e1->src->index, e2->src->index))
+	    SE_EXIT_FALSE_WITH_MSG("edge comparison returns false");
+
+	  if (!bb_dict_test (bb_dict, e1->dest->index, e2->dest->index))
+	    SE_EXIT_FALSE_WITH_MSG("BB comparison returns false");
+
+	  if (!m_checker->compare_edge (e1, e2))
+	    SE_EXIT_FALSE_WITH_MSG("edge comparison returns false");
+
+	  ei_next (&ei2);
+	}
+    }
+
+  /* Basic block PHI nodes comparison.  */
+  for (unsigned i = 0; i < bb_sorted.length (); i++)
+    if (!compare_phi_node (bb_sorted[i]->bb, m_compared_func->bb_sorted[i]->bb,
+			   decl, m_compared_func->decl))
+      SE_EXIT_FALSE_WITH_MSG ("PHI node comparison returns false");
+
+  return result;
+}
+
+/* Initializes references to another sem_item for tree T.  */
+
+void
+sem_function::init_refs_for_tree (tree t)
+{
+  switch (TREE_CODE (t))
+    {
+    case VAR_DECL:
+    case FUNCTION_DECL:
+      tree_refs.safe_push (t);
+      break;
+    case MEM_REF:
+    case ADDR_EXPR:
+    case OBJ_TYPE_REF:
+      init_refs_for_tree (TREE_OPERAND (t, 0));
+      break;
+    case FIELD_DECL:
+      init_refs_for_tree (DECL_FCONTEXT (t));
+      break;
+    default:
+      break;
+    }
+}
+
+/* Initializes references to another sem_item for gimple STMT of type assign.  */
+
+void
+sem_function::init_refs_for_assign (gimple stmt)
+{
+  if (gimple_num_ops (stmt) != 2)
+    return;
+
+  tree rhs = gimple_op (stmt, 1);
+
+  init_refs_for_tree (rhs);
+}
+
+/* Initializes references to other semantic functions/variables.  */
+
+void
+sem_function::init_refs (void)
+{
+  for (unsigned i = 0; i < bb_sorted.length (); i++)
+    {
+      basic_block bb = bb_sorted[i]->bb;
+
+      for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi);
+	   gsi_next (&gsi))
+	{
+	  gimple stmt = gsi_stmt (gsi);
+	  hashval_t code = (hashval_t) gimple_code (stmt);
+
+	  switch (code)
+	    {
+	    case GIMPLE_CALL:
+	      {
+		tree funcdecl = gimple_call_fndecl (stmt);
+
+		/* Function pointer variables are not supported yet.  */
+		if (funcdecl)
+		  tree_refs.safe_push (funcdecl);
+
+		break;
+	      }
+	    case GIMPLE_ASSIGN:
+	      init_refs_for_assign (stmt);
+	      break;
+	    default:
+	      break;
+	    }
+	}
+    }
+}
+
+/* Merges instance with an ALIAS_ITEM, where alias, thunk or redirection can
+   be applied.  */
+bool
+sem_function::merge (sem_item *alias_item)
+{
+  gcc_assert (alias_item->type == FUNC);
+
+  sem_function *alias_func = static_cast<sem_function *> (alias_item);
+
+  struct cgraph_node *original = get_node ();
+  struct cgraph_node *local_original = original;
+  struct cgraph_node *alias = alias_func->get_node ();
+  bool original_address_matters;
+  bool alias_address_matters;
+
+  bool create_thunk = false;
+  bool create_alias = false;
+  bool redirect_callers = false;
+  bool original_discardable = false;
+
+  /* Do not attempt to mix functions from different user sections;
+     we do not know what user intends with those.  */
+  if (((DECL_SECTION_NAME (original->decl) && !original->implicit_section)
+       || (DECL_SECTION_NAME (alias->decl) && !alias->implicit_section))
+      && DECL_SECTION_NAME (original->decl) != DECL_SECTION_NAME (alias->decl))
+    {
+      if (dump_file)
+	fprintf (dump_file,
+		 "Not unifying; original and alias are in different sections.\n\n");
+      return false;
+    }
+
+  /* See if original is in a section that can be discarded if the main
+     symbol is not used.  */
+  if (DECL_EXTERNAL (original->decl))
+    original_discardable = true;
+  if (original->resolution == LDPR_PREEMPTED_REG
+      || original->resolution == LDPR_PREEMPTED_IR)
+    original_discardable = true;
+  if (symtab_can_be_discarded (original))
+    original_discardable = true;
+
+  /* See if original and/or alias address can be compared for equality.  */
+  original_address_matters
+    = (!DECL_VIRTUAL_P (original->decl)
+       && (original->externally_visible
+	   || address_taken_from_non_vtable_p (original)));
+  alias_address_matters
+    = (!DECL_VIRTUAL_P (alias->decl)
+       && (alias->externally_visible
+	   || address_taken_from_non_vtable_p (alias)));
+
+  /* If alias and original can be compared for address equality, we need
+     to create a thunk.  Also we can not create extra aliases into discardable
+     section (or we risk link failures when section is discarded).  */
+  if ((original_address_matters
+       && alias_address_matters)
+      || original_discardable)
+    {
+      create_thunk = !stdarg_p (TREE_TYPE (alias->decl));
+      create_alias = false;
+      /* When both alias and original are not overwritable, we can save
+         the extra thunk wrapper for direct calls.  */
+      redirect_callers
+	= (!original_discardable
+	   && cgraph_function_body_availability (alias) > AVAIL_OVERWRITABLE
+	   && cgraph_function_body_availability (original) > AVAIL_OVERWRITABLE);
+    }
+  else
+    {
+      create_alias = true;
+      create_thunk = false;
+      redirect_callers = false;
+    }
+
+  if (create_alias && DECL_COMDAT_GROUP (alias->decl))
+    {
+      create_alias = false;
+      create_thunk = true;
+    }
+
+  /* We want thunk to always jump to the local function body
+     unless the body is comdat and may be optimized out.  */
+  if ((create_thunk || redirect_callers)
+      && (!original_discardable
+	  || (DECL_COMDAT_GROUP (original->decl)
+	      && (DECL_COMDAT_GROUP (original->decl)
+		  == DECL_COMDAT_GROUP (alias->decl)))))
+    local_original
+      = cgraph (symtab_nonoverwritable_alias (original));
+
+  if (redirect_callers)
+    {
+      /* If alias is non-overwritable then
+         all direct calls are safe to be redirected to the original.  */
+      bool redirected = false;
+      while (alias->callers)
+	{
+	  struct cgraph_edge *e = alias->callers;
+	  cgraph_redirect_edge_callee (e, local_original);
+	  push_cfun (DECL_STRUCT_FUNCTION (e->caller->decl));
+
+	  if (e->call_stmt)
+	    cgraph_redirect_edge_call_stmt_to_callee (e);
+
+	  pop_cfun ();
+	  redirected = true;
+	}
+
+      /* The alias function is removed if symbol address
+         does not matter.  */
+      if (!alias_address_matters)
+	cgraph_remove_node (alias);
+
+      if (dump_file && redirected)
+	fprintf (dump_file, "Callgraph local calls have been redirected.\n\n");
+    }
+  /* If the condtion above is not met, we are lucky and can turn the
+     function into real alias.  */
+  else if (create_alias)
+    {
+      /* Remove the function's body.  */
+      ipa_merge_profiles (original, alias);
+      cgraph_release_function_body (alias);
+      cgraph_reset_node (alias);
+
+      /* Create the alias.  */
+      cgraph_create_function_alias (alias_func->decl, decl);
+      symtab_resolve_alias (alias, original);
+
+      if (dump_file)
+	fprintf (dump_file, "Callgraph alias has been created.\n\n");
+    }
+  else if (create_thunk)
+    {
+      if (DECL_COMDAT_GROUP (alias->decl))
+	{
+	  if (dump_file)
+	    fprintf (dump_file, "Callgraph thunk cannot be created because of COMDAT\n");
+
+	  return 0;
+	}
+
+      ipa_merge_profiles (local_original, alias);
+      cgraph_make_wrapper (alias, local_original);
+
+      if (dump_file)
+	fprintf (dump_file, "Callgraph thunk has been created.\n\n");
+    }
+  else if (dump_file)
+    fprintf (dump_file, "Callgraph merge operation cannot be performed.\n\n");
+
+  return true;
+}
+
+/* Semantic item initialization function.  */
+
+void
+sem_function::init (void)
+{
+  if (in_lto_p)
+    cgraph_get_body (get_node ());
+
+  tree fndecl = node->decl;
+  struct function *func = DECL_STRUCT_FUNCTION (fndecl);
+
+  gcc_assert (func);
+  gcc_assert (SSANAMES (func));
+
+  ssa_names_size = SSANAMES (func)->length ();
+  node = node;
+
+  decl = fndecl;
+  region_tree = func->eh->region_tree;
+
+  /* iterating all function arguments.  */
+  arg_count = count_formal_params (fndecl);
+
+  edge_count = n_edges_for_fn (func);
+  cfg_checksum = coverage_compute_cfg_checksum (func);
+
+  gcode_hash = 0;
+
+  basic_block bb;
+  FOR_EACH_BB_FN (bb, func)
+  {
+    unsigned nondbg_stmt_count = 0;
+
+    edge e;
+    for (edge_iterator ei = ei_start (bb->preds); ei_cond (ei, &e); ei_next (&ei))
+      cfg_checksum = iterative_hash_host_wide_int (e->flags,
+		     cfg_checksum);
+
+    for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi);
+	 gsi_next (&gsi))
+      {
+	gimple stmt = gsi_stmt (gsi);
+	hashval_t code = (hashval_t) gimple_code (stmt);
+
+	/* We ignore all debug statements.  */
+	if (code != GIMPLE_DEBUG)
+	  {
+	    nondbg_stmt_count++;
+	    gcode_hash = iterative_hash_object (code, gcode_hash);
+	  }
+      }
+
+    bb_sizes.safe_push (nondbg_stmt_count);
+
+    /* Inserting basic block to hash table.  */
+    sem_bb_t *sem_bb = XNEW (sem_bb_t);
+    sem_bb->bb = bb;
+    sem_bb->nondbg_stmt_count = nondbg_stmt_count;
+    sem_bb->edge_count = EDGE_COUNT (bb->preds) + EDGE_COUNT (bb->succs);
+
+    bb_sorted.safe_push (sem_bb);
+  }
+
+  parse_tree_args ();
+}
+
+/* For a given call graph NODE, the function constructs new
+   semantic function item.  */
+
+sem_function *
+sem_function::parse (struct cgraph_node *node, bitmap_obstack *stack)
+{
+  tree fndecl = node->decl;
+  struct function *func = DECL_STRUCT_FUNCTION (fndecl);
+
+  if (!func || !cgraph_function_with_gimple_body_p (node))
+    return NULL;
+
+  if (lookup_attribute_by_prefix ("omp ", DECL_ATTRIBUTES (node->decl)) != NULL)
+    return NULL;
+
+  sem_function *f = new sem_function (node, 0, stack);
+
+  f->init ();
+
+  return f;
+}
+
+/* Parses function arguments and result type.  */
+
+void
+sem_function::parse_tree_args (void)
+{
+  tree result;
+
+  if (arg_types.exists ())
+    arg_types.release ();
+
+  arg_types.create (4);
+  tree fnargs = DECL_ARGUMENTS (decl);
+
+  for (tree parm = fnargs; parm; parm = DECL_CHAIN (parm))
+    arg_types.safe_push (TYPE_CANONICAL (DECL_ARG_TYPE (parm)));
+
+  /* Function result type.  */
+  result = DECL_RESULT (decl);
+  result_type = result ? TYPE_CANONICAL (TREE_TYPE (result)) : NULL;
+
+  /* During WPA, we can get arguments by following method.  */
+  if (!fnargs)
+    {
+      tree type = TYPE_ARG_TYPES (TREE_TYPE (decl));
+      for (tree parm = type; parm; parm = TREE_CHAIN (parm))
+	arg_types.safe_push (TYPE_CANONICAL (TREE_VALUE (parm)));
+
+      result_type = TREE_TYPE (TREE_TYPE (decl));
+    }
+}
+
+/* For given basic blocks BB1 and BB2 (from functions FUNC1 and FUNC),
+   return true if phi nodes are sematically equivalent in these blocks .  */
+
+bool
+sem_function::compare_phi_node (basic_block bb1, basic_block bb2,
+				tree func1, tree func2)
+{
+  gimple_stmt_iterator si1, si2;
+  gimple phi1, phi2;
+  unsigned size1, size2, i;
+  tree t1, t2;
+  edge e1, e2;
+
+  gcc_assert (bb1 != NULL);
+  gcc_assert (bb2 != NULL);
+
+  si2 = gsi_start_phis (bb2);
+  for (si1 = gsi_start_phis (bb1); !gsi_end_p (si1);
+       gsi_next (&si1))
+    {
+      gsi_next_nonvirtual_phi (&si1);
+      gsi_next_nonvirtual_phi (&si2);
+
+      if (gsi_end_p (si1) && gsi_end_p (si2))
+	break;
+
+      if (gsi_end_p (si1) || gsi_end_p (si2))
+	SE_EXIT_FALSE ();
+
+      phi1 = gsi_stmt (si1);
+      phi2 = gsi_stmt (si2);
+
+      size1 = gimple_phi_num_args (phi1);
+      size2 = gimple_phi_num_args (phi2);
+
+      if (size1 != size2)
+	SE_EXIT_FALSE ();
+
+      for (i = 0; i < size1; ++i)
+	{
+	  t1 = gimple_phi_arg (phi1, i)->def;
+	  t2 = gimple_phi_arg (phi2, i)->def;
+
+	  if (!compare_operand (t1, t2, func1, func2))
+	    SE_EXIT_FALSE ();
+
+	  e1 = gimple_phi_arg_edge (phi1, i);
+	  e2 = gimple_phi_arg_edge (phi2, i);
+
+	  if (!m_checker->compare_edge (e1, e2))
+	    SE_EXIT_FALSE ();
+	}
+
+      gsi_next (&si2);
+    }
+
+  return true;
+}
+
+/* For given basic blocks BB1 and BB2 (from functions FUNC1 and FUNC),
+   true value is returned if exception handling regions are equivalent
+   in these blocks.  */
+
+bool
+sem_function::compare_eh_region (eh_region r1, eh_region r2, tree func1,
+				 tree func2)
+{
+  eh_landing_pad lp1, lp2;
+  eh_catch c1, c2;
+  tree t1, t2;
+
+  while (1)
+    {
+      if (!r1 && !r2)
+	return true;
+
+      if (!r1 || !r2)
+	return false;
+
+      if (r1->index != r2->index || r1->type != r2->type)
+	return false;
+
+      /* Landing pads comparison */
+      lp1 = r1->landing_pads;
+      lp2 = r2->landing_pads;
+
+      while (lp1 && lp2)
+	{
+	  if (lp1->index != lp2->index)
+	    return false;
+
+	  /* Comparison of post landing pads. */
+	  if (lp1->post_landing_pad && lp2->post_landing_pad)
+	    {
+	      t1 = lp1->post_landing_pad;
+	      t2 = lp2->post_landing_pad;
+
+	      gcc_assert (TREE_CODE (t1) == LABEL_DECL);
+	      gcc_assert (TREE_CODE (t2) == LABEL_DECL);
+
+	      if (!compare_tree_ssa_label (t1, t2, func1, func2))
+		return false;
+	    }
+	  else if (lp1->post_landing_pad || lp2->post_landing_pad)
+	    return false;
+
+	  lp1 = lp1->next_lp;
+	  lp2 = lp2->next_lp;
+	}
+
+      if (lp1 || lp2)
+	return false;
+
+      switch (r1->type)
+	{
+	case ERT_TRY:
+	  c1 = r1->u.eh_try.first_catch;
+	  c2 = r2->u.eh_try.first_catch;
+
+	  while (c1 && c2)
+	    {
+	      /* Catch label checking */
+	      if (c1->label && c2->label)
+		{
+		  if (!compare_tree_ssa_label (c1->label, c2->label,
+					       func1, func2))
+		    return false;
+		}
+	      else if (c1->label || c2->label)
+		return false;
+
+	      /* Type list checking */
+	      if (!compare_type_list (c1->type_list, c2->type_list))
+		return false;
+
+	      c1 = c1->next_catch;
+	      c2 = c2->next_catch;
+	    }
+
+	  break;
+
+	case ERT_ALLOWED_EXCEPTIONS:
+	  if (r1->u.allowed.filter != r2->u.allowed.filter)
+	    return false;
+
+	  if (!compare_type_list (r1->u.allowed.type_list,
+				  r2->u.allowed.type_list))
+	    return false;
+
+	  break;
+	case ERT_CLEANUP:
+	  break;
+	case ERT_MUST_NOT_THROW:
+	  if (r1->u.must_not_throw.failure_decl != r1->u.must_not_throw.failure_decl)
+	    return false;
+	  break;
+	default:
+	  gcc_unreachable ();
+	  break;
+	}
+
+      /* If there are sub-regions, process them.  */
+      if ((!r1->inner && r2->inner) || (!r1->next_peer && r2->next_peer))
+	return false;
+
+      if (r1->inner)
+	{
+	  r1 = r1->inner;
+	  r2 = r2->inner;
+	}
+
+      /* If there are peers, process them.  */
+      else if (r1->next_peer)
+	{
+	  r1 = r1->next_peer;
+	  r2 = r2->next_peer;
+	}
+      /* Otherwise, step back up the tree to the next peer.  */
+      else
+	{
+	  do
+	    {
+	      r1 = r1->outer;
+	      r2 = r2->outer;
+
+	      /* All nodes have been visited. */
+	      if (!r1 && !r2)
+		return true;
+	    }
+	  while (r1->next_peer == NULL);
+
+	  r1 = r1->next_peer;
+	  r2 = r2->next_peer;
+	}
+    }
+
+  return false;
+}
+
+/* Verifies that trees T1 and T2, representing function declarations
+   are equivalent from perspective of ICF.  */
+
+bool
+sem_function::compare_function_decl (tree t1, tree t2)
+{
+  if (t1 == t2)
+    return true;
+
+  bool ret = pointer_set_contains (tree_refs_set, t1)
+	     && pointer_set_contains (m_compared_func->tree_refs_set, t2);
+
+  if (ret)
+    return true;
+
+  /* If function decl is WEAKREF, we compare targets.  */
+  struct cgraph_node *f1 = cgraph_get_node (t1);
+  struct cgraph_node *f2 = cgraph_get_node (t2);
+
+  if(f1 && f2 && f1->weakref && f2->weakref)
+    ret = f1->alias_target == f2->alias_target;
+
+  return ret;
+}
+
+/* Verifies that trees T1 and T2 do correspond.  */
+
+bool
+sem_function::compare_variable_decl (tree t1, tree t2, tree func1, tree func2)
+{
+  if (t1 == t2)
+    return true;
+
+  bool ret = pointer_set_contains (tree_refs_set, t1)
+	     && pointer_set_contains (m_compared_func->tree_refs_set, t2);
+
+  if (ret)
+    return true;
+
+  ret = m_checker->compare_decl (t1, t2, func1, func2);
+
+  SE_EXIT_DEBUG (ret);
+}
+
+
+/* Returns true if tree T can be compared as a handled component.  */
+
+bool
+sem_function::icf_handled_component_p (tree t)
+{
+  enum tree_code tc = TREE_CODE (t);
+
+  return ((handled_component_p (t))
+	  || tc == ADDR_EXPR || tc == MEM_REF || tc == REALPART_EXPR
+	  || tc == IMAGPART_EXPR || tc == OBJ_TYPE_REF);
+}
+
+/* Basic blocks dictionary BB_DICT returns true if SOURCE index BB
+   corresponds to TARGET.  */
+
+bool
+sem_function::bb_dict_test (int* bb_dict, int source, int target)
+{
+  if (bb_dict[source] == -1)
+    {
+      bb_dict[source] = target;
+      return true;
+    }
+  else
+    return bb_dict[source] == target;
+}
+
+/* If T1 and T2 are SSA names, dictionary comparison is processed. Otherwise,
+   declaration comparasion is executed.  */
+
+bool
+sem_function::compare_ssa_name (tree t1, tree t2, tree func1, tree func2)
+{
+  tree b1, b2;
+  bool ret;
+
+  if (!m_checker->compare_ssa_name (t1, t2))
+    SE_EXIT_FALSE ();
+
+  if (SSA_NAME_IS_DEFAULT_DEF (t1))
+    {
+      b1 = SSA_NAME_VAR (t1);
+      b2 = SSA_NAME_VAR (t2);
+
+      if (b1 == NULL && b2 == NULL)
+	return true;
+
+      if (b1 == NULL || b2 == NULL || TREE_CODE (b1) != TREE_CODE (b2))
+	SE_EXIT_FALSE ();
+
+      switch (TREE_CODE (b1))
+	{
+	case VAR_DECL:
+	  SE_EXIT_DEBUG (compare_variable_decl (t1, t2, func1, func2));
+	case PARM_DECL:
+	case RESULT_DECL:
+	  ret = m_checker->compare_decl (b1, b2, func1, func2);
+	  SE_EXIT_DEBUG (ret);
+	default:
+	  SE_EXIT_FALSE_WITH_MSG ("Unknown TREE code reached")
+	}
+    }
+  else
+    return true;
+}
+
+/* Function responsible for comparison of handled components T1 and T2.
+   If these components, from functions FUNC1 and FUNC2, are equal, true
+   is returned.  */
+
+bool
+sem_function::compare_operand (tree t1, tree t2,
+			       tree func1, tree func2)
+{
+  tree base1, base2, x1, x2, y1, y2, z1, z2;
+  HOST_WIDE_INT offset1, offset2;
+  bool ret;
+
+  if (!t1 && !t2)
+    return true;
+  else if (!t1 || !t2)
+    return false;
+
+  if (!types_compatible_p (TREE_TYPE (t1), TREE_TYPE (t2)))
+    SE_EXIT_FALSE_WITH_MSG ("types are not compatible");
+
+  if (TREE_CODE (t1) == RECORD_TYPE && TREE_CODE (t2) == RECORD_TYPE)
+    {
+      tree ctx1 = DECL_CONTEXT (t1);
+      tree ctx2 = DECL_CONTEXT (t2);
+
+      if (TYPE_BINFO (ctx1) && TYPE_BINFO (ctx2)
+	  && polymorphic_type_binfo_p (TYPE_BINFO (ctx1))
+	  && polymorphic_type_binfo_p (TYPE_BINFO (ctx2)))
+	if (!types_same_for_odr (t1, t2))
+	  SE_EXIT_FALSE_WITH_MSG ("polymorphic types detected");
+    }
+
+  base1 = get_addr_base_and_unit_offset (t1, &offset1);
+  base2 = get_addr_base_and_unit_offset (t2, &offset2);
+
+  if (offset1 != offset2)
+    SE_EXIT_FALSE_WITH_MSG ("base offsets are different");
+
+  if (base1 && base2)
+    {
+      t1 = base1;
+      t2 = base2;
+    }
+
+  if (TREE_CODE (t1) != TREE_CODE (t2))
+    SE_EXIT_FALSE_WITH_MSG ("");
+
+  switch (TREE_CODE (t1))
+    {
+    case CONSTRUCTOR:
+      {
+	unsigned length1 = vec_safe_length (CONSTRUCTOR_ELTS (t1));
+	unsigned length2 = vec_safe_length (CONSTRUCTOR_ELTS (t2));
+
+	if (length1 != length2)
+	  SE_EXIT_FALSE_WITH_MSG ("");
+
+	for (unsigned i = 0; i < length1; i++)
+	  if (!compare_operand (CONSTRUCTOR_ELT (t1, i)->value,
+				CONSTRUCTOR_ELT (t2, i)->value, func1, func2))
+	    SE_EXIT_FALSE_WITH_MSG ("");
+
+	return true;
+      }
+    case ARRAY_REF:
+    case ARRAY_RANGE_REF:
+      {
+	x1 = TREE_OPERAND (t1, 0);
+	x2 = TREE_OPERAND (t2, 0);
+	y1 = TREE_OPERAND (t1, 1);
+	y2 = TREE_OPERAND (t2, 1);
+
+	if (!compare_operand (array_ref_low_bound (t1),
+			      array_ref_low_bound (t2),
+			      func1, func2))
+	  SE_EXIT_FALSE_WITH_MSG ("");
+	if (!compare_operand (array_ref_element_size (t1),
+			      array_ref_element_size (t2),
+			      func1, func2))
+	  SE_EXIT_FALSE_WITH_MSG ("");
+	if (!compare_operand (x1, x2, func1, func2))
+	  SE_EXIT_FALSE_WITH_MSG ("");
+	return compare_operand (y1, y2, func1, func2);
+      }
+
+    case MEM_REF:
+      {
+	x1 = TREE_OPERAND (t1, 0);
+	x2 = TREE_OPERAND (t2, 0);
+	y1 = TREE_OPERAND (t1, 1);
+	y2 = TREE_OPERAND (t2, 1);
+
+	/* See if operand is an memory access (the test originate from
+	 gimple_load_p).
+
+	In this case the alias set of the function being replaced must
+	be subset of the alias set of the other function.  At the moment
+	we seek for equivalency classes, so simply require inclussion in
+	both directions.  */
+
+	if (!sem_item::compare_for_aliasing (y1, y2))
+	  SE_EXIT_FALSE_WITH_MSG ("strict aliasing types do not match");
+
+	if (!compare_operand (x1, x2, func1, func2))
+	  SE_EXIT_FALSE_WITH_MSG ("");
+
+	/* Type of the offset on MEM_REF does not matter.  */
+	return wi::to_offset  (y1) == wi::to_offset  (y2);
+      }
+    case COMPONENT_REF:
+      {
+	x1 = TREE_OPERAND (t1, 0);
+	x2 = TREE_OPERAND (t2, 0);
+	y1 = TREE_OPERAND (t1, 1);
+	y2 = TREE_OPERAND (t2, 1);
+
+	ret = compare_operand (x1, x2, func1, func2)
+	      && compare_operand (y1, y2, func1, func2);
+
+	SE_EXIT_DEBUG (ret);
+      }
+    /* Virtual table call.  */
+    case OBJ_TYPE_REF:
+      {
+	x1 = TREE_OPERAND (t1, 0);
+	x2 = TREE_OPERAND (t2, 0);
+	y1 = TREE_OPERAND (t1, 1);
+	y2 = TREE_OPERAND (t2, 1);
+	z1 = TREE_OPERAND (t1, 2);
+	z2 = TREE_OPERAND (t2, 2);
+
+	ret = compare_operand (x1, x2, func1, func2)
+	      && compare_operand (y1, y2, func1, func2)
+	      && compare_operand (z1, z2, func1, func2);
+
+	SE_EXIT_DEBUG (ret);
+      }
+    case ADDR_EXPR:
+      {
+	x1 = TREE_OPERAND (t1, 0);
+	x2 = TREE_OPERAND (t2, 0);
+
+	ret = compare_operand (x1, x2, func1, func2);
+	SE_EXIT_DEBUG (ret);
+      }
+    case SSA_NAME:
+      {
+	ret = compare_ssa_name (t1, t2, func1, func2);
+	SE_EXIT_DEBUG (ret);
+      }
+    case INTEGER_CST:
+      {
+	ret = types_compatible_p (TREE_TYPE (t1), TREE_TYPE (t2))
+	      && wi::to_offset  (t1) == wi::to_offset  (t2);
+
+	SE_EXIT_DEBUG (ret);
+      }
+    case COMPLEX_CST:
+    case VECTOR_CST:
+    case STRING_CST:
+    case REAL_CST:
+      {
+	ret = operand_equal_p (t1, t2, OEP_ONLY_CONST);
+	SE_EXIT_DEBUG (ret);
+      }
+    case FUNCTION_DECL:
+      {
+	ret = compare_function_decl (t1, t2);
+	SE_EXIT_DEBUG (ret);
+      }
+    case VAR_DECL:
+      SE_EXIT_DEBUG (compare_variable_decl (t1, t2, func1, func2));
+    case FIELD_DECL:
+      {
+	tree fctx1 = DECL_FCONTEXT (t1);
+	tree fctx2 = DECL_FCONTEXT (t2);
+
+	tree offset1 = DECL_FIELD_OFFSET (t1);
+	tree offset2 = DECL_FIELD_OFFSET (t1);
+
+	ret = compare_operand (fctx1, fctx2, func1, func2)
+	      && compare_operand (offset1, offset2, func1, func2);
+
+	SE_EXIT_DEBUG (ret);
+      }
+    case PARM_DECL:
+    case LABEL_DECL:
+    case RESULT_DECL:
+    case CONST_DECL:
+    case BIT_FIELD_REF:
+      {
+	ret = m_checker->compare_decl (t1, t2, func1, func2);
+	SE_EXIT_DEBUG (ret);
+      }
+    default:
+      SE_EXIT_FALSE_WITH_MSG ("Unknown TREE code reached")
+    }
+}
+
+/* Iterates all tree types in T1 and T2 and returns true if all types
+   are compatible.  */
+
+bool
+sem_function::compare_type_list (tree t1, tree t2)
+{
+  tree tv1, tv2;
+  enum tree_code tc1, tc2;
+
+  if (!t1 && !t2)
+    return true;
+
+  while (t1 != NULL && t2 != NULL)
+    {
+      tv1 = TREE_VALUE (t1);
+      tv2 = TREE_VALUE (t2);
+
+      tc1 = TREE_CODE (tv1);
+      tc2 = TREE_CODE (tv2);
+
+      if (tc1 == NOP_EXPR && tc2 == NOP_EXPR)
+	{}
+      else if (tc1 == NOP_EXPR || tc2 == NOP_EXPR)
+	return false;
+      else if (!types_compatible_p (tv1, tv2))
+	return false;
+
+      t1 = TREE_CHAIN (t1);
+      t2 = TREE_CHAIN (t2);
+    }
+
+  return !(t1 || t2);
+}
+
+
+/* Semantic variable constructor that uses STACK as bitmap memory stack.  */
+
+sem_variable::sem_variable (bitmap_obstack *stack): sem_item (VAR, stack)
+{
+}
+
+/*  Constructor based on varpool node _NODE with computed hash _HASH.
+    Bitmap STACK is used for memory allocation.  */
+
+sem_variable::sem_variable (varpool_node *node, hashval_t _hash,
+			    bitmap_obstack *stack): sem_item(VAR,
+				  node, _hash, stack)
+{
+  gcc_checking_assert (node);
+  gcc_checking_assert (get_node ());
+}
+
+/* Returns true if the item equals to ITEM given as argument.  */
+
+bool
+sem_variable::equals (sem_item *item)
+{
+  gcc_assert (item->type == VAR);
+
+  return sem_variable::equals (ctor, static_cast<sem_variable *>(item)->ctor);
+}
+
+/* Compares trees T1 and T2 for semantic equality.  */
+
+bool
+sem_variable::equals (tree t1, tree t2)
+{
+  tree_code tc1 = TREE_CODE (t1);
+  tree_code tc2 = TREE_CODE (t2);
+
+  if (tc1 != tc2)
+    return false;
+
+  switch (tc1)
+    {
+    case CONSTRUCTOR:
+      {
+	unsigned len1 = vec_safe_length (CONSTRUCTOR_ELTS (t1));
+	unsigned len2 = vec_safe_length (CONSTRUCTOR_ELTS (t2));
+
+	if (len1 != len2)
+	  return false;
+
+	for (unsigned i = 0; i < len1; i++)
+	  if (!sem_variable::equals (CONSTRUCTOR_ELT (t1, i)->value,
+				     CONSTRUCTOR_ELT (t2, i)->value))
+	    return false;
+
+	return true;
+      }
+    case MEM_REF:
+      {
+	tree x1 = TREE_OPERAND (t1, 0);
+	tree x2 = TREE_OPERAND (t2, 0);
+	tree y1 = TREE_OPERAND (t1, 1);
+	tree y2 = TREE_OPERAND (t2, 1);
+
+	if (!sem_item::compare_for_aliasing (y1, y2))
+	  SE_EXIT_FALSE_WITH_MSG ("strict aliasing types do not match");
+
+	/* Type of the offset on MEM_REF does not matter.  */
+	return sem_variable::equals (x1, x2)
+	       && wi::to_offset  (y1) == wi::to_offset  (y2);
+      }
+    case NOP_EXPR:
+    case ADDR_EXPR:
+      {
+	tree op1 = TREE_OPERAND (t1, 0);
+	tree op2 = TREE_OPERAND (t2, 0);
+	return sem_variable::equals (op1, op2);
+      }
+    case FUNCTION_DECL:
+    case VAR_DECL:
+    case FIELD_DECL:
+    case LABEL_DECL:
+      return t1 == t2;
+    case INTEGER_CST:
+      return types_compatible_p (TREE_TYPE (t1), TREE_TYPE (t2))
+	     && wi::to_offset (t1) == wi::to_offset (t2);
+    case STRING_CST:
+    case REAL_CST:
+    case COMPLEX_CST:
+      return operand_equal_p (t1, t2, OEP_ONLY_CONST);
+    case COMPONENT_REF:
+    case ARRAY_REF:
+    case POINTER_PLUS_EXPR:
+      {
+	tree x1 = TREE_OPERAND (t1, 0);
+	tree x2 = TREE_OPERAND (t2, 0);
+	tree y1 = TREE_OPERAND (t1, 1);
+	tree y2 = TREE_OPERAND (t2, 1);
+
+	return sem_variable::equals (x1, x2) && sem_variable::equals (y1, y2);
+      }
+    case ERROR_MARK:
+      SE_EXIT_FALSE_WITH_MSG ("ERROR_MARK");
+    default:
+      SE_EXIT_FALSE_WITH_MSG ("Unknown TREE code reached")
+    }
+}
+
+/* Parser function that visits a varpool NODE.  */
+
+sem_variable *
+sem_variable::parse (struct varpool_node *node, bitmap_obstack *stack)
+{
+  tree decl = node->decl;
+
+  bool readonly = TYPE_P (decl) ? TYPE_READONLY (decl) : TREE_READONLY (decl);
+  bool can_handle = readonly && (DECL_VIRTUAL_P (decl)
+				 || !TREE_ADDRESSABLE (decl));
+
+  if (!can_handle)
+    return NULL;
+
+  tree ctor = ctor_for_folding (decl);
+  if (!ctor)
+    return NULL;
+
+  sem_variable *v = new sem_variable (node, 0, stack);
+
+  v->init ();
+
+  return v;
+}
+
+/* References independent hash function.  */
+
+hashval_t
+sem_variable::get_hash (void)
+{
+  if (hash)
+    return hash;
+
+  hashval_t hash = 456346417;
+
+  tree_code tc = TREE_CODE (ctor);
+  hash = iterative_hash_object (tc, hash);
+
+  if (TREE_CODE (ctor) == CONSTRUCTOR)
+    {
+      unsigned length = vec_safe_length (CONSTRUCTOR_ELTS (ctor));
+      hash = iterative_hash_object (length, hash);
+    }
+
+  return hash;
+}
+
+/* Merges instance with an ALIAS_ITEM, where alias, thunk or redirection can
+   be applied.  */
+
+bool
+sem_variable::merge (sem_item *alias_item)
+{
+  gcc_assert (alias_item->type == VAR);
+
+  sem_variable *alias_var = static_cast<sem_variable *> (alias_item);
+
+  struct varpool_node *original = get_node ();
+  struct varpool_node *alias = alias_var->get_node ();
+  bool original_discardable = false;
+
+  /* See if original is in a section that can be discarded if the main
+     symbol is not used.  */
+  if (DECL_EXTERNAL (original->decl))
+    original_discardable = true;
+  if (original->resolution == LDPR_PREEMPTED_REG
+      || original->resolution == LDPR_PREEMPTED_IR)
+    original_discardable = true;
+  if (symtab_can_be_discarded (original))
+    original_discardable = true;
+
+  gcc_assert (!TREE_ASM_WRITTEN (alias->decl));
+
+  if (original_discardable || DECL_EXTERNAL (alias_var->decl) ||
+      !compare_sections (alias_var))
+    {
+      if (dump_file)
+	fprintf (dump_file, "Varpool alias cannot be created\n\n");
+
+      return false;
+    }
+  else
+    {
+      // alias cycle creation check
+      varpool_node *n = original;
+
+      while (n->alias)
+	{
+	  n = varpool_alias_target (n);
+	  if (n == alias)
+	    {
+	      if (dump_file)
+		fprintf (dump_file, "Varpool alias cannot be created (alias cycle).\n\n");
+
+	      return false;
+	    }
+	}
+
+      alias->analyzed = false;
+
+      DECL_INITIAL (alias->decl) = DECL_INITIAL (alias_var->decl);
+      alias->remove_all_references ();
+
+      varpool_create_variable_alias (alias_var->decl, decl);
+      symtab_resolve_alias (alias, original);
+
+      if (dump_file)
+	fprintf (dump_file, "Varpool alias has been created.\n\n");
+
+      return true;
+    }
+}
+
+bool
+sem_variable::compare_sections (sem_variable *alias)
+{
+  const char *source = node->get_section ();
+  const char *target = alias->node->get_section();
+
+  if (source == NULL && target == NULL)
+    return true;
+  else if(!source || !target)
+    return false;
+  else
+    return strcmp (source, target) == 0;
+}
+
+/* Dump symbol to FILE.  */
+
+void
+sem_variable::dump_to_file (FILE *file)
+{
+  gcc_assert (file);
+
+  print_node (file, "", decl, 0);
+  fprintf (file, "\n\n");
+}
+
+/* Iterates though a constructor and identifies tree references
+   we are interested in semantic function equality.  */
+
+void
+sem_variable::parse_tree_refs (tree t)
+{
+  switch (TREE_CODE (t))
+    {
+    case CONSTRUCTOR:
+      {
+	unsigned length = vec_safe_length (CONSTRUCTOR_ELTS (t));
+
+	for (unsigned i = 0; i < length; i++)
+	  parse_tree_refs(CONSTRUCTOR_ELT (t, i)->value);
+
+	break;
+      }
+    case NOP_EXPR:
+    case ADDR_EXPR:
+      {
+	tree op = TREE_OPERAND (t, 0);
+	parse_tree_refs (op);
+	break;
+      }
+    case FUNCTION_DECL:
+      {
+	tree_refs.safe_push (t);
+	break;
+      }
+    default:
+      break;
+    }
+}
+
+unsigned int sem_item_optimizer::class_id = 0;
+
+sem_item_optimizer::sem_item_optimizer (): worklist (0), m_classes (0),
+m_classes_count (0), m_cgraph_node_hooks (NULL), m_varpool_node_hooks (NULL)
+{
+  m_items.create (0);
+  m_removed_items_set = pointer_set_create ();
+  bitmap_obstack_initialize (&m_bmstack);
+}
+
+sem_item_optimizer::~sem_item_optimizer ()
+{
+  for (unsigned int i = 0; i < m_items.length (); i++)
+    delete m_items[i];
+
+  for (hash_table<congruence_class_group_hash>::iterator it = m_classes.begin ();
+       it != m_classes.end (); ++it)
+    {
+      for (unsigned int i = 0; i < (*it)->classes.length (); i++)
+	delete (*it)->classes[i];
+
+      (*it)->classes.release ();
+    }
+
+  m_items.release ();
+
+  bitmap_obstack_release (&m_bmstack);
+  pointer_set_destroy (m_removed_items_set);
+}
+
+/* Write IPA ICF summary for symbols.  */
+
+void
+sem_item_optimizer::write_summary (void)
+{
+  unsigned int count = 0;
+
+  struct output_block *ob = create_output_block (LTO_section_ipa_icf);
+  lto_symtab_encoder_t encoder = ob->decl_state->symtab_node_encoder;
+  ob->cgraph_node = NULL;
+
+  /* Calculate number of symbols to be serialized.  */
+  for (lto_symtab_encoder_iterator lsei = lsei_start_in_partition (encoder);
+       !lsei_end_p (lsei);
+       lsei_next_in_partition (&lsei))
+    {
+      struct symtab_node *node = lsei_node (lsei);
+
+      if (m_symtab_node_map.get (node))
+	count++;
+    }
+
+  streamer_write_uhwi (ob, count);
+
+  /* Process all of the symbols.  */
+  for (lto_symtab_encoder_iterator lsei = lsei_start_in_partition (encoder);
+       !lsei_end_p (lsei);
+       lsei_next_in_partition (&lsei))
+    {
+      struct symtab_node *node = lsei_node (lsei);
+
+      sem_item **item = m_symtab_node_map.get (node);
+
+      if (item && *item)
+	{
+	  int node_ref = lto_symtab_encoder_encode (encoder, node);
+	  streamer_write_uhwi_stream (ob->main_stream, node_ref);
+
+	  streamer_write_uhwi (ob, (*item)->get_hash ());
+	}
+    }
+
+  streamer_write_char_stream (ob->main_stream, 0);
+  produce_asm (ob, NULL);
+  destroy_output_block (ob);
+}
+
+/* Reads a section from LTO stream file FILE_DATA. Input block for DATA
+   contains LEN bytes.  */
+
+void
+sem_item_optimizer::read_section (struct lto_file_decl_data *file_data,
+				  const char *data, size_t len)
+{
+  const struct lto_function_header *header =
+  (const struct lto_function_header *) data;
+  const int cfg_offset = sizeof (struct lto_function_header);
+  const int main_offset = cfg_offset + header->cfg_size;
+  const int string_offset = main_offset + header->main_size;
+  struct data_in *data_in;
+  struct lto_input_block ib_main;
+  unsigned int i;
+  unsigned int count;
+
+  LTO_INIT_INPUT_BLOCK (ib_main, (const char *) data + main_offset, 0,
+			header->main_size);
+
+  data_in =
+    lto_data_in_create (file_data, (const char *) data + string_offset,
+			header->string_size, vNULL);
+
+  count = streamer_read_uhwi (&ib_main);
+
+  for (i = 0; i < count; i++)
+    {
+      unsigned int index;
+      struct symtab_node *node;
+      lto_symtab_encoder_t encoder;
+
+      index = streamer_read_uhwi (&ib_main);
+      encoder = file_data->symtab_node_encoder;
+      node = lto_symtab_encoder_deref (encoder, index);
+
+      hashval_t hash = streamer_read_uhwi (&ib_main);
+
+      gcc_assert (node->definition);
+
+      if (dump_file)
+	fprintf (dump_file, "Symbol added:%s (tree: %p, uid:%u)\n", node->asm_name (),
+		 (void *) node->decl, node->order);
+
+      if (is_a_helper<cgraph_node *>::test (node))
+	{
+	  cgraph_node *cnode = cgraph (node);
+
+	  m_items.safe_push (new sem_function (cnode, hash, &m_bmstack));
+	}
+      else
+	{
+	  varpool_node *vnode = varpool (node);
+
+	  m_items.safe_push (new sem_variable (vnode, hash, &m_bmstack));
+	}
+    }
+
+  lto_free_section_data (file_data, LTO_section_ipa_icf, NULL, data,
+			 len);
+  lto_data_in_delete (data_in);
+}
+
+/* Read IPA IPA ICF summary for symbols.  */
+
+void
+sem_item_optimizer::read_summary (void)
+{
+  struct lto_file_decl_data **file_data_vec = lto_get_file_decl_data ();
+  struct lto_file_decl_data *file_data;
+  unsigned int j = 0;
+
+  while ((file_data = file_data_vec[j++]))
+    {
+      size_t len;
+      const char *data = lto_get_section_data (file_data,
+			 LTO_section_ipa_icf, NULL, &len);
+
+      if (data)
+	read_section (file_data, data, len);
+    }
+}
+
+/* Register callgraph and varpool hooks.  */
+
+void
+sem_item_optimizer::register_hooks (void)
+{
+  m_cgraph_node_hooks = cgraph_add_node_removal_hook (
+			  &sem_item_optimizer::cgraph_removal_hook, this);
+
+  m_varpool_node_hooks = varpool_add_node_removal_hook (
+			   &sem_item_optimizer::varpool_removal_hook, this);
+}
+
+/* Unregister callgraph and varpool hooks.  */
+
+void
+sem_item_optimizer::unregister_hooks (void)
+{
+  if (m_cgraph_node_hooks)
+    cgraph_remove_node_removal_hook (m_cgraph_node_hooks);
+
+  if (m_varpool_node_hooks)
+    varpool_remove_node_removal_hook (m_varpool_node_hooks);
+}
+
+/* Adds a CLS to hashtable associated by hash value.  */
+
+void
+sem_item_optimizer::add_class (congruence_class *cls)
+{
+  gcc_assert (cls->members.length ());
+
+  congruence_class_group_t *group = get_group_by_hash (
+				      cls->members[0]->get_hash (),
+				      cls->members[0]->type);
+  group->classes.safe_push (cls);
+}
+
+/* Gets a congruence class group based on given HASH value and TYPE.  */
+
+congruence_class_group_t *
+sem_item_optimizer::get_group_by_hash (hashval_t hash, sem_item_type type)
+{
+  congruence_class_group_t *item = XNEW (congruence_class_group_t);
+  item->hash = hash;
+  item->type = type;
+
+  congruence_class_group **slot = m_classes.find_slot (item, INSERT);
+
+  if (*slot)
+    free (item);
+  else
+    {
+      item->classes.create (1);
+      *slot = item;
+    }
+
+  return *slot;
+}
+
+/* Callgraph removal hook called for a NODE with a custom DATA.  */
+
+void
+sem_item_optimizer::cgraph_removal_hook (struct cgraph_node *node, void *data)
+{
+  sem_item_optimizer *optimizer = (sem_item_optimizer *) data;
+  optimizer->remove_symtab_node (node);
+}
+
+/* Varpool removal hook called for a NODE with a custom DATA.  */
+
+void
+sem_item_optimizer::varpool_removal_hook (struct varpool_node *node, void *data)
+{
+  sem_item_optimizer *optimizer = (sem_item_optimizer *) data;
+  optimizer->remove_symtab_node (node);
+}
+
+/* Remove symtab NODE triggered by symtab removal hooks.  */
+
+void
+sem_item_optimizer::remove_symtab_node (struct symtab_node *node)
+{
+  gcc_assert (!m_classes.elements());
+
+  pointer_set_insert (m_removed_items_set, node);
+}
+
+/* Removes all callgraph and varpool nodes that are marked by symtab
+   as deleted.  */
+
+void
+sem_item_optimizer::filter_removed_items (void)
+{
+  vec <sem_item *> filtered;
+  filtered.create (m_items.length());
+
+  for (unsigned int i = 0; i < m_items.length(); i++)
+    {
+      sem_item *item = m_items[i];
+
+      if (!flag_ipa_icf_functions && item->type == FUNC)
+	continue;
+
+      if (!flag_ipa_icf_variables && item->type == VAR)
+	continue;
+
+      bool no_body_function = false;
+
+      if (item->type == FUNC)
+	{
+	  struct cgraph_node *cnode = static_cast <sem_function *>(item)->get_node ();
+
+	  no_body_function = in_lto_p && (cnode->alias || cnode->body_removed);
+	}
+
+      if(!pointer_set_contains (m_removed_items_set, m_items[i]->node)
+	  && !no_body_function)
+	{
+	  if (item->type == VAR || (!DECL_CXX_CONSTRUCTOR_P (item->decl)
+				    && !DECL_CXX_DESTRUCTOR_P (item->decl)))
+	    filtered.safe_push (m_items[i]);
+	}
+    }
+
+  m_items.release ();
+
+  for (unsigned int i = 0; i < filtered.length(); i++)
+    m_items.safe_push (filtered[i]);
+
+  filtered.release ();
+}
+
+/* Optimizer entry point.  */
+
+void
+sem_item_optimizer::execute (void)
+{
+  filter_removed_items ();
+  build_hash_based_classes ();
+
+  if (dump_file)
+    fprintf (dump_file, "Dump after hash based groups\n");
+  dump_cong_classes ();
+
+  for (unsigned int i = 0; i < m_items.length(); i++)
+    m_items[i]->init_wpa ();
+
+  subdivide_classes_by_equality (true);
+
+  if (dump_file)
+    fprintf (dump_file, "Dump after WPA based types groups\n");
+  dump_cong_classes ();
+
+  parse_nonsingleton_classes ();
+  subdivide_classes_by_equality ();
+
+  if (dump_file)
+    fprintf (dump_file, "Dump after full equality comparison of groups\n");
+
+  dump_cong_classes ();
+
+  unsigned int prev_class_count = m_classes_count;
+
+  process_cong_reduction ();
+  dump_cong_classes ();
+  merge_classes (prev_class_count);
+
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    dump_symtab (dump_file);
+}
+
+/* Function responsible for visiting all potential functions and
+   read-only variables that can be merged.  */
+
+void
+sem_item_optimizer::parse_funcs_and_vars (void)
+{
+  struct cgraph_node *cnode;
+
+  if (flag_ipa_icf_functions)
+    FOR_EACH_DEFINED_FUNCTION (cnode)
+    {
+      sem_function *f = sem_function::parse (cnode, &m_bmstack);
+      if (f)
+	{
+	  m_items.safe_push (f);
+	  m_symtab_node_map.put (cnode, f);
+
+	  if (dump_file)
+	    fprintf (dump_file, "Parsed function:%s\n", f->asm_name ());
+
+	  if (dump_file && (dump_flags & TDF_DETAILS))
+	    f->dump_to_file (dump_file);
+	}
+      else if (dump_file)
+	fprintf (dump_file, "Not parsed function:%s\n", cnode->asm_name ());
+    }
+
+  varpool_node *vnode;
+
+  if (flag_ipa_icf_variables)
+    FOR_EACH_DEFINED_VARIABLE (vnode)
+    {
+      sem_variable *v = sem_variable::parse (vnode, &m_bmstack);
+
+      if (v)
+	{
+	  m_items.safe_push (v);
+	  m_symtab_node_map.put (vnode, v);
+	}
+    }
+}
+
+/* Makes pairing between a congruence class CLS and semantic ITEM.  */
+
+void
+sem_item_optimizer::add_item_to_class (congruence_class *cls, sem_item *item)
+{
+  item->index_in_class = cls->members.length ();
+  cls->members.safe_push (item);
+  item->cls = cls;
+}
+
+/* Congruence classes are built by hash value.  */
+
+void
+sem_item_optimizer::build_hash_based_classes (void)
+{
+  for (unsigned i = 0; i < m_items.length (); i++)
+    {
+      sem_item *item = m_items[i];
+
+      congruence_class_group_t *group = get_group_by_hash (item->get_hash (),
+					item->type);
+
+      if (!group->classes.length ())
+	{
+	  m_classes_count++;
+	  group->classes.safe_push (new congruence_class (class_id++));
+	}
+
+      add_item_to_class (group->classes[0], item);
+    }
+}
+
+/* Semantic items in classes having more than one element and initialized.
+   In case of WPA, we load function body.  */
+
+void
+sem_item_optimizer::parse_nonsingleton_classes (void)
+{
+  for (hash_table <congruence_class_group_hash>::iterator it = m_classes.begin ();
+       it != m_classes.end (); ++it)
+    {
+      for (unsigned i = 0; i < (*it)->classes.length (); i++)
+	{
+	  congruence_class *c = (*it)->classes [i];
+
+	  if (c->members.length() > 1)
+	    for (unsigned j = 0; j < c->members.length (); j++)
+	      {
+		sem_item *item = c->members[j];
+		m_decl_map.put (item->decl, item);
+	      }
+	}
+    }
+
+  unsigned int init_called_count = 0;
+
+  for (hash_table <congruence_class_group_hash>::iterator it = m_classes.begin ();
+       it != m_classes.end (); ++it)
+    {
+      /* We fill in all declarations for sem_items.  */
+      for (unsigned i = 0; i < (*it)->classes.length (); i++)
+	{
+	  congruence_class *c = (*it)->classes [i];
+
+	  if (c->members.length() > 1)
+	    for (unsigned j = 0; j < c->members.length (); j++)
+	      {
+		sem_item *item = c->members[j];
+
+		item->init ();
+		item->init_refs ();
+
+		init_called_count++;
+
+		for (unsigned j = 0; j < item->tree_refs.length (); j++)
+		  {
+		    sem_item **result = m_decl_map.get (item->tree_refs[j]);
+
+		    if(result)
+		      {
+			sem_item *target = *result;
+			item->refs.safe_push (target);
+
+			unsigned index = item->refs.length ();
+			target->usages.safe_push (new sem_usage_pair(item, index));
+			bitmap_set_bit (target->usage_index_bitmap, index);
+			pointer_set_insert (item->tree_refs_set, item->tree_refs[j]);
+		      }
+		  }
+	      }
+	}
+    }
+
+  if (dump_file)
+    fprintf (dump_file, "Init called for %u items (%.2f%%).\n", init_called_count,
+	     100.0f * init_called_count / m_items.length ());
+}
+
+/* Equality function for semantic items is used to subdivide existing
+   classes. If IN_WPA, fast equality function is invoked.  */
+
+void
+sem_item_optimizer::subdivide_classes_by_equality (bool in_wpa)
+{
+  for (hash_table <congruence_class_group_hash>::iterator it = m_classes.begin ();
+       it != m_classes.end (); ++it)
+    {
+      unsigned int class_count = (*it)->classes.length ();
+
+      for (unsigned i = 0; i < class_count; i++)
+	{
+	  congruence_class *c = (*it)->classes [i];
+
+	  if (c->members.length() > 1)
+	    {
+	      vec <sem_item *> new_vector;
+	      new_vector.create (c->members.length ());
+
+	      sem_item *first = c->members[0];
+	      new_vector.safe_push (first);
+
+	      unsigned class_split_first = (*it)->classes.length ();
+
+	      for (unsigned j = 1; j < c->members.length (); j++)
+		{
+		  sem_item *item = c->members[j];
+
+		  bool equals = in_wpa ? first->equals_wpa (item) : first->equals (item);
+
+		  if (equals)
+		    new_vector.safe_push (item);
+		  else
+		    {
+		      bool integrated = false;
+
+		      for (unsigned k = class_split_first; k < (*it)->classes.length (); k++)
+			{
+			  sem_item *x = (*it)->classes[k]->members[0];
+			  bool equals = in_wpa ? x->equals_wpa (item) : x->equals (item);
+
+			  if (equals)
+			    {
+			      integrated = true;
+			      add_item_to_class ((*it)->classes[k], item);
+
+			      break;
+			    }
+			}
+
+		      if (!integrated)
+			{
+			  congruence_class *c = new congruence_class (class_id++);
+			  m_classes_count++;
+			  add_item_to_class (c, item);
+
+			  (*it)->classes.safe_push (c);
+			}
+		    }
+		}
+
+	      // we replace newly created new_vector for the class we've just splitted
+	      c->members.release ();
+	      c->members.create (new_vector.length ());
+
+	      for (unsigned int j = 0; j < new_vector.length (); j++)
+		add_item_to_class (c, new_vector[j]);
+
+	      new_vector.release ();
+	    }
+	}
+    }
+
+  verify_classes ();
+}
+
+/* Verify congruence classes if checking is enabled.  */
+
+void
+sem_item_optimizer::verify_classes (void)
+{
+#if ENABLE_CHECKING
+  for (hash_table <congruence_class_group_hash>::iterator it = m_classes.begin ();
+       it != m_classes.end (); ++it)
+    {
+      for (unsigned int i = 0; i < (*it)->classes.length (); i++)
+	{
+	  congruence_class *cls = (*it)->classes[i];
+
+	  gcc_checking_assert (cls);
+	  gcc_checking_assert (cls->members.length () > 0);
+
+	  for (unsigned int j = 0; j < cls->members.length (); j++)
+	    {
+	      sem_item *item = cls->members[j];
+
+	      gcc_checking_assert (item);
+
+	      for (unsigned k = 0; k < item->usages.length (); k++)
+		{
+		  sem_usage_pair *usage = item->usages[k];
+		  gcc_checking_assert (usage->item->index_in_class <
+				       usage->item->cls->members.length ());
+		}
+	    }
+	}
+    }
+#endif
+}
+
+/* Disposes split map traverse function. CLS_PTR is pointer to congruence
+   class, BSLOT is bitmap slot we want to release. DATA is mandatory,
+   but unused argument.  */
+
+bool
+sem_item_optimizer::release_split_map (__attribute__((__unused__)) congruence_class *
+				       const &cls,
+				       __attribute__((__unused__)) bitmap const &b,
+				       __attribute__((__unused__)) traverse_split_pair *pair)
+{
+  bitmap bmp = b; 
+
+  BITMAP_FREE (bmp);
+
+  return true;
+}
+
+/* Process split operation for a class given as pointer CLS_PTR,
+   where bitmap B splits congruence class members. DATA is used
+   as argument of split pair.  */
+
+bool
+sem_item_optimizer::traverse_congruence_split (congruence_class * const &cls,
+    bitmap const &b, traverse_split_pair *pair)
+{
+  sem_item_optimizer *optimizer = pair->optimizer;
+  const congruence_class *splitter_cls = pair->cls;
+
+  /* If counted bits are greater than zero and less than the number of members
+     a group will be splitted.  */
+  unsigned popcount = bitmap_count_bits (b);
+
+  if (popcount > 0 && popcount < cls->members.length ())
+    {
+      congruence_class* newclasses[2] = { new congruence_class (class_id++), new congruence_class (class_id++) };
+
+      for (unsigned int i = 0; i < cls->members.length (); i++)
+	{
+	  int target = bitmap_bit_p (b, i);
+	  congruence_class *tc = newclasses[target];
+
+	  add_item_to_class (tc, cls->members[i]);
+	}
+
+#ifdef ENABLE_CHECKING
+      for (unsigned int i = 0; i < 2; i++)
+	gcc_checking_assert (newclasses[i]->members.length ());
+#endif
+
+      if (splitter_cls == cls)
+	optimizer->splitter_class_removed = true;
+
+      /* Remove old class from worklist if presented.  */
+      bool in_work_list = optimizer->worklist_contains (cls);
+
+      if (in_work_list)
+	optimizer->worklist_remove (cls);
+
+      congruence_class_group g;
+      g.hash = cls->members[0]->get_hash ();
+      g.type = cls->members[0]->type;
+
+      congruence_class_group *slot = optimizer->m_classes.find(&g);
+
+      for (unsigned int i = 0; i < slot->classes.length (); i++)
+	if (slot->classes[i] == cls)
+	  {
+	    slot->classes.ordered_remove (i);
+	    break;
+	  }
+
+      /* New class will be inserted and integrated to work list.  */
+      for (unsigned int i = 0; i < 2; i++)
+	optimizer->add_class (newclasses[i]);
+
+      /* Two classes replace one, so that increment just by one.  */
+      optimizer->m_classes_count++;
+
+      /* If OLD class was presented in the worklist, we remove the class
+         are replace it will both newly created classes.  */
+      if (in_work_list)
+	for (unsigned int i = 0; i < 2; i++)
+	  optimizer->worklist_push (newclasses[i]);
+      else /* Just smaller class is inserted.  */
+	{
+	  unsigned int smaller_index = newclasses[0]->members.length () <
+				       newclasses[1]->members.length () ?
+				       0 : 1;
+	  optimizer->worklist_push (newclasses[smaller_index]);
+	}
+
+      if (dump_file && (dump_flags & TDF_DETAILS))
+	{
+	  fprintf (dump_file, "  congruence class splitted:\n");
+	  cls->dump (dump_file, 4);
+
+	  fprintf (dump_file, "  newly created groups:\n");
+	  for (unsigned int i = 0; i < 2; i++)
+	    newclasses[i]->dump (dump_file, 4);
+	}
+
+      delete cls;
+    }
+
+
+  return true;
+}
+
+/* Tests if a class CLS used as INDEXth splits any congruence classes.
+   Bitmap stack BMSTACK is used for bitmap allocation.  */
+
+void
+sem_item_optimizer::do_congruence_step_for_index (congruence_class *cls,
+    unsigned int index)
+{
+  hash_map <congruence_class *, bitmap> *split_map =
+  	new hash_map <congruence_class *, bitmap> ();
+
+  for (unsigned int i = 0; i < cls->members.length (); i++)
+    {
+      sem_item *item = cls->members[i];
+
+      /* Iterate all usages that have INDEX as usage of the item.  */
+      for (unsigned int j = 0; j < item->usages.length (); j++)
+	{
+	  sem_usage_pair *usage = item->usages[j];
+
+	  if (usage->index != index)
+	    continue;
+
+	  bitmap *slot = split_map->get (usage->item->cls);
+	  bitmap b;
+
+	  if(!slot)
+	  {
+	      b = BITMAP_ALLOC (&m_bmstack);
+	      split_map->put (usage->item->cls, b);
+	  }
+	  else
+	    b = *slot;
+
+#if ENABLE_CHECKING
+	  gcc_checking_assert (usage->item->cls);
+	  gcc_checking_assert (usage->item->index_in_class <
+			       usage->item->cls->members.length ());
+#endif
+
+	  bitmap_set_bit (b, usage->item->index_in_class);
+	}
+    }
+
+  traverse_split_pair pair;
+  pair.optimizer = this;
+  pair.cls = cls;
+
+  splitter_class_removed = false;
+  split_map->traverse <traverse_split_pair *, sem_item_optimizer::traverse_congruence_split> (&pair);
+
+  /* Bitmap clean-up.  */
+  split_map->traverse <traverse_split_pair *, sem_item_optimizer::release_split_map> (NULL);
+}
+
+/* Every usage of a congruence class CLS is a candidate that can split the
+   collection of classes. Bitmap stack BMSTACK is used for bitmap
+   allocation.  */
+
+void
+sem_item_optimizer::do_congruence_step (congruence_class *cls)
+{
+  bitmap_iterator bi;
+  unsigned int i;
+
+  bitmap usage = BITMAP_ALLOC (&m_bmstack);
+
+  for (unsigned int i = 0; i < cls->members.length (); i++)
+    bitmap_ior_into (usage, cls->members[i]->usage_index_bitmap);
+
+  EXECUTE_IF_SET_IN_BITMAP (usage, 0, i, bi)
+  {
+    if (dump_file && (dump_flags & TDF_DETAILS))
+      fprintf (dump_file, "  processing congruece step for class: %u, index: %u\n",
+	       cls->id, i);
+
+    do_congruence_step_for_index (cls, i);
+
+    if (splitter_class_removed)
+      break;
+  }
+
+  BITMAP_FREE (usage);
+}
+
+/* Adds a newly created congruence class CLS to worklist.  */
+
+void
+sem_item_optimizer::worklist_push (congruence_class *cls)
+{
+  congruence_class **slot = worklist.find_slot (cls, INSERT);
+
+  if (*slot)
+    return;
+
+  *slot = cls;
+}
+
+/* Pops a class from worklist. */
+
+congruence_class *
+sem_item_optimizer::worklist_pop (void)
+{
+  gcc_assert (worklist.elements ());
+
+  congruence_class *cls = *worklist.begin ();
+  worklist.remove_elt (cls);
+
+  return cls;
+}
+
+/* Iterative congruence reduction function.  */
+
+void
+sem_item_optimizer::process_cong_reduction (void)
+{
+  for (hash_table<congruence_class_group_hash>::iterator it = m_classes.begin ();
+       it != m_classes.end (); ++it)
+    for (unsigned i = 0; i < (*it)->classes.length (); i++)
+      if ((*it)->classes[i]->is_class_used ())
+	worklist_push ((*it)->classes[i]);
+
+  if (dump_file)
+    fprintf (dump_file, "Worklist has been filled with: %lu\n",
+	     worklist.elements ());
+
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    fprintf (dump_file, "Congruence class reduction\n");
+
+  while (worklist.elements ())
+    {
+      congruence_class *cls = worklist_pop ();
+      do_congruence_step (cls);
+    }
+}
+
+/* Debug function prints all informations about congruence classes.  */
+
+void
+sem_item_optimizer::dump_cong_classes (void)
+{
+  if (!dump_file)
+    return;
+
+  fprintf (dump_file,
+	   "Congruence classes: %u (unique hash values: %lu), with total: %u items\n",
+	   m_classes_count, m_classes.elements(), m_items.length ());
+
+  /* Histogram calculation.  */
+  unsigned int max_index = 0;
+  unsigned int* histogram = XCNEWVEC (unsigned int, m_items.length ());
+
+  for (hash_table<congruence_class_group_hash>::iterator it = m_classes.begin ();
+       it != m_classes.end (); ++it)
+
+    for (unsigned i = 0; i < (*it)->classes.length (); i++)
+      {
+	unsigned int c = (*it)->classes[i]->members.length ();
+	histogram[c]++;
+
+	if (c > max_index)
+	  max_index = c;
+      }
+
+  fprintf (dump_file,
+	   "Class size histogram [num of members]: number of classe number of classess\n");
+
+  for (unsigned int i = 0; i <= max_index; i++)
+    if (histogram[i])
+      fprintf (dump_file, "[%u]: %u classes\n", i, histogram[i]);
+
+  fprintf (dump_file, "\n\n");
+
+
+  if (dump_flags & TDF_DETAILS)
+    for (hash_table<congruence_class_group_hash>::iterator it = m_classes.begin ();
+	 it != m_classes.end (); ++it)
+      {
+	fprintf (dump_file, "  group: with %u classes:\n", (*it)->classes.length ());
+
+	for (unsigned i = 0; i < (*it)->classes.length (); i++)
+	  {
+	    (*it)->classes[i]->dump (dump_file, 4);
+
+	    if(i < (*it)->classes.length () - 1)
+	      fprintf (dump_file, " ");
+	  }
+      }
+
+  free (histogram);
+}
+
+/* After reduction is done, we can declare all items in a group
+   to be equal. PREV_CLASS_COUNT is start number of classes
+   before reduction.  */
+
+void
+sem_item_optimizer::merge_classes (unsigned int prev_class_count)
+{
+  unsigned int item_count = m_items.length ();
+  unsigned int class_count = m_classes_count;
+  unsigned int equal_items = item_count - class_count;
+
+  if (dump_file)
+    {
+      fprintf (dump_file, "\nItem count: %u\n", item_count);
+      fprintf (dump_file, "Congruent classes before: %u, after: %u\n",
+	       prev_class_count, class_count);
+      fprintf (dump_file, "Average class size before: %.2f, after: %.2f\n",
+	       1.0f * item_count / prev_class_count,
+	       1.0f * item_count / class_count);
+      fprintf (dump_file, "Equal symbols: %u\n", equal_items);
+      fprintf (dump_file, "Fraction of visited symbols: %.2f%%\n\n",
+	       100.0f * equal_items / item_count);
+    }
+
+  for (hash_table<congruence_class_group_hash>::iterator it = m_classes.begin ();
+       it != m_classes.end (); ++it)
+    for (unsigned int i = 0; i < (*it)->classes.length (); i++)
+      {
+	congruence_class *c = (*it)->classes[i];
+
+	if (c->members.length () == 1)
+	  continue;
+
+	gcc_assert (c->members.length ());
+
+	sem_item *source = c->members[0];
+
+	for (unsigned int j = 1; j < c->members.length (); j++)
+	  {
+	    sem_item *alias = c->members[j];
+
+	    if (dump_file)
+	      {
+		fprintf (dump_file, "Semantic equality hit:%s->%s\n",
+			 source->name (), alias->name ());
+		fprintf (dump_file, "Assembler symbol names:%s->%s\n",
+			 source->asm_name (), alias->asm_name ());
+	      }
+
+	    if (dump_file && (dump_flags & TDF_DETAILS))
+	      {
+		source->dump_to_file (dump_file);
+		alias->dump_to_file (dump_file);
+	      }
+
+	    source->merge (alias);
+	  }
+      }
+}
+
+/* Dump function prints all class members to a FILE with an INDENT.  */
+
+void
+congruence_class::dump (FILE *file, unsigned int indent) const
+{
+  FPRINTF_SPACES (file, indent, "class with id: %u, hash: %u, items: %u\n",
+		  id, members[0]->get_hash (), members.length ());
+
+  FPUTS_SPACES (file, indent + 2, "");
+  for (unsigned i = 0; i < members.length (); i++)
+    fprintf (file, "%s(%p/%u)", members[i]->asm_name (), (void *) members[i]->decl,
+	     members[i]->node->order);
+
+  fprintf (file, "\n");
+}
+
+/* Returns true if there's a member that is used from another group.  */
+
+bool
+congruence_class::is_class_used (void)
+{
+  for (unsigned int i = 0; i < members.length (); i++)
+    if (members[i]->usages.length ())
+      return true;
+
+  return false;
+}
+
+/* Initialization and computation of symtab node hash, there data
+   are propagated later on.  */
+
+static sem_item_optimizer *optimizer = NULL;
+
+/* Generate pass summary for IPA ICF pass.  */
+
+static void
+ipa_icf_generate_summary (void)
+{
+  if (!optimizer)
+    optimizer = new sem_item_optimizer ();
+
+  optimizer->parse_funcs_and_vars ();
+}
+
+/* Write pass summary for IPA ICF pass.  */
+
+static void
+ipa_icf_write_summary (void)
+{
+  gcc_assert (optimizer);
+
+  optimizer->write_summary ();
+}
+
+/* Read pass summary for IPA ICF pass.  */
+
+static void
+ipa_icf_read_summary (void)
+{
+  if (!optimizer)
+    optimizer = new sem_item_optimizer ();
+
+  optimizer->read_summary ();
+  optimizer->register_hooks ();
+}
+
+/* Semantic equality exection function.  */
+
+static unsigned int
+ipa_icf_driver (void)
+{
+  gcc_assert (optimizer);
+
+  optimizer->execute ();
+  optimizer->unregister_hooks ();
+
+  delete optimizer;
+
+  return 0;
+}
+
+const pass_data pass_data_ipa_icf =
+{
+  IPA_PASS,		    /* type */
+  "icf",		    /* name */
+  OPTGROUP_IPA,             /* optinfo_flags */
+  true,                     /* has_execute */
+  TV_IPA_ICF,		    /* tv_id */
+  0,                        /* properties_required */
+  0,                        /* properties_provided */
+  0,                        /* properties_destroyed */
+  0,                        /* todo_flags_start */
+  0,                        /* todo_flags_finish */
+};
+
+class pass_ipa_icf : public ipa_opt_pass_d
+{
+public:
+  pass_ipa_icf (gcc::context *ctxt)
+    : ipa_opt_pass_d (pass_data_ipa_icf, ctxt,
+		      ipa_icf_generate_summary, /* generate_summary */
+		      ipa_icf_write_summary, /* write_summary */
+		      ipa_icf_read_summary, /* read_summary */
+		      NULL, /*
+		      write_optimization_summary */
+		      NULL, /*
+		      read_optimization_summary */
+		      NULL, /* stmt_fixup */
+		      0, /* function_transform_todo_flags_start */
+		      NULL, /* function_transform */
+		      NULL) /* variable_transform */
+  {}
+
+  /* opt_pass methods: */
+  virtual bool gate (function *)
+  {
+    return flag_ipa_icf_variables || flag_ipa_icf_functions;
+  }
+
+  virtual unsigned int execute (function *)
+  {
+    return ipa_icf_driver();
+  }
+}; // class pass_ipa_icf
+
+} // ipa_icf namespace
+
+ipa_opt_pass_d *
+make_pass_ipa_icf (gcc::context *ctxt)
+{
+  return new ipa_icf::pass_ipa_icf (ctxt);
+}
diff --git a/gcc/ipa-icf.h b/gcc/ipa-icf.h
new file mode 100644
index 0000000..d328dd6
--- /dev/null
+++ b/gcc/ipa-icf.h
@@ -0,0 +1,743 @@
+/* Interprocedural semantic function equality pass
+   Copyright (C) 2014 Free Software Foundation, Inc.
+
+   Contributed by Jan Hubicka <hubicka@ucw.cz> and Martin Liska <mliska@suse.cz>
+
+This file is part of GCC.
+
+GCC 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, or (at your option) any later
+version.
+
+GCC 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 GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+/* Prints string STRING to a FILE with a given number of SPACE_COUNT.  */
+#define FPUTS_SPACES(file, space_count, string) \
+  do \
+  { \
+    fprintf (file, "%*s" string, space_count, " "); \
+  } \
+  while (false);
+
+/* fprintf function wrapper that transforms given FORMAT to follow given
+   number for SPACE_COUNT and call fprintf for a FILE.  */
+#define FPRINTF_SPACES(file, space_count, format, ...) \
+  fprintf (file, "%*s" format, space_count, " ", ##__VA_ARGS__);
+
+/* Prints a MESSAGE to dump_file if exists.  */
+#define SE_DUMP_MESSAGE(message) \
+  do \
+  { \
+    if (dump_file && (dump_flags & TDF_DETAILS)) \
+      fprintf (dump_file, "  debug message: %s (%s:%u)\n", message, __func__, __LINE__); \
+  } \
+  while (false);
+
+/* Logs a MESSAGE to dump_file if exists and returns false.  */
+#define SE_EXIT_FALSE_WITH_MSG(message) \
+  do \
+  { \
+    if (dump_file && (dump_flags & TDF_DETAILS)) \
+      fprintf (dump_file, "  false returned: '%s' (%s:%u)\n", message, __func__, __LINE__); \
+    return false; \
+  } \
+  while (false);
+
+/* Return false and log that false value is returned.  */
+#define SE_EXIT_FALSE() \
+  SE_EXIT_FALSE_WITH_MSG("")
+
+/* Logs return value if RESULT is false.  */
+#define SE_EXIT_DEBUG(result) \
+  do \
+  { \
+    if (!(result) && dump_file && (dump_flags & TDF_DETAILS)) \
+      fprintf (dump_file, "  false returned (%s:%u)\n", __func__, __LINE__); \
+    return result; \
+  } \
+  while (false);
+
+/* Verbose logging function logging statements S1 and S2 of a CODE.  */
+#define SE_DIFF_STATEMENT(s1, s2, code) \
+  do \
+  { \
+    if (dump_file && (dump_flags & TDF_DETAILS)) \
+      { \
+        fprintf (dump_file, "  different statement for code: %s:\n", code); \
+        print_gimple_stmt (dump_file, s1, 3, TDF_DETAILS); \
+        print_gimple_stmt (dump_file, s2, 3, TDF_DETAILS); \
+      } \
+    return false; \
+  } \
+  while (false);
+
+namespace ipa_icf {
+
+/* Forward declaration for sem_func class.  */
+class sem_item;
+
+/* A class aggregating all connections and semantic equivalents
+   for a given pair of semantic function candidates.  */
+class func_checker
+{
+public:
+  /* Initializes internal structures according to given number of
+     source and target SSA names. The number of source names is SSA_SOURCE,
+     respectively SSA_TARGET.  */
+  func_checker (unsigned ssa_source, unsigned sss_target);
+
+  /* Memory release routine.  */
+  ~func_checker();
+
+  /* Verifies that trees T1 and T2 are equivalent from perspective of ICF.  */
+  bool compare_ssa_name (tree t1, tree t2);
+
+  /* Verification function for edges E1 and E2.  */
+  bool compare_edge (edge e1, edge e2);
+
+  /* Verification function for declaration trees T1 and T2 that
+     come from functions FUNC1 and FUNC2.  */
+  bool compare_decl (tree t1, tree t2, tree func1, tree func2);
+
+private:
+  /* Vector mapping source SSA names to target ones.  */
+  vec <int> m_source_ssa_names;
+
+  /* Vector mapping target SSA names to source ones.  */
+  vec <int> m_target_ssa_names;
+
+  /* Source to target edge map.  */
+  hash_map <edge, edge> *m_edge_map;
+
+  /* Source to target declaration map.  */
+  hash_map <tree, tree> *m_decl_map;
+};
+
+/* Congruence class encompasses a collection of either functions or
+   read-only variables. These items are considered to be equivalent
+   if not proved the oposite.  */
+class congruence_class
+{
+public:
+  /* Congruence class constructor for a new class with _ID.  */
+  congruence_class (unsigned int _id): id(_id)
+  {
+    members.create (2);
+  }
+
+  /* Destructor.  */
+  ~congruence_class ()
+  {
+    members.release ();
+  }
+
+  /* Dump function prints all class members to a FILE with an INDENT.  */
+  void dump (FILE *file, unsigned int indent = 0) const;
+
+  /* Returns true if there's a member that is used from another group.  */
+  bool is_class_used (void);
+
+  /* Vector of all group members.  */
+  vec <sem_item *> members;
+
+  /* Global unique class identifier.  */
+  unsigned int id;
+};
+
+/* Semantic item type enum.  */
+enum sem_item_type
+{
+  FUNC,
+  VAR
+};
+
+/* Semantic item usage pair.  */
+class sem_usage_pair
+{
+public:
+  /* Constructor for key value pair, where _ITEM is key and _INDEX is a target.  */
+  sem_usage_pair (sem_item *_item, unsigned int _index);
+
+  /* Target semantic item where an item is used.  */
+  sem_item *item;
+
+  /* Index of usage of such an item.  */
+  unsigned int index;
+};
+
+/* Basic block struct for sematic equality pass.  */
+typedef struct sem_bb
+{
+  /* Basic block the structure belongs to.  */
+  basic_block bb;
+
+  /* Number of non-debug statements in the basic block.  */
+  unsigned nondbg_stmt_count;
+
+  /* Number of edges connected to the block.  */
+  unsigned edge_count;
+} sem_bb_t;
+
+/* Semantic item is a base class that encapsulates all shared functionality
+   for both semantic function and variable items.  */
+class sem_item
+{
+public:
+  /* Semantic item constructor for a node of _TYPE, where STACK is used
+     for bitmap memory allocation.  */
+  sem_item (enum sem_item_type _type, bitmap_obstack *stack);
+
+  /* Semantic item constructor for a node of _TYPE, where STACK is used
+     for bitmap memory allocation. The item is based on symtab node _NODE
+     with computed _HASH.  */
+  sem_item (enum sem_item_type _type, struct symtab_node *_node, hashval_t _hash,
+	    bitmap_obstack *stack);
+
+  virtual ~sem_item ();
+
+  /* Dump function for debugging purpose.  */
+  DEBUG_FUNCTION void dump (void);
+
+  /* Initialize semantic item by info reachable during LTO WPA phase.  */
+  virtual void init_wpa (void) = 0;
+
+  /* Semantic item initialization function.  */
+  virtual void init (void) = 0;
+
+  /* Gets symbol name of the item.  */
+  const char *name (void)
+  {
+    return node->name ();
+  }
+
+  /* Gets assembler name of the item.  */
+  const char *asm_name (void)
+  {
+    return node->asm_name ();
+  }
+
+  /* Initializes references to other semantic functions/variables.  */
+  virtual void init_refs () = 0;
+
+  /* Fast equality function based on knowledge known in WPA.  */
+  virtual bool equals_wpa (sem_item *item) = 0;
+
+  /* Returns true if the item equals to ITEM given as arguemnt.  */
+  virtual bool equals (sem_item *item) = 0;
+
+  /* References independent hash function.  */
+  virtual hashval_t get_hash (void) = 0;
+
+  /* Merges instance with an ALIAS_ITEM, where alias, thunk or redirection can
+     be applied.  */
+  virtual bool merge (sem_item *alias_item) = 0;
+
+  /* Dump symbol to FILE.  */
+  virtual void dump_to_file (FILE *file) = 0;
+
+  /* Compare two types if are same aliases in case of strict aliasing
+     is enabled.  */
+  static bool compare_for_aliasing (tree t1, tree t2);
+
+  /* Item type.  */
+  enum sem_item_type type;
+
+  /* Global unique function index.  */
+  unsigned int index;
+
+  /* Symtab node.  */
+  struct symtab_node *node;
+
+  /* Declaration tree node.  */
+  tree decl;
+
+  /* Semantic references used that generate congruence groups.  */
+  vec <sem_item *> refs;
+
+  /* Pointer to a congruence class the item belongs to.  */
+  congruence_class *cls;
+
+  /* Index of the item in a class belonging to.  */
+  unsigned int index_in_class;
+
+  /* List of semantic items where the instance is used.  */
+  vec <sem_usage_pair *> usages;
+
+  /* A bitmap with indices of all classes referencing this item.  */
+  bitmap usage_index_bitmap;
+
+  /* List of tree references (either FUNC_DECL or VAR_DECL).  */
+  vec <tree> tree_refs;
+
+  /* A set with tree references (either FUNC_DECL or VAR_DECL).  */
+  pointer_set_t *tree_refs_set;
+
+protected:
+  /* Cached, once calculated hash for the item.  */
+  hashval_t hash;
+
+private:
+  /* Initialize internal data structures. Bitmap STACK is used for
+     bitmap memory allocation process.  */
+  void setup (bitmap_obstack *stack);
+}; // class sem_item
+
+class sem_function: public sem_item
+{
+public:
+  /* Semantic function constructor that uses STACK as bitmap memory stack.  */
+  sem_function (bitmap_obstack *stack);
+
+  /*  Constructor based on callgraph node _NODE with computed hash _HASH.
+      Bitmap STACK is used for memory allocation.  */
+  sem_function (cgraph_node *_node, hashval_t _hash, bitmap_obstack *stack);
+
+  ~sem_function ();
+
+  inline virtual void init_wpa (void)
+  {
+    parse_tree_args ();
+  }
+
+  virtual void init (void);
+  virtual bool equals_wpa (sem_item *item);
+  virtual hashval_t get_hash (void);
+  virtual bool equals (sem_item *item);
+  virtual void init_refs ();
+  virtual bool merge (sem_item *alias_item);
+
+  /* Dump symbol to FILE.  */
+  virtual void dump_to_file (FILE *file)
+  {
+    gcc_assert (file);
+    dump_function_to_file (decl, file, TDF_DETAILS);
+  }
+
+  /* Parses function arguments and result type.  */
+  void parse_tree_args (void);
+
+  /* Returns cgraph_node.  */
+  inline struct cgraph_node *get_node (void)
+  {
+    return cgraph (node);
+  }
+
+  /* For a given call graph NODE, the function constructs new
+     semantic function item.  */
+  static sem_function *parse (struct cgraph_node *node, bitmap_obstack *stack);
+
+  /* Exception handling region tree.  */
+  eh_region region_tree;
+
+  /* Result type tree node.  */
+  tree result_type;
+
+  /* Array of argument tree types.  */
+  vec <tree> arg_types;
+
+  /* Number of function arguments.  */
+  unsigned int arg_count;
+
+  /* Total amount of edges in the function.  */
+  unsigned int edge_count;
+
+  /* Vector of sizes of all basic blocks.  */
+  vec <unsigned int> bb_sizes;
+
+  /* Control flow graph checksum.  */
+  hashval_t cfg_checksum;
+
+  /* GIMPLE codes hash value.  */
+  hashval_t gcode_hash;
+
+  /* Total number of SSA names used in the function.  */
+  unsigned ssa_names_size;
+
+  /* Array of structures for all basic blocks.  */
+  vec <sem_bb_t *> bb_sorted;
+
+private:
+  /* Calculates hash value based on a BASIC_BLOCK.  */
+  hashval_t get_bb_hash (const sem_bb_t *basic_block);
+
+  /* Basic block equivalence comparison function that returns true if
+     basic blocks BB1 and BB2 (from functions FUNC1 and FUNC2) correspond.  */
+  bool compare_bb (sem_bb_t *bb1, sem_bb_t *bb2, tree func1, tree func2);
+
+  /* For given basic blocks BB1 and BB2 (from functions FUNC1 and FUNC),
+     true value is returned if phi nodes are sematically
+     equivalent in these blocks .  */
+  bool compare_phi_node (basic_block bb1, basic_block bb2, tree func1,
+			 tree func2);
+
+  /* For given basic blocks BB1 and BB2 (from functions FUNC1 and FUNC),
+     true value is returned if exception handling regions are equivalent
+     in these blocks.  */
+  bool compare_eh_region (eh_region r1, eh_region r2, tree func1, tree func2);
+
+  /* Verifies that trees T1 and T2, representing function declarations
+     are equivalent from perspective of ICF.  */
+  bool compare_function_decl (tree t1, tree t2);
+
+  /* Verifies that trees T1 and T2 do correspond.  */
+  bool compare_variable_decl (tree t1, tree t2, tree func1, tree func2);
+
+  /* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
+     call statements are semantically equivalent.  */
+  bool compare_gimple_call (gimple s1, gimple s2,
+			    tree func1, tree func2);
+
+  /* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
+     assignment statements are semantically equivalent.  */
+  bool compare_gimple_assign (gimple s1, gimple s2, tree func1, tree func2);
+
+  /* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
+     condition statements are semantically equivalent.  */
+  bool compare_gimple_cond (gimple s1, gimple s2, tree func1, tree func2);
+
+  /* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
+     label statements are semantically equivalent.  */
+  bool compare_gimple_label (gimple s1, gimple s2, tree func1, tree func2);
+
+  /* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
+     switch statements are semantically equivalent.  */
+  bool compare_gimple_switch (gimple s1, gimple s2, tree func1, tree func2);
+
+  /* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
+     return statements are semantically equivalent.  */
+  bool compare_gimple_return (gimple s1, gimple s2, tree func1, tree func2);
+
+  /* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
+     goto statements are semantically equivalent.  */
+  bool compare_gimple_goto (gimple s1, gimple s2, tree func1, tree func2);
+
+  /* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
+     resx statements are semantically equivalent.  */
+  bool compare_gimple_resx (gimple s1, gimple s2);
+
+  /* Verifies for given GIMPLEs S1 and S2 that ASM statements are equivalent.
+     For the beginning, the pass only supports equality for
+     '__asm__ __volatile__ ("", "", "", "memory")'.  */
+  bool compare_gimple_asm (gimple s1, gimple s2);
+
+  /* Verifies that tree labels T1 and T2 correspond in FUNC1 and FUNC2.  */
+  bool compare_tree_ssa_label (tree t1, tree t2, tree func1, tree func2);
+
+  /* Function compares two operands T1 and T2 and returns true if these
+     two trees from FUNC1 (respectively FUNC2) are semantically equivalent.  */
+  bool compare_operand (tree t1, tree t2, tree func1, tree func2);
+
+  /* If T1 and T2 are SSA names, dictionary comparison is processed. Otherwise,
+     declaration comparasion is executed.  */
+  bool compare_ssa_name (tree t1, tree t2, tree func1, tree func2);
+
+  /* Basic blocks dictionary BB_DICT returns true if SOURCE index BB
+     corresponds to TARGET.  */
+  bool bb_dict_test (int* bb_dict, int source, int target);
+
+  /* Iterates all tree types in T1 and T2 and returns true if all types
+     are compatible.  */
+  bool compare_type_list (tree t1, tree t2);
+
+  /* Processes function equality comparison.  */
+  bool equals_private (sem_item *item);
+
+  /* Initializes references to another sem_item for gimple STMT of type assign.  */
+  void init_refs_for_assign (gimple stmt);
+
+  /* Initializes references to another sem_item for tree T.  */
+  void init_refs_for_tree (tree t);
+
+  /* Returns true if tree T can be compared as a handled component.  */
+  static bool icf_handled_component_p (tree t);
+
+  /* Function checker stores binding between functions.   */
+  func_checker *m_checker;
+
+  /* COMPARED_FUNC is a function that we compare to.  */
+  sem_function *m_compared_func;
+}; // class sem_function
+
+class sem_variable: public sem_item
+{
+public:
+  /* Semantic variable constructor that uses STACK as bitmap memory stack.  */
+  sem_variable (bitmap_obstack *stack);
+
+  /*  Constructor based on callgraph node _NODE with computed hash _HASH.
+      Bitmap STACK is used for memory allocation.  */
+
+  sem_variable (varpool_node *_node, hashval_t _hash, bitmap_obstack *stack);
+
+  inline virtual void init_wpa (void) {}
+
+  /* Semantic variable initialization function.  */
+  inline virtual void init (void)
+  {
+    decl = get_node ()->decl;
+    ctor = ctor_for_folding (decl);
+  }
+
+  /* Initializes references to other semantic functions/variables.  */
+  inline virtual void init_refs ()
+  {
+    parse_tree_refs (ctor);
+  }
+
+  virtual hashval_t get_hash (void);
+  virtual bool merge (sem_item *alias_item);
+  virtual void dump_to_file (FILE *file);
+  virtual bool equals (sem_item *item);
+
+  /* Fast equality variable based on knowledge known in WPA.  */
+  inline virtual bool equals_wpa (sem_item *item)
+  {
+    gcc_assert (item->type == VAR);
+    return true;
+  }
+
+  /* Returns varpool_node.  */
+  inline struct varpool_node *get_node (void)
+  {
+    return varpool (node);
+  }
+
+  /* Parser function that visits a varpool NODE.  */
+  static sem_variable *parse (struct varpool_node *node, bitmap_obstack *stack);
+
+  /* Variable constructor.  */
+  tree ctor;
+
+private:
+  /* Iterates though a constructor and identifies tree references
+     we are interested in semantic function equality.  */
+  void parse_tree_refs (tree t);
+
+  /* Compares trees T1 and T2 for semantic equality.  */
+  static bool equals (tree t1, tree t2);
+
+  /* Compare that symbol sections are either NULL or have same name.  */
+  bool compare_sections (sem_variable *alias);
+
+}; // class sem_variable
+
+class sem_item_optimizer;
+
+/* Congruence class set structure.  */
+struct congruence_class_var_hash: typed_noop_remove <congruence_class>
+{
+  typedef congruence_class value_type;
+  typedef congruence_class compare_type;
+
+  static inline hashval_t hash (const value_type *item)
+  {
+    return htab_hash_pointer (item);
+  }
+
+  static inline int equal (const value_type *item1, const compare_type *item2)
+  {
+    return item1 == item2;
+  }
+};
+
+typedef struct congruence_class_group
+{
+  hashval_t hash;
+  sem_item_type type;
+  vec <congruence_class *> classes;
+} congruence_class_group_t;
+
+/* Congruence class set structure.  */
+struct congruence_class_group_hash: typed_noop_remove <congruence_class_group_t>
+{
+  typedef congruence_class_group value_type;
+  typedef congruence_class_group compare_type;
+
+  static inline hashval_t hash (const value_type *item)
+  {
+    return item->hash;
+  }
+
+  static inline int equal (const value_type *item1, const compare_type *item2)
+  {
+    return item1->hash == item2->hash && item1->type == item2->type;
+  }
+};
+
+struct traverse_split_pair
+{
+  sem_item_optimizer *optimizer;
+  class congruence_class *cls;
+};
+
+/* Semantic item optimizer includes all top-level logic
+   related to semantic equality comparison.  */
+class sem_item_optimizer
+{
+public:
+  sem_item_optimizer ();
+  ~sem_item_optimizer ();
+
+  /* Function responsible for visiting all potential functions and
+     read-only variables that can be merged.  */
+  void parse_funcs_and_vars (void);
+
+  /* Optimizer entry point.  */
+  void execute (void);
+
+  /* Dump function. */
+  void dump (void);
+
+  /* Verify congruence classes if checking is enabled.  */
+  void verify_classes (void);
+
+  /* Write IPA ICF summary for symbols.  */
+  void write_summary (void);
+
+  /* Read IPA IPA ICF summary for symbols.  */
+  void read_summary (void);
+
+  /* Callgraph removal hook called for a NODE with a custom DATA.  */
+  static void cgraph_removal_hook (struct cgraph_node *node, void *data);
+
+  /* Varpool removal hook called for a NODE with a custom DATA.  */
+  static void varpool_removal_hook (struct varpool_node *node, void *data);
+
+  /* Worklist of congruence classes that can potentially
+     refine classes of congruence.  */
+  hash_table <congruence_class_var_hash> worklist;
+
+  /* Remove symtab NODE triggered by symtab removal hooks.  */
+  void remove_symtab_node (struct symtab_node *node);
+
+  /* Register callgraph and varpool hooks.  */
+  void register_hooks (void);
+
+  /* Unregister callgraph and varpool hooks.  */
+  void unregister_hooks (void);
+
+  /* Adds a CLS to hashtable associated by hash value.  */
+  void add_class (congruence_class *cls);
+
+  /* Gets a congruence class group based on given HASH value and TYPE.  */
+  congruence_class_group_t *get_group_by_hash (hashval_t hash,
+      sem_item_type type);
+
+private:
+
+  /* Congruence classes are built by hash value.  */
+  void build_hash_based_classes (void);
+
+  /* Semantic items in classes having more than one element and initialized.
+     In case of WPA, we load function body.  */
+  void parse_nonsingleton_classes (void);
+
+  /* Equality function for semantic items is used to subdivide existing
+     classes. If IN_WPA, fast equality function is invoked.  */
+  void subdivide_classes_by_equality (bool in_wpa = false);
+
+  /* Debug function prints all informations about congruence classes.  */
+  void dump_cong_classes (void);
+
+  /* Iterative congruence reduction function.  */
+  void process_cong_reduction (void);
+
+  /* After reduction is done, we can declare all items in a group
+     to be equal. PREV_CLASS_COUNT is start number of classes
+     before reduction.  */
+  void merge_classes (unsigned int prev_class_count);
+
+  /* Adds a newly created congruence class CLS to worklist.  */
+  void worklist_push (congruence_class *cls);
+
+  /* Pops a class from worklist. */
+  congruence_class *worklist_pop ();
+
+  /* Returns true if a congruence class CLS is presented in worklist.  */
+  inline bool worklist_contains (const congruence_class *cls)
+  {
+    return worklist.find (cls);
+  }
+
+  /* Removes given congruence class CLS from worklist.  */
+  inline void worklist_remove (const congruence_class *cls)
+  {
+    worklist.remove_elt (cls);
+  }
+
+  /* Every usage of a congruence class CLS is a candidate that can split the
+     collection of classes. Bitmap stack BMSTACK is used for bitmap
+     allocation.  */
+  void do_congruence_step (congruence_class *cls);
+
+  /* Tests if a class CLS used as INDEXth splits any congruence classes.
+     Bitmap stack BMSTACK is used for bitmap allocation.  */
+  void do_congruence_step_for_index (congruence_class *cls, unsigned int index);
+
+  /* Makes pairing between a congruence class CLS and semantic ITEM.  */
+  static void add_item_to_class (congruence_class *cls, sem_item *item);
+
+  /* Disposes split map traverse function. CLS is congruence
+     class, BSLOT is bitmap slot we want to release. DATA is mandatory,
+     but unused argument.  */
+  static bool release_split_map (congruence_class * const &cls, bitmap const &b,
+					 traverse_split_pair *pair);
+
+  /* Process split operation for a cognruence class CLS,
+     where bitmap B splits congruence class members. DATA is used
+     as argument of split pair.  */
+  static bool traverse_congruence_split (congruence_class * const &cls, bitmap const &b,
+					 traverse_split_pair *pair);
+
+  /* Reads a section from LTO stream file FILE_DATA. Input block for DATA
+     contains LEN bytes.  */
+  void read_section (struct lto_file_decl_data *file_data, const char *data,
+		     size_t len);
+
+  /* Removes all callgraph and varpool nodes that are marked by symtab
+     as deleted.  */
+  void filter_removed_items (void);
+
+  /* Vector of semantic items.  */
+  vec <sem_item *> m_items;
+
+  /* A set containing all items removed by hooks.  */
+  pointer_set_t *m_removed_items_set;
+
+  /* Hashtable of congruence classes */
+  hash_table <congruence_class_group_hash> m_classes;
+
+  /* Count of congruence classes.  */
+  unsigned int m_classes_count;
+
+  /* Map data structure maps trees to semantic items.  */
+  hash_map <tree, sem_item *> m_decl_map;
+
+  /* Map data structure maps symtab nodes to semantic items.  */
+  hash_map <symtab_node *, sem_item *> m_symtab_node_map;
+
+  /* Set to true if a splitter class is removed.  */
+  bool splitter_class_removed;
+
+  /* Global unique class id counter.  */
+  static unsigned int class_id;
+
+  /* Callgraph node removal hook holder.  */
+  struct cgraph_node_hook_list *m_cgraph_node_hooks;
+
+  /* Varpool node removal hook holder.  */
+  struct varpool_node_hook_list *m_varpool_node_hooks;
+
+  /* Bitmap stack.  */
+  bitmap_obstack m_bmstack;
+}; // class sem_item_optimizer
+
+} // ipa_icf namespace
diff --git a/gcc/lto-section-in.c b/gcc/lto-section-in.c
index d887763..f9587cf 100644
--- a/gcc/lto-section-in.c
+++ b/gcc/lto-section-in.c
@@ -60,7 +60,8 @@ const char *lto_section_name[LTO_N_SECTION_TYPES] =
   "opts",
   "cgraphopt",
   "inline",
-  "ipcp_trans"
+  "ipcp_trans",
+  "icf"
 };
 
 
diff --git a/gcc/lto-streamer.h b/gcc/lto-streamer.h
index cfa965c..435d976 100644
--- a/gcc/lto-streamer.h
+++ b/gcc/lto-streamer.h
@@ -248,6 +248,7 @@ enum lto_section_type
   LTO_section_cgraph_opt_sum,
   LTO_section_inline_summary,
   LTO_section_ipcp_transform,
+  LTO_section_ipa_icf,
   LTO_N_SECTION_TYPES		/* Must be last.  */
 };
 
diff --git a/gcc/opts.c b/gcc/opts.c
index 19203dc..d4da27c 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -497,6 +497,7 @@ static const struct default_options default_options_table[] =
     { OPT_LEVELS_2_PLUS, OPT_fvect_cost_model_, NULL, VECT_COST_MODEL_CHEAP },
     { OPT_LEVELS_2_PLUS_SPEED_ONLY, OPT_foptimize_strlen, NULL, 1 },
     { OPT_LEVELS_2_PLUS, OPT_fhoist_adjacent_loads, NULL, 1 },
+    { OPT_LEVELS_2_PLUS, OPT_fipa_icf, NULL, 1 },
     { OPT_LEVELS_2_PLUS, OPT_fisolate_erroneous_paths_dereference, NULL, 1 },
     { OPT_LEVELS_2_PLUS, OPT_fuse_caller_save, NULL, 1 },
 
@@ -1915,6 +1916,11 @@ common_handle_option (struct gcc_options *opts,
 	opts->x_flag_wrapv = 0;
       break;
 
+    case OPT_fipa_icf:
+	opts->x_flag_ipa_icf_functions = value;
+	opts->x_flag_ipa_icf_variables = value;
+      break;
+
     default:
       /* If the flag was handled in a standard way, assume the lack of
 	 processing here is intentional.  */
diff --git a/gcc/passes.def b/gcc/passes.def
index 280cf16..898a1ea 100644
--- a/gcc/passes.def
+++ b/gcc/passes.def
@@ -104,6 +104,7 @@ along with GCC; see the file COPYING3.  If not see
   NEXT_PASS (pass_ipa_whole_program_visibility);
   NEXT_PASS (pass_ipa_profile);
   NEXT_PASS (pass_ipa_devirt);
+  NEXT_PASS (pass_ipa_icf);
   NEXT_PASS (pass_ipa_cp);
   NEXT_PASS (pass_ipa_cdtor_merge);
   NEXT_PASS (pass_ipa_inline);
diff --git a/gcc/timevar.def b/gcc/timevar.def
index 6b1b6df..7476796 100644
--- a/gcc/timevar.def
+++ b/gcc/timevar.def
@@ -89,6 +89,7 @@ DEFTIMEVAR (TV_WHOPR_LTRANS          , "whopr ltrans")
 DEFTIMEVAR (TV_IPA_REFERENCE         , "ipa reference")
 DEFTIMEVAR (TV_IPA_PROFILE           , "ipa profile")
 DEFTIMEVAR (TV_IPA_PURE_CONST        , "ipa pure const")
+DEFTIMEVAR (TV_IPA_ICF		     , "ipa icf")
 DEFTIMEVAR (TV_IPA_PTA               , "ipa points-to")
 DEFTIMEVAR (TV_IPA_SRA               , "ipa SRA")
 DEFTIMEVAR (TV_IPA_FREE_LANG_DATA    , "ipa free lang data")
diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h
index b3a9de2..3a9490e 100644
--- a/gcc/tree-pass.h
+++ b/gcc/tree-pass.h
@@ -464,6 +464,7 @@ extern simple_ipa_opt_pass *make_pass_ipa_free_lang_data (gcc::context *ctxt);
 extern simple_ipa_opt_pass *make_pass_ipa_free_inline_summary (gcc::context
 							       *ctxt);
 extern ipa_opt_pass_d *make_pass_ipa_cp (gcc::context *ctxt);
+extern ipa_opt_pass_d *make_pass_ipa_icf (gcc::context *ctxt);
 extern ipa_opt_pass_d *make_pass_ipa_devirt (gcc::context *ctxt);
 extern ipa_opt_pass_d *make_pass_ipa_reference (gcc::context *ctxt);
 extern ipa_opt_pass_d *make_pass_ipa_pure_const (gcc::context *ctxt);
-- 
1.8.4.5


^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 4/5] Existing tests fix
  2014-06-17 19:52   ` Jeff Law
  2014-06-17 20:50     ` Rainer Orth
@ 2014-06-30 12:12     ` Martin Liška
  2014-09-26 12:21       ` Martin Liška
  1 sibling, 1 reply; 70+ messages in thread
From: Martin Liška @ 2014-06-30 12:12 UTC (permalink / raw)
  To: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 4435 bytes --]


On 06/17/2014 09:52 PM, Jeff Law wrote:
> On 06/13/14 04:48, mliska wrote:
>> Hi,
>>    many tests rely on a precise number of scanned functions in a dump file. If IPA ICF decides to merge some function and(or) read-only variables, counts do not match.
>>
>> Martin
>>
>> Changelog:
>>
>> 2014-06-13  Martin Liska  <mliska@suse.cz>
>>         Honza Hubicka  <hubicka@ucw.cz>
>>
>>     * c-c++-common/rotate-1.c: Text
>>     * c-c++-common/rotate-2.c: New test.
>>     * c-c++-common/rotate-3.c: Likewise.
>>     * c-c++-common/rotate-4.c: Likewise.
>>     * g++.dg/cpp0x/rv-return.C: Likewise.
>>     * g++.dg/cpp0x/rv1n.C: Likewise.
>>     * g++.dg/cpp0x/rv1p.C: Likewise.
>>     * g++.dg/cpp0x/rv2n.C: Likewise.
>>     * g++.dg/cpp0x/rv3n.C: Likewise.
>>     * g++.dg/cpp0x/rv4n.C: Likewise.
>>     * g++.dg/cpp0x/rv5n.C: Likewise.
>>     * g++.dg/cpp0x/rv6n.C: Likewise.
>>     * g++.dg/cpp0x/rv7n.C: Likewise.
>>     * gcc.dg/ipa/ipacost-1.c: Likewise.
>>     * gcc.dg/ipa/ipacost-2.c: Likewise.
>>     * gcc.dg/ipa/ipcp-agg-6.c: Likewise.
>>     * gcc.dg/ipa/remref-2a.c: Likewise.
>>     * gcc.dg/ipa/remref-2b.c: Likewise.
>>     * gcc.dg/pr46309-2.c: Likewise.
>>     * gcc.dg/torture/ipa-pta-1.c: Likewise.
>>     * gcc.dg/tree-ssa/andor-3.c: Likewise.
>>     * gcc.dg/tree-ssa/andor-4.c: Likewise.
>>     * gcc.dg/tree-ssa/andor-5.c: Likewise.
>>     * gcc.dg/vect/no-vfa-pr29145.c: Likewise.
>>     * gcc.dg/vect/vect-cond-10.c: Likewise.
>>     * gcc.dg/vect/vect-cond-9.c: Likewise.
>>     * gcc.dg/vect/vect-widen-mult-const-s16.c: Likewise.
>>     * gcc.dg/vect/vect-widen-mult-const-u16.c: Likewise.
>>     * gcc.dg/vect/vect-widen-mult-half-u8.c: Likewise.
>>     * gcc.target/i386/bmi-1.c: Likewise.
>>     * gcc.target/i386/bmi-2.c: Likewise.
>>     * gcc.target/i386/pr56564-2.c: Likewise.
>>     * g++.dg/opt/pr30965.C: Likewise.
>>     * g++.dg/tree-ssa/pr19637.C: Likewise.
>>     * gcc.dg/guality/csttest.c: Likewise.
>>     * gcc.dg/ipa/iinline-4.c: Likewise.
>>     * gcc.dg/ipa/iinline-7.c: Likewise.
>>     * gcc.dg/ipa/ipa-pta-13.c: Likewise.
> I know this is the least interesting part of your changes, but it's also simple and mechanical and thus trivial to review. Approved, but obviously don't install until the rest of your patch has been approved.
>
> Similar changes for recently added tests or cases where you might improve ICF requiring similar tweaks to existing tests are pre-approved as well.
>
> jeff
>
Hello,
    I fixed few more tests and added correct ChangeLog message.

gcc/testsuite/ChangeLog

2014-06-30  Martin Liska  <mliska@suse.cz>
         Honza Hubicka  <hubicka@ucw.cz>

     * c-c++-common/rotate-1.c: Test fixed.
     * c-c++-common/rotate-2.c: Likewise.
     * c-c++-common/rotate-3.c: Likewise.
     * c-c++-common/rotate-4.c: Likewise.
     * g++.dg/cpp0x/rv-return.C: Likewise.
     * g++.dg/cpp0x/rv1n.C: Likewise.
     * g++.dg/cpp0x/rv1p.C: Likewise.
     * g++.dg/cpp0x/rv2n.C: Likewise.
     * g++.dg/cpp0x/rv3n.C: Likewise.
     * g++.dg/cpp0x/rv4n.C: Likewise.
     * g++.dg/cpp0x/rv5n.C: Likewise.
     * g++.dg/cpp0x/rv6n.C: Likewise.
     * g++.dg/cpp0x/rv7n.C: Likewise.
     * g++.dg/ipa/devirt-g-1.C: Likewise.
     * g++.dg/ipa/inline-1.C: Likewise.
     * g++.dg/ipa/inline-2.C: Likewise.
     * g++.dg/ipa/inline-3.C: Likewise.
     * g++.dg/opt/pr30965.C: Likewise.
     * g++.dg/tree-ssa/pr19637.C: Likewise.
     * gcc.dg/guality/csttest.c: Likewise.
     * gcc.dg/ipa/iinline-4.c: Likewise.
     * gcc.dg/ipa/iinline-7.c: Likewise.
     * gcc.dg/ipa/ipa-pta-13.c: Likewise.
     * gcc.dg/ipa/ipacost-1.c: Likewise.
     * gcc.dg/ipa/ipacost-2.c: Likewise.
     * gcc.dg/ipa/ipcp-agg-6.c: Likewise.
     * gcc.dg/ipa/remref-2a.c: Likewise.
     * gcc.dg/ipa/remref-2b.c: Likewise.
     * gcc.dg/pr46309-2.c: Likewise.
     * gcc.dg/torture/ipa-pta-1.c: Likewise.
     * gcc.dg/tree-ssa/andor-3.c: Likewise.
     * gcc.dg/tree-ssa/andor-4.c: Likewise.
     * gcc.dg/tree-ssa/andor-5.c: Likewise.
     * gcc.dg/vect/no-vfa-pr29145.c: Likewise.
     * gcc.dg/vect/vect-cond-10.c: Likewise.
     * gcc.dg/vect/vect-cond-9.c: Likewise.
     * gcc.dg/vect/vect-widen-mult-const-s16.c: Likewise.
     * gcc.dg/vect/vect-widen-mult-const-u16.c: Likewise.
     * gcc.dg/vect/vect-widen-mult-half-u8.c: Likewise.
     * gcc.target/i386/bmi-1.c: Likewise.
     * gcc.target/i386/bmi-2.c: Likewise.
     * gcc.target/i386/pr56564-2.c: Likewise.

Thank you,
Martin


[-- Attachment #2: 0003-Existing-tests-fix.patch --]
[-- Type: text/x-patch, Size: 19975 bytes --]

diff --git a/gcc/testsuite/c-c++-common/rotate-1.c b/gcc/testsuite/c-c++-common/rotate-1.c
index afdaa28..bca9dd8 100644
--- a/gcc/testsuite/c-c++-common/rotate-1.c
+++ b/gcc/testsuite/c-c++-common/rotate-1.c
@@ -1,6 +1,6 @@
 /* Check rotate pattern detection.  */
 /* { dg-do compile } */
-/* { dg-options "-O2 -fdump-tree-optimized" } */
+/* { dg-options "-O2 -fno-ipa-icf -fdump-tree-optimized" } */
 /* { dg-final { scan-tree-dump-times "r\[<>]\[<>]" 96 "optimized" } } */
 /* { dg-final { cleanup-tree-dump "optimized" } } */
 
diff --git a/gcc/testsuite/c-c++-common/rotate-2.c b/gcc/testsuite/c-c++-common/rotate-2.c
index 109fd32..4ffa218 100644
--- a/gcc/testsuite/c-c++-common/rotate-2.c
+++ b/gcc/testsuite/c-c++-common/rotate-2.c
@@ -1,6 +1,6 @@
 /* Check rotate pattern detection.  */
 /* { dg-do compile } */
-/* { dg-options "-O2 -fdump-tree-optimized" } */
+/* { dg-options "-O2 -fno-ipa-icf -fdump-tree-optimized" } */
 /* Rotates should be recognized only in functions with | instead of + or ^,
    or in functions that have constant shift counts (unused attribute on y).  */
 /* { dg-final { scan-tree-dump-times "r\[<>]\[<>]" 48 "optimized" } } */
diff --git a/gcc/testsuite/c-c++-common/rotate-3.c b/gcc/testsuite/c-c++-common/rotate-3.c
index 8dc8313..aaa9f50 100644
--- a/gcc/testsuite/c-c++-common/rotate-3.c
+++ b/gcc/testsuite/c-c++-common/rotate-3.c
@@ -1,6 +1,6 @@
 /* Check rotate pattern detection.  */
 /* { dg-do compile } */
-/* { dg-options "-O2 -fdump-tree-optimized" } */
+/* { dg-options "-O2 -fno-ipa-icf -fdump-tree-optimized" } */
 /* { dg-final { scan-tree-dump-times "r\[<>]\[<>]" 96 "optimized" } } */
 /* { dg-final { cleanup-tree-dump "optimized" } } */
 
diff --git a/gcc/testsuite/c-c++-common/rotate-4.c b/gcc/testsuite/c-c++-common/rotate-4.c
index 2f433b3..0a21177 100644
--- a/gcc/testsuite/c-c++-common/rotate-4.c
+++ b/gcc/testsuite/c-c++-common/rotate-4.c
@@ -1,6 +1,6 @@
 /* Check rotate pattern detection.  */
 /* { dg-do compile } */
-/* { dg-options "-O2 -fdump-tree-optimized" } */
+/* { dg-options "-O2 -fno-ipa-icf -fdump-tree-optimized" } */
 /* Rotates should be recognized only in functions with | instead of + or ^,
    or in functions that have constant shift counts (unused attribute on y).  */
 /* { dg-final { scan-tree-dump-times "r\[<>]\[<>]" 48 "optimized" } } */
diff --git a/gcc/testsuite/g++.dg/cpp0x/rv-return.C b/gcc/testsuite/g++.dg/cpp0x/rv-return.C
index 12a15aa..6d57209 100644
--- a/gcc/testsuite/g++.dg/cpp0x/rv-return.C
+++ b/gcc/testsuite/g++.dg/cpp0x/rv-return.C
@@ -1,5 +1,6 @@
 // PR c++/41815
 // { dg-do compile { target c++11 } }
+// { dg-options "-fno-ipa-icf" }
 
 template<typename T, typename U> struct same_type;
 template<typename T> struct same_type<T, T> {};
diff --git a/gcc/testsuite/g++.dg/cpp0x/rv1n.C b/gcc/testsuite/g++.dg/cpp0x/rv1n.C
index 204ca31..f5e7568 100644
--- a/gcc/testsuite/g++.dg/cpp0x/rv1n.C
+++ b/gcc/testsuite/g++.dg/cpp0x/rv1n.C
@@ -1,8 +1,10 @@
 // I, Howard Hinnant, hereby place this code in the public domain.
+/* { dg-additional-options "-fno-ipa-icf" } */
 
 // Test overload resolution among reference types
 
 // { dg-do compile { target c++11 } }
+// { dg-additional-options "-fno-ipa-icf" }
 
 template <bool> struct sa;
 template <> struct sa<true> {};
diff --git a/gcc/testsuite/g++.dg/cpp0x/rv1p.C b/gcc/testsuite/g++.dg/cpp0x/rv1p.C
index e4c0ab1..e87ec0e 100644
--- a/gcc/testsuite/g++.dg/cpp0x/rv1p.C
+++ b/gcc/testsuite/g++.dg/cpp0x/rv1p.C
@@ -4,6 +4,7 @@
 
 // { dg-do compile { target c++11 } }
 // { dg-skip-if "packed attribute missing for struct one/three/five/seven" { "epiphany-*-*" } { "*" } { "" } }
+// { dg-additional-options "-fno-ipa-icf" }
 
 template <bool> struct sa;
 template <> struct sa<true> {};
diff --git a/gcc/testsuite/g++.dg/cpp0x/rv2n.C b/gcc/testsuite/g++.dg/cpp0x/rv2n.C
index 9677f58..663a66b 100644
--- a/gcc/testsuite/g++.dg/cpp0x/rv2n.C
+++ b/gcc/testsuite/g++.dg/cpp0x/rv2n.C
@@ -3,7 +3,7 @@
 // Test overload resolution among reference types
 
 // { dg-do compile { target c++11 } }
-// { dg-options "" }
+// { dg-options "-fno-ipa-icf" }
 
 template <bool> struct sa;
 template <> struct sa<true> {};
diff --git a/gcc/testsuite/g++.dg/cpp0x/rv3n.C b/gcc/testsuite/g++.dg/cpp0x/rv3n.C
index 8a1730b..b7c1d7a 100644
--- a/gcc/testsuite/g++.dg/cpp0x/rv3n.C
+++ b/gcc/testsuite/g++.dg/cpp0x/rv3n.C
@@ -3,7 +3,7 @@
 // Test overload resolution among reference types
 
 // { dg-do compile { target c++11 } }
-// { dg-options "" }
+// { dg-options "-fno-ipa-icf" }
 
 template <bool> struct sa;
 template <> struct sa<true> {};
diff --git a/gcc/testsuite/g++.dg/cpp0x/rv4n.C b/gcc/testsuite/g++.dg/cpp0x/rv4n.C
index e64856d..29deb3f 100644
--- a/gcc/testsuite/g++.dg/cpp0x/rv4n.C
+++ b/gcc/testsuite/g++.dg/cpp0x/rv4n.C
@@ -3,7 +3,7 @@
 // Test overload resolution among reference types
 
 // { dg-do compile { target c++11 } }
-// { dg-options "" }
+// { dg-options "-fno-ipa-icf" }
 
 template <bool> struct sa;
 template <> struct sa<true> {};
diff --git a/gcc/testsuite/g++.dg/cpp0x/rv5n.C b/gcc/testsuite/g++.dg/cpp0x/rv5n.C
index 90d5418..f11d07a 100644
--- a/gcc/testsuite/g++.dg/cpp0x/rv5n.C
+++ b/gcc/testsuite/g++.dg/cpp0x/rv5n.C
@@ -3,7 +3,7 @@
 // Test overload resolution among reference types
 
 // { dg-do compile { target c++11 } }
-// { dg-options "" }
+// { dg-options "-fno-ipa-icf" }
 
 template <bool> struct sa;
 template <> struct sa<true> {};
diff --git a/gcc/testsuite/g++.dg/cpp0x/rv6n.C b/gcc/testsuite/g++.dg/cpp0x/rv6n.C
index 5bd9a23..0ebbe33 100644
--- a/gcc/testsuite/g++.dg/cpp0x/rv6n.C
+++ b/gcc/testsuite/g++.dg/cpp0x/rv6n.C
@@ -3,7 +3,7 @@
 // Test overload resolution among reference types
 
 // { dg-do compile { target c++11 } }
-// { dg-options "" }
+// { dg-options "-fno-ipa-icf" }
 
 template <bool> struct sa;
 template <> struct sa<true> {};
diff --git a/gcc/testsuite/g++.dg/cpp0x/rv7n.C b/gcc/testsuite/g++.dg/cpp0x/rv7n.C
index 38ca7b8..d9e371b 100644
--- a/gcc/testsuite/g++.dg/cpp0x/rv7n.C
+++ b/gcc/testsuite/g++.dg/cpp0x/rv7n.C
@@ -3,7 +3,7 @@
 // Test overload resolution among reference types
 
 // { dg-do compile { target c++11 } }
-// { dg-options "" }
+// { dg-options "-fno-ipa-icf" }
 
 template <bool> struct sa;
 template <> struct sa<true> {};
diff --git a/gcc/testsuite/g++.dg/ipa/devirt-g-1.C b/gcc/testsuite/g++.dg/ipa/devirt-g-1.C
index 175f24e..1530fdb 100644
--- a/gcc/testsuite/g++.dg/ipa/devirt-g-1.C
+++ b/gcc/testsuite/g++.dg/ipa/devirt-g-1.C
@@ -1,5 +1,5 @@
 // { dg-do compile }
-// { dg-options "-O2 -fdump-ipa-cp -fdump-tree-optimized" }
+// { dg-options "-O2 -fdump-ipa-cp -fno-ipa-icf -fdump-tree-optimized" }
 
 struct S { S(); virtual void xyzzy(); void otherstuff(); };
 struct R { int a; S s; R(); };
diff --git a/gcc/testsuite/g++.dg/ipa/inline-1.C b/gcc/testsuite/g++.dg/ipa/inline-1.C
index dbbfb4e..3a6a041 100644
--- a/gcc/testsuite/g++.dg/ipa/inline-1.C
+++ b/gcc/testsuite/g++.dg/ipa/inline-1.C
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O2 -fdump-ipa-inline --param max-early-inliner-iterations=1" } */
+/* { dg-options "-O2 -fdump-ipa-inline -fno-ipa-icf --param max-early-inliner-iterations=1" } */
 /* { dg-add-options bind_pic_locally } */
 
 namespace std {
diff --git a/gcc/testsuite/g++.dg/ipa/inline-2.C b/gcc/testsuite/g++.dg/ipa/inline-2.C
index fd284a1e2..d1e46c0 100644
--- a/gcc/testsuite/g++.dg/ipa/inline-2.C
+++ b/gcc/testsuite/g++.dg/ipa/inline-2.C
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O2 -fdump-ipa-inline --param max-early-inliner-iterations=1" } */
+/* { dg-options "-O2 -fdump-ipa-inline -fno-ipa-icf --param max-early-inliner-iterations=1" } */
 /* { dg-add-options bind_pic_locally } */
 
 namespace std {
diff --git a/gcc/testsuite/g++.dg/ipa/inline-3.C b/gcc/testsuite/g++.dg/ipa/inline-3.C
index 8d5f905..7315bf5 100644
--- a/gcc/testsuite/g++.dg/ipa/inline-3.C
+++ b/gcc/testsuite/g++.dg/ipa/inline-3.C
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O2 -fdump-ipa-inline --param max-early-inliner-iterations=1" } */
+/* { dg-options "-O2 -fdump-ipa-inline -fno-ipa-icf --param max-early-inliner-iterations=1" } */
 /* { dg-add-options bind_pic_locally } */
 
 #include <algorithm>
diff --git a/gcc/testsuite/g++.dg/opt/pr30965.C b/gcc/testsuite/g++.dg/opt/pr30965.C
index 91bb55c..45393fd 100644
--- a/gcc/testsuite/g++.dg/opt/pr30965.C
+++ b/gcc/testsuite/g++.dg/opt/pr30965.C
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O3 -fdump-tree-optimized" } */
+/* { dg-options "-O3 -fno-ipa-icf -fdump-tree-optimized" } */
 
 #include <tr1/functional>
 #include <algorithm>
diff --git a/gcc/testsuite/g++.dg/tree-ssa/pr19637.C b/gcc/testsuite/g++.dg/tree-ssa/pr19637.C
index 2d1dcce..92f673f 100644
--- a/gcc/testsuite/g++.dg/tree-ssa/pr19637.C
+++ b/gcc/testsuite/g++.dg/tree-ssa/pr19637.C
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O2 -fdump-tree-dom1" } */
+/* { dg-options "-O2 -fno-ipa-icf -fdump-tree-dom1" } */
 
 #include <new>
 
diff --git a/gcc/testsuite/gcc.dg/guality/csttest.c b/gcc/testsuite/gcc.dg/guality/csttest.c
index 4480c71..05e1ad7 100644
--- a/gcc/testsuite/gcc.dg/guality/csttest.c
+++ b/gcc/testsuite/gcc.dg/guality/csttest.c
@@ -1,6 +1,6 @@
 /* PR debug/49676 */
 /* { dg-do run { target lp64 } } */
-/* { dg-options "-g" } */
+/* { dg-options "-g -fno-ipa-icf" } */
 
 volatile int v;
 
diff --git a/gcc/testsuite/gcc.dg/ipa/iinline-4.c b/gcc/testsuite/gcc.dg/ipa/iinline-4.c
index 71faae2..3f295df 100644
--- a/gcc/testsuite/gcc.dg/ipa/iinline-4.c
+++ b/gcc/testsuite/gcc.dg/ipa/iinline-4.c
@@ -1,7 +1,7 @@
 /* Verify that simple indirect calls are inlined even without early
    inlining..  */
 /* { dg-do compile } */
-/* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining"  } */
+/* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining -fno-ipa-icf"  } */
 
 struct S
 {
diff --git a/gcc/testsuite/gcc.dg/ipa/iinline-7.c b/gcc/testsuite/gcc.dg/ipa/iinline-7.c
index c95d374..61e8d0b 100644
--- a/gcc/testsuite/gcc.dg/ipa/iinline-7.c
+++ b/gcc/testsuite/gcc.dg/ipa/iinline-7.c
@@ -1,7 +1,7 @@
 /* Verify that simple indirect calls are inlined even without early
    inlining..  */
 /* { dg-do run } */
-/* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining"  } */
+/* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining -fno-ipa-icf"  } */
 
 extern void abort (void);
 
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-pta-13.c b/gcc/testsuite/gcc.dg/ipa/ipa-pta-13.c
index 0f46e98..f7f95f4 100644
--- a/gcc/testsuite/gcc.dg/ipa/ipa-pta-13.c
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-pta-13.c
@@ -1,5 +1,5 @@
 /* { dg-do link } */
-/* { dg-options "-O2 -fipa-pta -fdump-ipa-pta-details -fdump-tree-fre2" } */
+/* { dg-options "-O2 -fipa-pta -fdump-ipa-pta-details -fdump-tree-fre2 -fno-ipa-icf" } */
 
 static int x, y;
 
diff --git a/gcc/testsuite/gcc.dg/ipa/ipacost-1.c b/gcc/testsuite/gcc.dg/ipa/ipacost-1.c
index 4fce41e..9603afe 100644
--- a/gcc/testsuite/gcc.dg/ipa/ipacost-1.c
+++ b/gcc/testsuite/gcc.dg/ipa/ipacost-1.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-Os -fipa-cp -fdump-ipa-cp -fno-early-inlining -fdump-tree-optimized"  } */
+/* { dg-options "-Os -fipa-cp -fdump-ipa-cp -fno-early-inlining -fdump-tree-optimized -fno-ipa-icf"  } */
 
 int array[100];
 
diff --git a/gcc/testsuite/gcc.dg/ipa/ipacost-2.c b/gcc/testsuite/gcc.dg/ipa/ipacost-2.c
index ceb524e..e7074f1 100644
--- a/gcc/testsuite/gcc.dg/ipa/ipacost-2.c
+++ b/gcc/testsuite/gcc.dg/ipa/ipacost-2.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O3 -fipa-cp -fipa-cp-clone -fdump-ipa-cp -fno-early-inlining -fdump-tree-optimized"  } */
+/* { dg-options "-O3 -fipa-cp -fipa-cp-clone -fdump-ipa-cp -fno-early-inlining -fdump-tree-optimized -fno-ipa-icf"  } */
 /* { dg-add-options bind_pic_locally } */
 
 int array[100];
diff --git a/gcc/testsuite/gcc.dg/ipa/ipcp-agg-6.c b/gcc/testsuite/gcc.dg/ipa/ipcp-agg-6.c
index 050e13b..5d6425b 100644
--- a/gcc/testsuite/gcc.dg/ipa/ipcp-agg-6.c
+++ b/gcc/testsuite/gcc.dg/ipa/ipcp-agg-6.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O3 -fno-ipa-sra -fdump-ipa-cp-details -fdump-tree-optimized-slim"  } */
+/* { dg-options "-O3 -fno-ipa-sra -fdump-ipa-cp-details -fdump-tree-optimized-slim -fno-ipa-icf"  } */
 /* { dg-add-options bind_pic_locally } */
 
 struct S
diff --git a/gcc/testsuite/gcc.dg/ipa/remref-2a.c b/gcc/testsuite/gcc.dg/ipa/remref-2a.c
index 1e0df2e..4697a18 100644
--- a/gcc/testsuite/gcc.dg/ipa/remref-2a.c
+++ b/gcc/testsuite/gcc.dg/ipa/remref-2a.c
@@ -1,7 +1,7 @@
 /* Verify that indirect inlining can also remove references of the functions it
    discovers calls for.  */
 /* { dg-do compile } */
-/* { dg-options "-O3 -fno-early-inlining -fno-ipa-cp -fdump-ipa-inline -fdump-tree-optimized"  } */
+/* { dg-options "-O3 -fno-early-inlining -fno-ipa-cp -fdump-ipa-inline -fdump-tree-optimized -fno-ipa-icf"  } */
 
 int global;
 
diff --git a/gcc/testsuite/gcc.dg/ipa/remref-2b.c b/gcc/testsuite/gcc.dg/ipa/remref-2b.c
index 554f306..7799033 100644
--- a/gcc/testsuite/gcc.dg/ipa/remref-2b.c
+++ b/gcc/testsuite/gcc.dg/ipa/remref-2b.c
@@ -2,7 +2,7 @@
    discovers calls for, even when nodes being inlined are virtual IPA-CP
    clones.  */
 /* { dg-do compile } */
-/* { dg-options "-O3 -fno-early-inlining -fdump-ipa-cp-details -fdump-ipa-inline -fdump-tree-optimized"  } */
+/* { dg-options "-O3 -fno-early-inlining -fdump-ipa-cp-details -fdump-ipa-inline -fdump-tree-optimized -fno-ipa-icf"  } */
 
 
 int global;
diff --git a/gcc/testsuite/gcc.dg/pr46309-2.c b/gcc/testsuite/gcc.dg/pr46309-2.c
index f407e66..00ffee1 100644
--- a/gcc/testsuite/gcc.dg/pr46309-2.c
+++ b/gcc/testsuite/gcc.dg/pr46309-2.c
@@ -1,6 +1,6 @@
 /* PR tree-optimization/46309 */
 /* { dg-do compile } */
-/* { dg-options "-O2 -fdump-tree-reassoc-details" } */
+/* { dg-options "-O2 -fno-ipa-icf -fdump-tree-reassoc-details" } */
 
 int foo (void);
 
diff --git a/gcc/testsuite/gcc.dg/torture/ipa-pta-1.c b/gcc/testsuite/gcc.dg/torture/ipa-pta-1.c
index aae987d..80303a5 100644
--- a/gcc/testsuite/gcc.dg/torture/ipa-pta-1.c
+++ b/gcc/testsuite/gcc.dg/torture/ipa-pta-1.c
@@ -1,5 +1,5 @@
 /* { dg-do compile { target { nonpic } } } */
-/* { dg-options "-fipa-pta -fdump-ipa-pta" } */
+/* { dg-options "-fipa-pta -fdump-ipa-pta -fno-ipa-icf" } */
 /* { dg-skip-if "" { *-*-* } { "-O0" "-fno-fat-lto-objects" } { "" } } */
 
 struct X { char x; char y; };
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/andor-3.c b/gcc/testsuite/gcc.dg/tree-ssa/andor-3.c
index a1401c0..8b2f206 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/andor-3.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/andor-3.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O2 -fdump-tree-optimized" } */
+/* { dg-options "-O2 -fno-ipa-icf -fdump-tree-optimized" } */
 
 int f(int y, int x)
 {
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/andor-4.c b/gcc/testsuite/gcc.dg/tree-ssa/andor-4.c
index 1dbdca7..46a4826 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/andor-4.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/andor-4.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O2 -fdump-tree-optimized" } */
+/* { dg-options "-O2 -fno-ipa-icf -fdump-tree-optimized" } */
 
 int f(int y, int x)
 {
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/andor-5.c b/gcc/testsuite/gcc.dg/tree-ssa/andor-5.c
index 1509727..929851c 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/andor-5.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/andor-5.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O2 -fdump-tree-optimized" } */
+/* { dg-options "-O2 -fno-ipa-icf -fdump-tree-optimized" } */
 
 int f(int y, int x)
 {
diff --git a/gcc/testsuite/gcc.dg/vect/no-vfa-pr29145.c b/gcc/testsuite/gcc.dg/vect/no-vfa-pr29145.c
index e475fff..5896271 100644
--- a/gcc/testsuite/gcc.dg/vect/no-vfa-pr29145.c
+++ b/gcc/testsuite/gcc.dg/vect/no-vfa-pr29145.c
@@ -1,4 +1,5 @@
 /* { dg-require-effective-target vect_int } */
+/* { dg-additional-options "-fno-ipa-icf" } */
 
 #include <stdarg.h>
 #include "tree-vect.h"
diff --git a/gcc/testsuite/gcc.dg/vect/vect-cond-10.c b/gcc/testsuite/gcc.dg/vect/vect-cond-10.c
index 687d42f..da2eb05 100644
--- a/gcc/testsuite/gcc.dg/vect/vect-cond-10.c
+++ b/gcc/testsuite/gcc.dg/vect/vect-cond-10.c
@@ -1,4 +1,5 @@
 /* { dg-require-effective-target vect_cond_mixed } */
+/* { dg-additional-options "-fno-ipa-icf" } */
 
 #include "tree-vect.h"
 
diff --git a/gcc/testsuite/gcc.dg/vect/vect-cond-9.c b/gcc/testsuite/gcc.dg/vect/vect-cond-9.c
index cfa0363..de88fc5 100644
--- a/gcc/testsuite/gcc.dg/vect/vect-cond-9.c
+++ b/gcc/testsuite/gcc.dg/vect/vect-cond-9.c
@@ -1,4 +1,5 @@
 /* { dg-require-effective-target vect_cond_mixed } */
+/* { dg-additional-options "-fno-ipa-icf" } */
 
 #include "tree-vect.h"
 
diff --git a/gcc/testsuite/gcc.dg/vect/vect-widen-mult-const-s16.c b/gcc/testsuite/gcc.dg/vect/vect-widen-mult-const-s16.c
index a2fe975..895bbd0 100644
--- a/gcc/testsuite/gcc.dg/vect/vect-widen-mult-const-s16.c
+++ b/gcc/testsuite/gcc.dg/vect/vect-widen-mult-const-s16.c
@@ -1,4 +1,5 @@
 /* { dg-require-effective-target vect_int } */
+/* { dg-additional-options "-fno-ipa-icf" } */
 
 #include "tree-vect.h"
 #include <stdlib.h>
diff --git a/gcc/testsuite/gcc.dg/vect/vect-widen-mult-const-u16.c b/gcc/testsuite/gcc.dg/vect/vect-widen-mult-const-u16.c
index e712da9..f69abfd 100644
--- a/gcc/testsuite/gcc.dg/vect/vect-widen-mult-const-u16.c
+++ b/gcc/testsuite/gcc.dg/vect/vect-widen-mult-const-u16.c
@@ -1,4 +1,5 @@
 /* { dg-require-effective-target vect_int } */
+/* { dg-additional-options "-fno-ipa-icf" } */
 
 #include "tree-vect.h"
 #include <stdlib.h>
@@ -74,4 +75,3 @@ int main (void)
 /* { dg-final { scan-tree-dump-times "vect_recog_widen_mult_pattern: detected" 2 "vect" { target vect_widen_mult_hi_to_si_pattern } } } */
 /* { dg-final { scan-tree-dump-times "pattern recognized" 2 "vect" { target vect_widen_mult_hi_to_si_pattern } } } */
 /* { dg-final { cleanup-tree-dump "vect" } } */
-
diff --git a/gcc/testsuite/gcc.dg/vect/vect-widen-mult-half-u8.c b/gcc/testsuite/gcc.dg/vect/vect-widen-mult-half-u8.c
index 39078df..3f07585 100644
--- a/gcc/testsuite/gcc.dg/vect/vect-widen-mult-half-u8.c
+++ b/gcc/testsuite/gcc.dg/vect/vect-widen-mult-half-u8.c
@@ -1,4 +1,5 @@
 /* { dg-require-effective-target vect_int } */
+/* { dg-additional-options "-fno-ipa-icf" } */
 
 #include "tree-vect.h"
 #include <stdlib.h>
diff --git a/gcc/testsuite/gcc.target/i386/bmi-1.c b/gcc/testsuite/gcc.target/i386/bmi-1.c
index c66a9d8..738705e 100644
--- a/gcc/testsuite/gcc.target/i386/bmi-1.c
+++ b/gcc/testsuite/gcc.target/i386/bmi-1.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O2 -mbmi " } */
+/* { dg-options "-O2 -fno-ipa-icf -mbmi " } */
 /* { dg-final { scan-assembler "andn\[^\\n]*eax" } } */
 /* { dg-final { scan-assembler-times "bextr\[ \\t]+\[^\\n]*eax" 2 } } */
 /* { dg-final { scan-assembler-times "blsi\[^\\n]*eax" 2 } } */
diff --git a/gcc/testsuite/gcc.target/i386/bmi-2.c b/gcc/testsuite/gcc.target/i386/bmi-2.c
index 6eea66a..25fb86b 100644
--- a/gcc/testsuite/gcc.target/i386/bmi-2.c
+++ b/gcc/testsuite/gcc.target/i386/bmi-2.c
@@ -1,5 +1,5 @@
 /* { dg-do compile { target { ! { ia32 }  } } } */
-/* { dg-options "-O2 -mbmi " } */
+/* { dg-options "-O2 -fno-ipa-icf -mbmi " } */
 /* { dg-final { scan-assembler "andn\[^\\n]*rax" } } */
 /* { dg-final { scan-assembler-times "bextr\[ \\t]+\[^\\n]*rax" 2 } } */
 /* { dg-final { scan-assembler-times "blsi\[^\\n]*rax" 2 } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr56564-2.c b/gcc/testsuite/gcc.target/i386/pr56564-2.c
index fc89a4c..c42bfae 100644
--- a/gcc/testsuite/gcc.target/i386/pr56564-2.c
+++ b/gcc/testsuite/gcc.target/i386/pr56564-2.c
@@ -1,6 +1,6 @@
 /* PR target/56564 */
 /* { dg-do compile { target { *-*-linux* && lp64 } } } */
-/* { dg-options "-O3 -fno-pic -fdump-tree-optimized" } */
+/* { dg-options "-O3 -fno-pic -fno-ipa-icf -fdump-tree-optimized" } */
 
 struct S { long a, b; } s = { 5, 6 };
 char t[16] = { 7 };
-- 
1.8.4.5


^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 5/5] New tests introduction
  2014-06-17 19:53   ` Jeff Law
@ 2014-06-30 12:14     ` Martin Liška
  2014-10-19  8:19       ` Andreas Schwab
  0 siblings, 1 reply; 70+ messages in thread
From: Martin Liška @ 2014-06-30 12:14 UTC (permalink / raw)
  To: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 4301 bytes --]


On 06/17/2014 09:53 PM, Jeff Law wrote:
> On 06/13/14 05:16, mliska wrote:
>> Hi,
>>     this is a new collection of tests for IPA ICF pass.
>>
>> Martin
>>
>> Changelog:
>>
>> 2014-06-13  Martin Liska  <mliska@suse.cz>
>>         Honza Hubicka  <hubicka@ucw.cz>
>>
>>     * gcc/testsuite/g++.dg/ipa/ipa-se-1.C: New test.
>>     * gcc/testsuite/g++.dg/ipa/ipa-se-2.C: Likewise.
>>     * gcc/testsuite/g++.dg/ipa/ipa-se-3.C: Likewise.
>>     * gcc/testsuite/g++.dg/ipa/ipa-se-4.C: Likewise.
>>     * gcc/testsuite/g++.dg/ipa/ipa-se-5.C: Likewise.
>>     * gcc/testsuite/gcc.dg/ipa/ipa-se-1.c: Likewise.
>>     * gcc/testsuite/gcc.dg/ipa/ipa-se-10.c: Likewise.
>>     * gcc/testsuite/gcc.dg/ipa/ipa-se-11.c: Likewise.
>>     * gcc/testsuite/gcc.dg/ipa/ipa-se-12.c: Likewise.
>>     * gcc/testsuite/gcc.dg/ipa/ipa-se-13.c: Likewise.
>>     * gcc/testsuite/gcc.dg/ipa/ipa-se-14.c: Likewise.
>>     * gcc/testsuite/gcc.dg/ipa/ipa-se-15.c: Likewise.
>>     * gcc/testsuite/gcc.dg/ipa/ipa-se-16.c: Likewise.
>>     * gcc/testsuite/gcc.dg/ipa/ipa-se-17.c: Likewise.
>>     * gcc/testsuite/gcc.dg/ipa/ipa-se-18.c: Likewise.
>>     * gcc/testsuite/gcc.dg/ipa/ipa-se-19.c: Likewise.
>>     * gcc/testsuite/gcc.dg/ipa/ipa-se-2.c: Likewise.
>>     * gcc/testsuite/gcc.dg/ipa/ipa-se-20.c: Likewise.
>>     * gcc/testsuite/gcc.dg/ipa/ipa-se-21.c: Likewise.
>>     * gcc/testsuite/gcc.dg/ipa/ipa-se-22.c: Likewise.
>>     * gcc/testsuite/gcc.dg/ipa/ipa-se-23.c: Likewise.
>>     * gcc/testsuite/gcc.dg/ipa/ipa-se-24.c: Likewise.
>>     * gcc/testsuite/gcc.dg/ipa/ipa-se-25.c: Likewise.
>>     * gcc/testsuite/gcc.dg/ipa/ipa-se-26.c: Likewise.
>>     * gcc/testsuite/gcc.dg/ipa/ipa-se-27.c: Likewise.
>>     * gcc/testsuite/gcc.dg/ipa/ipa-se-28.c: Likewise.
>>     * gcc/testsuite/gcc.dg/ipa/ipa-se-3.c: Likewise.
>>     * gcc/testsuite/gcc.dg/ipa/ipa-se-4.c: Likewise.
>>     * gcc/testsuite/gcc.dg/ipa/ipa-se-5.c: Likewise.
>>     * gcc/testsuite/gcc.dg/ipa/ipa-se-6.c: Likewise.
>>     * gcc/testsuite/gcc.dg/ipa/ipa-se-7.c: Likewise.
>>     * gcc/testsuite/gcc.dg/ipa/ipa-se-8.c: Likewise.
>>     * gcc/testsuite/gcc.dg/ipa/ipa-se-9.c: Likewise.
> Also approved, but please don't install entire the entire kit is approved.
>
> I'd like to applaud you and Jan for including a nice baseline of tests.
>
> jeff
>
Hi,
    there's updatd baseline of tests.

Martin

gcc/testsuite/ChangeLog:

2014-06-30 Martin Liska  <mliska@suse.cz>
         Honza Hubicka  <hubicka@ucw.cz>

     * gcc/testsuite/g++.dg/ipa/ipa-icf-1.C: New test.
     * gcc/testsuite/g++.dg/ipa/ipa-icf-2.C: Likewise.
     * gcc/testsuite/g++.dg/ipa/ipa-icf-3.C: Likewise.
     * gcc/testsuite/g++.dg/ipa/ipa-icf-4.C: Likewise.
     * gcc/testsuite/g++.dg/ipa/ipa-icf-5.C: Likewise.
     * gcc/testsuite/gcc.dg/ipa/ipa-icf-1.c: Likewise.
     * gcc/testsuite/gcc.dg/ipa/ipa-icf-10.c: Likewise.
     * gcc/testsuite/gcc.dg/ipa/ipa-icf-11.c: Likewise.
     * gcc/testsuite/gcc.dg/ipa/ipa-icf-12.c: Likewise.
     * gcc/testsuite/gcc.dg/ipa/ipa-icf-13.c: Likewise.
     * gcc/testsuite/gcc.dg/ipa/ipa-icf-14.c: Likewise.
     * gcc/testsuite/gcc.dg/ipa/ipa-icf-15.c: Likewise.
     * gcc/testsuite/gcc.dg/ipa/ipa-icf-16.c: Likewise.
     * gcc/testsuite/gcc.dg/ipa/ipa-icf-17.c: Likewise.
     * gcc/testsuite/gcc.dg/ipa/ipa-icf-18.c: Likewise.
     * gcc/testsuite/gcc.dg/ipa/ipa-icf-19.c: Likewise.
     * gcc/testsuite/gcc.dg/ipa/ipa-icf-2.c: Likewise.
     * gcc/testsuite/gcc.dg/ipa/ipa-icf-20.c: Likewise.
     * gcc/testsuite/gcc.dg/ipa/ipa-icf-21.c: Likewise.
     * gcc/testsuite/gcc.dg/ipa/ipa-icf-22.c: Likewise.
     * gcc/testsuite/gcc.dg/ipa/ipa-icf-23.c: Likewise.
     * gcc/testsuite/gcc.dg/ipa/ipa-icf-24.c: Likewise.
     * gcc/testsuite/gcc.dg/ipa/ipa-icf-25.c: Likewise.
     * gcc/testsuite/gcc.dg/ipa/ipa-icf-26.c: Likewise.
     * gcc/testsuite/gcc.dg/ipa/ipa-icf-27.c: Likewise.
     * gcc/testsuite/gcc.dg/ipa/ipa-icf-28.c: Likewise.
     * gcc/testsuite/gcc.dg/ipa/ipa-icf-3.c: Likewise.
     * gcc/testsuite/gcc.dg/ipa/ipa-icf-4.c: Likewise.
     * gcc/testsuite/gcc.dg/ipa/ipa-icf-5.c: Likewise.
     * gcc/testsuite/gcc.dg/ipa/ipa-icf-6.c: Likewise.
     * gcc/testsuite/gcc.dg/ipa/ipa-icf-7.c: Likewise.
     * gcc/testsuite/gcc.dg/ipa/ipa-icf-8.c: Likewise.
     * gcc/testsuite/gcc.dg/ipa/ipa-icf-9.c: Likewise.

Martin

[-- Attachment #2: 0004-New-tests-introduction.patch --]
[-- Type: text/x-patch, Size: 31428 bytes --]

diff --git a/gcc/testsuite/g++.dg/ipa/ipa-icf-1.C b/gcc/testsuite/g++.dg/ipa/ipa-icf-1.C
new file mode 100644
index 0000000..d27abf4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ipa/ipa-icf-1.C
@@ -0,0 +1,43 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf"  } */
+
+class A
+{
+public:
+  __attribute__ ((noinline))
+  virtual int Foo2()
+  {
+    return v;
+  }
+
+  float f;
+  int v;
+};
+
+class B
+{
+public:
+  __attribute__ ((noinline))
+  int Bar2()
+  {
+    return v;
+  }
+
+  float f, aaa;
+  int v;
+};
+
+int main()
+{
+  A a;
+  B b;
+
+  a.Foo2();
+  b.Bar2();
+
+  return 12345;
+}
+
+/* { dg-final { scan-ipa-dump-not "Semantic equality hit:" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 0" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/g++.dg/ipa/ipa-icf-2.C b/gcc/testsuite/g++.dg/ipa/ipa-icf-2.C
new file mode 100644
index 0000000..48badd7
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ipa/ipa-icf-2.C
@@ -0,0 +1,40 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf"  } */
+
+class A
+{
+public:
+  __attribute__ ((noinline))
+  int Foo2()
+  {
+    return 1;
+  }
+
+  int v;
+  float f;
+};
+
+class B
+{
+public:
+  __attribute__ ((noinline))
+  int Bar2()
+  {
+    return 1;
+  }
+
+  int v;
+  float f, aaa;
+};
+
+int main()
+{
+  A a;
+  B b;
+
+  return a.Foo2() + b.Bar2();
+}
+
+/* { dg-final { scan-ipa-dump "Semantic equality hit:" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 1" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/g++.dg/ipa/ipa-icf-3.C b/gcc/testsuite/g++.dg/ipa/ipa-icf-3.C
new file mode 100644
index 0000000..042f789
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ipa/ipa-icf-3.C
@@ -0,0 +1,36 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf"  } */
+
+__attribute__ ((noinline))
+int zero()
+{
+  return 0;
+}
+
+__attribute__ ((noinline))
+int nula()
+{
+  return 0;
+}
+
+__attribute__ ((noinline))
+int foo()
+{
+  return zero();
+}
+
+__attribute__ ((noinline))
+int bar()
+{
+  return nula();
+}
+
+int main()
+{
+  return foo() + bar();
+}
+
+/* { dg-final { scan-ipa-dump "Semantic equality hit:.*bar.*->.*foo.*" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Semantic equality hit:.*nula.*->.*zero.*" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 2" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/g++.dg/ipa/ipa-icf-4.C b/gcc/testsuite/g++.dg/ipa/ipa-icf-4.C
new file mode 100644
index 0000000..4ac2cf9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ipa/ipa-icf-4.C
@@ -0,0 +1,49 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf -fno-inline" } */
+
+namespace {
+struct A
+{
+  virtual void foo(void) {}
+};
+struct B: virtual A
+{
+  virtual void foo(void) {}
+};
+struct C: virtual A
+{
+  virtual void bar(void) {}
+};
+struct D: virtual A
+{
+  virtual void sparta(void) {}
+};
+struct E: B,C,D
+{
+  virtual void foo(void) {}
+  virtual void barbar(void) {}
+};
+} // anonymous namespace
+
+int main()
+{
+  struct A a;
+  struct B b;
+  struct C c;
+  struct D d;
+  struct E e;
+
+  a.foo();
+  b.foo();
+  c.bar();
+  d.foo();
+  d.sparta();
+  e.barbar();
+
+  return 123;
+}
+
+/* { dg-final { scan-ipa-dump "Varpool alias has been created" "icf"  } } */
+/* { dg-final { scan-ipa-dump-times "Callgraph alias has been created" 5 "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 6" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/g++.dg/ipa/ipa-icf-5.C b/gcc/testsuite/g++.dg/ipa/ipa-icf-5.C
new file mode 100644
index 0000000..728df20
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ipa/ipa-icf-5.C
@@ -0,0 +1,24 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf" } */
+
+struct test
+{
+  int a;
+  float b;
+};
+
+extern const struct test myarray __attribute__ ((visibility("hidden")));
+extern const struct test myarray_alias __attribute__ ((visibility("hidden")));
+
+const struct test myarray = {1, 1.5f};
+
+extern const struct test myarray_alias __attribute__ ((alias ("myarray")));
+
+int main()
+{
+  return myarray.a - myarray_alias.a;
+}
+
+/* { dg-final { scan-ipa-dump "Varpool alias cannot be created \\(alias cycle\\)." "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 1" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-icf-1.c b/gcc/testsuite/gcc.dg/ipa/ipa-icf-1.c
new file mode 100644
index 0000000..aeee356
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-icf-1.c
@@ -0,0 +1,61 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf"  } */
+
+#include <stdio.h>
+
+struct container
+{
+  int x;
+  int y;
+};
+
+static struct container max;
+static int array[3][3];
+static int array2[123];
+
+__attribute__ ((noinline))
+void foo(void)
+{
+  printf("Foo()");
+}
+
+__attribute__ ((noinline))
+int order(int x, int y)
+{
+  return x < y ? 2 : 4;
+}
+
+__attribute__ ((noinline))
+int order2(int y, int x)
+{
+  return x < y ? 2 : 4;
+}
+
+__attribute__ ((noinline))
+void x1(int x)
+{
+  int i;
+  for(i = 0; i < 20; ++i)
+    array2[i] = i;
+
+  array2[2] = 13;
+}
+
+__attribute__ ((noinline))
+void x2(int a)
+{
+  int i;
+  for(i = 0; i < 20; ++i)
+    array2[i] = i;
+
+  array2[2] = 13;
+}
+
+int main(int argc, char **argv)
+{
+  return 0;
+}
+
+/* { dg-final { scan-ipa-dump "Semantic equality hit:x2->x1" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 1" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-icf-10.c b/gcc/testsuite/gcc.dg/ipa/ipa-icf-10.c
new file mode 100644
index 0000000..b9bca60
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-icf-10.c
@@ -0,0 +1,34 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf"  } */
+
+int ferda(int x, int y) __attribute__ ((pure));
+int funkce(int a, int b) __attribute__ ((pure));
+
+__attribute__ ((noinline))
+int ferda(int x, int y)
+{
+  if (x < y)
+    {
+      return x;
+    }
+  else
+    return y;
+}
+
+__attribute__ ((noinline))
+int funkce(int a, int b)
+{
+  if(a < b)
+    return a;
+  else
+    return b;
+}
+
+int main(int argc, char **argv)
+{
+  return 0;
+}
+
+/* { dg-final { scan-ipa-dump "Semantic equality hit:funkce->ferda" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 1" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-icf-11.c b/gcc/testsuite/gcc.dg/ipa/ipa-icf-11.c
new file mode 100644
index 0000000..2eb90da
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-icf-11.c
@@ -0,0 +1,29 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf"  } */
+
+__attribute__ ((noinline))
+int fce(int a, int b)
+{
+  return a + b;
+}
+
+__attribute__ ((noinline))
+int f0(int a)
+{
+  return fce(a, 5) + fce(a, 7);
+}
+
+__attribute__ ((noinline))
+int f1(int a)
+{
+  return fce(a, 5) + fce(a, 7);
+}
+
+int main(int argc, char **argv)
+{
+  return f0(argc) * f1(argc);
+}
+
+/* { dg-final { scan-ipa-dump "Semantic equality hit:f1->f0" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 1" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-icf-12.c b/gcc/testsuite/gcc.dg/ipa/ipa-icf-12.c
new file mode 100644
index 0000000..d4b7c38
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-icf-12.c
@@ -0,0 +1,78 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf"  } */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+int gcd(int x, int y) __attribute__ ((pure));
+
+__attribute__ ((noinline))
+int gcd(int x, int y)
+{
+  int swap;
+
+  if(x <= 0 || y <= 0)
+    return 0;
+
+  if(x < y)
+    {
+      swap = x;
+      x = y;
+      y = swap;
+    }
+
+  while(x != y)
+    {
+      x = x - y;
+
+      if(y > x)
+	{
+	  swap = x;
+	  x = y;
+	  y = swap;
+	}
+    }
+
+  return x;
+}
+
+int nsd(int x, int y) __attribute__ ((pure));
+
+__attribute__ ((noinline))
+int nsd(int x, int y)
+{
+  int swap;
+
+  if(x <= 0 || y <= 0)
+    return 0;
+
+  if(x < y)
+    {
+      swap = x;
+      x = y;
+      y = swap;
+    }
+
+  while(x != y)
+    {
+      x = x - y;
+
+      if(y > x)
+	{
+	  swap = x;
+	  x = y;
+	  y = swap;
+	}
+    }
+
+  return x;
+}
+
+int main(int argc, char **argv)
+{
+  return 0;
+}
+
+/* { dg-final { scan-ipa-dump "Semantic equality hit:nsd->gcd" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 1" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-icf-13.c b/gcc/testsuite/gcc.dg/ipa/ipa-icf-13.c
new file mode 100644
index 0000000..e409ee4
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-icf-13.c
@@ -0,0 +1,194 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf"  } */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+int gcd(int x, int y) __attribute__ ((pure));
+
+__attribute__ ((noinline))
+int gcd(int x, int y)
+{
+  int swap;
+
+  if(x <= 0 || y <= 0)
+    return 0;
+
+  if(x < y)
+    {
+      swap = x;
+      x = y;
+      y = swap;
+    }
+
+  while(x != y)
+    {
+      x = x - y;
+
+      if(y > x)
+	{
+	  swap = x;
+	  x = y;
+	  y = swap;
+	}
+    }
+
+  return x;
+}
+
+int nsd(int x, int y) __attribute__ ((pure));
+
+__attribute__ ((noinline))
+int nsd(int x, int y)
+{
+  int swap;
+
+  if(x <= 0 || y <= 0)
+    return 0;
+
+  if(x < y)
+    {
+      swap = x;
+      x = y;
+      y = swap;
+    }
+
+  while(x != y)
+    {
+      x = x - y;
+
+      if(y > x)
+	{
+	  swap = x;
+	  x = y;
+	  y = swap;
+	}
+    }
+
+  return x;
+}
+
+int nsd_different_result(int x, int y) __attribute__ ((pure));
+
+__attribute__ ((noinline))
+int nsd_different_result(int x, int y)
+{
+  int pes;
+
+  if(x <= 0 || y <= 0)
+    return 1;
+
+  if(x < 10)
+    y = 12;
+  else if(x == 44)
+    y = 124;
+  else
+    y = 1111;
+
+  if(x < y)
+    {
+      pes = x;
+      x = y;
+      y = pes;
+    }
+
+  while(x != y)
+    {
+      x = x - y;
+
+      if(y > x)
+	{
+	  pes = x;
+	  x = y;
+	  y = pes;
+	}
+    }
+
+  return x;
+}
+
+int nsd_different_result2(int x, int y) __attribute__ ((pure));
+
+__attribute__ ((noinline))
+int nsd_different_result2(int x, int y)
+{
+  int pes;
+
+  if(x <= 0 || y <= 0)
+    return 1;
+
+  if(x < 10)
+    y = 12;
+  else if(x == 44)
+    y = 124;
+  else
+    y = 1111;
+
+  if(x < y)
+    {
+      pes = x;
+      x = y;
+      y = pes;
+    }
+
+  while(x != y)
+    {
+      x = x - y;
+
+      if(y > x)
+	{
+	  pes = x;
+	  x = y;
+	  y = pes;
+	}
+    }
+
+  return x;
+}
+
+__attribute__ ((noinline))
+int s1(int x)
+{
+  switch (x)
+    {
+    case 10:
+    case 11:
+      return 2;
+    case 12:
+      return 123;
+    default:
+      return x + 2;
+    }
+}
+
+__attribute__ ((noinline))
+int s2(int x)
+{
+  switch (x)
+    {
+    case 10:
+    case 11:
+      return 2;
+    case 12:
+      return 123;
+    default:
+      return x + 2;
+    }
+}
+int main(int argc, char **argv)
+{
+  if(argc < 3)
+    return 1;
+
+  int a = atoi(argv[1]);
+  int b = atoi(argv[2]);
+
+  printf("Test1: %d, %d, gdc: %d\n", a, b, gcd(a, b));
+  printf("Test2: %d, %d, gdc: %d\n", a, b, nsd(a, b));
+}
+
+/* { dg-final { scan-ipa-dump "Semantic equality hit:s2->s1" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Semantic equality hit:nsd_different_result2->nsd_different_result" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Semantic equality hit:nsd->gcd" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 3" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-icf-14.c b/gcc/testsuite/gcc.dg/ipa/ipa-icf-14.c
new file mode 100644
index 0000000..cae02fb
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-icf-14.c
@@ -0,0 +1,47 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf"  } */
+
+#include <stdio.h>
+
+__attribute__ ((noinline))
+int foo(int a)
+{
+  void *l = &&error;
+
+  if(a == 4)
+    goto *l;
+
+  return 150;
+
+error:
+  return a;
+failure:
+  return a + 2;
+}
+
+__attribute__ ((noinline))
+int foo_wrong(int a)
+{
+  void *l = &&failure;
+
+  if(a == 4)
+    goto *l;
+
+  return 150;
+
+error:
+  return a;
+failure:
+  return a + 2;
+}
+
+int main(int argc, char **argv)
+{
+  printf("value: %d\n", foo(argc));
+
+  return 0;
+}
+
+/* { dg-final { scan-ipa-dump-not "Semantic equality hit:" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 0" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-icf-15.c b/gcc/testsuite/gcc.dg/ipa/ipa-icf-15.c
new file mode 100644
index 0000000..f6e7fb3
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-icf-15.c
@@ -0,0 +1,47 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf"  } */
+
+#include <stdio.h>
+
+__attribute__ ((noinline))
+int bar(int a)
+{
+  void *l = &&error;
+
+  if(a == 4)
+    goto *l;
+
+  return 150;
+
+error:
+  return a;
+failure:
+  return a + 2;
+}
+
+__attribute__ ((noinline))
+int foo(int a)
+{
+  void *l = &&error;
+
+  if(a == 4)
+    goto *l;
+
+  return 150;
+
+error:
+  return a;
+failure:
+  return a + 2;
+}
+
+int main(int argc, char **argv)
+{
+  printf("value: %d\n", foo(argc));
+
+  return 0;
+}
+
+/* { dg-final { scan-ipa-dump "Semantic equality hit:foo->bar" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 1" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-icf-16.c b/gcc/testsuite/gcc.dg/ipa/ipa-icf-16.c
new file mode 100644
index 0000000..fb0b116
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-icf-16.c
@@ -0,0 +1,27 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf"  } */
+
+#include <stdio.h>
+
+__attribute__ ((noinline))
+int foo()
+{
+  printf ("Hello world.\n");
+  return 0;
+}
+
+__attribute__ ((noinline))
+int bar()
+{
+  printf ("Hello world.\n");
+  return 0;
+}
+
+int main()
+{
+  return foo() + bar();
+}
+
+/* { dg-final { scan-ipa-dump "Semantic equality hit:bar->foo" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 1" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-icf-17.c b/gcc/testsuite/gcc.dg/ipa/ipa-icf-17.c
new file mode 100644
index 0000000..7c9172d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-icf-17.c
@@ -0,0 +1,37 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf"  } */
+
+__attribute__ ((noinline))
+int foo(int x)
+{
+  int c = x;
+
+  if (x > 10)
+    c += 2;
+  else
+    c -= 3;
+
+  return x;
+}
+
+__attribute__ ((noinline))
+int bar(int y)
+{
+  int d = y;
+
+  if (y > 10)
+    d += 2;
+  else
+    d -= 3;
+
+  return d;
+}
+
+int main()
+{
+  return 0;
+}
+
+/* { dg-final { scan-ipa-dump-not "Semantic equality hit:" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 0" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-icf-18.c b/gcc/testsuite/gcc.dg/ipa/ipa-icf-18.c
new file mode 100644
index 0000000..9dc3979
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-icf-18.c
@@ -0,0 +1,37 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf"  } */
+
+__attribute__ ((noinline))
+int foo(int x)
+{
+  int c = x;
+
+  if (x > 10)
+    c += 2;
+  else
+    c -= 3;
+
+  return c;
+}
+
+__attribute__ ((noinline))
+int bar(int y)
+{
+  int d = y;
+
+  if (y > 10)
+    d += 2;
+  else
+    d -= 3;
+
+  return d;
+}
+
+int main()
+{
+  return 0;
+}
+
+/* { dg-final { scan-ipa-dump "Semantic equality hit:bar->foo" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 1" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-icf-19.c b/gcc/testsuite/gcc.dg/ipa/ipa-icf-19.c
new file mode 100644
index 0000000..7a29cf3
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-icf-19.c
@@ -0,0 +1,37 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf"  } */
+
+__attribute__ ((noinline))
+int foo(int x)
+{
+  int c = x;
+
+  if (x > 10)
+    c += 2;
+  else
+    c -= 3;
+
+  return c;
+}
+
+__attribute__ ((noinline))
+int bar(int y)
+{
+  int d = y;
+
+  if (y > 11)
+    d += 2;
+  else
+    d -= 3;
+
+  return d;
+}
+
+int main()
+{
+  return 0;
+}
+
+/* { dg-final { scan-ipa-dump-not "Semantic equality hit:" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 0" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-icf-2.c b/gcc/testsuite/gcc.dg/ipa/ipa-icf-2.c
new file mode 100644
index 0000000..385db0c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-icf-2.c
@@ -0,0 +1,69 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf"  } */
+
+#include <stdio.h>
+
+struct container
+{
+  int x;
+  int y;
+};
+
+static struct container max;
+static int pole[3][3];
+static int pole2[123];
+
+static struct container superpole[10][10];
+
+void f1(struct container *c)
+{
+  struct container pes;
+  pes.x = 123;
+  pes.y = 123;
+
+  struct container *pesp = c;
+  pesp->x = 5;
+
+  pole[1][2] = 3;
+
+  superpole[4][3].x = 4;
+  max.x = 3;
+  void *x = &pole;
+
+  int **a = (int**)pole;
+  a[1][2] = 543;
+
+  if(x != 0)
+    pole[1][2] = 123;
+}
+
+void f2(struct container *c)
+{
+  struct container pes;
+  pes.x = 123;
+  pes.y = 123;
+
+  struct container *pesp = c;
+  pesp->x = 5;
+
+  pole[1][2] = 3;
+
+  superpole[4][3].x = 4;
+  max.x = 3;
+  void *x = &pole;
+
+  int **a = (int**)pole;
+  a[1][2] = 543;
+
+  if(x != 0)
+    pole[1][2] = 123;
+}
+
+int main(int argc, char **argv)
+{
+  return 0;
+}
+
+/* { dg-final { scan-ipa-dump "Semantic equality hit:f2->f1" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 1" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-icf-20.c b/gcc/testsuite/gcc.dg/ipa/ipa-icf-20.c
new file mode 100644
index 0000000..9912a9a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-icf-20.c
@@ -0,0 +1,28 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf"  } */
+
+#include <math.h>
+
+__attribute__ ((noinline))
+float foo()
+{
+  return sin(12.4f);
+}
+
+__attribute__ ((noinline))
+float bar()
+{
+  return sin(12.4f);
+}
+
+int main()
+{
+  foo();
+  bar();
+
+  return 0;
+}
+
+/* { dg-final { scan-ipa-dump "Semantic equality hit:bar->foo" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 1" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-icf-21.c b/gcc/testsuite/gcc.dg/ipa/ipa-icf-21.c
new file mode 100644
index 0000000..7358e43
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-icf-21.c
@@ -0,0 +1,27 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf"  } */
+
+#include <xmmintrin.h>
+
+__attribute__ ((noinline))
+void foo()
+{
+  float x = 1.2345f;
+  __m128 v =_mm_load1_ps(&x);
+}
+
+__attribute__ ((noinline))
+void bar()
+{
+  float x = 1.2345f;
+  __m128 v =_mm_load1_ps(&x);
+}
+
+int main()
+{
+  return 2;
+}
+
+/* { dg-final { scan-ipa-dump "Semantic equality hit:bar->foo" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 1" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-icf-22.c b/gcc/testsuite/gcc.dg/ipa/ipa-icf-22.c
new file mode 100644
index 0000000..9561026
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-icf-22.c
@@ -0,0 +1,37 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf"  } */
+
+struct A
+{
+  int a, b, c;
+};
+
+struct B
+{
+  int x, y;
+};
+
+__attribute__ ((noinline))
+int foo(struct A *a)
+{
+  a->c = 1;
+
+  return 123;
+}
+
+__attribute__ ((noinline))
+int bar(struct B *b)
+{
+  b->y = 1;
+
+  return 123;
+}
+
+int main()
+{
+  return foo(0) + bar(0);
+}
+
+/* { dg-final { scan-ipa-dump-not "Semantic equality hit:" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 0" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-icf-23.c b/gcc/testsuite/gcc.dg/ipa/ipa-icf-23.c
new file mode 100644
index 0000000..7e81ae2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-icf-23.c
@@ -0,0 +1,29 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf"  } */
+
+struct A
+{
+  int a;
+  int b;
+};
+
+__attribute__ ((noinline))
+int foo(struct A *a)
+{
+  return 123;
+}
+
+__attribute__ ((noinline))
+int bar(struct A *b)
+{
+  return 123;
+}
+
+int main()
+{
+  return foo(0) + bar(0);
+}
+
+/* { dg-final { scan-ipa-dump "Semantic equality hit:bar->foo" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 1" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-icf-24.c b/gcc/testsuite/gcc.dg/ipa/ipa-icf-24.c
new file mode 100644
index 0000000..3cd476f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-icf-24.c
@@ -0,0 +1,36 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf"  } */
+
+struct A
+{
+  int a, b, c, d;
+};
+
+struct B
+{
+  int x, y, z;
+};
+
+__attribute__ ((noinline))
+int foo(struct A *a)
+{
+  a->c = 1;
+
+  return 123;
+}
+
+__attribute__ ((noinline))
+int bar(struct B *b)
+{
+  b->z = 1;
+
+  return 123;
+}
+
+int main()
+{
+  return foo(0) + bar(0);
+}
+
+/* { dg-final { scan-ipa-dump "Equal symbols: 0" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-icf-25.c b/gcc/testsuite/gcc.dg/ipa/ipa-icf-25.c
new file mode 100644
index 0000000..e6cf8ac
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-icf-25.c
@@ -0,0 +1,48 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf"  } */
+
+__attribute__ ((noinline))
+int foo()
+{
+  return zip();
+}
+
+__attribute__ ((noinline))
+int bar()
+{
+  return zap();
+}
+
+__attribute__ ((noinline))
+int baz()
+{
+  return two();
+}
+
+__attribute__ ((noinline))
+int zip()
+{
+  return 0;
+}
+
+__attribute__ ((noinline))
+int zap()
+{
+  return 0;
+}
+
+__attribute__ ((noinline))
+int two()
+{
+  return 2;
+}
+
+int main()
+{
+  return foo() + bar();
+}
+
+/* { dg-final { scan-ipa-dump "Semantic equality hit:bar->foo" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Semantic equality hit:zap->zip" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 2" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-icf-26.c b/gcc/testsuite/gcc.dg/ipa/ipa-icf-26.c
new file mode 100644
index 0000000..0c5a5a6
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-icf-26.c
@@ -0,0 +1,44 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf"  } */
+
+void destroy (void)
+{
+}
+
+void remove (void)
+{
+}
+
+
+struct callbacks
+{
+  void (*success) (void);
+  void (*error) (void);
+};
+
+struct callbacks my_callbacks;
+
+__attribute__ ((noinline))
+void foo()
+{
+  my_callbacks.success = destroy;
+}
+
+__attribute__ ((noinline))
+void bar()
+{
+  my_callbacks.success = remove;
+}
+
+int main()
+{
+  foo();
+  bar();
+
+  return 0;
+}
+
+/* { dg-final { scan-ipa-dump "Semantic equality hit:bar->foo" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Semantic equality hit:remove->destroy" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 2" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-icf-27.c b/gcc/testsuite/gcc.dg/ipa/ipa-icf-27.c
new file mode 100644
index 0000000..fab2e41
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-icf-27.c
@@ -0,0 +1,30 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf -fno-inline"  } */
+
+void destroy (void)
+{
+  __asm__ __volatile__ ("" : : : "memory");
+}
+
+void remove (void)
+{
+  __asm__ __volatile__ ("" : : : "memory");
+}
+
+void remove2 (void)
+{
+  __asm__ __volatile__ ("" : : : );
+}
+
+int main()
+{
+  destroy ();
+  remove ();
+  remove2 ();
+
+  return 0;
+}
+
+/* { dg-final { scan-ipa-dump "Semantic equality hit:remove->destroy" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 1" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-icf-28.c b/gcc/testsuite/gcc.dg/ipa/ipa-icf-28.c
new file mode 100644
index 0000000..538e0ab
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-icf-28.c
@@ -0,0 +1,24 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf-details -fno-inline"  } */
+
+__attribute__ ((noinline, constructor(200)))
+int foo()
+{
+  return 123;
+}
+
+__attribute__ ((noinline, constructor(400)))
+int bar()
+{
+  return 123;
+}
+
+int main()
+{
+  foo() + bar();
+
+  return 0;
+}
+/* { dg-final { scan-ipa-dump "Equal symbols: 0" "icf"  } } */
+/* { dg-final { scan-ipa-dump "attribute values are different" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-icf-3.c b/gcc/testsuite/gcc.dg/ipa/ipa-icf-3.c
new file mode 100644
index 0000000..e4e2eb6
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-icf-3.c
@@ -0,0 +1,36 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf"  } */
+
+typedef int v4si __attribute__ ((vector_size (16)));
+
+__attribute__ ((noinline))
+int foo(void)
+{
+  v4si a = {1,2,3,4};
+  v4si b = {3,2,1,4};
+  v4si c;
+
+  return 54;
+}
+
+__attribute__ ((noinline))
+int bar(void)
+{
+  v4si a = {1,2,3,4};
+  v4si b = {3,2,1,4};
+  v4si c;
+
+  return 54;
+}
+
+int main()
+{
+  foo();
+  bar();
+
+  return 0;
+}
+
+/* { dg-final { scan-ipa-dump "Semantic equality hit:bar->foo" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 1" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-icf-4.c b/gcc/testsuite/gcc.dg/ipa/ipa-icf-4.c
new file mode 100644
index 0000000..9434fb0
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-icf-4.c
@@ -0,0 +1,32 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf"  } */
+
+__attribute__ ((noinline))
+int foo(int a)
+{
+  return a * a;
+}
+
+__attribute__ ((noinline))
+int bar(int b)
+{
+  return b;
+}
+
+__attribute__ ((noinline))
+void caller(int x)
+{
+  return;
+}
+
+int main(int argc, char **argv)
+{
+  caller(foo(argc));
+  caller(bar(argc));
+
+  return 123;
+}
+
+/* { dg-final { scan-ipa-dump-not "Semantic equality hit:" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 0" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-icf-5.c b/gcc/testsuite/gcc.dg/ipa/ipa-icf-5.c
new file mode 100644
index 0000000..45fddf5
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-icf-5.c
@@ -0,0 +1,55 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf"  } */
+
+#include <complex.h>
+
+static double test;
+
+__attribute__ ((noinline))
+double f1(void)
+{
+  double complex z1 = 1.0 + 3.0 * I;
+  double complex z2 = 1.0 - 4.0 * I;
+
+  unsigned a = 123;
+  unsigned b = 321;
+
+  if (a & b)
+    return 1.2f;
+
+  if(cimag(z1) > 1)
+    return 1.0f;
+
+  test = cimag(z1) + 2;
+
+  return cimag(z1 + z2);
+}
+
+__attribute__ ((noinline))
+double f2(void)
+{
+  double complex z1 = 1.0 + 3.0 * I;
+  double complex z2 = 1.0 - 4.0 * I;
+
+  unsigned a = 123;
+  unsigned b = 321;
+
+  if (a & b)
+    return 1.2f;
+
+  if(cimag(z1) > 1)
+    return 1.0f;
+
+  test = cimag(z1) + 2;
+
+  return cimag(z1 + z2);
+}
+
+int main()
+{
+  return 1;
+}
+
+/* { dg-final { scan-ipa-dump "Semantic equality hit:f2->f1" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 1" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-icf-6.c b/gcc/testsuite/gcc.dg/ipa/ipa-icf-6.c
new file mode 100644
index 0000000..6e6758e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-icf-6.c
@@ -0,0 +1,36 @@
+/* { dg-do compile } */
+/* { dg-options "-O0 -fipa-icf -fdump-ipa-icf"  } */
+
+typedef int v4si __attribute__ ((vector_size (16)));
+
+__attribute__ ((noinline))
+int foo(void)
+{
+  v4si a = {1,2,3,4};
+  v4si b = {3,2,1,4};
+  v4si c;
+
+  return 54;
+}
+
+__attribute__ ((noinline))
+int bar(void)
+{
+  v4si a = {1,2,3,4};
+  v4si b = {3,2,5,4};
+  v4si c;
+
+  return 54;
+}
+
+int main()
+{
+  foo();
+  bar();
+
+  return 0;
+}
+
+/* { dg-final { scan-ipa-dump-not "Semantic equality hit:" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 0" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-icf-7.c b/gcc/testsuite/gcc.dg/ipa/ipa-icf-7.c
new file mode 100644
index 0000000..ec7961d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-icf-7.c
@@ -0,0 +1,72 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf"  } */
+
+#include <complex.h>
+
+#if (__SIZEOF_INT__ == __SIZEOF_FLOAT__)
+typedef int intflt;
+#elif (__SIZEOF_LONG__ == __SIZEOF_FLOAT__)
+typedef long intflt;
+#else
+#error Add target support here for type that will union float size
+#endif
+
+
+static double test;
+
+struct struktura
+{
+  union
+  {
+    long i;
+    float f;
+  } u;
+};
+
+struct struktura sss;
+
+struct X
+{
+  int i;
+  union
+  {
+    intflt j;
+    intflt k;
+    float f;
+  } u;
+};
+
+__attribute__ ((noinline))
+intflt foo(intflt j)
+{
+  struct X a;
+
+  a.u.j = j;
+  a.u.f = a.u.f;
+  a.u.f = a.u.f;
+  a.u.j = a.u.j;
+  a.u.f = a.u.f;
+  return a.u.k;
+}
+
+__attribute__ ((noinline))
+intflt foo2(intflt j)
+{
+  struct X a;
+
+  a.u.j = j;
+  a.u.f = a.u.f;
+  a.u.f = a.u.f;
+  a.u.j = a.u.j;
+  a.u.f = a.u.f;
+  return a.u.k;
+}
+
+int main()
+{
+  return 1;
+}
+
+/* { dg-final { scan-ipa-dump "Semantic equality hit:foo2->foo" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 1" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-icf-8.c b/gcc/testsuite/gcc.dg/ipa/ipa-icf-8.c
new file mode 100644
index 0000000..d35df90
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-icf-8.c
@@ -0,0 +1,45 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf"  } */
+
+#include <stdio.h>
+
+__attribute__ ((noinline))
+int fce1(int a, int b)
+{
+  int swap;
+
+  if(a < b)
+    {
+      swap = a;
+      a = b;
+      b = swap;
+    }
+
+  return a / b;
+}
+
+__attribute__ ((noinline))
+int fce2(int x, int y)
+{
+  int tmp;
+
+  if(x < y)
+    {
+      tmp = x;
+      x = y;
+      y = tmp;
+    }
+
+  return x / y;
+}
+
+
+int main(int argc, char **argv)
+{
+  printf("fce1: %d\n", fce1(argc, argc + 2));
+  printf("fce2: %d\n", fce2(argc, 2 * argc));
+}
+
+/* { dg-final { scan-ipa-dump "Semantic equality hit:fce2->fce1" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 1" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-icf-9.c b/gcc/testsuite/gcc.dg/ipa/ipa-icf-9.c
new file mode 100644
index 0000000..9d04dd1
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-icf-9.c
@@ -0,0 +1,33 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-icf"  } */
+
+int funkce(int a, int b) __attribute__ ((pure));
+
+__attribute__ ((noinline))
+int ferda(int x, int y)
+{
+  if (x < y)
+    {
+      return x;
+    }
+  else
+    return y;
+}
+
+__attribute__ ((noinline))
+int funkce(int a, int b)
+{
+  if(a < b)
+    return a;
+  else
+    return b;
+}
+
+int main(int argc, char **argv)
+{
+  return 0;
+}
+
+/* { dg-final { scan-ipa-dump-not "Semantic equality hit:" "icf"  } } */
+/* { dg-final { scan-ipa-dump "Equal symbols: 0" "icf"  } } */
+/* { dg-final { cleanup-ipa-dump "icf" } } */
-- 
1.8.4.5


^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 2/5] Existing call graph infrastructure enhancement
  2014-06-30 11:49     ` Martin Liška
@ 2014-06-30 18:54       ` Jeff Law
  2014-07-17 14:54         ` Martin Liška
  0 siblings, 1 reply; 70+ messages in thread
From: Jeff Law @ 2014-06-30 18:54 UTC (permalink / raw)
  To: Martin Liška, gcc-patches

On 06/30/14 05:49, Martin Liška wrote:
>
> On 06/17/2014 10:00 PM, Jeff Law wrote:
>> On 06/13/14 04:26, mliska wrote:
>>> Hi,
>>>      this small patch prepares remaining needed infrastructure for
>>> the new pass.
>>>
>>> Changelog:
>>>
>>> 2014-06-13  Martin Liska  <mliska@suse.cz>
>>>         Honza Hubicka  <hubicka@ucw.cz>
>>>
>>>     * ipa-utils.h (polymorphic_type_binfo_p): Function marked external
>>>     instead of static.
>>>     * ipa-devirt.c (polymorphic_type_binfo_p): Likewise.
>>>     * ipa-prop.h (count_formal_params): Likewise.
>>>     * ipa-prop.c (count_formal_params): Likewise.
>>>     * ipa-utils.c (ipa_merge_profiles): Be more tolerant if we merge
>>>     profiles for semantically equivalent functions.
>>>     * passes.c (do_per_function): If we load body of a function
>>> during WPA,
>>>     this condition should behave same.
>>>     * varpool.c (ctor_for_folding): More tolerant assert for variable
>>>     aliases created during WPA.
>> Presumably we don't have any useful way to merge the cases where we
>> have provides for SRC & DST in ipa_merge_profiles or even to guess
>> which is more useful when presented with both?  Does it make sense to
>> log this into a debugging file when we drop one?
> Hello,
>     this merge function was written by Honza, what do you think Honza
> about this note?
>
>> I think this patch is fine.  If adding logging makes sense, then feel
>> free to do so and consider that trivial change pre-approved.
> I made a small change to this patch, where I moved
> 'gsi_next_nonvirtual_phi' from the pass to gimple-iterator.h.
>
> Ready for trunk with this change?
Yes.  I think with the exception of patch #3/5 everything looks good. 
I'll try to get another pass over #3 this week.  What I looked at last 
week was pretty good; I'm pretty confident this will be wrapped up shortly.

If #1/#2 make sense to install independent of #3, go ahead.  #4/#5 are 
obviously dependent on #3.

Jeff

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 3/5] IPA ICF pass
  2014-06-26 18:46     ` Jan Hubicka
  2014-06-30 12:05       ` Martin Liška
@ 2014-06-30 19:06       ` Jeff Law
  1 sibling, 0 replies; 70+ messages in thread
From: Jeff Law @ 2014-06-30 19:06 UTC (permalink / raw)
  To: Jan Hubicka; +Cc: mliska, gcc-patches

On 06/26/14 12:46, Jan Hubicka wrote:
> So you've added this at -O2, what is the general compile-time
>> impact? Would it make more sense to instead have it be part of -O3,
>> particularly since ICF is rarely going to improve performance (sans
>> icache issues).
>
> I think code size optimization not sacrifying any (or too much of) performance are
> generally very welcome at -O2.  Compared to LLVM and Microsoft compilers we are
> on code bloat side at -O2.
I'm not so much worried about runtime performance here, but compile-time 
performance.  ICF would seem like a general win as we're likely going to 
be more icache efficient.

So, just to be clear, if the compile-time impact is minimal, then I'll 
fully support -O2, but my worry is that it won't be minimal :(


>> Is returning TRUE really the conservatively correct thing to do in
>> the absence of aliasing information?  Isn't that case really "I
>> don't know" in which case the proper return value is FALSE?
>
> I think with -fno-strict-aliasing the set should be 0 (Richi?) and thus we can
> compare for equality.  We probably can be on agressive side and let 0 alias
> set prevail the non-0.  But that can be done incrementally.
I'd think it should be zero in the -fno-strict-aliasing case.  My 
concern was that in the -fno-strict-aliasing case we seems to always 
assume the objects are the same.  Is that the safe thing to do?

That hints that the comment for the function needs tweaking.  It really 
doesn't say anything about what the return values really mean.


> There are few, like we can ignore "weak" or "visibility" attribute because we do
> produce alias with proper visibility anyway.  My plan is to start removing those
> attributes from declarations once they are turned into suitable representation
> in symbol table (or for attributes like const/noreturn/pure where we have explicit
> decl flags).  This will make our life bit easier later, too.
>
> We probably then can whitelist some attributes, but I would deal with this later.
Sure, I don't mind going with the conservative approach and iterating to 
remove some of the limitations.

>
> Yep, there are no resonable orders on it.  If function starts same in source code they ought
> to end up same here.  Plan was to first match for exact equality and then play with
> smarter tricks here.
Yea, that's fine too.  It's conservatively correct and we may find that 
there just isn't much to gain by doing hte dfs walk to build indices and 
such for the CFG.  I guess ultimately I just want someone to look at 
that issue and evaluate if what we're doing now is "good enough" or if 
we're missing most of the benefit because of something like bb index 
stability or something dumb like that.

>
> Yep, the pass has grown up to be rather long. The gimple equality testing is the
> main body of work, so perhaps doing this in separate file is good idea.
I'd certainly like to see that happen both because of its size and 
because I think those bits are useful in other contexts.

Overall, most of the stuff looks quite reasonable.

jeff

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 3/5] IPA ICF pass
  2014-06-30 12:05       ` Martin Liška
@ 2014-07-01 23:45         ` Trevor Saunders
  0 siblings, 0 replies; 70+ messages in thread
From: Trevor Saunders @ 2014-07-01 23:45 UTC (permalink / raw)
  To: Martin Liška; +Cc: gcc-patches

> diff --git a/gcc/ipa-icf.c b/gcc/ipa-icf.c
> new file mode 100644
> index 0000000..8a13dca
> --- /dev/null
> +++ b/gcc/ipa-icf.c
> +/* Itializes internal structures according to given number of

initialize

> +      if (is_a_helper<cgraph_node *>::test (node))

shouldn't you just use is_a<cgraph_node*> (node) ?

> +sem_item_optimizer::filter_removed_items (void)
> +{
> +  vec <sem_item *> filtered;
> +  filtered.create (m_items.length());

use auto_vec here?

> +  m_items.release ();
> +
> +  for (unsigned int i = 0; i < filtered.length(); i++)
> +    m_items.safe_push (filtered[i]);

hrm, maybe adding vec::swap makes sense.

> +	  if (c->members.length() > 1)
> +	    {
> +	      vec <sem_item *> new_vector;
> +	      new_vector.create (c->members.length ());

same comment about auto_vec and swap.

> +bool
> +sem_item_optimizer::release_split_map (__attribute__((__unused__)) congruence_class *
> +				       const &cls,
> +				       __attribute__((__unused__)) bitmap const &b,

that one is definitly used

> +				       __attribute__((__unused__)) traverse_split_pair *pair)

Why can't you just leave the arguments unnamed to fix the warning?

> +sem_item_optimizer::do_congruence_step_for_index (congruence_class *cls,
> +    unsigned int index)
> +{
> +  hash_map <congruence_class *, bitmap> *split_map =
> +  	new hash_map <congruence_class *, bitmap> ();

why aren't you putting the hash_map on  the stack? in any case it looks
like you fail to delete it when your done with it.

> diff --git a/gcc/ipa-icf.h b/gcc/ipa-icf.h
> new file mode 100644
> index 0000000..d328dd6
> --- /dev/null
> +++ b/gcc/ipa-icf.h
> @@ -0,0 +1,743 @@
> +/* Prints string STRING to a FILE with a given number of SPACE_COUNT.  */
> +#define FPUTS_SPACES(file, space_count, string) \
> +  do \
> +  { \
> +    fprintf (file, "%*s" string, space_count, " "); \

seems like you could do this with a static inline function.

> +/* Prints a MESSAGE to dump_file if exists.  */
> +#define SE_DUMP_MESSAGE(message) \
> +  do \
> +  { \
> +    if (dump_file && (dump_flags & TDF_DETAILS)) \
> +      fprintf (dump_file, "  debug message: %s (%s:%u)\n", message, __func__, __LINE__); \

a inline function that used the builtins like the memory statis stuff
might be slightly less ugly imho

> +/* Logs a MESSAGE to dump_file if exists and returns false.  */
> +#define SE_EXIT_FALSE_WITH_MSG(message) \
> +  do \
> +  { \
> +    if (dump_file && (dump_flags & TDF_DETAILS)) \
> +      fprintf (dump_file, "  false returned: '%s' (%s:%u)\n", message, __func__, __LINE__); \
> +    return false; \
> +  } \
> +  while (false);

ugh macros that effect control flow, instead maybe define a inline
function that does the logging and then returns the value you want your
function to return so you can write

if (whatever)
  return SE_LOG (NULL, "something or other");
?

> +/* Forward declaration for sem_func class.  */
> +class sem_item;

comment is wrong, and imho useless.

> +  /* Initializes internal structures according to given number of
> +     source and target SSA names. The number of source names is SSA_SOURCE,
> +     respectively SSA_TARGET.  */
> +  func_checker (unsigned ssa_source, unsigned sss_target);

ssa_target?

> +  /* Source to target edge map.  */
> +  hash_map <edge, edge> *m_edge_map;

is there a reason to not embedd these in the object? you seem to create
them in the ctor and delete them in the dtor, so I expect they have teh
same lifetime.

> +/* Basic block struct for sematic equality pass.  */

semantic?

> +typedef struct sem_bb

you don't need the typedef in C++

> +  /* Item type.  */
> +  enum sem_item_type type;

 loose the enum keyword since you don't need it?

> +class sem_function: public sem_item
> +{
> +  /* COMPARED_FUNC is a function that we compare to.  */
> +  sem_function *m_compared_func;

this feels like a weird place for this, would func_checker maybe make
more sense as a place to put it?

> +class sem_variable: public sem_item
> +{
> +  /* Initializes references to other semantic functions/variables.  */
> +  inline virtual void init_refs ()

iirc defining with in a class definition implies inline.

> +typedef struct congruence_class_group
> +{
> +  hashval_t hash;
> +  sem_item_type type;
> +  vec <congruence_class *> classes;
> +} congruence_class_group_t;

lose the typedef?

> +  /* Returns true if a congruence class CLS is presented in worklist.  */

s/presented/present/ ?

Trev

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 3/5] IPA ICF pass
  2014-06-20  7:32   ` Trevor Saunders
  2014-06-26 11:18     ` Martin Liška
@ 2014-07-05 21:44     ` Gerald Pfeifer
  2014-07-05 22:53       ` Jan Hubicka
  1 sibling, 1 reply; 70+ messages in thread
From: Gerald Pfeifer @ 2014-07-05 21:44 UTC (permalink / raw)
  To: Trevor Saunders; +Cc: Martin Liška, gcc-patches, Jan Hubicka

On Fri, 20 Jun 2014, Trevor Saunders wrote:
>> +@item -fipa-icf
>> +@opindex fipa-icf
>> +Perform Identical Code Folding for functions and read-only variables.
>> +Behavior is similar to Gold Linker ICF optimization. Symbols proved
>> +as semantically equivalent are redirected to corresponding symbol. The pass
>> +sensitively decides for usage of alias, thunk or local redirection.
>> +This flag is enabled by default at @option{-O2}.

I found this a bit hard to read/understand.

Perhaps first describe what it does and then, before "This flag is
enabled..." note that "This is similar to the ICF optimization performed
by the Gold linker".

"Symbols proved" (plural) vs "to corresponding symbol" seems to miss
an an "a" as in "a corresponding symbol".  Alas, how is that one 
determined?  Is this more "...are merged into one", from the user's
perspective?

What does it mean to "sensitively decide for usage of alias, thunk,
or local redirection"?

Gerald

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 3/5] IPA ICF pass
  2014-07-05 21:44     ` Gerald Pfeifer
@ 2014-07-05 22:53       ` Jan Hubicka
  2014-07-17 15:23         ` Martin Liška
  0 siblings, 1 reply; 70+ messages in thread
From: Jan Hubicka @ 2014-07-05 22:53 UTC (permalink / raw)
  To: Gerald Pfeifer
  Cc: Trevor Saunders, Martin Liška, gcc-patches, Jan Hubicka

> On Fri, 20 Jun 2014, Trevor Saunders wrote:
> >> +@item -fipa-icf
> >> +@opindex fipa-icf
> >> +Perform Identical Code Folding for functions and read-only variables.

I would perhaps explicitly say that the optimizations reduce code size
and may disturb unwind stacks by replacing a function by equivalent
one with different name.
> >> +Behavior is similar to Gold Linker ICF optimization. Symbols proved

Perhaps tell a bit more here. The optimization works more effectively with link
time optimization enabled and that the Gold and GCC ICF works on different
levels and thus are not equivalent optimizations - there are equivallences that
are found only by GCC and equivalences found only by Gold.

>> +as semantically equivalent are redirected to corresponding symbol. The pass
> >> +sensitively decides for usage of alias, thunk or local redirection.
> >> +This flag is enabled by default at @option{-O2}.
Probably at -Os too.
> 
> I found this a bit hard to read/understand.
> 
> Perhaps first describe what it does and then, before "This flag is
> enabled..." note that "This is similar to the ICF optimization performed
> by the Gold linker".

> 
> "Symbols proved" (plural) vs "to corresponding symbol" seems to miss
> an an "a" as in "a corresponding symbol".  Alas, how is that one 
> determined?  Is this more "...are merged into one", from the user's
> perspective?
> 
> What does it mean to "sensitively decide for usage of alias, thunk,
> or local redirection"?

I think this is just a technical detail of the implementation.  I would not put that
into user manual.  It means that for some functions you can make alias, for others
you need thunk (so addresses stay different)
> 
> Gerald

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 2/5] Existing call graph infrastructure enhancement
  2014-06-30 18:54       ` Jeff Law
@ 2014-07-17 14:54         ` Martin Liška
  2014-08-25  9:56           ` Martin Liška
  0 siblings, 1 reply; 70+ messages in thread
From: Martin Liška @ 2014-07-17 14:54 UTC (permalink / raw)
  To: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 2248 bytes --]


On 06/30/2014 08:54 PM, Jeff Law wrote:
> On 06/30/14 05:49, Martin Liška wrote:
>>
>> On 06/17/2014 10:00 PM, Jeff Law wrote:
>>> On 06/13/14 04:26, mliska wrote:
>>>> Hi,
>>>>      this small patch prepares remaining needed infrastructure for
>>>> the new pass.
>>>>
>>>> Changelog:
>>>>
>>>> 2014-06-13  Martin Liska  <mliska@suse.cz>
>>>>         Honza Hubicka  <hubicka@ucw.cz>
>>>>
>>>>     * ipa-utils.h (polymorphic_type_binfo_p): Function marked external
>>>>     instead of static.
>>>>     * ipa-devirt.c (polymorphic_type_binfo_p): Likewise.
>>>>     * ipa-prop.h (count_formal_params): Likewise.
>>>>     * ipa-prop.c (count_formal_params): Likewise.
>>>>     * ipa-utils.c (ipa_merge_profiles): Be more tolerant if we merge
>>>>     profiles for semantically equivalent functions.
>>>>     * passes.c (do_per_function): If we load body of a function
>>>> during WPA,
>>>>     this condition should behave same.
>>>>     * varpool.c (ctor_for_folding): More tolerant assert for variable
>>>>     aliases created during WPA.
>>> Presumably we don't have any useful way to merge the cases where we
>>> have provides for SRC & DST in ipa_merge_profiles or even to guess
>>> which is more useful when presented with both?  Does it make sense to
>>> log this into a debugging file when we drop one?
>> Hello,
>>     this merge function was written by Honza, what do you think Honza
>> about this note?
>>
>>> I think this patch is fine.  If adding logging makes sense, then feel
>>> free to do so and consider that trivial change pre-approved.
>> I made a small change to this patch, where I moved
>> 'gsi_next_nonvirtual_phi' from the pass to gimple-iterator.h.
>>
>> Ready for trunk with this change?
> Yes.  I think with the exception of patch #3/5 everything looks good. I'll try to get another pass over #3 this week.  What I looked at last week was pretty good; I'm pretty confident this will be wrapped up shortly.
>
> If #1/#2 make sense to install independent of #3, go ahead.  #4/#5 are obviously dependent on #3.
>
> Jeff

Hello,
    thank you for approval, this final version removes few hunks that are not needed any more. Changes are just cosmetic and I will commit the patch at the beginning of next week.

Thanks,
Martin


[-- Attachment #2: ipa-icf-part-2.patch --]
[-- Type: text/x-patch, Size: 3064 bytes --]

diff --git a/gcc/gimple-iterator.h b/gcc/gimple-iterator.h
index 909d58b..47168b9 100644
--- a/gcc/gimple-iterator.h
+++ b/gcc/gimple-iterator.h
@@ -281,6 +281,30 @@ gsi_last_nondebug_bb (basic_block bb)
   return i;
 }
 
+/* Iterates I statement iterator to the next non-virtual statement.  */
+
+static inline void
+gsi_next_nonvirtual_phi (gimple_stmt_iterator *i)
+{
+  gimple phi;
+
+  if (gsi_end_p (*i))
+    return;
+
+  phi = gsi_stmt (*i);
+  gcc_assert (phi != NULL);
+
+  while (virtual_operand_p (gimple_phi_result (phi)))
+    {
+      gsi_next (i);
+
+      if (gsi_end_p (*i))
+	return;
+
+      phi = gsi_stmt (*i);
+    }
+}
+
 /* Return the basic block associated with this iterator.  */
 
 static inline basic_block
diff --git a/gcc/ipa-prop.c b/gcc/ipa-prop.c
index 40f696b..aecba07 100644
--- a/gcc/ipa-prop.c
+++ b/gcc/ipa-prop.c
@@ -211,7 +211,7 @@ ipa_populate_param_decls (struct cgraph_node *node,
 
 /* Return how many formal parameters FNDECL has.  */
 
-static inline int
+int
 count_formal_params (tree fndecl)
 {
   tree parm;
diff --git a/gcc/ipa-prop.h b/gcc/ipa-prop.h
index 8886e93..bc6249e 100644
--- a/gcc/ipa-prop.h
+++ b/gcc/ipa-prop.h
@@ -529,6 +529,7 @@ void ipa_free_all_edge_args (void);
 void ipa_free_all_structures_after_ipa_cp (void);
 void ipa_free_all_structures_after_iinln (void);
 void ipa_register_cgraph_hooks (void);
+int count_formal_params (tree fndecl);
 
 /* This function ensures the array of node param infos is big enough to
    accommodate a structure for all nodes and reallocates it if not.  */
diff --git a/gcc/ipa-utils.c b/gcc/ipa-utils.c
index c191210..d58b170 100644
--- a/gcc/ipa-utils.c
+++ b/gcc/ipa-utils.c
@@ -660,13 +660,8 @@ ipa_merge_profiles (struct cgraph_node *dst,
   if (dst->tp_first_run > src->tp_first_run && src->tp_first_run)
     dst->tp_first_run = src->tp_first_run;
 
-  if (src->profile_id)
-    {
-      if (!dst->profile_id)
-	dst->profile_id = src->profile_id;
-      else
-	gcc_assert (src->profile_id == dst->profile_id);
-    }
+  if (src->profile_id && !dst->profile_id)
+    dst->profile_id = src->profile_id;
 
   if (!dst->count)
     return;
diff --git a/gcc/passes.c b/gcc/passes.c
index 61b4c12..bae302b 100644
--- a/gcc/passes.c
+++ b/gcc/passes.c
@@ -1478,7 +1478,7 @@ do_per_function (void (*callback) (function *, void *data), void *data)
     {
       struct cgraph_node *node;
       FOR_EACH_DEFINED_FUNCTION (node)
-	if (node->analyzed && gimple_has_body_p (node->decl)
+	if (node->analyzed && (gimple_has_body_p (node->decl) && !in_lto_p)
 	    && (!node->clone_of || node->decl != node->clone_of->decl))
 	  callback (DECL_STRUCT_FUNCTION (node->decl), data);
     }
diff --git a/gcc/varpool.c b/gcc/varpool.c
index 04ce714..5662985 100644
--- a/gcc/varpool.c
+++ b/gcc/varpool.c
@@ -397,6 +397,7 @@ ctor_for_folding (tree decl)
   if (decl != real_decl)
     {
       gcc_assert (!DECL_INITIAL (decl)
+		  || (node->alias && varpool_alias_target (node) == real_node)
 		  || DECL_INITIAL (decl) == error_mark_node);
       if (node->weakref)
 	{

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 3/5] IPA ICF pass
  2014-07-05 22:53       ` Jan Hubicka
@ 2014-07-17 15:23         ` Martin Liška
  2014-09-26 12:20           ` Martin Liška
  0 siblings, 1 reply; 70+ messages in thread
From: Martin Liška @ 2014-07-17 15:23 UTC (permalink / raw)
  To: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 2162 bytes --]


On 07/06/2014 12:53 AM, Jan Hubicka wrote:
>> On Fri, 20 Jun 2014, Trevor Saunders wrote:
>>>> +@item -fipa-icf
>>>> +@opindex fipa-icf
>>>> +Perform Identical Code Folding for functions and read-only variables.
> I would perhaps explicitly say that the optimizations reduce code size
> and may disturb unwind stacks by replacing a function by equivalent
> one with different name.
>>>> +Behavior is similar to Gold Linker ICF optimization. Symbols proved
> Perhaps tell a bit more here. The optimization works more effectively with link
> time optimization enabled and that the Gold and GCC ICF works on different
> levels and thus are not equivalent optimizations - there are equivallences that
> are found only by GCC and equivalences found only by Gold.
>
>>> +as semantically equivalent are redirected to corresponding symbol. The pass
>>>> +sensitively decides for usage of alias, thunk or local redirection.
>>>> +This flag is enabled by default at @option{-O2}.
> Probably at -Os too.
>> I found this a bit hard to read/understand.
>>
>> Perhaps first describe what it does and then, before "This flag is
>> enabled..." note that "This is similar to the ICF optimization performed
>> by the Gold linker".
>> "Symbols proved" (plural) vs "to corresponding symbol" seems to miss
>> an an "a" as in "a corresponding symbol".  Alas, how is that one
>> determined?  Is this more "...are merged into one", from the user's
>> perspective?
>>
>> What does it mean to "sensitively decide for usage of alias, thunk,
>> or local redirection"?
> I think this is just a technical detail of the implementation.  I would not put that
> into user manual.  It means that for some functions you can make alias, for others
> you need thunk (so addresses stay different)
>> Gerald

Hello,
    there's updated version of patch that newly uses devirtualization machinery to identify polymorphic types that can potentially break ICF (There are such examples in Firefox).

Apart from that, I did many small updates, incorporated Trevor's comments and I tried to improve documentation entry for the pass.
Patch has been tested for Firefox and Inkscape with LTO.

Thanks,
Martin

[-- Attachment #2: ipa-icf-part-3.patch --]
[-- Type: text/x-patch, Size: 115474 bytes --]

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 187e6b6..2722a37 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1282,6 +1282,8 @@ OBJS = \
 	ipa-profile.o \
 	ipa-prop.o \
 	ipa-pure-const.o \
+	ipa-icf.o \
+	ipa-icf-gimple.o \
 	ipa-reference.o \
 	ipa-ref.o \
 	ipa-utils.o \
diff --git a/gcc/common.opt b/gcc/common.opt
index a385ee0..70dc9d1 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -1426,6 +1426,18 @@ fipa-pure-const
 Common Report Var(flag_ipa_pure_const) Init(0) Optimization
 Discover pure and const functions
 
+fipa-icf
+Common Report Var(flag_ipa_icf) Optimization
+Perform Identical Code Folding for functions and read-only variables
+
+fipa-icf-functions
+Common Report Var(flag_ipa_icf_functions) Optimization
+Perform Identical Code Folding for functions
+
+fipa-icf-variables
+Common Report Var(flag_ipa_icf_variables) Optimization
+Perform Identical Code Folding for variables
+
 fipa-reference
 Common Report Var(flag_ipa_reference) Init(0) Optimization
 Discover readonly and non addressable static variables
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index b5e8d98..ab707e3c 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -378,7 +378,7 @@ Objective-C and Objective-C++ Dialects}.
 -fif-conversion2 -findirect-inlining @gol
 -finline-functions -finline-functions-called-once -finline-limit=@var{n} @gol
 -finline-small-functions -fipa-cp -fipa-cp-clone @gol
--fipa-pta -fipa-profile -fipa-pure-const -fipa-reference @gol
+-fipa-pta -fipa-profile -fipa-pure-const -fipa-reference -fipa-icf @gol
 -fira-algorithm=@var{algorithm} @gol
 -fira-region=@var{region} -fira-hoist-pressure @gol
 -fira-loop-pressure -fno-ira-share-save-slots @gol
@@ -7008,6 +7008,7 @@ also turns on the following optimization flags:
 -finline-small-functions @gol
 -findirect-inlining @gol
 -fipa-sra @gol
+-fipa-icf @gol
 -fisolate-erroneous-paths-dereference @gol
 -foptimize-sibling-calls @gol
 -fpartial-inlining @gol
@@ -7939,6 +7940,19 @@ it may significantly increase code size
 (see @option{--param ipcp-unit-growth=@var{value}}).
 This flag is enabled by default at @option{-O3}.
 
+@item -fipa-icf
+@opindex fipa-icf
+Perform Identical Code Folding for functions and read-only variables.
+The optimization reduces code size and may disturb unwind stacks by replacing
+a function by equivalent one with a different name. The optimization works
+more effectively with link time optimization enabled.
+
+Nevertheless the behavior is similar to Gold Linker ICF optimization, GCC ICF
+works on different levels and thus the optimizations are not same - there are
+equivalences that are found only by GCC and equivalences found only by Gold.
+
+This flag is enabled by default at @option{-O2}.
+
 @item -fisolate-erroneous-paths-dereference
 Detect paths which trigger erroneous or undefined behaviour due to
 dereferencing a NULL pointer.  Isolate those paths from the main control
diff --git a/gcc/ipa-icf-gimple.c b/gcc/ipa-icf-gimple.c
new file mode 100644
index 0000000..8dfcfba
--- /dev/null
+++ b/gcc/ipa-icf-gimple.c
@@ -0,0 +1,384 @@
+/* Interprocedural Identical Code Folding pass
+   Copyright (C) 2014 Free Software Foundation, Inc.
+
+   Contributed by Jan Hubicka <hubicka@ucw.cz> and Martin Liska <mliska@suse.cz>
+
+This file is part of GCC.
+
+GCC 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, or (at your option) any later
+version.
+
+GCC 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 GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tree.h"
+#include "basic-block.h"
+#include "tree-ssa-alias.h"
+#include "internal-fn.h"
+#include "gimple-expr.h"
+#include "is-a.h"
+#include "gimple.h"
+#include "expr.h"
+#include "gimple-iterator.h"
+#include "gimple-ssa.h"
+#include "tree-cfg.h"
+#include "stringpool.h"
+#include "tree-dfa.h"
+#include "tree-pass.h"
+#include "gimple-pretty-print.h"
+#include "cfgloop.h"
+#include "except.h"
+#include "data-streamer.h"
+#include "ipa-utils.h"
+#include "ipa-icf.h"
+
+namespace ipa_icf {
+
+/* Basic block equivalence comparison function that returns true if
+   basic blocks BB1 and BB2 (from functions FUNC1 and FUNC2) correspond.  */
+
+bool
+sem_function::compare_bb (sem_bb *bb1, sem_bb *bb2, tree func1, tree func2)
+{
+  unsigned i;
+  gimple_stmt_iterator gsi1, gsi2;
+  gimple s1, s2;
+
+  if (bb1->nondbg_stmt_count != bb2->nondbg_stmt_count
+      || bb1->edge_count != bb2->edge_count)
+    return RETURN_FALSE ();
+
+  gsi1 = gsi_start_bb (bb1->bb);
+  gsi2 = gsi_start_bb (bb2->bb);
+
+  for (i = 0; i < bb1->nondbg_stmt_count; i++)
+    {
+      if (is_gimple_debug (gsi_stmt (gsi1)))
+	gsi_next_nondebug (&gsi1);
+
+      if (is_gimple_debug (gsi_stmt (gsi2)))
+	gsi_next_nondebug (&gsi2);
+
+      s1 = gsi_stmt (gsi1);
+      s2 = gsi_stmt (gsi2);
+
+      if (gimple_code (s1) != gimple_code (s2))
+	return RETURN_FALSE_WITH_MSG ("gimple codes are different");
+
+      switch (gimple_code (s1))
+	{
+	case GIMPLE_CALL:
+	  if (!compare_gimple_call (s1, s2, func1, func2))
+	    return RETURN_DIFFERENT_STMTS (s1, s2, "GIMPLE_CALL");
+	  break;
+	case GIMPLE_ASSIGN:
+	  if (!compare_gimple_assign (s1, s2, func1, func2))
+	    return RETURN_DIFFERENT_STMTS (s1, s2, "GIMPLE_ASSIGN");
+	  break;
+	case GIMPLE_COND:
+	  if (!compare_gimple_cond (s1, s2, func1, func2))
+	    return RETURN_DIFFERENT_STMTS (s1, s2, "GIMPLE_COND");
+	  break;
+	case GIMPLE_SWITCH:
+	  if (!compare_gimple_switch (s1, s2, func1, func2))
+	    return RETURN_DIFFERENT_STMTS (s1, s2, "GIMPLE_SWITCH");
+	  break;
+	case GIMPLE_DEBUG:
+	case GIMPLE_EH_DISPATCH:
+	  break;
+	case GIMPLE_RESX:
+	  if (!compare_gimple_resx (s1, s2))
+	    return RETURN_DIFFERENT_STMTS (s1, s2, "GIMPLE_RESX");
+	  break;
+	case GIMPLE_LABEL:
+	  if (!compare_gimple_label (s1, s2, func1, func2))
+	    return RETURN_DIFFERENT_STMTS (s1, s2, "GIMPLE_LABEL");
+	  break;
+	case GIMPLE_RETURN:
+	  if (!compare_gimple_return (s1, s2, func1, func2))
+	    return RETURN_DIFFERENT_STMTS (s1, s2, "GIMPLE_RETURN");
+	  break;
+	case GIMPLE_GOTO:
+	  if (!compare_gimple_goto (s1, s2, func1, func2))
+	    return RETURN_DIFFERENT_STMTS (s1, s2, "GIMPLE_GOTO");
+	  break;
+	case GIMPLE_ASM:
+	  if (!compare_gimple_asm (s1, s2))
+	    return RETURN_DIFFERENT_STMTS (s1, s2, "GIMPLE_ASM");
+	  break;
+	case GIMPLE_PREDICT:
+	case GIMPLE_NOP:
+	  return true;
+	default:
+	  return RETURN_FALSE_WITH_MSG ("Unknown GIMPLE code reached");
+	}
+
+      gsi_next (&gsi1);
+      gsi_next (&gsi2);
+    }
+
+  return true;
+}
+
+
+/* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
+   call statements are semantically equivalent.  */
+
+bool
+sem_function::compare_gimple_call (gimple s1, gimple s2, tree func1, tree func2)
+{
+  unsigned i;
+  tree t1, t2;
+
+  if (gimple_call_num_args (s1) != gimple_call_num_args (s2))
+    return false;
+
+  t1 = gimple_call_fndecl (s1);
+  t2 = gimple_call_fndecl (s2);
+
+  /* Function pointer variables are not supported yet.  */
+  if (t1 == NULL || t2 == NULL)
+    {
+      if (!compare_operand (t1, t2, func1, func2))
+	return RETURN_FALSE();
+    }
+  else if (!compare_function_decl (t1, t2))
+    return false;
+
+  /* Checking of argument.  */
+  for (i = 0; i < gimple_call_num_args (s1); ++i)
+    {
+      t1 = gimple_call_arg (s1, i);
+      t2 = gimple_call_arg (s2, i);
+
+      if (!compare_operand (t1, t2, func1, func2))
+	return false;
+    }
+
+  /* Return value checking.  */
+  t1 = gimple_get_lhs (s1);
+  t2 = gimple_get_lhs (s2);
+
+  return compare_operand (t1, t2, func1, func2);
+}
+
+/* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
+   assignment statements are semantically equivalent.  */
+
+bool
+sem_function::compare_gimple_assign (gimple s1, gimple s2, tree func1,
+				     tree func2)
+{
+  tree arg1, arg2;
+  tree_code code1, code2;
+  unsigned i;
+
+  code1 = gimple_expr_code (s1);
+  code2 = gimple_expr_code (s2);
+
+  if (code1 != code2)
+    return false;
+
+  code1 = gimple_assign_rhs_code (s1);
+  code2 = gimple_assign_rhs_code (s2);
+
+  if (code1 != code2)
+    return false;
+
+  for (i = 0; i < gimple_num_ops (s1); i++)
+    {
+      arg1 = gimple_op (s1, i);
+      arg2 = gimple_op (s2, i);
+
+      if (!compare_operand (arg1, arg2, func1, func2))
+	return false;
+    }
+
+  return true;
+}
+
+/* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
+   condition statements are semantically equivalent.  */
+
+bool
+sem_function::compare_gimple_cond (gimple s1, gimple s2, tree func1, tree func2)
+{
+  tree t1, t2;
+  tree_code code1, code2;
+
+  code1 = gimple_expr_code (s1);
+  code2 = gimple_expr_code (s2);
+
+  if (code1 != code2)
+    return false;
+
+  t1 = gimple_cond_lhs (s1);
+  t2 = gimple_cond_lhs (s2);
+
+  if (!compare_operand (t1, t2, func1, func2))
+    return false;
+
+  t1 = gimple_cond_rhs (s1);
+  t2 = gimple_cond_rhs (s2);
+
+  return compare_operand (t1, t2, func1, func2);
+}
+
+/* Verifies that tree labels T1 and T2 correspond in FUNC1 and FUNC2.  */
+
+bool
+sem_function::compare_tree_ssa_label (tree t1, tree t2, tree func1, tree func2)
+{
+  return compare_operand (t1, t2, func1, func2);
+}
+
+/* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
+   label statements are semantically equivalent.  */
+
+bool
+sem_function::compare_gimple_label (gimple g1, gimple g2, tree func1,
+				    tree func2)
+{
+  tree t1 = gimple_label_label (g1);
+  tree t2 = gimple_label_label (g2);
+
+  return compare_tree_ssa_label (t1, t2, func1, func2);
+}
+
+/* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
+   switch statements are semantically equivalent.  */
+
+bool
+sem_function::compare_gimple_switch (gimple g1, gimple g2, tree func1,
+				     tree func2)
+{
+  unsigned lsize1, lsize2, i;
+  tree t1, t2, low1, low2, high1, high2;
+
+  lsize1 = gimple_switch_num_labels (g1);
+  lsize2 = gimple_switch_num_labels (g2);
+
+  if (lsize1 != lsize2)
+    return false;
+
+  t1 = gimple_switch_index (g1);
+  t2 = gimple_switch_index (g2);
+
+  if (TREE_CODE (t1) != SSA_NAME || TREE_CODE(t2) != SSA_NAME)
+    return false;
+
+  if (!compare_operand (t1, t2, func1, func2))
+    return false;
+
+  for (i = 0; i < lsize1; i++)
+    {
+      low1 = CASE_LOW (gimple_switch_label (g1, i));
+      low2 = CASE_LOW (gimple_switch_label (g2, i));
+
+      if ((low1 != NULL) != (low2 != NULL)
+	  || (low1 && low2 && TREE_INT_CST_LOW (low1) != TREE_INT_CST_LOW (low2)))
+	return false;
+
+      high1 = CASE_HIGH (gimple_switch_label (g1, i));
+      high2 = CASE_HIGH (gimple_switch_label (g2, i));
+
+      if ((high1 != NULL) != (high2 != NULL)
+	  || (high1 && high2
+	      && TREE_INT_CST_LOW (high1) != TREE_INT_CST_LOW (high2)))
+	return false;
+    }
+
+  return true;
+}
+
+/* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
+   return statements are semantically equivalent.  */
+
+bool
+sem_function::compare_gimple_return (gimple g1, gimple g2, tree func1,
+				     tree func2)
+{
+  tree t1, t2;
+
+  t1 = gimple_return_retval (g1);
+  t2 = gimple_return_retval (g2);
+
+  /* Void return type.  */
+  if (t1 == NULL && t2 == NULL)
+    return true;
+  else
+    return compare_operand (t1, t2, func1, func2);
+}
+
+/* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
+   goto statements are semantically equivalent.  */
+
+bool
+sem_function::compare_gimple_goto (gimple g1, gimple g2, tree func1, tree func2)
+{
+  tree dest1, dest2;
+
+  dest1 = gimple_goto_dest (g1);
+  dest2 = gimple_goto_dest (g2);
+
+  if (TREE_CODE (dest1) != TREE_CODE (dest2) || TREE_CODE (dest1) != SSA_NAME)
+    return false;
+
+  return compare_operand (dest1, dest2, func1, func2);
+}
+
+/* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
+   resx statements are semantically equivalent.  */
+
+bool
+sem_function::compare_gimple_resx (gimple g1, gimple g2)
+{
+  return gimple_resx_region (g1) == gimple_resx_region (g2);
+}
+
+/* Verifies for given GIMPLEs S1 and S2 that ASM statements are equivalent.
+   For the beginning, the pass only supports equality for
+   '__asm__ __volatile__ ("", "", "", "memory")'.  */
+
+bool
+sem_function::compare_gimple_asm (gimple g1, gimple g2)
+{
+  if (gimple_asm_volatile_p (g1) != gimple_asm_volatile_p (g2))
+    return false;
+
+  if (gimple_asm_ninputs (g1) || gimple_asm_ninputs (g2))
+    return false;
+
+  if (gimple_asm_noutputs (g1) || gimple_asm_noutputs (g2))
+    return false;
+
+  if (gimple_asm_nlabels (g1) || gimple_asm_nlabels (g2))
+    return false;
+
+  if (gimple_asm_nclobbers (g1) != gimple_asm_nclobbers (g2))
+    return false;
+
+  for (unsigned i = 0; i < gimple_asm_nclobbers (g1); i++)
+    {
+      tree clobber1 = TREE_VALUE (gimple_asm_clobber_op (g1, i));
+      tree clobber2 = TREE_VALUE (gimple_asm_clobber_op (g2, i));
+
+      if (!operand_equal_p (clobber1, clobber2, OEP_ONLY_CONST))
+	return false;
+    }
+
+  return true;
+}
+
+} // ipa_icf namespace
diff --git a/gcc/ipa-icf.c b/gcc/ipa-icf.c
new file mode 100644
index 0000000..4533192
--- /dev/null
+++ b/gcc/ipa-icf.c
@@ -0,0 +1,2752 @@
+/* Interprocedural Identical Code Folding pass
+   Copyright (C) 2014 Free Software Foundation, Inc.
+
+   Contributed by Jan Hubicka <hubicka@ucw.cz> and Martin Liska <mliska@suse.cz>
+
+This file is part of GCC.
+
+GCC 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, or (at your option) any later
+version.
+
+GCC 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 GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+/* Interprocedural Identical Code Folding for functions and
+   read-only variables.
+
+   The goal of this transformation is to discover functions and read-only
+   variables which do have exactly the same semantics.
+
+   In case of functions,
+   we could either create a virtual clone or do a simple function wrapper
+   that will call equivalent function. If the function is just locally visible,
+   all function calls can be redirected. For read-only variables, we create
+   aliases if possible.
+
+   Optimization pass arranges as follows:
+   1) All functions and read-only variables are visited and internal
+      data structure, either sem_function or sem_variables is created.
+   2) For every symbol from the previous step, VAR_DECL and FUNCTION_DECL are
+      saved and matched to corresponding sem_items.
+   3) These declaration are ignored for equality check and are solved
+      by Value Numbering algorithm published by Alpert, Zadeck in 1992.
+   4) We compute hash value for each symbol.
+   5) Congruence classes are created based on hash value. If hash value are
+      equal, equals function is called and symbols are deeply compared.
+      We must prove that all SSA names, declarations and other items
+      correspond.
+   6) Value Numbering is executed for these classes. At the end of the process
+      all symbol members in remaining classes can be merged.
+   7) Merge operation creates alias in case of read-only variables. For
+      callgraph node, we must decide if we can redirect local calls,
+      create an alias or a thunk.
+
+*/
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tree.h"
+#include "basic-block.h"
+#include "tree-ssa-alias.h"
+#include "internal-fn.h"
+#include "gimple-expr.h"
+#include "is-a.h"
+#include "gimple.h"
+#include "expr.h"
+#include "gimple-iterator.h"
+#include "gimple-ssa.h"
+#include "tree-cfg.h"
+#include "tree-phinodes.h"
+#include "stringpool.h"
+#include "tree-ssanames.h"
+#include "tree-dfa.h"
+#include "tree-pass.h"
+#include "gimple-pretty-print.h"
+#include "ipa-inline.h"
+#include "cfgloop.h"
+#include "except.h"
+#include "hash-table.h"
+#include "coverage.h"
+#include "pointer-set.h"
+#include "attribs.h"
+#include "print-tree.h"
+#include "lto-streamer.h"
+#include "data-streamer.h"
+#include "ipa-utils.h"
+#include "ipa-icf.h"
+
+namespace ipa_icf {
+
+/* Initialize internal structures according to given number of
+   source and target SSA names. The number of source names is SSA_SOURCE,
+   respectively SSA_TARGET.  */
+
+func_checker::func_checker (unsigned ssa_source, unsigned ssa_target)
+{
+  m_source_ssa_names.create (ssa_source);
+  m_target_ssa_names.create (ssa_target);
+
+  for (unsigned int i = 0; i < ssa_source; i++)
+    m_source_ssa_names.safe_push (-1);
+
+  for (unsigned int i = 0; i < ssa_target; i++)
+    m_target_ssa_names.safe_push (-1);
+}
+
+/* Memory release routine.  */
+
+func_checker::~func_checker ()
+{
+  m_source_ssa_names.release();
+  m_target_ssa_names.release();
+}
+
+/* Verifies that trees T1 and T2 are equivalent from perspective of ICF.  */
+
+bool
+func_checker::compare_ssa_name (tree t1, tree t2)
+{
+  unsigned i1 = SSA_NAME_VERSION (t1);
+  unsigned i2 = SSA_NAME_VERSION (t2);
+
+  if (m_source_ssa_names[i1] == -1)
+    m_source_ssa_names[i1] = i2;
+  else if (m_source_ssa_names[i1] != (int) i2)
+    return false;
+
+  if(m_target_ssa_names[i2] == -1)
+    m_target_ssa_names[i2] = i1;
+  else if (m_target_ssa_names[i2] != (int) i1)
+    return false;
+
+  return true;
+}
+
+/* Verification function for edges E1 and E2.  */
+
+bool
+func_checker::compare_edge (edge e1, edge e2)
+{
+  if (e1->flags != e2->flags)
+    return false;
+
+  bool existed_p;
+
+  edge &slot = m_edge_map.get_or_insert (e1, &existed_p);
+  if (existed_p)
+    return RETURN_WITH_DEBUG (slot == e2);
+  else
+    slot = e2;
+
+  return true;
+}
+
+/* Verification function for declaration trees T1 and T2 that
+   come from functions FUNC1 and FUNC2.  */
+
+bool
+func_checker::compare_decl (tree t1, tree t2, tree func1, tree func2)
+{
+  if (!auto_var_in_fn_p (t1, func1) || !auto_var_in_fn_p (t2, func2))
+    return RETURN_WITH_DEBUG (t1 == t2);
+
+  if (!sem_item::types_are_compatible_p (TREE_TYPE (t1), TREE_TYPE (t2)))
+    return RETURN_FALSE ();
+
+  bool existed_p;
+
+  tree &slot = m_decl_map.get_or_insert (t1, &existed_p);
+  if (existed_p)
+    return RETURN_WITH_DEBUG (slot == t2);
+  else
+    slot = t2;
+
+  return true;
+}
+
+/* Constructor for key value pair, where _ITEM is key and _INDEX is a target.  */
+
+sem_usage_pair::sem_usage_pair (sem_item *_item, unsigned int _index):
+  item (_item), index (_index)
+{
+}
+
+/* Semantic item constructor for a node of _TYPE, where STACK is used
+   for bitmap memory allocation.  */
+
+sem_item::sem_item (sem_item_type _type,
+		    bitmap_obstack *stack): type(_type), hash(0)
+{
+  setup (stack);
+}
+
+/* Semantic item constructor for a node of _TYPE, where STACK is used
+   for bitmap memory allocation. The item is based on symtab node _NODE
+   with computed _HASH.  */
+
+sem_item::sem_item (sem_item_type _type, struct symtab_node *_node,
+		    hashval_t _hash, bitmap_obstack *stack): type(_type),
+  node (_node), hash (_hash)
+{
+  decl = node->decl;
+  setup (stack);
+}
+
+/* Initialize internal data structures. Bitmap STACK is used for
+   bitmap memory allocation process.  */
+
+void
+sem_item::setup (bitmap_obstack *stack)
+{
+  gcc_checking_assert (node);
+
+  refs.create (0);
+  tree_refs.create (0);
+  usages.create (0);
+  tree_refs_set = pointer_set_create ();
+  usage_index_bitmap = BITMAP_ALLOC (stack);
+}
+
+sem_item::~sem_item ()
+{
+  if (tree_refs_set)
+    pointer_set_destroy (tree_refs_set);
+
+  for (unsigned i = 0; i < usages.length (); i++)
+    delete usages[i];
+
+  refs.release ();
+  tree_refs.release ();
+  usages.release ();
+
+  BITMAP_FREE (usage_index_bitmap);
+}
+
+/* Dump function for debugging purpose.  */
+
+DEBUG_FUNCTION void
+sem_item::dump (void)
+{
+  if (dump_file)
+    {
+      fprintf (dump_file, "[%s] %s (%u) (tree:%p)\n", type == FUNC ? "func" : "var",
+	       name(), node->order, (void *) node->decl);
+      fprintf (dump_file, "  hash: %u\n", get_hash ());
+      fprintf (dump_file, "  references: ");
+
+      for (unsigned i = 0; i < refs.length (); i++)
+	fprintf (dump_file, "%s%s ", refs[i]->name (),
+		 i < refs.length() - 1 ? "," : "");
+
+      fprintf (dump_file, "\n");
+    }
+}
+
+/* Return true if types are compatible from perspective of ICF.  */
+bool sem_item::types_are_compatible_p (tree t1, tree t2, bool first_argument)
+{
+  if (TREE_CODE (t1) != TREE_CODE (t2))
+    return RETURN_FALSE_WITH_MSG ("different tree types");
+
+  if (!types_compatible_p (t1, t2))
+    return RETURN_FALSE_WITH_MSG ("types are not compatible");
+
+  if (get_alias_set (t1) != get_alias_set (t2))
+    return RETURN_FALSE_WITH_MSG ("alias sets are different");
+
+  /* We call contains_polymorphic_type_p with this pointer type.  */
+  if (first_argument && TREE_CODE (t1) == POINTER_TYPE)
+    {
+      t1 = TREE_TYPE (t1);
+      t2 = TREE_TYPE (t2);
+    }
+
+  if (contains_polymorphic_type_p (t1) || contains_polymorphic_type_p (t2))
+    {
+      if (!contains_polymorphic_type_p (t1) || !contains_polymorphic_type_p (t2))
+	return RETURN_FALSE_WITH_MSG ("one type is not polymorphic");
+
+      if (TYPE_MAIN_VARIANT (t1) != TYPE_MAIN_VARIANT (t2))
+	return RETURN_FALSE_WITH_MSG ("type variants are different for "
+				      "polymorphic type");
+    }
+
+  return true;
+}
+
+
+/* Semantic function constructor that uses STACK as bitmap memory stack.  */
+
+sem_function::sem_function (bitmap_obstack *stack): sem_item (FUNC, stack),
+  m_checker (NULL), m_compared_func (NULL)
+{
+  arg_types.create (0);
+  bb_sizes.create (0);
+  bb_sorted.create (0);
+}
+
+/*  Constructor based on callgraph node _NODE with computed hash _HASH.
+    Bitmap STACK is used for memory allocation.  */
+sem_function::sem_function (cgraph_node *node, hashval_t hash,
+			    bitmap_obstack *stack):
+  sem_item (FUNC, node, hash, stack),
+  m_checker (NULL), m_compared_func (NULL)
+{
+  arg_types.create (0);
+  bb_sizes.create (0);
+  bb_sorted.create (0);
+}
+
+sem_function::~sem_function ()
+{
+  for (unsigned i = 0; i < bb_sorted.length (); i++)
+    free (bb_sorted[i]);
+
+  arg_types.release ();
+  bb_sizes.release ();
+  bb_sorted.release ();
+}
+
+/* Calculates hash value based on a BASIC_BLOCK.  */
+
+hashval_t
+sem_function::get_bb_hash (const sem_bb *basic_block)
+{
+  hashval_t hash = basic_block->nondbg_stmt_count;
+  hash = iterative_hash_object (basic_block->edge_count, hash);
+
+  return hash;
+}
+
+/* References independent hash function.  */
+
+hashval_t
+sem_function::get_hash (void)
+{
+  if(!hash)
+    {
+      hash = 177454; /* Random number for function type.  */
+
+      hash = iterative_hash_object (arg_count, hash);
+      hash = iterative_hash_object (cfg_checksum, hash);
+      hash = iterative_hash_object (gcode_hash, hash);
+
+      for (unsigned i = 0; i < bb_sorted.length (); i++)
+	hash = iterative_hash_object (hash, get_bb_hash (bb_sorted[i]));
+
+      for (unsigned i = 0; i < bb_sizes.length (); i++)
+	hash = iterative_hash_object (bb_sizes[i], hash);
+    }
+
+  return hash;
+}
+
+/* Fast equality function based on knowledge known in WPA.  */
+
+bool
+sem_function::equals_wpa (sem_item *item)
+{
+  gcc_assert (item->type == FUNC);
+
+  m_compared_func = static_cast<sem_function *> (item);
+
+  if (arg_types.length () != m_compared_func->arg_types.length ())
+    return RETURN_FALSE_WITH_MSG ("different number of arguments");
+
+  /* Checking types of arguments.  */
+  for (unsigned i = 0; i < arg_types.length (); i++)
+    {
+      /* This guard is here for function pointer with attributes (pr59927.c).  */
+      if (!arg_types[i] || !m_compared_func->arg_types[i])
+	return RETURN_FALSE_WITH_MSG ("NULL arg type");
+
+      if (!types_are_compatible_p (arg_types[i], m_compared_func->arg_types[i],
+				   i == 0))
+	return RETURN_FALSE_WITH_MSG ("argument type is different");
+    }
+
+  /* Result type checking.  */
+  if (!types_are_compatible_p (result_type, m_compared_func->result_type))
+    return RETURN_FALSE_WITH_MSG ("result types are different");
+
+  return true;
+}
+
+/* Returns true if the item equals to ITEM given as argument.  */
+
+bool
+sem_function::equals (sem_item *item)
+{
+  gcc_assert (item->type == FUNC);
+  bool eq = equals_private (item);
+
+  if (m_checker != NULL)
+    {
+      delete m_checker;
+      m_checker = NULL;
+    }
+
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    fprintf (dump_file,
+	     "Equals called for:%s:%s (%u:%u) (%s:%s) with result: %s\n\n",
+	     name(), item->name (), node->order, item->node->order, asm_name (),
+	     item->asm_name (), eq ? "true" : "false");
+
+  return eq;
+}
+
+/* Processes function equality comparison.  */
+
+bool
+sem_function::equals_private (sem_item *item)
+{
+  if (item->type != FUNC)
+    return false;
+
+  basic_block bb1, bb2;
+  edge e1, e2;
+  edge_iterator ei1, ei2;
+  int *bb_dict = NULL;
+  bool result = true;
+  tree arg1, arg2;
+
+  m_compared_func = static_cast<sem_function *> (item);
+
+  gcc_assert (decl != item->decl);
+
+  if (bb_sorted.length () != m_compared_func->bb_sorted.length ()
+      || edge_count != m_compared_func->edge_count
+      || cfg_checksum != m_compared_func->cfg_checksum)
+    return RETURN_FALSE ();
+
+  if (!equals_wpa (item))
+    return false;
+
+  /* Checking function arguments.  */
+  tree decl1 = DECL_ATTRIBUTES (decl);
+  tree decl2 = DECL_ATTRIBUTES (m_compared_func->decl);
+
+  while (decl1)
+    {
+      if (decl2 == NULL)
+	return RETURN_FALSE ();
+
+      if (get_attribute_name (decl1) != get_attribute_name (decl2))
+	return RETURN_FALSE ();
+
+      tree attr_value1 = TREE_VALUE (decl1);
+      tree attr_value2 = TREE_VALUE (decl2);
+
+      if (attr_value1 && attr_value2)
+	{
+	  bool ret = compare_operand (TREE_VALUE (attr_value1),
+				      TREE_VALUE (attr_value2), decl,
+				      m_compared_func->decl);
+	  if (!ret)
+	    return RETURN_FALSE_WITH_MSG ("attribute values are different");
+	}
+      else if (!attr_value1 && !attr_value2)
+	{}
+      else
+	return RETURN_FALSE ();
+
+      decl1 = TREE_CHAIN (decl1);
+      decl2 = TREE_CHAIN (decl2);
+    }
+
+  if (decl1 != decl2)
+    return RETURN_FALSE();
+
+  m_checker = new func_checker (ssa_names_size, m_compared_func->ssa_names_size);
+
+  for (arg1 = DECL_ARGUMENTS (decl),
+       arg2 = DECL_ARGUMENTS (m_compared_func->decl);
+       arg1; arg1 = DECL_CHAIN (arg1), arg2 = DECL_CHAIN (arg2))
+    if (!m_checker->compare_decl (arg1, arg2, decl, m_compared_func->decl))
+      return RETURN_FALSE ();
+
+  /* Exception handling regions comparison.  */
+  if (!compare_eh_region (region_tree, m_compared_func->region_tree, decl,
+			  m_compared_func->decl))
+    return RETURN_FALSE();
+
+  /* Checking all basic blocks.  */
+  for (unsigned i = 0; i < bb_sorted.length (); ++i)
+    if(!compare_bb (bb_sorted[i], m_compared_func->bb_sorted[i], decl,
+		    m_compared_func->decl))
+      return RETURN_FALSE();
+
+  DUMP_MESSAGE ("All BBs are equal\n");
+
+  /* Basic block edges check.  */
+  for (unsigned i = 0; i < bb_sorted.length (); ++i)
+    {
+      bb_dict = XNEWVEC (int, bb_sorted.length () + 2);
+      memset (bb_dict, -1, (bb_sorted.length () + 2) * sizeof (int));
+
+      bb1 = bb_sorted[i]->bb;
+      bb2 = m_compared_func->bb_sorted[i]->bb;
+
+      ei2 = ei_start (bb2->preds);
+
+      for (ei1 = ei_start (bb1->preds); ei_cond (ei1, &e1); ei_next (&ei1))
+	{
+	  ei_cond (ei2, &e2);
+
+	  if (e1->flags != e2->flags)
+	    return RETURN_FALSE_WITH_MSG ("flags comparison returns false");
+
+	  if (!bb_dict_test (bb_dict, e1->src->index, e2->src->index))
+	    return RETURN_FALSE_WITH_MSG ("edge comparison returns false");
+
+	  if (!bb_dict_test (bb_dict, e1->dest->index, e2->dest->index))
+	    return RETURN_FALSE_WITH_MSG ("BB comparison returns false");
+
+	  if (!m_checker->compare_edge (e1, e2))
+	    return RETURN_FALSE_WITH_MSG ("edge comparison returns false");
+
+	  ei_next (&ei2);
+	}
+    }
+
+  /* Basic block PHI nodes comparison.  */
+  for (unsigned i = 0; i < bb_sorted.length (); i++)
+    if (!compare_phi_node (bb_sorted[i]->bb, m_compared_func->bb_sorted[i]->bb,
+			   decl, m_compared_func->decl))
+      return RETURN_FALSE_WITH_MSG ("PHI node comparison returns false");
+
+  return result;
+}
+
+/* Initialize references to another sem_item for tree T.  */
+
+void
+sem_function::init_refs_for_tree (tree t)
+{
+  switch (TREE_CODE (t))
+    {
+    case VAR_DECL:
+    case FUNCTION_DECL:
+      tree_refs.safe_push (t);
+      break;
+    case MEM_REF:
+    case ADDR_EXPR:
+    case OBJ_TYPE_REF:
+      init_refs_for_tree (TREE_OPERAND (t, 0));
+      break;
+    case FIELD_DECL:
+      init_refs_for_tree (DECL_FCONTEXT (t));
+      break;
+    default:
+      break;
+    }
+}
+
+/* Initialize references to another sem_item for gimple STMT of type assign.  */
+
+void
+sem_function::init_refs_for_assign (gimple stmt)
+{
+  if (gimple_num_ops (stmt) != 2)
+    return;
+
+  tree rhs = gimple_op (stmt, 1);
+
+  init_refs_for_tree (rhs);
+}
+
+/* Initialize references to other semantic functions/variables.  */
+
+void
+sem_function::init_refs (void)
+{
+  for (unsigned i = 0; i < bb_sorted.length (); i++)
+    {
+      basic_block bb = bb_sorted[i]->bb;
+
+      for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi);
+	   gsi_next (&gsi))
+	{
+	  gimple stmt = gsi_stmt (gsi);
+	  hashval_t code = (hashval_t) gimple_code (stmt);
+
+	  switch (code)
+	    {
+	    case GIMPLE_CALL:
+	      {
+		tree funcdecl = gimple_call_fndecl (stmt);
+
+		/* Function pointer variables are not supported yet.  */
+		if (funcdecl)
+		  tree_refs.safe_push (funcdecl);
+
+		break;
+	      }
+	    case GIMPLE_ASSIGN:
+	      init_refs_for_assign (stmt);
+	      break;
+	    default:
+	      break;
+	    }
+	}
+    }
+}
+
+/* Merges instance with an ALIAS_ITEM, where alias, thunk or redirection can
+   be applied.  */
+bool
+sem_function::merge (sem_item *alias_item)
+{
+  gcc_assert (alias_item->type == FUNC);
+
+  sem_function *alias_func = static_cast<sem_function *> (alias_item);
+
+  struct cgraph_node *original = get_node ();
+  struct cgraph_node *local_original = original;
+  struct cgraph_node *alias = alias_func->get_node ();
+  bool original_address_matters;
+  bool alias_address_matters;
+
+  bool create_thunk = false;
+  bool create_alias = false;
+  bool redirect_callers = false;
+  bool original_discardable = false;
+
+  /* Do not attempt to mix functions from different user sections;
+     we do not know what user intends with those.  */
+  if (((DECL_SECTION_NAME (original->decl) && !original->implicit_section)
+       || (DECL_SECTION_NAME (alias->decl) && !alias->implicit_section))
+      && DECL_SECTION_NAME (original->decl) != DECL_SECTION_NAME (alias->decl))
+    {
+      if (dump_file)
+	fprintf (dump_file,
+		 "Not unifying; original and alias are in different sections.\n\n");
+      return false;
+    }
+
+  /* See if original is in a section that can be discarded if the main
+     symbol is not used.  */
+  if (DECL_EXTERNAL (original->decl))
+    original_discardable = true;
+  if (original->resolution == LDPR_PREEMPTED_REG
+      || original->resolution == LDPR_PREEMPTED_IR)
+    original_discardable = true;
+  if (symtab_can_be_discarded (original))
+    original_discardable = true;
+
+  /* See if original and/or alias address can be compared for equality.  */
+  original_address_matters
+    = (!DECL_VIRTUAL_P (original->decl)
+       && (original->externally_visible
+	   || address_taken_from_non_vtable_p (original)));
+  alias_address_matters
+    = (!DECL_VIRTUAL_P (alias->decl)
+       && (alias->externally_visible
+	   || address_taken_from_non_vtable_p (alias)));
+
+  /* If alias and original can be compared for address equality, we need
+     to create a thunk.  Also we can not create extra aliases into discardable
+     section (or we risk link failures when section is discarded).  */
+  if ((original_address_matters
+       && alias_address_matters)
+      || original_discardable)
+    {
+      create_thunk = !stdarg_p (TREE_TYPE (alias->decl));
+      create_alias = false;
+      /* When both alias and original are not overwritable, we can save
+         the extra thunk wrapper for direct calls.  */
+      redirect_callers
+	= (!original_discardable
+	   && cgraph_function_body_availability (alias) > AVAIL_OVERWRITABLE
+	   && cgraph_function_body_availability (original) > AVAIL_OVERWRITABLE);
+    }
+  else
+    {
+      create_alias = true;
+      create_thunk = false;
+      redirect_callers = false;
+    }
+
+  if (create_alias && DECL_COMDAT_GROUP (alias->decl))
+    {
+      create_alias = false;
+      create_thunk = true;
+    }
+
+  /* We want thunk to always jump to the local function body
+     unless the body is comdat and may be optimized out.  */
+  if ((create_thunk || redirect_callers)
+      && (!original_discardable
+	  || (DECL_COMDAT_GROUP (original->decl)
+	      && (DECL_COMDAT_GROUP (original->decl)
+		  == DECL_COMDAT_GROUP (alias->decl)))))
+    local_original
+      = cgraph (symtab_nonoverwritable_alias (original));
+
+  if (redirect_callers)
+    {
+      /* If alias is non-overwritable then
+         all direct calls are safe to be redirected to the original.  */
+      bool redirected = false;
+      while (alias->callers)
+	{
+	  struct cgraph_edge *e = alias->callers;
+	  cgraph_redirect_edge_callee (e, local_original);
+	  push_cfun (DECL_STRUCT_FUNCTION (e->caller->decl));
+
+	  if (e->call_stmt)
+	    cgraph_redirect_edge_call_stmt_to_callee (e);
+
+	  pop_cfun ();
+	  redirected = true;
+	}
+
+      /* The alias function is removed if symbol address
+         does not matter.  */
+      if (!alias_address_matters)
+	cgraph_remove_node (alias);
+
+      if (dump_file && redirected)
+	fprintf (dump_file, "Callgraph local calls have been redirected.\n\n");
+    }
+  /* If the condtion above is not met, we are lucky and can turn the
+     function into real alias.  */
+  else if (create_alias)
+    {
+      /* Remove the function's body.  */
+      ipa_merge_profiles (original, alias);
+      cgraph_release_function_body (alias);
+      cgraph_reset_node (alias);
+
+      /* Create the alias.  */
+      cgraph_create_function_alias (alias_func->decl, decl);
+      symtab_resolve_alias (alias, original);
+
+      if (dump_file)
+	fprintf (dump_file, "Callgraph alias has been created.\n\n");
+    }
+  else if (create_thunk)
+    {
+      if (DECL_COMDAT_GROUP (alias->decl))
+	{
+	  if (dump_file)
+	    fprintf (dump_file, "Callgraph thunk cannot be created because of COMDAT\n");
+
+	  return 0;
+	}
+
+      ipa_merge_profiles (local_original, alias);
+      cgraph_make_wrapper (alias, local_original);
+
+      if (dump_file)
+	fprintf (dump_file, "Callgraph thunk has been created.\n\n");
+    }
+  else if (dump_file)
+    fprintf (dump_file, "Callgraph merge operation cannot be performed.\n\n");
+
+  return true;
+}
+
+/* Semantic item initialization function.  */
+
+void
+sem_function::init (void)
+{
+  if (in_lto_p)
+    cgraph_get_body (get_node ());
+
+  tree fndecl = node->decl;
+  struct function *func = DECL_STRUCT_FUNCTION (fndecl);
+
+  gcc_assert (func);
+  gcc_assert (SSANAMES (func));
+
+  ssa_names_size = SSANAMES (func)->length ();
+  node = node;
+
+  decl = fndecl;
+  region_tree = func->eh->region_tree;
+
+  /* iterating all function arguments.  */
+  arg_count = count_formal_params (fndecl);
+
+  edge_count = n_edges_for_fn (func);
+  cfg_checksum = coverage_compute_cfg_checksum (func);
+
+  gcode_hash = 0;
+
+  basic_block bb;
+  FOR_EACH_BB_FN (bb, func)
+  {
+    unsigned nondbg_stmt_count = 0;
+
+    edge e;
+    for (edge_iterator ei = ei_start (bb->preds); ei_cond (ei, &e); ei_next (&ei))
+      cfg_checksum = iterative_hash_host_wide_int (e->flags,
+		     cfg_checksum);
+
+    for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi);
+	 gsi_next (&gsi))
+      {
+	gimple stmt = gsi_stmt (gsi);
+	hashval_t code = (hashval_t) gimple_code (stmt);
+
+	/* We ignore all debug statements.  */
+	if (code != GIMPLE_DEBUG)
+	  {
+	    nondbg_stmt_count++;
+	    gcode_hash = iterative_hash_object (code, gcode_hash);
+	  }
+      }
+
+    bb_sizes.safe_push (nondbg_stmt_count);
+
+    /* Inserting basic block to hash table.  */
+    sem_bb *semantic_bb = new sem_bb (bb, nondbg_stmt_count,
+				      EDGE_COUNT (bb->preds) + EDGE_COUNT (bb->succs));
+
+    bb_sorted.safe_push (semantic_bb);
+  }
+
+  parse_tree_args ();
+}
+
+/* For a given call graph NODE, the function constructs new
+   semantic function item.  */
+
+sem_function *
+sem_function::parse (struct cgraph_node *node, bitmap_obstack *stack)
+{
+  tree fndecl = node->decl;
+  struct function *func = DECL_STRUCT_FUNCTION (fndecl);
+
+  if (!func || !cgraph_function_with_gimple_body_p (node))
+    return NULL;
+
+  if (lookup_attribute_by_prefix ("omp ", DECL_ATTRIBUTES (node->decl)) != NULL)
+    return NULL;
+
+  sem_function *f = new sem_function (node, 0, stack);
+
+  f->init ();
+
+  return f;
+}
+
+/* Parses function arguments and result type.  */
+
+void
+sem_function::parse_tree_args (void)
+{
+  tree result;
+
+  if (arg_types.exists ())
+    arg_types.release ();
+
+  arg_types.create (4);
+  tree fnargs = DECL_ARGUMENTS (decl);
+
+  for (tree parm = fnargs; parm; parm = DECL_CHAIN (parm))
+    arg_types.safe_push (DECL_ARG_TYPE (parm));
+
+  /* Function result type.  */
+  result = DECL_RESULT (decl);
+  result_type = result ? TREE_TYPE (result) : NULL;
+
+  /* During WPA, we can get arguments by following method.  */
+  if (!fnargs)
+    {
+      tree type = TYPE_ARG_TYPES (TREE_TYPE (decl));
+      for (tree parm = type; parm; parm = TREE_CHAIN (parm))
+	arg_types.safe_push (TYPE_CANONICAL (TREE_VALUE (parm)));
+
+      result_type = TREE_TYPE (TREE_TYPE (decl));
+    }
+}
+
+/* For given basic blocks BB1 and BB2 (from functions FUNC1 and FUNC),
+   return true if phi nodes are semantically equivalent in these blocks .  */
+
+bool
+sem_function::compare_phi_node (basic_block bb1, basic_block bb2,
+				tree func1, tree func2)
+{
+  gimple_stmt_iterator si1, si2;
+  gimple phi1, phi2;
+  unsigned size1, size2, i;
+  tree t1, t2;
+  edge e1, e2;
+
+  gcc_assert (bb1 != NULL);
+  gcc_assert (bb2 != NULL);
+
+  si2 = gsi_start_phis (bb2);
+  for (si1 = gsi_start_phis (bb1); !gsi_end_p (si1);
+       gsi_next (&si1))
+    {
+      gsi_next_nonvirtual_phi (&si1);
+      gsi_next_nonvirtual_phi (&si2);
+
+      if (gsi_end_p (si1) && gsi_end_p (si2))
+	break;
+
+      if (gsi_end_p (si1) || gsi_end_p (si2))
+	return RETURN_FALSE();
+
+      phi1 = gsi_stmt (si1);
+      phi2 = gsi_stmt (si2);
+
+      size1 = gimple_phi_num_args (phi1);
+      size2 = gimple_phi_num_args (phi2);
+
+      if (size1 != size2)
+	return RETURN_FALSE ();
+
+      for (i = 0; i < size1; ++i)
+	{
+	  t1 = gimple_phi_arg (phi1, i)->def;
+	  t2 = gimple_phi_arg (phi2, i)->def;
+
+	  if (!compare_operand (t1, t2, func1, func2))
+	    return RETURN_FALSE ();
+
+	  e1 = gimple_phi_arg_edge (phi1, i);
+	  e2 = gimple_phi_arg_edge (phi2, i);
+
+	  if (!m_checker->compare_edge (e1, e2))
+	    return RETURN_FALSE ();
+	}
+
+      gsi_next (&si2);
+    }
+
+  return true;
+}
+
+/* For given basic blocks BB1 and BB2 (from functions FUNC1 and FUNC),
+   true value is returned if exception handling regions are equivalent
+   in these blocks.  */
+
+bool
+sem_function::compare_eh_region (eh_region r1, eh_region r2, tree func1,
+				 tree func2)
+{
+  eh_landing_pad lp1, lp2;
+  eh_catch c1, c2;
+  tree t1, t2;
+
+  while (1)
+    {
+      if (!r1 && !r2)
+	return true;
+
+      if (!r1 || !r2)
+	return false;
+
+      if (r1->index != r2->index || r1->type != r2->type)
+	return false;
+
+      /* Landing pads comparison */
+      lp1 = r1->landing_pads;
+      lp2 = r2->landing_pads;
+
+      while (lp1 && lp2)
+	{
+	  if (lp1->index != lp2->index)
+	    return false;
+
+	  /* Comparison of post landing pads. */
+	  if (lp1->post_landing_pad && lp2->post_landing_pad)
+	    {
+	      t1 = lp1->post_landing_pad;
+	      t2 = lp2->post_landing_pad;
+
+	      gcc_assert (TREE_CODE (t1) == LABEL_DECL);
+	      gcc_assert (TREE_CODE (t2) == LABEL_DECL);
+
+	      if (!compare_tree_ssa_label (t1, t2, func1, func2))
+		return false;
+	    }
+	  else if (lp1->post_landing_pad || lp2->post_landing_pad)
+	    return false;
+
+	  lp1 = lp1->next_lp;
+	  lp2 = lp2->next_lp;
+	}
+
+      if (lp1 || lp2)
+	return false;
+
+      switch (r1->type)
+	{
+	case ERT_TRY:
+	  c1 = r1->u.eh_try.first_catch;
+	  c2 = r2->u.eh_try.first_catch;
+
+	  while (c1 && c2)
+	    {
+	      /* Catch label checking */
+	      if (c1->label && c2->label)
+		{
+		  if (!compare_tree_ssa_label (c1->label, c2->label,
+					       func1, func2))
+		    return false;
+		}
+	      else if (c1->label || c2->label)
+		return false;
+
+	      /* Type list checking */
+	      if (!compare_type_list (c1->type_list, c2->type_list))
+		return false;
+
+	      c1 = c1->next_catch;
+	      c2 = c2->next_catch;
+	    }
+
+	  break;
+
+	case ERT_ALLOWED_EXCEPTIONS:
+	  if (r1->u.allowed.filter != r2->u.allowed.filter)
+	    return false;
+
+	  if (!compare_type_list (r1->u.allowed.type_list,
+				  r2->u.allowed.type_list))
+	    return false;
+
+	  break;
+	case ERT_CLEANUP:
+	  break;
+	case ERT_MUST_NOT_THROW:
+	  if (r1->u.must_not_throw.failure_decl != r1->u.must_not_throw.failure_decl)
+	    return false;
+	  break;
+	default:
+	  gcc_unreachable ();
+	  break;
+	}
+
+      /* If there are sub-regions, process them.  */
+      if ((!r1->inner && r2->inner) || (!r1->next_peer && r2->next_peer))
+	return false;
+
+      if (r1->inner)
+	{
+	  r1 = r1->inner;
+	  r2 = r2->inner;
+	}
+
+      /* If there are peers, process them.  */
+      else if (r1->next_peer)
+	{
+	  r1 = r1->next_peer;
+	  r2 = r2->next_peer;
+	}
+      /* Otherwise, step back up the tree to the next peer.  */
+      else
+	{
+	  do
+	    {
+	      r1 = r1->outer;
+	      r2 = r2->outer;
+
+	      /* All nodes have been visited. */
+	      if (!r1 && !r2)
+		return true;
+	    }
+	  while (r1->next_peer == NULL);
+
+	  r1 = r1->next_peer;
+	  r2 = r2->next_peer;
+	}
+    }
+
+  return false;
+}
+
+/* Verifies that trees T1 and T2, representing function declarations
+   are equivalent from perspective of ICF.  */
+
+bool
+sem_function::compare_function_decl (tree t1, tree t2)
+{
+  if (t1 == t2)
+    return true;
+
+  bool ret = pointer_set_contains (tree_refs_set, t1)
+	     && pointer_set_contains (m_compared_func->tree_refs_set, t2);
+
+  if (ret)
+    return true;
+
+  /* If function decl is WEAKREF, we compare targets.  */
+  struct cgraph_node *f1 = cgraph_get_node (t1);
+  struct cgraph_node *f2 = cgraph_get_node (t2);
+
+  if(f1 && f2 && f1->weakref && f2->weakref)
+    ret = f1->alias_target == f2->alias_target;
+
+  return ret;
+}
+
+/* Verifies that trees T1 and T2 do correspond.  */
+
+bool
+sem_function::compare_variable_decl (tree t1, tree t2, tree func1, tree func2)
+{
+  if (t1 == t2)
+    return true;
+
+  bool ret = pointer_set_contains (tree_refs_set, t1)
+	     && pointer_set_contains (m_compared_func->tree_refs_set, t2);
+
+  if (ret)
+    return true;
+
+  ret = m_checker->compare_decl (t1, t2, func1, func2);
+
+  return RETURN_WITH_DEBUG (ret);
+}
+
+
+/* Returns true if tree T can be compared as a handled component.  */
+
+bool
+sem_function::icf_handled_component_p (tree t)
+{
+  tree_code tc = TREE_CODE (t);
+
+  return ((handled_component_p (t))
+	  || tc == ADDR_EXPR || tc == MEM_REF || tc == REALPART_EXPR
+	  || tc == IMAGPART_EXPR || tc == OBJ_TYPE_REF);
+}
+
+/* Basic blocks dictionary BB_DICT returns true if SOURCE index BB
+   corresponds to TARGET.  */
+
+bool
+sem_function::bb_dict_test (int* bb_dict, int source, int target)
+{
+  if (bb_dict[source] == -1)
+    {
+      bb_dict[source] = target;
+      return true;
+    }
+  else
+    return bb_dict[source] == target;
+}
+
+/* If T1 and T2 are SSA names, dictionary comparison is processed. Otherwise,
+   declaration comparasion is executed.  */
+
+bool
+sem_function::compare_ssa_name (tree t1, tree t2, tree func1, tree func2)
+{
+  tree b1, b2;
+  bool ret;
+
+  if (!m_checker->compare_ssa_name (t1, t2))
+    return RETURN_FALSE ();
+
+  if (SSA_NAME_IS_DEFAULT_DEF (t1))
+    {
+      b1 = SSA_NAME_VAR (t1);
+      b2 = SSA_NAME_VAR (t2);
+
+      if (b1 == NULL && b2 == NULL)
+	return true;
+
+      if (b1 == NULL || b2 == NULL || TREE_CODE (b1) != TREE_CODE (b2))
+	return RETURN_FALSE ();
+
+      switch (TREE_CODE (b1))
+	{
+	case VAR_DECL:
+	  return RETURN_WITH_DEBUG (compare_variable_decl (t1, t2, func1, func2));
+	case PARM_DECL:
+	case RESULT_DECL:
+	  ret = m_checker->compare_decl (b1, b2, func1, func2);
+	  return RETURN_WITH_DEBUG (ret);
+	default:
+	  return RETURN_FALSE_WITH_MSG ("Unknown TREE code reached");
+	}
+    }
+  else
+    return true;
+}
+
+/* Function responsible for comparison of handled components T1 and T2.
+   If these components, from functions FUNC1 and FUNC2, are equal, true
+   is returned.  */
+
+bool
+sem_function::compare_operand (tree t1, tree t2,
+			       tree func1, tree func2)
+{
+  tree base1, base2, x1, x2, y1, y2, z1, z2;
+  HOST_WIDE_INT offset1, offset2;
+  bool ret;
+
+  if (!t1 && !t2)
+    return true;
+  else if (!t1 || !t2)
+    return false;
+
+  tree tt1 = TREE_TYPE (t1);
+  tree tt2 = TREE_TYPE (t2);
+
+  if (!types_are_compatible_p (tt1, tt2))
+    return false;
+
+  base1 = get_addr_base_and_unit_offset (t1, &offset1);
+  base2 = get_addr_base_and_unit_offset (t2, &offset2);
+
+  if (offset1 != offset2)
+    return RETURN_FALSE_WITH_MSG ("base offsets are different");
+
+  if (base1 && base2)
+    {
+      t1 = base1;
+      t2 = base2;
+    }
+
+  if (TREE_CODE (t1) != TREE_CODE (t2))
+    return RETURN_FALSE ();
+
+  switch (TREE_CODE (t1))
+    {
+    case CONSTRUCTOR:
+      {
+	unsigned length1 = vec_safe_length (CONSTRUCTOR_ELTS (t1));
+	unsigned length2 = vec_safe_length (CONSTRUCTOR_ELTS (t2));
+
+	if (length1 != length2)
+	  return RETURN_FALSE ();
+
+	for (unsigned i = 0; i < length1; i++)
+	  if (!compare_operand (CONSTRUCTOR_ELT (t1, i)->value,
+				CONSTRUCTOR_ELT (t2, i)->value, func1, func2))
+	    return RETURN_FALSE();
+
+	return true;
+      }
+    case ARRAY_REF:
+    case ARRAY_RANGE_REF:
+      {
+	x1 = TREE_OPERAND (t1, 0);
+	x2 = TREE_OPERAND (t2, 0);
+	y1 = TREE_OPERAND (t1, 1);
+	y2 = TREE_OPERAND (t2, 1);
+
+	if (!compare_operand (array_ref_low_bound (t1),
+			      array_ref_low_bound (t2),
+			      func1, func2))
+	  return RETURN_FALSE_WITH_MSG ("");
+	if (!compare_operand (array_ref_element_size (t1),
+			      array_ref_element_size (t2),
+			      func1, func2))
+	  return RETURN_FALSE_WITH_MSG ("");
+	if (!compare_operand (x1, x2, func1, func2))
+	  return RETURN_FALSE_WITH_MSG ("");
+	return compare_operand (y1, y2, func1, func2);
+      }
+
+    case MEM_REF:
+      {
+	x1 = TREE_OPERAND (t1, 0);
+	x2 = TREE_OPERAND (t2, 0);
+	y1 = TREE_OPERAND (t1, 1);
+	y2 = TREE_OPERAND (t2, 1);
+
+	/* See if operand is an memory access (the test originate from
+	 gimple_load_p).
+
+	In this case the alias set of the function being replaced must
+	be subset of the alias set of the other function.  At the moment
+	we seek for equivalency classes, so simply require inclussion in
+	both directions.  */
+
+	if (!sem_item::types_are_compatible_p (TREE_TYPE (x1), TREE_TYPE (x2)))
+	  return RETURN_FALSE ();
+
+	if (!compare_operand (x1, x2, func1, func2))
+	  return RETURN_FALSE_WITH_MSG ("");
+
+	/* Type of the offset on MEM_REF does not matter.  */
+	return wi::to_offset  (y1) == wi::to_offset  (y2);
+      }
+    case COMPONENT_REF:
+      {
+	x1 = TREE_OPERAND (t1, 0);
+	x2 = TREE_OPERAND (t2, 0);
+	y1 = TREE_OPERAND (t1, 1);
+	y2 = TREE_OPERAND (t2, 1);
+
+	ret = compare_operand (x1, x2, func1, func2)
+	      && compare_operand (y1, y2, func1, func2);
+
+	return RETURN_WITH_DEBUG (ret);
+      }
+    /* Virtual table call.  */
+    case OBJ_TYPE_REF:
+      {
+	x1 = TREE_OPERAND (t1, 0);
+	x2 = TREE_OPERAND (t2, 0);
+	y1 = TREE_OPERAND (t1, 1);
+	y2 = TREE_OPERAND (t2, 1);
+	z1 = TREE_OPERAND (t1, 2);
+	z2 = TREE_OPERAND (t2, 2);
+
+	ret = compare_operand (x1, x2, func1, func2)
+	      && compare_operand (y1, y2, func1, func2)
+	      && compare_operand (z1, z2, func1, func2);
+
+	return RETURN_WITH_DEBUG (ret);
+      }
+    case ADDR_EXPR:
+      {
+	x1 = TREE_OPERAND (t1, 0);
+	x2 = TREE_OPERAND (t2, 0);
+
+	ret = compare_operand (x1, x2, func1, func2);
+	return RETURN_WITH_DEBUG (ret);
+      }
+    case SSA_NAME:
+      {
+	ret = compare_ssa_name (t1, t2, func1, func2);
+	return RETURN_WITH_DEBUG (ret);
+      }
+    case INTEGER_CST:
+      {
+	ret = types_are_compatible_p (TREE_TYPE (t1), TREE_TYPE (t2))
+	      && wi::to_offset  (t1) == wi::to_offset  (t2);
+
+	return RETURN_WITH_DEBUG (ret);
+      }
+    case COMPLEX_CST:
+    case VECTOR_CST:
+    case STRING_CST:
+    case REAL_CST:
+      {
+	ret = operand_equal_p (t1, t2, OEP_ONLY_CONST);
+	return RETURN_WITH_DEBUG (ret);
+      }
+    case FUNCTION_DECL:
+      {
+	ret = compare_function_decl (t1, t2);
+	return RETURN_WITH_DEBUG (ret);
+      }
+    case VAR_DECL:
+      return RETURN_WITH_DEBUG (compare_variable_decl (t1, t2, func1, func2));
+    case FIELD_DECL:
+      {
+	tree fctx1 = DECL_FCONTEXT (t1);
+	tree fctx2 = DECL_FCONTEXT (t2);
+
+	tree offset1 = DECL_FIELD_OFFSET (t1);
+	tree offset2 = DECL_FIELD_OFFSET (t1);
+
+	ret = compare_operand (fctx1, fctx2, func1, func2)
+	      && compare_operand (offset1, offset2, func1, func2);
+
+	return RETURN_WITH_DEBUG (ret);
+      }
+    case PARM_DECL:
+    case LABEL_DECL:
+    case RESULT_DECL:
+    case CONST_DECL:
+    case BIT_FIELD_REF:
+      {
+	ret = m_checker->compare_decl (t1, t2, func1, func2);
+	return RETURN_WITH_DEBUG (ret);
+      }
+    default:
+      return RETURN_FALSE_WITH_MSG ("Unknown TREE code reached");
+    }
+}
+
+/* Iterates all tree types in T1 and T2 and returns true if all types
+   are compatible.  */
+
+bool
+sem_function::compare_type_list (tree t1, tree t2)
+{
+  tree tv1, tv2;
+  tree_code tc1, tc2;
+
+  if (!t1 && !t2)
+    return true;
+
+  while (t1 != NULL && t2 != NULL)
+    {
+      tv1 = TREE_VALUE (t1);
+      tv2 = TREE_VALUE (t2);
+
+      tc1 = TREE_CODE (tv1);
+      tc2 = TREE_CODE (tv2);
+
+      if (tc1 == NOP_EXPR && tc2 == NOP_EXPR)
+	{}
+      else if (tc1 == NOP_EXPR || tc2 == NOP_EXPR)
+	return false;
+      else if (!types_are_compatible_p (tv1, tv2))
+	return false;
+
+      t1 = TREE_CHAIN (t1);
+      t2 = TREE_CHAIN (t2);
+    }
+
+  return !(t1 || t2);
+}
+
+
+/* Semantic variable constructor that uses STACK as bitmap memory stack.  */
+
+sem_variable::sem_variable (bitmap_obstack *stack): sem_item (VAR, stack)
+{
+}
+
+/*  Constructor based on varpool node _NODE with computed hash _HASH.
+    Bitmap STACK is used for memory allocation.  */
+
+sem_variable::sem_variable (varpool_node *node, hashval_t _hash,
+			    bitmap_obstack *stack): sem_item(VAR,
+				  node, _hash, stack)
+{
+  gcc_checking_assert (node);
+  gcc_checking_assert (get_node ());
+}
+
+/* Returns true if the item equals to ITEM given as argument.  */
+
+bool
+sem_variable::equals (sem_item *item)
+{
+  gcc_assert (item->type == VAR);
+
+  return sem_variable::equals (ctor, static_cast<sem_variable *>(item)->ctor);
+}
+
+/* Compares trees T1 and T2 for semantic equality.  */
+
+bool
+sem_variable::equals (tree t1, tree t2)
+{
+  tree_code tc1 = TREE_CODE (t1);
+  tree_code tc2 = TREE_CODE (t2);
+
+  if (tc1 != tc2)
+    return false;
+
+  switch (tc1)
+    {
+    case CONSTRUCTOR:
+      {
+	unsigned len1 = vec_safe_length (CONSTRUCTOR_ELTS (t1));
+	unsigned len2 = vec_safe_length (CONSTRUCTOR_ELTS (t2));
+
+	if (len1 != len2)
+	  return false;
+
+	for (unsigned i = 0; i < len1; i++)
+	  if (!sem_variable::equals (CONSTRUCTOR_ELT (t1, i)->value,
+				     CONSTRUCTOR_ELT (t2, i)->value))
+	    return false;
+
+	return true;
+      }
+    case MEM_REF:
+      {
+	tree x1 = TREE_OPERAND (t1, 0);
+	tree x2 = TREE_OPERAND (t2, 0);
+	tree y1 = TREE_OPERAND (t1, 1);
+	tree y2 = TREE_OPERAND (t2, 1);
+
+	if (!sem_item::types_are_compatible_p (TREE_TYPE (x1), TREE_TYPE (x2)))
+	  return RETURN_FALSE ();
+
+	/* Type of the offset on MEM_REF does not matter.  */
+	return sem_variable::equals (x1, x2)
+	       && wi::to_offset  (y1) == wi::to_offset  (y2);
+      }
+    case NOP_EXPR:
+    case ADDR_EXPR:
+      {
+	tree op1 = TREE_OPERAND (t1, 0);
+	tree op2 = TREE_OPERAND (t2, 0);
+	return sem_variable::equals (op1, op2);
+      }
+    case FUNCTION_DECL:
+    case VAR_DECL:
+    case FIELD_DECL:
+    case LABEL_DECL:
+      return t1 == t2;
+    case INTEGER_CST:
+      return types_are_compatible_p (TREE_TYPE (t1), TREE_TYPE (t2))
+	     && wi::to_offset (t1) == wi::to_offset (t2);
+    case STRING_CST:
+    case REAL_CST:
+    case COMPLEX_CST:
+      return operand_equal_p (t1, t2, OEP_ONLY_CONST);
+    case COMPONENT_REF:
+    case ARRAY_REF:
+    case POINTER_PLUS_EXPR:
+      {
+	tree x1 = TREE_OPERAND (t1, 0);
+	tree x2 = TREE_OPERAND (t2, 0);
+	tree y1 = TREE_OPERAND (t1, 1);
+	tree y2 = TREE_OPERAND (t2, 1);
+
+	return sem_variable::equals (x1, x2) && sem_variable::equals (y1, y2);
+      }
+    case ERROR_MARK:
+      return RETURN_FALSE_WITH_MSG ("ERROR_MARK");
+    default:
+      return RETURN_FALSE_WITH_MSG ("Unknown TREE code reached");
+    }
+}
+
+/* Parser function that visits a varpool NODE.  */
+
+sem_variable *
+sem_variable::parse (struct varpool_node *node, bitmap_obstack *stack)
+{
+  tree decl = node->decl;
+
+  bool readonly = TYPE_P (decl) ? TYPE_READONLY (decl) : TREE_READONLY (decl);
+  bool can_handle = readonly && (DECL_VIRTUAL_P (decl)
+				 || !TREE_ADDRESSABLE (decl));
+
+  if (!can_handle)
+    return NULL;
+
+  tree ctor = ctor_for_folding (decl);
+  if (!ctor)
+    return NULL;
+
+  sem_variable *v = new sem_variable (node, 0, stack);
+
+  v->init ();
+
+  return v;
+}
+
+/* References independent hash function.  */
+
+hashval_t
+sem_variable::get_hash (void)
+{
+  if (hash)
+    return hash;
+
+  hashval_t hash = 456346417;
+
+  tree_code tc = TREE_CODE (ctor);
+  hash = iterative_hash_object (tc, hash);
+
+  if (TREE_CODE (ctor) == CONSTRUCTOR)
+    {
+      unsigned length = vec_safe_length (CONSTRUCTOR_ELTS (ctor));
+      hash = iterative_hash_object (length, hash);
+    }
+
+  return hash;
+}
+
+/* Merges instance with an ALIAS_ITEM, where alias, thunk or redirection can
+   be applied.  */
+
+bool
+sem_variable::merge (sem_item *alias_item)
+{
+  gcc_assert (alias_item->type == VAR);
+
+  sem_variable *alias_var = static_cast<sem_variable *> (alias_item);
+
+  struct varpool_node *original = get_node ();
+  struct varpool_node *alias = alias_var->get_node ();
+  bool original_discardable = false;
+
+  /* See if original is in a section that can be discarded if the main
+     symbol is not used.  */
+  if (DECL_EXTERNAL (original->decl))
+    original_discardable = true;
+  if (original->resolution == LDPR_PREEMPTED_REG
+      || original->resolution == LDPR_PREEMPTED_IR)
+    original_discardable = true;
+  if (symtab_can_be_discarded (original))
+    original_discardable = true;
+
+  gcc_assert (!TREE_ASM_WRITTEN (alias->decl));
+
+  if (original_discardable || DECL_EXTERNAL (alias_var->decl) ||
+      !compare_sections (alias_var))
+    {
+      if (dump_file)
+	fprintf (dump_file, "Varpool alias cannot be created\n\n");
+
+      return false;
+    }
+  else
+    {
+      // alias cycle creation check
+      varpool_node *n = original;
+
+      while (n->alias)
+	{
+	  n = varpool_alias_target (n);
+	  if (n == alias)
+	    {
+	      if (dump_file)
+		fprintf (dump_file, "Varpool alias cannot be created (alias cycle).\n\n");
+
+	      return false;
+	    }
+	}
+
+      alias->analyzed = false;
+
+      DECL_INITIAL (alias->decl) = NULL;
+      alias->remove_all_references ();
+
+      varpool_create_variable_alias (alias_var->decl, decl);
+      symtab_resolve_alias (alias, original);
+
+      if (dump_file)
+	fprintf (dump_file, "Varpool alias has been created.\n\n");
+
+      return true;
+    }
+}
+
+bool
+sem_variable::compare_sections (sem_variable *alias)
+{
+  const char *source = node->get_section ();
+  const char *target = alias->node->get_section();
+
+  if (source == NULL && target == NULL)
+    return true;
+  else if(!source || !target)
+    return false;
+  else
+    return strcmp (source, target) == 0;
+}
+
+/* Dump symbol to FILE.  */
+
+void
+sem_variable::dump_to_file (FILE *file)
+{
+  gcc_assert (file);
+
+  print_node (file, "", decl, 0);
+  fprintf (file, "\n\n");
+}
+
+/* Iterates though a constructor and identifies tree references
+   we are interested in semantic function equality.  */
+
+void
+sem_variable::parse_tree_refs (tree t)
+{
+  switch (TREE_CODE (t))
+    {
+    case CONSTRUCTOR:
+      {
+	unsigned length = vec_safe_length (CONSTRUCTOR_ELTS (t));
+
+	for (unsigned i = 0; i < length; i++)
+	  parse_tree_refs(CONSTRUCTOR_ELT (t, i)->value);
+
+	break;
+      }
+    case NOP_EXPR:
+    case ADDR_EXPR:
+      {
+	tree op = TREE_OPERAND (t, 0);
+	parse_tree_refs (op);
+	break;
+      }
+    case FUNCTION_DECL:
+      {
+	tree_refs.safe_push (t);
+	break;
+      }
+    default:
+      break;
+    }
+}
+
+unsigned int sem_item_optimizer::class_id = 0;
+
+sem_item_optimizer::sem_item_optimizer (): worklist (0), m_classes (0),
+  m_classes_count (0), m_cgraph_node_hooks (NULL), m_varpool_node_hooks (NULL)
+{
+  m_items.create (0);
+  m_removed_items_set = pointer_set_create ();
+  bitmap_obstack_initialize (&m_bmstack);
+}
+
+sem_item_optimizer::~sem_item_optimizer ()
+{
+  for (unsigned int i = 0; i < m_items.length (); i++)
+    delete m_items[i];
+
+  for (hash_table<congruence_class_group_hash>::iterator it = m_classes.begin ();
+       it != m_classes.end (); ++it)
+    {
+      for (unsigned int i = 0; i < (*it)->classes.length (); i++)
+	delete (*it)->classes[i];
+
+      (*it)->classes.release ();
+    }
+
+  m_items.release ();
+
+  bitmap_obstack_release (&m_bmstack);
+  pointer_set_destroy (m_removed_items_set);
+}
+
+/* Write IPA ICF summary for symbols.  */
+
+void
+sem_item_optimizer::write_summary (void)
+{
+  unsigned int count = 0;
+
+  struct output_block *ob = create_output_block (LTO_section_ipa_icf);
+  lto_symtab_encoder_t encoder = ob->decl_state->symtab_node_encoder;
+  ob->symbol = NULL;
+
+  /* Calculate number of symbols to be serialized.  */
+  for (lto_symtab_encoder_iterator lsei = lsei_start_in_partition (encoder);
+       !lsei_end_p (lsei);
+       lsei_next_in_partition (&lsei))
+    {
+      struct symtab_node *node = lsei_node (lsei);
+
+      if (m_symtab_node_map.get (node))
+	count++;
+    }
+
+  streamer_write_uhwi (ob, count);
+
+  /* Process all of the symbols.  */
+  for (lto_symtab_encoder_iterator lsei = lsei_start_in_partition (encoder);
+       !lsei_end_p (lsei);
+       lsei_next_in_partition (&lsei))
+    {
+      struct symtab_node *node = lsei_node (lsei);
+
+      sem_item **item = m_symtab_node_map.get (node);
+
+      if (item && *item)
+	{
+	  int node_ref = lto_symtab_encoder_encode (encoder, node);
+	  streamer_write_uhwi_stream (ob->main_stream, node_ref);
+
+	  streamer_write_uhwi (ob, (*item)->get_hash ());
+	}
+    }
+
+  streamer_write_char_stream (ob->main_stream, 0);
+  produce_asm (ob, NULL);
+  destroy_output_block (ob);
+}
+
+/* Reads a section from LTO stream file FILE_DATA. Input block for DATA
+   contains LEN bytes.  */
+
+void
+sem_item_optimizer::read_section (struct lto_file_decl_data *file_data,
+				  const char *data, size_t len)
+{
+  const struct lto_function_header *header =
+  (const struct lto_function_header *) data;
+  const int cfg_offset = sizeof (struct lto_function_header);
+  const int main_offset = cfg_offset + header->cfg_size;
+  const int string_offset = main_offset + header->main_size;
+  struct data_in *data_in;
+  struct lto_input_block ib_main;
+  unsigned int i;
+  unsigned int count;
+
+  LTO_INIT_INPUT_BLOCK (ib_main, (const char *) data + main_offset, 0,
+			header->main_size);
+
+  data_in =
+    lto_data_in_create (file_data, (const char *) data + string_offset,
+			header->string_size, vNULL);
+
+  count = streamer_read_uhwi (&ib_main);
+
+  for (i = 0; i < count; i++)
+    {
+      unsigned int index;
+      struct symtab_node *node;
+      lto_symtab_encoder_t encoder;
+
+      index = streamer_read_uhwi (&ib_main);
+      encoder = file_data->symtab_node_encoder;
+      node = lto_symtab_encoder_deref (encoder, index);
+
+      hashval_t hash = streamer_read_uhwi (&ib_main);
+
+      gcc_assert (node->definition);
+
+      if (dump_file)
+	fprintf (dump_file, "Symbol added:%s (tree: %p, uid:%u)\n", node->asm_name (),
+		 (void *) node->decl, node->order);
+
+      if (is_a<cgraph_node *> (node))
+	{
+	  cgraph_node *cnode = cgraph (node);
+
+	  m_items.safe_push (new sem_function (cnode, hash, &m_bmstack));
+	}
+      else
+	{
+	  varpool_node *vnode = varpool (node);
+
+	  m_items.safe_push (new sem_variable (vnode, hash, &m_bmstack));
+	}
+    }
+
+  lto_free_section_data (file_data, LTO_section_ipa_icf, NULL, data,
+			 len);
+  lto_data_in_delete (data_in);
+}
+
+/* Read IPA IPA ICF summary for symbols.  */
+
+void
+sem_item_optimizer::read_summary (void)
+{
+  struct lto_file_decl_data **file_data_vec = lto_get_file_decl_data ();
+  struct lto_file_decl_data *file_data;
+  unsigned int j = 0;
+
+  while ((file_data = file_data_vec[j++]))
+    {
+      size_t len;
+      const char *data = lto_get_section_data (file_data,
+			 LTO_section_ipa_icf, NULL, &len);
+
+      if (data)
+	read_section (file_data, data, len);
+    }
+}
+
+/* Register callgraph and varpool hooks.  */
+
+void
+sem_item_optimizer::register_hooks (void)
+{
+  m_cgraph_node_hooks = cgraph_add_node_removal_hook (
+			  &sem_item_optimizer::cgraph_removal_hook, this);
+
+  m_varpool_node_hooks = varpool_add_node_removal_hook (
+			   &sem_item_optimizer::varpool_removal_hook, this);
+}
+
+/* Unregister callgraph and varpool hooks.  */
+
+void
+sem_item_optimizer::unregister_hooks (void)
+{
+  if (m_cgraph_node_hooks)
+    cgraph_remove_node_removal_hook (m_cgraph_node_hooks);
+
+  if (m_varpool_node_hooks)
+    varpool_remove_node_removal_hook (m_varpool_node_hooks);
+}
+
+/* Adds a CLS to hashtable associated by hash value.  */
+
+void
+sem_item_optimizer::add_class (congruence_class *cls)
+{
+  gcc_assert (cls->members.length ());
+
+  congruence_class_group *group = get_group_by_hash (
+				    cls->members[0]->get_hash (),
+				    cls->members[0]->type);
+  group->classes.safe_push (cls);
+}
+
+/* Gets a congruence class group based on given HASH value and TYPE.  */
+
+congruence_class_group *
+sem_item_optimizer::get_group_by_hash (hashval_t hash, sem_item_type type)
+{
+  congruence_class_group *item = XNEW (congruence_class_group);
+  item->hash = hash;
+  item->type = type;
+
+  congruence_class_group **slot = m_classes.find_slot (item, INSERT);
+
+  if (*slot)
+    free (item);
+  else
+    {
+      item->classes.create (1);
+      *slot = item;
+    }
+
+  return *slot;
+}
+
+/* Callgraph removal hook called for a NODE with a custom DATA.  */
+
+void
+sem_item_optimizer::cgraph_removal_hook (struct cgraph_node *node, void *data)
+{
+  sem_item_optimizer *optimizer = (sem_item_optimizer *) data;
+  optimizer->remove_symtab_node (node);
+}
+
+/* Varpool removal hook called for a NODE with a custom DATA.  */
+
+void
+sem_item_optimizer::varpool_removal_hook (struct varpool_node *node, void *data)
+{
+  sem_item_optimizer *optimizer = (sem_item_optimizer *) data;
+  optimizer->remove_symtab_node (node);
+}
+
+/* Remove symtab NODE triggered by symtab removal hooks.  */
+
+void
+sem_item_optimizer::remove_symtab_node (struct symtab_node *node)
+{
+  gcc_assert (!m_classes.elements());
+
+  pointer_set_insert (m_removed_items_set, node);
+}
+
+/* Removes all callgraph and varpool nodes that are marked by symtab
+   as deleted.  */
+
+void
+sem_item_optimizer::filter_removed_items (void)
+{
+  auto_vec <sem_item *> filtered;
+
+  for (unsigned int i = 0; i < m_items.length(); i++)
+    {
+      sem_item *item = m_items[i];
+
+      if (!flag_ipa_icf_functions && item->type == FUNC)
+	continue;
+
+      if (!flag_ipa_icf_variables && item->type == VAR)
+	continue;
+
+      bool no_body_function = false;
+
+      if (item->type == FUNC)
+	{
+	  struct cgraph_node *cnode = static_cast <sem_function *>(item)->get_node ();
+
+	  no_body_function = in_lto_p && (cnode->alias || cnode->body_removed);
+	}
+
+      if(!pointer_set_contains (m_removed_items_set, m_items[i]->node)
+	  && !no_body_function)
+	{
+	  if (item->type == VAR || (!DECL_CXX_CONSTRUCTOR_P (item->decl)
+				    && !DECL_CXX_DESTRUCTOR_P (item->decl)))
+	    filtered.safe_push (m_items[i]);
+	}
+    }
+
+  m_items.release ();
+  for (unsigned int i = 0; i < filtered.length(); i++)
+    m_items.safe_push (filtered[i]);
+}
+
+/* Optimizer entry point.  */
+
+void
+sem_item_optimizer::execute (void)
+{
+  filter_removed_items ();
+  build_hash_based_classes ();
+
+  if (dump_file)
+    fprintf (dump_file, "Dump after hash based groups\n");
+  dump_cong_classes ();
+
+  for (unsigned int i = 0; i < m_items.length(); i++)
+    m_items[i]->init_wpa ();
+
+  subdivide_classes_by_equality (true);
+
+  if (dump_file)
+    fprintf (dump_file, "Dump after WPA based types groups\n");
+  dump_cong_classes ();
+
+  parse_nonsingleton_classes ();
+  subdivide_classes_by_equality ();
+
+  if (dump_file)
+    fprintf (dump_file, "Dump after full equality comparison of groups\n");
+
+  dump_cong_classes ();
+
+  unsigned int prev_class_count = m_classes_count;
+
+  process_cong_reduction ();
+  dump_cong_classes ();
+  merge_classes (prev_class_count);
+
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    dump_symtab (dump_file);
+}
+
+/* Function responsible for visiting all potential functions and
+   read-only variables that can be merged.  */
+
+void
+sem_item_optimizer::parse_funcs_and_vars (void)
+{
+  struct cgraph_node *cnode;
+
+  if (flag_ipa_icf_functions)
+    FOR_EACH_DEFINED_FUNCTION (cnode)
+    {
+      sem_function *f = sem_function::parse (cnode, &m_bmstack);
+      if (f)
+	{
+	  m_items.safe_push (f);
+	  m_symtab_node_map.put (cnode, f);
+
+	  if (dump_file)
+	    fprintf (dump_file, "Parsed function:%s\n", f->asm_name ());
+
+	  if (dump_file && (dump_flags & TDF_DETAILS))
+	    f->dump_to_file (dump_file);
+	}
+      else if (dump_file)
+	fprintf (dump_file, "Not parsed function:%s\n", cnode->asm_name ());
+    }
+
+  varpool_node *vnode;
+
+  if (flag_ipa_icf_variables)
+    FOR_EACH_DEFINED_VARIABLE (vnode)
+    {
+      sem_variable *v = sem_variable::parse (vnode, &m_bmstack);
+
+      if (v)
+	{
+	  m_items.safe_push (v);
+	  m_symtab_node_map.put (vnode, v);
+	}
+    }
+}
+
+/* Makes pairing between a congruence class CLS and semantic ITEM.  */
+
+void
+sem_item_optimizer::add_item_to_class (congruence_class *cls, sem_item *item)
+{
+  item->index_in_class = cls->members.length ();
+  cls->members.safe_push (item);
+  item->cls = cls;
+}
+
+/* Congruence classes are built by hash value.  */
+
+void
+sem_item_optimizer::build_hash_based_classes (void)
+{
+  for (unsigned i = 0; i < m_items.length (); i++)
+    {
+      sem_item *item = m_items[i];
+
+      congruence_class_group *group = get_group_by_hash (item->get_hash (),
+				      item->type);
+
+      if (!group->classes.length ())
+	{
+	  m_classes_count++;
+	  group->classes.safe_push (new congruence_class (class_id++));
+	}
+
+      add_item_to_class (group->classes[0], item);
+    }
+}
+
+/* Semantic items in classes having more than one element and initialized.
+   In case of WPA, we load function body.  */
+
+void
+sem_item_optimizer::parse_nonsingleton_classes (void)
+{
+  for (hash_table <congruence_class_group_hash>::iterator it = m_classes.begin ();
+       it != m_classes.end (); ++it)
+    {
+      for (unsigned i = 0; i < (*it)->classes.length (); i++)
+	{
+	  congruence_class *c = (*it)->classes [i];
+
+	  if (c->members.length() > 1)
+	    for (unsigned j = 0; j < c->members.length (); j++)
+	      {
+		sem_item *item = c->members[j];
+		m_decl_map.put (item->decl, item);
+	      }
+	}
+    }
+
+  unsigned int init_called_count = 0;
+
+  for (hash_table <congruence_class_group_hash>::iterator it = m_classes.begin ();
+       it != m_classes.end (); ++it)
+    {
+      /* We fill in all declarations for sem_items.  */
+      for (unsigned i = 0; i < (*it)->classes.length (); i++)
+	{
+	  congruence_class *c = (*it)->classes [i];
+
+	  if (c->members.length() > 1)
+	    for (unsigned j = 0; j < c->members.length (); j++)
+	      {
+		sem_item *item = c->members[j];
+
+		item->init ();
+		item->init_refs ();
+
+		init_called_count++;
+
+		for (unsigned j = 0; j < item->tree_refs.length (); j++)
+		  {
+		    sem_item **result = m_decl_map.get (item->tree_refs[j]);
+
+		    if(result)
+		      {
+			sem_item *target = *result;
+			item->refs.safe_push (target);
+
+			unsigned index = item->refs.length ();
+			target->usages.safe_push (new sem_usage_pair(item, index));
+			bitmap_set_bit (target->usage_index_bitmap, index);
+			pointer_set_insert (item->tree_refs_set, item->tree_refs[j]);
+		      }
+		  }
+	      }
+	}
+    }
+
+  if (dump_file)
+    fprintf (dump_file, "Init called for %u items (%.2f%%).\n", init_called_count,
+	     100.0f * init_called_count / m_items.length ());
+}
+
+/* Equality function for semantic items is used to subdivide existing
+   classes. If IN_WPA, fast equality function is invoked.  */
+
+void
+sem_item_optimizer::subdivide_classes_by_equality (bool in_wpa)
+{
+  for (hash_table <congruence_class_group_hash>::iterator it = m_classes.begin ();
+       it != m_classes.end (); ++it)
+    {
+      unsigned int class_count = (*it)->classes.length ();
+
+      for (unsigned i = 0; i < class_count; i++)
+	{
+	  congruence_class *c = (*it)->classes [i];
+
+	  if (c->members.length() > 1)
+	    {
+	      auto_vec <sem_item *> new_vector;
+
+	      sem_item *first = c->members[0];
+	      new_vector.safe_push (first);
+
+	      unsigned class_split_first = (*it)->classes.length ();
+
+	      for (unsigned j = 1; j < c->members.length (); j++)
+		{
+		  sem_item *item = c->members[j];
+
+		  bool equals = in_wpa ? first->equals_wpa (item) : first->equals (item);
+
+		  if (equals)
+		    new_vector.safe_push (item);
+		  else
+		    {
+		      bool integrated = false;
+
+		      for (unsigned k = class_split_first; k < (*it)->classes.length (); k++)
+			{
+			  sem_item *x = (*it)->classes[k]->members[0];
+			  bool equals = in_wpa ? x->equals_wpa (item) : x->equals (item);
+
+			  if (equals)
+			    {
+			      integrated = true;
+			      add_item_to_class ((*it)->classes[k], item);
+
+			      break;
+			    }
+			}
+
+		      if (!integrated)
+			{
+			  congruence_class *c = new congruence_class (class_id++);
+			  m_classes_count++;
+			  add_item_to_class (c, item);
+
+			  (*it)->classes.safe_push (c);
+			}
+		    }
+		}
+
+	      // we replace newly created new_vector for the class we've just splitted
+	      c->members.release ();
+	      c->members.create (new_vector.length ());
+
+	      for (unsigned int j = 0; j < new_vector.length (); j++)
+		add_item_to_class (c, new_vector[j]);
+	    }
+	}
+    }
+
+  verify_classes ();
+}
+
+/* Verify congruence classes if checking is enabled.  */
+
+void
+sem_item_optimizer::verify_classes (void)
+{
+#if ENABLE_CHECKING
+  for (hash_table <congruence_class_group_hash>::iterator it = m_classes.begin ();
+       it != m_classes.end (); ++it)
+    {
+      for (unsigned int i = 0; i < (*it)->classes.length (); i++)
+	{
+	  congruence_class *cls = (*it)->classes[i];
+
+	  gcc_checking_assert (cls);
+	  gcc_checking_assert (cls->members.length () > 0);
+
+	  for (unsigned int j = 0; j < cls->members.length (); j++)
+	    {
+	      sem_item *item = cls->members[j];
+
+	      gcc_checking_assert (item);
+
+	      for (unsigned k = 0; k < item->usages.length (); k++)
+		{
+		  sem_usage_pair *usage = item->usages[k];
+		  gcc_checking_assert (usage->item->index_in_class <
+				       usage->item->cls->members.length ());
+		}
+	    }
+	}
+    }
+#endif
+}
+
+/* Disposes split map traverse function. CLS_PTR is pointer to congruence
+   class, BSLOT is bitmap slot we want to release. DATA is mandatory,
+   but unused argument.  */
+
+bool
+sem_item_optimizer::release_split_map (congruence_class * const &,
+				       bitmap const &b, traverse_split_pair *)
+{
+  bitmap bmp = b;
+
+  BITMAP_FREE (bmp);
+
+  return true;
+}
+
+/* Process split operation for a class given as pointer CLS_PTR,
+   where bitmap B splits congruence class members. DATA is used
+   as argument of split pair.  */
+
+bool
+sem_item_optimizer::traverse_congruence_split (congruence_class * const &cls,
+    bitmap const &b, traverse_split_pair *pair)
+{
+  sem_item_optimizer *optimizer = pair->optimizer;
+  const congruence_class *splitter_cls = pair->cls;
+
+  /* If counted bits are greater than zero and less than the number of members
+     a group will be splitted.  */
+  unsigned popcount = bitmap_count_bits (b);
+
+  if (popcount > 0 && popcount < cls->members.length ())
+    {
+      congruence_class* newclasses[2] = { new congruence_class (class_id++), new congruence_class (class_id++) };
+
+      for (unsigned int i = 0; i < cls->members.length (); i++)
+	{
+	  int target = bitmap_bit_p (b, i);
+	  congruence_class *tc = newclasses[target];
+
+	  add_item_to_class (tc, cls->members[i]);
+	}
+
+#ifdef ENABLE_CHECKING
+      for (unsigned int i = 0; i < 2; i++)
+	gcc_checking_assert (newclasses[i]->members.length ());
+#endif
+
+      if (splitter_cls == cls)
+	optimizer->splitter_class_removed = true;
+
+      /* Remove old class from worklist if presented.  */
+      bool in_work_list = optimizer->worklist_contains (cls);
+
+      if (in_work_list)
+	optimizer->worklist_remove (cls);
+
+      congruence_class_group g;
+      g.hash = cls->members[0]->get_hash ();
+      g.type = cls->members[0]->type;
+
+      congruence_class_group *slot = optimizer->m_classes.find(&g);
+
+      for (unsigned int i = 0; i < slot->classes.length (); i++)
+	if (slot->classes[i] == cls)
+	  {
+	    slot->classes.ordered_remove (i);
+	    break;
+	  }
+
+      /* New class will be inserted and integrated to work list.  */
+      for (unsigned int i = 0; i < 2; i++)
+	optimizer->add_class (newclasses[i]);
+
+      /* Two classes replace one, so that increment just by one.  */
+      optimizer->m_classes_count++;
+
+      /* If OLD class was presented in the worklist, we remove the class
+         are replace it will both newly created classes.  */
+      if (in_work_list)
+	for (unsigned int i = 0; i < 2; i++)
+	  optimizer->worklist_push (newclasses[i]);
+      else /* Just smaller class is inserted.  */
+	{
+	  unsigned int smaller_index = newclasses[0]->members.length () <
+				       newclasses[1]->members.length () ?
+				       0 : 1;
+	  optimizer->worklist_push (newclasses[smaller_index]);
+	}
+
+      if (dump_file && (dump_flags & TDF_DETAILS))
+	{
+	  fprintf (dump_file, "  congruence class splitted:\n");
+	  cls->dump (dump_file, 4);
+
+	  fprintf (dump_file, "  newly created groups:\n");
+	  for (unsigned int i = 0; i < 2; i++)
+	    newclasses[i]->dump (dump_file, 4);
+	}
+
+      delete cls;
+    }
+
+
+  return true;
+}
+
+/* Tests if a class CLS used as INDEXth splits any congruence classes.
+   Bitmap stack BMSTACK is used for bitmap allocation.  */
+
+void
+sem_item_optimizer::do_congruence_step_for_index (congruence_class *cls,
+    unsigned int index)
+{
+  hash_map <congruence_class *, bitmap> split_map;
+
+  for (unsigned int i = 0; i < cls->members.length (); i++)
+    {
+      sem_item *item = cls->members[i];
+
+      /* Iterate all usages that have INDEX as usage of the item.  */
+      for (unsigned int j = 0; j < item->usages.length (); j++)
+	{
+	  sem_usage_pair *usage = item->usages[j];
+
+	  if (usage->index != index)
+	    continue;
+
+	  bitmap *slot = split_map.get (usage->item->cls);
+	  bitmap b;
+
+	  if(!slot)
+	    {
+	      b = BITMAP_ALLOC (&m_bmstack);
+	      split_map.put (usage->item->cls, b);
+	    }
+	  else
+	    b = *slot;
+
+#if ENABLE_CHECKING
+	  gcc_checking_assert (usage->item->cls);
+	  gcc_checking_assert (usage->item->index_in_class <
+			       usage->item->cls->members.length ());
+#endif
+
+	  bitmap_set_bit (b, usage->item->index_in_class);
+	}
+    }
+
+  traverse_split_pair pair;
+  pair.optimizer = this;
+  pair.cls = cls;
+
+  splitter_class_removed = false;
+  split_map.traverse
+  <traverse_split_pair *, sem_item_optimizer::traverse_congruence_split> (&pair);
+
+  /* Bitmap clean-up.  */
+  split_map.traverse
+  <traverse_split_pair *, sem_item_optimizer::release_split_map> (NULL);
+}
+
+/* Every usage of a congruence class CLS is a candidate that can split the
+   collection of classes. Bitmap stack BMSTACK is used for bitmap
+   allocation.  */
+
+void
+sem_item_optimizer::do_congruence_step (congruence_class *cls)
+{
+  bitmap_iterator bi;
+  unsigned int i;
+
+  bitmap usage = BITMAP_ALLOC (&m_bmstack);
+
+  for (unsigned int i = 0; i < cls->members.length (); i++)
+    bitmap_ior_into (usage, cls->members[i]->usage_index_bitmap);
+
+  EXECUTE_IF_SET_IN_BITMAP (usage, 0, i, bi)
+  {
+    if (dump_file && (dump_flags & TDF_DETAILS))
+      fprintf (dump_file, "  processing congruece step for class: %u, index: %u\n",
+	       cls->id, i);
+
+    do_congruence_step_for_index (cls, i);
+
+    if (splitter_class_removed)
+      break;
+  }
+
+  BITMAP_FREE (usage);
+}
+
+/* Adds a newly created congruence class CLS to worklist.  */
+
+void
+sem_item_optimizer::worklist_push (congruence_class *cls)
+{
+  congruence_class **slot = worklist.find_slot (cls, INSERT);
+
+  if (*slot)
+    return;
+
+  *slot = cls;
+}
+
+/* Pops a class from worklist. */
+
+congruence_class *
+sem_item_optimizer::worklist_pop (void)
+{
+  gcc_assert (worklist.elements ());
+
+  congruence_class *cls = *worklist.begin ();
+  worklist.remove_elt (cls);
+
+  return cls;
+}
+
+/* Iterative congruence reduction function.  */
+
+void
+sem_item_optimizer::process_cong_reduction (void)
+{
+  for (hash_table<congruence_class_group_hash>::iterator it = m_classes.begin ();
+       it != m_classes.end (); ++it)
+    for (unsigned i = 0; i < (*it)->classes.length (); i++)
+      if ((*it)->classes[i]->is_class_used ())
+	worklist_push ((*it)->classes[i]);
+
+  if (dump_file)
+    fprintf (dump_file, "Worklist has been filled with: %lu\n",
+	     worklist.elements ());
+
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    fprintf (dump_file, "Congruence class reduction\n");
+
+  while (worklist.elements ())
+    {
+      congruence_class *cls = worklist_pop ();
+      do_congruence_step (cls);
+    }
+}
+
+/* Debug function prints all informations about congruence classes.  */
+
+void
+sem_item_optimizer::dump_cong_classes (void)
+{
+  if (!dump_file)
+    return;
+
+  fprintf (dump_file,
+	   "Congruence classes: %u (unique hash values: %lu), with total: %u items\n",
+	   m_classes_count, m_classes.elements(), m_items.length ());
+
+  /* Histogram calculation.  */
+  unsigned int max_index = 0;
+  unsigned int* histogram = XCNEWVEC (unsigned int, m_items.length ());
+
+  for (hash_table<congruence_class_group_hash>::iterator it = m_classes.begin ();
+       it != m_classes.end (); ++it)
+
+    for (unsigned i = 0; i < (*it)->classes.length (); i++)
+      {
+	unsigned int c = (*it)->classes[i]->members.length ();
+	histogram[c]++;
+
+	if (c > max_index)
+	  max_index = c;
+      }
+
+  fprintf (dump_file,
+	   "Class size histogram [num of members]: number of classe number of classess\n");
+
+  for (unsigned int i = 0; i <= max_index; i++)
+    if (histogram[i])
+      fprintf (dump_file, "[%u]: %u classes\n", i, histogram[i]);
+
+  fprintf (dump_file, "\n\n");
+
+
+  if (dump_flags & TDF_DETAILS)
+    for (hash_table<congruence_class_group_hash>::iterator it = m_classes.begin ();
+	 it != m_classes.end (); ++it)
+      {
+	fprintf (dump_file, "  group: with %u classes:\n", (*it)->classes.length ());
+
+	for (unsigned i = 0; i < (*it)->classes.length (); i++)
+	  {
+	    (*it)->classes[i]->dump (dump_file, 4);
+
+	    if(i < (*it)->classes.length () - 1)
+	      fprintf (dump_file, " ");
+	  }
+      }
+
+  free (histogram);
+}
+
+/* After reduction is done, we can declare all items in a group
+   to be equal. PREV_CLASS_COUNT is start number of classes
+   before reduction.  */
+
+void
+sem_item_optimizer::merge_classes (unsigned int prev_class_count)
+{
+  unsigned int item_count = m_items.length ();
+  unsigned int class_count = m_classes_count;
+  unsigned int equal_items = item_count - class_count;
+
+  if (dump_file)
+    {
+      fprintf (dump_file, "\nItem count: %u\n", item_count);
+      fprintf (dump_file, "Congruent classes before: %u, after: %u\n",
+	       prev_class_count, class_count);
+      fprintf (dump_file, "Average class size before: %.2f, after: %.2f\n",
+	       1.0f * item_count / prev_class_count,
+	       1.0f * item_count / class_count);
+      fprintf (dump_file, "Equal symbols: %u\n", equal_items);
+      fprintf (dump_file, "Fraction of visited symbols: %.2f%%\n\n",
+	       100.0f * equal_items / item_count);
+    }
+
+  for (hash_table<congruence_class_group_hash>::iterator it = m_classes.begin ();
+       it != m_classes.end (); ++it)
+    for (unsigned int i = 0; i < (*it)->classes.length (); i++)
+      {
+	congruence_class *c = (*it)->classes[i];
+
+	if (c->members.length () == 1)
+	  continue;
+
+	gcc_assert (c->members.length ());
+
+	sem_item *source = c->members[0];
+
+	for (unsigned int j = 1; j < c->members.length (); j++)
+	  {
+	    sem_item *alias = c->members[j];
+
+	    if (dump_file)
+	      {
+		fprintf (dump_file, "Semantic equality hit:%s->%s\n",
+			 source->name (), alias->name ());
+		fprintf (dump_file, "Assembler symbol names:%s->%s\n",
+			 source->asm_name (), alias->asm_name ());
+	      }
+
+	    if (dump_file && (dump_flags & TDF_DETAILS))
+	      {
+		source->dump_to_file (dump_file);
+		alias->dump_to_file (dump_file);
+	      }
+
+	    source->merge (alias);
+	  }
+      }
+}
+
+/* Dump function prints all class members to a FILE with an INDENT.  */
+
+void
+congruence_class::dump (FILE *file, unsigned int indent) const
+{
+  FPRINTF_SPACES (file, indent, "class with id: %u, hash: %u, items: %u\n",
+		  id, members[0]->get_hash (), members.length ());
+
+  FPUTS_SPACES (file, indent + 2, "");
+  for (unsigned i = 0; i < members.length (); i++)
+    fprintf (file, "%s(%p/%u)", members[i]->asm_name (), (void *) members[i]->decl,
+	     members[i]->node->order);
+
+  fprintf (file, "\n");
+}
+
+/* Returns true if there's a member that is used from another group.  */
+
+bool
+congruence_class::is_class_used (void)
+{
+  for (unsigned int i = 0; i < members.length (); i++)
+    if (members[i]->usages.length ())
+      return true;
+
+  return false;
+}
+
+/* Initialization and computation of symtab node hash, there data
+   are propagated later on.  */
+
+static sem_item_optimizer *optimizer = NULL;
+
+/* Generate pass summary for IPA ICF pass.  */
+
+static void
+ipa_icf_generate_summary (void)
+{
+  if (!optimizer)
+    optimizer = new sem_item_optimizer ();
+
+  optimizer->parse_funcs_and_vars ();
+}
+
+/* Write pass summary for IPA ICF pass.  */
+
+static void
+ipa_icf_write_summary (void)
+{
+  gcc_assert (optimizer);
+
+  optimizer->write_summary ();
+}
+
+/* Read pass summary for IPA ICF pass.  */
+
+static void
+ipa_icf_read_summary (void)
+{
+  if (!optimizer)
+    optimizer = new sem_item_optimizer ();
+
+  optimizer->read_summary ();
+  optimizer->register_hooks ();
+}
+
+/* Semantic equality exection function.  */
+
+static unsigned int
+ipa_icf_driver (void)
+{
+  gcc_assert (optimizer);
+
+  optimizer->execute ();
+  optimizer->unregister_hooks ();
+
+  delete optimizer;
+
+  return 0;
+}
+
+const pass_data pass_data_ipa_icf =
+{
+  IPA_PASS,		    /* type */
+  "icf",		    /* name */
+  OPTGROUP_IPA,             /* optinfo_flags */
+  TV_IPA_ICF,		    /* tv_id */
+  0,                        /* properties_required */
+  0,                        /* properties_provided */
+  0,                        /* properties_destroyed */
+  0,                        /* todo_flags_start */
+  0,                        /* todo_flags_finish */
+};
+
+class pass_ipa_icf : public ipa_opt_pass_d
+{
+public:
+  pass_ipa_icf (gcc::context *ctxt)
+    : ipa_opt_pass_d (pass_data_ipa_icf, ctxt,
+		      ipa_icf_generate_summary, /* generate_summary */
+		      ipa_icf_write_summary, /* write_summary */
+		      ipa_icf_read_summary, /* read_summary */
+		      NULL, /*
+		      write_optimization_summary */
+		      NULL, /*
+		      read_optimization_summary */
+		      NULL, /* stmt_fixup */
+		      0, /* function_transform_todo_flags_start */
+		      NULL, /* function_transform */
+		      NULL) /* variable_transform */
+  {}
+
+  /* opt_pass methods: */
+  virtual bool gate (function *)
+  {
+    return flag_ipa_icf_variables || flag_ipa_icf_functions;
+  }
+
+  virtual unsigned int execute (function *)
+  {
+    return ipa_icf_driver();
+  }
+}; // class pass_ipa_icf
+
+} // ipa_icf namespace
+
+ipa_opt_pass_d *
+make_pass_ipa_icf (gcc::context *ctxt)
+{
+  return new ipa_icf::pass_ipa_icf (ctxt);
+}
diff --git a/gcc/ipa-icf.h b/gcc/ipa-icf.h
new file mode 100644
index 0000000..52f5722
--- /dev/null
+++ b/gcc/ipa-icf.h
@@ -0,0 +1,772 @@
+/* Interprocedural semantic function equality pass
+   Copyright (C) 2014 Free Software Foundation, Inc.
+
+   Contributed by Jan Hubicka <hubicka@ucw.cz> and Martin Liska <mliska@suse.cz>
+
+This file is part of GCC.
+
+GCC 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, or (at your option) any later
+version.
+
+GCC 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 GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+/* Prints string STRING to a FILE with a given number of SPACE_COUNT.  */
+#define FPUTS_SPACES(file, space_count, string) \
+  fprintf (file, "%*s" string, space_count, " "); \
+ 
+/* fprintf function wrapper that transforms given FORMAT to follow given
+   number for SPACE_COUNT and call fprintf for a FILE.  */
+#define FPRINTF_SPACES(file, space_count, format, ...) \
+  fprintf (file, "%*s" format, space_count, " ", ##__VA_ARGS__);
+
+/* Prints a MESSAGE to dump_file if exists. FUNC is name of function and
+   LINE is location in the source file.  */
+
+static inline void
+dump_message (const char *message, const char *func, unsigned int line)
+{
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    fprintf (dump_file, "  debug message: %s (%s:%u)\n", message, func, line);
+}
+
+/* Prints a MESSAGE to dump_file if exists.  */
+#define DUMP_MESSAGE(message) dump_message (message, __func__, __LINE__)
+
+/* Logs a MESSAGE to dump_file if exists and returns false. FUNC is name
+   of function and LINE is location in the source file.  */
+
+static inline bool
+return_false_with_message (const char *message, const char *func,
+			   unsigned int line)
+{
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    fprintf (dump_file, "  false returned: '%s' (%s:%u)\n", message, func, line);
+  return false;
+}
+
+/* Logs a MESSAGE to dump_file if exists and returns false.  */
+#define RETURN_FALSE_WITH_MSG(message) \
+  return_false_with_message (message, __func__, __LINE__)
+
+/* Return false and log that false value is returned.  */
+#define RETURN_FALSE() RETURN_FALSE_WITH_MSG ("")
+
+/* Logs return value if RESULT is false. FUNC is name of function and LINE
+   is location in the source file.  */
+
+static inline bool
+return_with_result (bool result, const char *func, unsigned int line)
+{
+  if (!result && dump_file && (dump_flags & TDF_DETAILS))
+    fprintf (dump_file, "  false returned (%s:%u)\n", func, line);
+
+  return result;
+}
+
+/* Logs return value if RESULT is false.  */
+#define RETURN_WITH_DEBUG(result) return_with_result (result, __func__, __LINE__)
+
+/* Verbose logging function logging statements S1 and S2 of a CODE.
+   FUNC is name of function and LINE is location in the source file.  */
+
+static inline bool
+return_different_stmts (gimple s1, gimple s2, const char *code,
+			const char *func, unsigned int line)
+{
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    {
+      fprintf (dump_file, "  different statement for code: %s (%s:%u):\n",
+	       code, func, line);
+
+      print_gimple_stmt (dump_file, s1, 3, TDF_DETAILS);
+      print_gimple_stmt (dump_file, s2, 3, TDF_DETAILS);
+    }
+
+  return false;
+}
+
+/* Verbose logging function logging statements S1 and S2 of a CODE.  */
+#define RETURN_DIFFERENT_STMTS(s1, s2, code) \
+  return_different_stmts (s1, s2, code, __func__, __LINE__)
+
+namespace ipa_icf {
+
+class sem_item;
+
+/* A class aggregating all connections and semantic equivalents
+   for a given pair of semantic function candidates.  */
+class func_checker
+{
+public:
+  /* Initialize internal structures according to given number of
+     source and target SSA names. The number of source names is SSA_SOURCE,
+     respectively SSA_TARGET.  */
+  func_checker (unsigned ssa_source, unsigned ssa_target);
+
+  /* Memory release routine.  */
+  ~func_checker();
+
+  /* Verifies that trees T1 and T2 are equivalent from perspective of ICF.  */
+  bool compare_ssa_name (tree t1, tree t2);
+
+  /* Verification function for edges E1 and E2.  */
+  bool compare_edge (edge e1, edge e2);
+
+  /* Verification function for declaration trees T1 and T2 that
+     come from functions FUNC1 and FUNC2.  */
+  bool compare_decl (tree t1, tree t2, tree func1, tree func2);
+
+private:
+  /* Vector mapping source SSA names to target ones.  */
+  vec <int> m_source_ssa_names;
+
+  /* Vector mapping target SSA names to source ones.  */
+  vec <int> m_target_ssa_names;
+
+  /* Source to target edge map.  */
+  hash_map <edge, edge> m_edge_map;
+
+  /* Source to target declaration map.  */
+  hash_map <tree, tree> m_decl_map;
+};
+
+/* Congruence class encompasses a collection of either functions or
+   read-only variables. These items are considered to be equivalent
+   if not proved the oposite.  */
+class congruence_class
+{
+public:
+  /* Congruence class constructor for a new class with _ID.  */
+  congruence_class (unsigned int _id): id(_id)
+  {
+    members.create (2);
+  }
+
+  /* Destructor.  */
+  ~congruence_class ()
+  {
+    members.release ();
+  }
+
+  /* Dump function prints all class members to a FILE with an INDENT.  */
+  void dump (FILE *file, unsigned int indent = 0) const;
+
+  /* Returns true if there's a member that is used from another group.  */
+  bool is_class_used (void);
+
+  /* Vector of all group members.  */
+  vec <sem_item *> members;
+
+  /* Global unique class identifier.  */
+  unsigned int id;
+};
+
+/* Semantic item type enum.  */
+enum sem_item_type
+{
+  FUNC,
+  VAR
+};
+
+/* Semantic item usage pair.  */
+class sem_usage_pair
+{
+public:
+  /* Constructor for key value pair, where _ITEM is key and _INDEX is a target.  */
+  sem_usage_pair (sem_item *_item, unsigned int _index);
+
+  /* Target semantic item where an item is used.  */
+  sem_item *item;
+
+  /* Index of usage of such an item.  */
+  unsigned int index;
+};
+
+/* Basic block struct for semantic equality pass.  */
+class sem_bb
+{
+public:
+  sem_bb (basic_block bb_, unsigned nondbg_stmt_count_, unsigned edge_count_):
+    bb (bb_), nondbg_stmt_count (nondbg_stmt_count_), edge_count (edge_count_) {}
+
+  /* Basic block the structure belongs to.  */
+  basic_block bb;
+
+  /* Number of non-debug statements in the basic block.  */
+  unsigned nondbg_stmt_count;
+
+  /* Number of edges connected to the block.  */
+  unsigned edge_count;
+};
+
+/* Semantic item is a base class that encapsulates all shared functionality
+   for both semantic function and variable items.  */
+class sem_item
+{
+public:
+  /* Semantic item constructor for a node of _TYPE, where STACK is used
+     for bitmap memory allocation.  */
+  sem_item (sem_item_type _type, bitmap_obstack *stack);
+
+  /* Semantic item constructor for a node of _TYPE, where STACK is used
+     for bitmap memory allocation. The item is based on symtab node _NODE
+     with computed _HASH.  */
+  sem_item (sem_item_type _type, struct symtab_node *_node, hashval_t _hash,
+	    bitmap_obstack *stack);
+
+  virtual ~sem_item ();
+
+  /* Dump function for debugging purpose.  */
+  DEBUG_FUNCTION void dump (void);
+
+  /* Initialize semantic item by info reachable during LTO WPA phase.  */
+  virtual void init_wpa (void) = 0;
+
+  /* Semantic item initialization function.  */
+  virtual void init (void) = 0;
+
+  /* Gets symbol name of the item.  */
+  const char *name (void)
+  {
+    return node->name ();
+  }
+
+  /* Gets assembler name of the item.  */
+  const char *asm_name (void)
+  {
+    return node->asm_name ();
+  }
+
+  /* Initialize references to other semantic functions/variables.  */
+  virtual void init_refs () = 0;
+
+  /* Fast equality function based on knowledge known in WPA.  */
+  virtual bool equals_wpa (sem_item *item) = 0;
+
+  /* Returns true if the item equals to ITEM given as arguemnt.  */
+  virtual bool equals (sem_item *item) = 0;
+
+  /* References independent hash function.  */
+  virtual hashval_t get_hash (void) = 0;
+
+  /* Merges instance with an ALIAS_ITEM, where alias, thunk or redirection can
+     be applied.  */
+  virtual bool merge (sem_item *alias_item) = 0;
+
+  /* Dump symbol to FILE.  */
+  virtual void dump_to_file (FILE *file) = 0;
+
+  /* Return base tree that can be used for types_compatible_p and
+     contains_polymorphic_type_p comparison.  */
+
+  static bool get_base_types (tree *t1, tree *t2);
+
+  /* Return true if types are compatible from perspective of ICF.
+     FIRST_ARGUMENT indicates if the comparison is called for first parameter of a function.  */
+  static bool types_are_compatible_p (tree t1, tree t2,
+				      bool first_argument = false);
+
+  /* Item type.  */
+  sem_item_type type;
+
+  /* Global unique function index.  */
+  unsigned int index;
+
+  /* Symtab node.  */
+  struct symtab_node *node;
+
+  /* Declaration tree node.  */
+  tree decl;
+
+  /* Semantic references used that generate congruence groups.  */
+  vec <sem_item *> refs;
+
+  /* Pointer to a congruence class the item belongs to.  */
+  congruence_class *cls;
+
+  /* Index of the item in a class belonging to.  */
+  unsigned int index_in_class;
+
+  /* List of semantic items where the instance is used.  */
+  vec <sem_usage_pair *> usages;
+
+  /* A bitmap with indices of all classes referencing this item.  */
+  bitmap usage_index_bitmap;
+
+  /* List of tree references (either FUNC_DECL or VAR_DECL).  */
+  vec <tree> tree_refs;
+
+  /* A set with tree references (either FUNC_DECL or VAR_DECL).  */
+  pointer_set_t *tree_refs_set;
+
+protected:
+  /* Cached, once calculated hash for the item.  */
+  hashval_t hash;
+
+private:
+  /* Initialize internal data structures. Bitmap STACK is used for
+     bitmap memory allocation process.  */
+  void setup (bitmap_obstack *stack);
+}; // class sem_item
+
+class sem_function: public sem_item
+{
+public:
+  /* Semantic function constructor that uses STACK as bitmap memory stack.  */
+  sem_function (bitmap_obstack *stack);
+
+  /*  Constructor based on callgraph node _NODE with computed hash _HASH.
+      Bitmap STACK is used for memory allocation.  */
+  sem_function (cgraph_node *_node, hashval_t _hash, bitmap_obstack *stack);
+
+  ~sem_function ();
+
+  inline virtual void init_wpa (void)
+  {
+    parse_tree_args ();
+  }
+
+  virtual void init (void);
+  virtual bool equals_wpa (sem_item *item);
+  virtual hashval_t get_hash (void);
+  virtual bool equals (sem_item *item);
+  virtual void init_refs ();
+  virtual bool merge (sem_item *alias_item);
+
+  /* Dump symbol to FILE.  */
+  virtual void dump_to_file (FILE *file)
+  {
+    gcc_assert (file);
+    dump_function_to_file (decl, file, TDF_DETAILS);
+  }
+
+  /* Parses function arguments and result type.  */
+  void parse_tree_args (void);
+
+  /* Returns cgraph_node.  */
+  inline struct cgraph_node *get_node (void)
+  {
+    return cgraph (node);
+  }
+
+  /* For a given call graph NODE, the function constructs new
+     semantic function item.  */
+  static sem_function *parse (struct cgraph_node *node, bitmap_obstack *stack);
+
+  /* Exception handling region tree.  */
+  eh_region region_tree;
+
+  /* Result type tree node.  */
+  tree result_type;
+
+  /* Array of argument tree types.  */
+  vec <tree> arg_types;
+
+  /* Number of function arguments.  */
+  unsigned int arg_count;
+
+  /* Total amount of edges in the function.  */
+  unsigned int edge_count;
+
+  /* Vector of sizes of all basic blocks.  */
+  vec <unsigned int> bb_sizes;
+
+  /* Control flow graph checksum.  */
+  hashval_t cfg_checksum;
+
+  /* GIMPLE codes hash value.  */
+  hashval_t gcode_hash;
+
+  /* Total number of SSA names used in the function.  */
+  unsigned ssa_names_size;
+
+  /* Array of structures for all basic blocks.  */
+  vec <sem_bb *> bb_sorted;
+
+private:
+  /* Calculates hash value based on a BASIC_BLOCK.  */
+  hashval_t get_bb_hash (const sem_bb *basic_block);
+
+  /* Basic block equivalence comparison function that returns true if
+     basic blocks BB1 and BB2 (from functions FUNC1 and FUNC2) correspond.  */
+  bool compare_bb (sem_bb *bb1, sem_bb *bb2, tree func1, tree func2);
+
+  /* For given basic blocks BB1 and BB2 (from functions FUNC1 and FUNC),
+     true value is returned if phi nodes are semantically
+     equivalent in these blocks .  */
+  bool compare_phi_node (basic_block bb1, basic_block bb2, tree func1,
+			 tree func2);
+
+  /* For given basic blocks BB1 and BB2 (from functions FUNC1 and FUNC),
+     true value is returned if exception handling regions are equivalent
+     in these blocks.  */
+  bool compare_eh_region (eh_region r1, eh_region r2, tree func1, tree func2);
+
+  /* Verifies that trees T1 and T2, representing function declarations
+     are equivalent from perspective of ICF.  */
+  bool compare_function_decl (tree t1, tree t2);
+
+  /* Verifies that trees T1 and T2 do correspond.  */
+  bool compare_variable_decl (tree t1, tree t2, tree func1, tree func2);
+
+  /* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
+     call statements are semantically equivalent.  */
+  bool compare_gimple_call (gimple s1, gimple s2,
+			    tree func1, tree func2);
+
+  /* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
+     assignment statements are semantically equivalent.  */
+  bool compare_gimple_assign (gimple s1, gimple s2, tree func1, tree func2);
+
+  /* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
+     condition statements are semantically equivalent.  */
+  bool compare_gimple_cond (gimple s1, gimple s2, tree func1, tree func2);
+
+  /* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
+     label statements are semantically equivalent.  */
+  bool compare_gimple_label (gimple s1, gimple s2, tree func1, tree func2);
+
+  /* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
+     switch statements are semantically equivalent.  */
+  bool compare_gimple_switch (gimple s1, gimple s2, tree func1, tree func2);
+
+  /* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
+     return statements are semantically equivalent.  */
+  bool compare_gimple_return (gimple s1, gimple s2, tree func1, tree func2);
+
+  /* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
+     goto statements are semantically equivalent.  */
+  bool compare_gimple_goto (gimple s1, gimple s2, tree func1, tree func2);
+
+  /* Verifies for given GIMPLEs S1 and S2 (from function FUNC1, resp. FUNC2) that
+     resx statements are semantically equivalent.  */
+  bool compare_gimple_resx (gimple s1, gimple s2);
+
+  /* Verifies for given GIMPLEs S1 and S2 that ASM statements are equivalent.
+     For the beginning, the pass only supports equality for
+     '__asm__ __volatile__ ("", "", "", "memory")'.  */
+  bool compare_gimple_asm (gimple s1, gimple s2);
+
+  /* Verifies that tree labels T1 and T2 correspond in FUNC1 and FUNC2.  */
+  bool compare_tree_ssa_label (tree t1, tree t2, tree func1, tree func2);
+
+  /* Function compares two operands T1 and T2 and returns true if these
+     two trees from FUNC1 (respectively FUNC2) are semantically equivalent.  */
+  bool compare_operand (tree t1, tree t2, tree func1, tree func2);
+
+  /* If T1 and T2 are SSA names, dictionary comparison is processed. Otherwise,
+     declaration comparasion is executed.  */
+  bool compare_ssa_name (tree t1, tree t2, tree func1, tree func2);
+
+  /* Basic blocks dictionary BB_DICT returns true if SOURCE index BB
+     corresponds to TARGET.  */
+  bool bb_dict_test (int* bb_dict, int source, int target);
+
+  /* Iterates all tree types in T1 and T2 and returns true if all types
+     are compatible.  */
+  bool compare_type_list (tree t1, tree t2);
+
+  /* Processes function equality comparison.  */
+  bool equals_private (sem_item *item);
+
+  /* Initialize references to another sem_item for gimple STMT of type assign.  */
+  void init_refs_for_assign (gimple stmt);
+
+  /* Initialize references to another sem_item for tree T.  */
+  void init_refs_for_tree (tree t);
+
+  /* Returns true if tree T can be compared as a handled component.  */
+  static bool icf_handled_component_p (tree t);
+
+  /* Function checker stores binding between functions.   */
+  func_checker *m_checker;
+
+  /* COMPARED_FUNC is a function that we compare to.  */
+  sem_function *m_compared_func;
+}; // class sem_function
+
+class sem_variable: public sem_item
+{
+public:
+  /* Semantic variable constructor that uses STACK as bitmap memory stack.  */
+  sem_variable (bitmap_obstack *stack);
+
+  /*  Constructor based on callgraph node _NODE with computed hash _HASH.
+      Bitmap STACK is used for memory allocation.  */
+
+  sem_variable (varpool_node *_node, hashval_t _hash, bitmap_obstack *stack);
+
+  inline virtual void init_wpa (void) {}
+
+  /* Semantic variable initialization function.  */
+  inline virtual void init (void)
+  {
+    decl = get_node ()->decl;
+    ctor = ctor_for_folding (decl);
+  }
+
+  /* Initialize references to other semantic functions/variables.  */
+  inline virtual void init_refs ()
+  {
+    parse_tree_refs (ctor);
+  }
+
+  virtual hashval_t get_hash (void);
+  virtual bool merge (sem_item *alias_item);
+  virtual void dump_to_file (FILE *file);
+  virtual bool equals (sem_item *item);
+
+  /* Fast equality variable based on knowledge known in WPA.  */
+  inline virtual bool equals_wpa (sem_item *item)
+  {
+    gcc_assert (item->type == VAR);
+    return true;
+  }
+
+  /* Returns varpool_node.  */
+  inline struct varpool_node *get_node (void)
+  {
+    return varpool (node);
+  }
+
+  /* Parser function that visits a varpool NODE.  */
+  static sem_variable *parse (struct varpool_node *node, bitmap_obstack *stack);
+
+  /* Variable constructor.  */
+  tree ctor;
+
+private:
+  /* Iterates though a constructor and identifies tree references
+     we are interested in semantic function equality.  */
+  void parse_tree_refs (tree t);
+
+  /* Compares trees T1 and T2 for semantic equality.  */
+  static bool equals (tree t1, tree t2);
+
+  /* Compare that symbol sections are either NULL or have same name.  */
+  bool compare_sections (sem_variable *alias);
+
+}; // class sem_variable
+
+class sem_item_optimizer;
+
+/* Congruence class set structure.  */
+struct congruence_class_var_hash: typed_noop_remove <congruence_class>
+{
+  typedef congruence_class value_type;
+  typedef congruence_class compare_type;
+
+  static inline hashval_t hash (const value_type *item)
+  {
+    return htab_hash_pointer (item);
+  }
+
+  static inline int equal (const value_type *item1, const compare_type *item2)
+  {
+    return item1 == item2;
+  }
+};
+
+struct congruence_class_group
+{
+  hashval_t hash;
+  sem_item_type type;
+  vec <congruence_class *> classes;
+};
+
+/* Congruence class set structure.  */
+struct congruence_class_group_hash: typed_noop_remove <congruence_class_group>
+{
+  typedef congruence_class_group value_type;
+  typedef congruence_class_group compare_type;
+
+  static inline hashval_t hash (const value_type *item)
+  {
+    return item->hash;
+  }
+
+  static inline int equal (const value_type *item1, const compare_type *item2)
+  {
+    return item1->hash == item2->hash && item1->type == item2->type;
+  }
+};
+
+struct traverse_split_pair
+{
+  sem_item_optimizer *optimizer;
+  class congruence_class *cls;
+};
+
+/* Semantic item optimizer includes all top-level logic
+   related to semantic equality comparison.  */
+class sem_item_optimizer
+{
+public:
+  sem_item_optimizer ();
+  ~sem_item_optimizer ();
+
+  /* Function responsible for visiting all potential functions and
+     read-only variables that can be merged.  */
+  void parse_funcs_and_vars (void);
+
+  /* Optimizer entry point.  */
+  void execute (void);
+
+  /* Dump function. */
+  void dump (void);
+
+  /* Verify congruence classes if checking is enabled.  */
+  void verify_classes (void);
+
+  /* Write IPA ICF summary for symbols.  */
+  void write_summary (void);
+
+  /* Read IPA IPA ICF summary for symbols.  */
+  void read_summary (void);
+
+  /* Callgraph removal hook called for a NODE with a custom DATA.  */
+  static void cgraph_removal_hook (struct cgraph_node *node, void *data);
+
+  /* Varpool removal hook called for a NODE with a custom DATA.  */
+  static void varpool_removal_hook (struct varpool_node *node, void *data);
+
+  /* Worklist of congruence classes that can potentially
+     refine classes of congruence.  */
+  hash_table <congruence_class_var_hash> worklist;
+
+  /* Remove symtab NODE triggered by symtab removal hooks.  */
+  void remove_symtab_node (struct symtab_node *node);
+
+  /* Register callgraph and varpool hooks.  */
+  void register_hooks (void);
+
+  /* Unregister callgraph and varpool hooks.  */
+  void unregister_hooks (void);
+
+  /* Adds a CLS to hashtable associated by hash value.  */
+  void add_class (congruence_class *cls);
+
+  /* Gets a congruence class group based on given HASH value and TYPE.  */
+  congruence_class_group *get_group_by_hash (hashval_t hash,
+      sem_item_type type);
+
+private:
+
+  /* Congruence classes are built by hash value.  */
+  void build_hash_based_classes (void);
+
+  /* Semantic items in classes having more than one element and initialized.
+     In case of WPA, we load function body.  */
+  void parse_nonsingleton_classes (void);
+
+  /* Equality function for semantic items is used to subdivide existing
+     classes. If IN_WPA, fast equality function is invoked.  */
+  void subdivide_classes_by_equality (bool in_wpa = false);
+
+  /* Debug function prints all informations about congruence classes.  */
+  void dump_cong_classes (void);
+
+  /* Iterative congruence reduction function.  */
+  void process_cong_reduction (void);
+
+  /* After reduction is done, we can declare all items in a group
+     to be equal. PREV_CLASS_COUNT is start number of classes
+     before reduction.  */
+  void merge_classes (unsigned int prev_class_count);
+
+  /* Adds a newly created congruence class CLS to worklist.  */
+  void worklist_push (congruence_class *cls);
+
+  /* Pops a class from worklist. */
+  congruence_class *worklist_pop ();
+
+  /* Returns true if a congruence class CLS is present in worklist.  */
+  inline bool worklist_contains (const congruence_class *cls)
+  {
+    return worklist.find (cls);
+  }
+
+  /* Removes given congruence class CLS from worklist.  */
+  inline void worklist_remove (const congruence_class *cls)
+  {
+    worklist.remove_elt (cls);
+  }
+
+  /* Every usage of a congruence class CLS is a candidate that can split the
+     collection of classes. Bitmap stack BMSTACK is used for bitmap
+     allocation.  */
+  void do_congruence_step (congruence_class *cls);
+
+  /* Tests if a class CLS used as INDEXth splits any congruence classes.
+     Bitmap stack BMSTACK is used for bitmap allocation.  */
+  void do_congruence_step_for_index (congruence_class *cls, unsigned int index);
+
+  /* Makes pairing between a congruence class CLS and semantic ITEM.  */
+  static void add_item_to_class (congruence_class *cls, sem_item *item);
+
+  /* Disposes split map traverse function. CLS is congruence
+     class, BSLOT is bitmap slot we want to release. DATA is mandatory,
+     but unused argument.  */
+  static bool release_split_map (congruence_class * const &cls, bitmap const &b,
+				 traverse_split_pair *pair);
+
+  /* Process split operation for a cognruence class CLS,
+     where bitmap B splits congruence class members. DATA is used
+     as argument of split pair.  */
+  static bool traverse_congruence_split (congruence_class * const &cls,
+					 bitmap const &b,
+					 traverse_split_pair *pair);
+
+  /* Reads a section from LTO stream file FILE_DATA. Input block for DATA
+     contains LEN bytes.  */
+  void read_section (struct lto_file_decl_data *file_data, const char *data,
+		     size_t len);
+
+  /* Removes all callgraph and varpool nodes that are marked by symtab
+     as deleted.  */
+  void filter_removed_items (void);
+
+  /* Vector of semantic items.  */
+  vec <sem_item *> m_items;
+
+  /* A set containing all items removed by hooks.  */
+  pointer_set_t *m_removed_items_set;
+
+  /* Hashtable of congruence classes */
+  hash_table <congruence_class_group_hash> m_classes;
+
+  /* Count of congruence classes.  */
+  unsigned int m_classes_count;
+
+  /* Map data structure maps trees to semantic items.  */
+  hash_map <tree, sem_item *> m_decl_map;
+
+  /* Map data structure maps symtab nodes to semantic items.  */
+  hash_map <symtab_node *, sem_item *> m_symtab_node_map;
+
+  /* Set to true if a splitter class is removed.  */
+  bool splitter_class_removed;
+
+  /* Global unique class id counter.  */
+  static unsigned int class_id;
+
+  /* Callgraph node removal hook holder.  */
+  struct cgraph_node_hook_list *m_cgraph_node_hooks;
+
+  /* Varpool node removal hook holder.  */
+  struct varpool_node_hook_list *m_varpool_node_hooks;
+
+  /* Bitmap stack.  */
+  bitmap_obstack m_bmstack;
+}; // class sem_item_optimizer
+
+} // ipa_icf namespace
diff --git a/gcc/lto-section-in.c b/gcc/lto-section-in.c
index d887763..f9587cf 100644
--- a/gcc/lto-section-in.c
+++ b/gcc/lto-section-in.c
@@ -60,7 +60,8 @@ const char *lto_section_name[LTO_N_SECTION_TYPES] =
   "opts",
   "cgraphopt",
   "inline",
-  "ipcp_trans"
+  "ipcp_trans",
+  "icf"
 };
 
 
diff --git a/gcc/lto-streamer.h b/gcc/lto-streamer.h
index c6cf72c..01af67a 100644
--- a/gcc/lto-streamer.h
+++ b/gcc/lto-streamer.h
@@ -248,6 +248,7 @@ enum lto_section_type
   LTO_section_cgraph_opt_sum,
   LTO_section_inline_summary,
   LTO_section_ipcp_transform,
+  LTO_section_ipa_icf,
   LTO_N_SECTION_TYPES		/* Must be last.  */
 };
 
diff --git a/gcc/opts.c b/gcc/opts.c
index 5fed6f0..8775f8b 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -497,6 +497,7 @@ static const struct default_options default_options_table[] =
     { OPT_LEVELS_2_PLUS, OPT_fvect_cost_model_, NULL, VECT_COST_MODEL_CHEAP },
     { OPT_LEVELS_2_PLUS_SPEED_ONLY, OPT_foptimize_strlen, NULL, 1 },
     { OPT_LEVELS_2_PLUS, OPT_fhoist_adjacent_loads, NULL, 1 },
+    { OPT_LEVELS_2_PLUS, OPT_fipa_icf, NULL, 1 },
     { OPT_LEVELS_2_PLUS, OPT_fisolate_erroneous_paths_dereference, NULL, 1 },
     { OPT_LEVELS_2_PLUS, OPT_fuse_caller_save, NULL, 1 },
 
@@ -1920,6 +1921,11 @@ common_handle_option (struct gcc_options *opts,
 	opts->x_flag_wrapv = 0;
       break;
 
+    case OPT_fipa_icf:
+	opts->x_flag_ipa_icf_functions = value;
+	opts->x_flag_ipa_icf_variables = value;
+      break;
+
     default:
       /* If the flag was handled in a standard way, assume the lack of
 	 processing here is intentional.  */
diff --git a/gcc/passes.def b/gcc/passes.def
index f13df6c..90dedd0 100644
--- a/gcc/passes.def
+++ b/gcc/passes.def
@@ -104,6 +104,7 @@ along with GCC; see the file COPYING3.  If not see
   NEXT_PASS (pass_ipa_whole_program_visibility);
   NEXT_PASS (pass_ipa_profile);
   NEXT_PASS (pass_ipa_devirt);
+  NEXT_PASS (pass_ipa_icf);
   NEXT_PASS (pass_ipa_cp);
   NEXT_PASS (pass_ipa_cdtor_merge);
   NEXT_PASS (pass_ipa_inline);
diff --git a/gcc/timevar.def b/gcc/timevar.def
index a04d05c..55a230b 100644
--- a/gcc/timevar.def
+++ b/gcc/timevar.def
@@ -90,6 +90,7 @@ DEFTIMEVAR (TV_WHOPR_LTRANS          , "whopr ltrans")
 DEFTIMEVAR (TV_IPA_REFERENCE         , "ipa reference")
 DEFTIMEVAR (TV_IPA_PROFILE           , "ipa profile")
 DEFTIMEVAR (TV_IPA_PURE_CONST        , "ipa pure const")
+DEFTIMEVAR (TV_IPA_ICF		     , "ipa icf")
 DEFTIMEVAR (TV_IPA_PTA               , "ipa points-to")
 DEFTIMEVAR (TV_IPA_SRA               , "ipa SRA")
 DEFTIMEVAR (TV_IPA_FREE_LANG_DATA    , "ipa free lang data")
diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h
index 1477d1f..95e9c96 100644
--- a/gcc/tree-pass.h
+++ b/gcc/tree-pass.h
@@ -460,6 +460,7 @@ extern simple_ipa_opt_pass *make_pass_ipa_free_lang_data (gcc::context *ctxt);
 extern simple_ipa_opt_pass *make_pass_ipa_free_inline_summary (gcc::context
 							       *ctxt);
 extern ipa_opt_pass_d *make_pass_ipa_cp (gcc::context *ctxt);
+extern ipa_opt_pass_d *make_pass_ipa_icf (gcc::context *ctxt);
 extern ipa_opt_pass_d *make_pass_ipa_devirt (gcc::context *ctxt);
 extern ipa_opt_pass_d *make_pass_ipa_reference (gcc::context *ctxt);
 extern ipa_opt_pass_d *make_pass_ipa_pure_const (gcc::context *ctxt);

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 2/5] Existing call graph infrastructure enhancement
  2014-07-17 14:54         ` Martin Liška
@ 2014-08-25  9:56           ` Martin Liška
  2014-08-25 16:12             ` Jan Hubicka
  2014-08-27 21:12             ` Jeff Law
  0 siblings, 2 replies; 70+ messages in thread
From: Martin Liška @ 2014-08-25  9:56 UTC (permalink / raw)
  To: gcc-patches; +Cc: Jan Hubicka

[-- Attachment #1: Type: text/plain, Size: 2663 bytes --]

Hello,
    after fixing an issue with callgraph thunk creation, I would like to enhance callgraph API a bit. My problem was that I was trying to expand_thunk after a body of the original function was removed. As a result, I created a call without arguments. The change was suggested by Honza.

Thank you,
Martin

On 07/17/2014 03:49 PM, Martin Liška wrote:
>
> On 06/30/2014 08:54 PM, Jeff Law wrote:
>> On 06/30/14 05:49, Martin Liška wrote:
>>>
>>> On 06/17/2014 10:00 PM, Jeff Law wrote:
>>>> On 06/13/14 04:26, mliska wrote:
>>>>> Hi,
>>>>>      this small patch prepares remaining needed infrastructure for
>>>>> the new pass.
>>>>>
>>>>> Changelog:
>>>>>
>>>>> 2014-06-13  Martin Liska  <mliska@suse.cz>
>>>>>         Honza Hubicka  <hubicka@ucw.cz>
>>>>>
>>>>>     * ipa-utils.h (polymorphic_type_binfo_p): Function marked external
>>>>>     instead of static.
>>>>>     * ipa-devirt.c (polymorphic_type_binfo_p): Likewise.
>>>>>     * ipa-prop.h (count_formal_params): Likewise.
>>>>>     * ipa-prop.c (count_formal_params): Likewise.
>>>>>     * ipa-utils.c (ipa_merge_profiles): Be more tolerant if we merge
>>>>>     profiles for semantically equivalent functions.
>>>>>     * passes.c (do_per_function): If we load body of a function
>>>>> during WPA,
>>>>>     this condition should behave same.
>>>>>     * varpool.c (ctor_for_folding): More tolerant assert for variable
>>>>>     aliases created during WPA.
>>>> Presumably we don't have any useful way to merge the cases where we
>>>> have provides for SRC & DST in ipa_merge_profiles or even to guess
>>>> which is more useful when presented with both?  Does it make sense to
>>>> log this into a debugging file when we drop one?
>>> Hello,
>>>     this merge function was written by Honza, what do you think Honza
>>> about this note?
>>>
>>>> I think this patch is fine.  If adding logging makes sense, then feel
>>>> free to do so and consider that trivial change pre-approved.
>>> I made a small change to this patch, where I moved
>>> 'gsi_next_nonvirtual_phi' from the pass to gimple-iterator.h.
>>>
>>> Ready for trunk with this change?
>> Yes.  I think with the exception of patch #3/5 everything looks good. I'll try to get another pass over #3 this week.  What I looked at last week was pretty good; I'm pretty confident this will be wrapped up shortly.
>>
>> If #1/#2 make sense to install independent of #3, go ahead. #4/#5 are obviously dependent on #3.
>>
>> Jeff
>
> Hello,
>    thank you for approval, this final version removes few hunks that are not needed any more. Changes are just cosmetic and I will commit the patch at the beginning of next week.
>
> Thanks,
> Martin
>


[-- Attachment #2: ipa-icf-release-body.changelog --]
[-- Type: text/plain, Size: 146 bytes --]

2014-08-25  Martin Liska  <mliska@suse.cz>

	* cgraph.h (release_body):  new argument keep_arguments added.
	* cgraph.c (release_body): Likewise.

[-- Attachment #3: ipa-icf-release-body.patch --]
[-- Type: text/x-patch, Size: 1266 bytes --]

diff --git a/gcc/cgraph.c b/gcc/cgraph.c
index cb49cdc..ad32fb8 100644
--- a/gcc/cgraph.c
+++ b/gcc/cgraph.c
@@ -1716,13 +1716,15 @@ release_function_body (tree decl)
    are free'd in final.c via free_after_compilation().  */
 
 void
-cgraph_node::release_body (void)
+cgraph_node::release_body (bool keep_arguments)
 {
   ipa_transforms_to_apply.release ();
   if (!used_as_abstract_origin && cgraph_state != CGRAPH_STATE_PARSING)
     {
       DECL_RESULT (decl) = NULL;
-      DECL_ARGUMENTS (decl) = NULL;
+
+      if (!keep_arguments)
+       DECL_ARGUMENTS (decl) = NULL;
     }
   /* If the node is abstract and needed, then do not clear DECL_INITIAL
      of its associated function function declaration because it's
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index 2594ae5..5edcf74 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -890,7 +890,7 @@ public:
      Use this only for functions that are released before being translated to
      target code (i.e. RTL).  Functions that are compiled to RTL and beyond
      are free'd in final.c via free_after_compilation().  */
-  void release_body (void);
+  void release_body (bool keep_arguments = false);
 
   /* cgraph_node is no longer nested function; update cgraph accordingly.  */
   void unnest (void);

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 2/5] Existing call graph infrastructure enhancement
  2014-08-25  9:56           ` Martin Liška
@ 2014-08-25 16:12             ` Jan Hubicka
  2014-08-27 21:12             ` Jeff Law
  1 sibling, 0 replies; 70+ messages in thread
From: Jan Hubicka @ 2014-08-25 16:12 UTC (permalink / raw)
  To: Martin Liška; +Cc: gcc-patches, Jan Hubicka

> Hello,
>    after fixing an issue with callgraph thunk creation, I would like to enhance callgraph API a bit. My problem was that I was trying to expand_thunk after a body of the original function was removed. As a result, I created a call without arguments. The change was suggested by Honza.
> 
> Thank you,
> Martin
> 
> On 07/17/2014 03:49 PM, Martin Liška wrote:
> >
> >On 06/30/2014 08:54 PM, Jeff Law wrote:
> >>On 06/30/14 05:49, Martin Liška wrote:
> >>>
> >>>On 06/17/2014 10:00 PM, Jeff Law wrote:
> >>>>On 06/13/14 04:26, mliska wrote:
> >>>>>Hi,
> >>>>>     this small patch prepares remaining needed infrastructure for
> >>>>>the new pass.
> >>>>>
> >>>>>Changelog:
> >>>>>
> >>>>>2014-06-13  Martin Liska  <mliska@suse.cz>
> >>>>>        Honza Hubicka  <hubicka@ucw.cz>
> >>>>>
> >>>>>    * ipa-utils.h (polymorphic_type_binfo_p): Function marked external
> >>>>>    instead of static.
> >>>>>    * ipa-devirt.c (polymorphic_type_binfo_p): Likewise.
> >>>>>    * ipa-prop.h (count_formal_params): Likewise.
> >>>>>    * ipa-prop.c (count_formal_params): Likewise.
> >>>>>    * ipa-utils.c (ipa_merge_profiles): Be more tolerant if we merge
> >>>>>    profiles for semantically equivalent functions.
> >>>>>    * passes.c (do_per_function): If we load body of a function
> >>>>>during WPA,
> >>>>>    this condition should behave same.
> >>>>>    * varpool.c (ctor_for_folding): More tolerant assert for variable
> >>>>>    aliases created during WPA.
> >>>>Presumably we don't have any useful way to merge the cases where we
> >>>>have provides for SRC & DST in ipa_merge_profiles or even to guess
> >>>>which is more useful when presented with both?  Does it make sense to
> >>>>log this into a debugging file when we drop one?
> >>>Hello,
> >>>    this merge function was written by Honza, what do you think Honza
> >>>about this note?
> >>>
> >>>>I think this patch is fine.  If adding logging makes sense, then feel
> >>>>free to do so and consider that trivial change pre-approved.
> >>>I made a small change to this patch, where I moved
> >>>'gsi_next_nonvirtual_phi' from the pass to gimple-iterator.h.
> >>>
> >>>Ready for trunk with this change?
> >>Yes.  I think with the exception of patch #3/5 everything looks good. I'll try to get another pass over #3 this week.  What I looked at last week was pretty good; I'm pretty confident this will be wrapped up shortly.
> >>
> >>If #1/#2 make sense to install independent of #3, go ahead. #4/#5 are obviously dependent on #3.
> >>
> >>Jeff
> >
> >Hello,
> >   thank you for approval, this final version removes few hunks that are not needed any more. Changes are just cosmetic and I will commit the patch at the beginning of next week.
> >
> >Thanks,
> >Martin
> >
> 

> 2014-08-25  Martin Liska  <mliska@suse.cz>
> 
> 	* cgraph.h (release_body):  new argument keep_arguments added.
> 	* cgraph.c (release_body): Likewise.

OK,
thanks!
Honza

> diff --git a/gcc/cgraph.c b/gcc/cgraph.c
> index cb49cdc..ad32fb8 100644
> --- a/gcc/cgraph.c
> +++ b/gcc/cgraph.c
> @@ -1716,13 +1716,15 @@ release_function_body (tree decl)
>     are free'd in final.c via free_after_compilation().  */
>  
>  void
> -cgraph_node::release_body (void)
> +cgraph_node::release_body (bool keep_arguments)
>  {
>    ipa_transforms_to_apply.release ();
>    if (!used_as_abstract_origin && cgraph_state != CGRAPH_STATE_PARSING)
>      {
>        DECL_RESULT (decl) = NULL;
> -      DECL_ARGUMENTS (decl) = NULL;
> +
> +      if (!keep_arguments)
> +       DECL_ARGUMENTS (decl) = NULL;
>      }
>    /* If the node is abstract and needed, then do not clear DECL_INITIAL
>       of its associated function function declaration because it's
> diff --git a/gcc/cgraph.h b/gcc/cgraph.h
> index 2594ae5..5edcf74 100644
> --- a/gcc/cgraph.h
> +++ b/gcc/cgraph.h
> @@ -890,7 +890,7 @@ public:
>       Use this only for functions that are released before being translated to
>       target code (i.e. RTL).  Functions that are compiled to RTL and beyond
>       are free'd in final.c via free_after_compilation().  */
> -  void release_body (void);
> +  void release_body (bool keep_arguments = false);
>  
>    /* cgraph_node is no longer nested function; update cgraph accordingly.  */
>    void unnest (void);

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 2/5] Existing call graph infrastructure enhancement
  2014-08-25  9:56           ` Martin Liška
  2014-08-25 16:12             ` Jan Hubicka
@ 2014-08-27 21:12             ` Jeff Law
  1 sibling, 0 replies; 70+ messages in thread
From: Jeff Law @ 2014-08-27 21:12 UTC (permalink / raw)
  To: Martin Liška, gcc-patches; +Cc: Jan Hubicka

On 08/25/14 03:55, Martin Liška wrote:
> Hello,
>     after fixing an issue with callgraph thunk creation, I would like to
> enhance callgraph API a bit. My problem was that I was trying to
> expand_thunk after a body of the original function was removed. As a
> result, I created a call without arguments. The change was suggested by
> Honza.
>
[ ... ]

>>
>
>
> ipa-icf-release-body.changelog
>
>
> 2014-08-25  Martin Liska<mliska@suse.cz>
>
> 	* cgraph.h (release_body):  new argument keep_arguments added.
> 	* cgraph.c (release_body): Likewise.
OK.  Capitalize "New" in the ChangeLog entry.

jeff

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 2/5] Existing call graph infrastructure enhancement
  2014-06-16 10:07 ` [PATCH 2/5] Existing call graph infrastructure enhancement mliska
  2014-06-17 20:00   ` Jeff Law
@ 2014-09-24 14:23   ` Martin Liška
  2014-09-24 15:01     ` Jan Hubicka
  1 sibling, 1 reply; 70+ messages in thread
From: Martin Liška @ 2014-09-24 14:23 UTC (permalink / raw)
  To: gcc-patches; +Cc: hubicka

[-- Attachment #1: Type: text/plain, Size: 4389 bytes --]

On 06/13/2014 12:26 PM, mliska wrote:
> Hi,
>      this small patch prepares remaining needed infrastructure for the new pass.
>
> Changelog:
>
> 2014-06-13  Martin Liska  <mliska@suse.cz>
> 	    Honza Hubicka  <hubicka@ucw.cz>
>
> 	* ipa-utils.h (polymorphic_type_binfo_p): Function marked external
> 	instead of static.
> 	* ipa-devirt.c (polymorphic_type_binfo_p): Likewise.
> 	* ipa-prop.h (count_formal_params): Likewise.
> 	* ipa-prop.c (count_formal_params): Likewise.
> 	* ipa-utils.c (ipa_merge_profiles): Be more tolerant if we merge
> 	profiles for semantically equivalent functions.
> 	* passes.c (do_per_function): If we load body of a function during WPA,
> 	this condition should behave same.
> 	* varpool.c (ctor_for_folding): More tolerant assert for variable
> 	aliases created during WPA.
>
> diff --git a/gcc/ipa-devirt.c b/gcc/ipa-devirt.c
> index d733461..18592d7 100644
> --- a/gcc/ipa-devirt.c
> +++ b/gcc/ipa-devirt.c
> @@ -176,7 +176,7 @@ struct GTY(()) odr_type_d
>      inheritance (because vtables are shared).  Look up the BINFO of type
>      and check presence of its vtable.  */
>
> -static inline bool
> +bool
>   polymorphic_type_binfo_p (tree binfo)
>   {
>     /* See if BINFO's type has an virtual table associtated with it.  */
> diff --git a/gcc/ipa-prop.c b/gcc/ipa-prop.c
> index b67deed..60bda71 100644
> --- a/gcc/ipa-prop.c
> +++ b/gcc/ipa-prop.c
> @@ -210,7 +210,7 @@ ipa_populate_param_decls (struct cgraph_node *node,
>
>   /* Return how many formal parameters FNDECL has.  */
>
> -static inline int
> +int
>   count_formal_params (tree fndecl)
>   {
>     tree parm;
> diff --git a/gcc/ipa-prop.h b/gcc/ipa-prop.h
> index cb23698..87573ff 100644
> --- a/gcc/ipa-prop.h
> +++ b/gcc/ipa-prop.h
> @@ -529,6 +529,7 @@ void ipa_free_all_edge_args (void);
>   void ipa_free_all_structures_after_ipa_cp (void);
>   void ipa_free_all_structures_after_iinln (void);
>   void ipa_register_cgraph_hooks (void);
> +int count_formal_params (tree fndecl);
>
>   /* This function ensures the array of node param infos is big enough to
>      accommodate a structure for all nodes and reallocates it if not.  */
> diff --git a/gcc/ipa-utils.c b/gcc/ipa-utils.c
> index 8e7c7cb..bc2b958 100644
> --- a/gcc/ipa-utils.c
> +++ b/gcc/ipa-utils.c
> @@ -660,13 +660,8 @@ ipa_merge_profiles (struct cgraph_node *dst,
>     if (dst->tp_first_run > src->tp_first_run && src->tp_first_run)
>       dst->tp_first_run = src->tp_first_run;
>
> -  if (src->profile_id)
> -    {
> -      if (!dst->profile_id)
> -	dst->profile_id = src->profile_id;
> -      else
> -	gcc_assert (src->profile_id == dst->profile_id);
> -    }
> +  if (src->profile_id && !dst->profile_id)
> +    dst->profile_id = src->profile_id;
>
>     if (!dst->count)
>       return;
> diff --git a/gcc/ipa-utils.h b/gcc/ipa-utils.h
> index a2c985a..996249a 100644
> --- a/gcc/ipa-utils.h
> +++ b/gcc/ipa-utils.h
> @@ -72,6 +72,8 @@ struct odr_type_d;
>   typedef odr_type_d *odr_type;
>   void build_type_inheritance_graph (void);
>   void update_type_inheritance_graph (void);
> +bool polymorphic_type_binfo_p (tree binfo);
> +
>   vec <cgraph_node *>
>   possible_polymorphic_call_targets (tree, HOST_WIDE_INT,
>   				   ipa_polymorphic_call_context,
> diff --git a/gcc/passes.c b/gcc/passes.c
> index 4366251..9fdfe51 100644
> --- a/gcc/passes.c
> +++ b/gcc/passes.c
> @@ -1506,7 +1506,7 @@ do_per_function (void (*callback) (function *, void *data), void *data)
>       {
>         struct cgraph_node *node;
>         FOR_EACH_DEFINED_FUNCTION (node)
> -	if (node->analyzed && gimple_has_body_p (node->decl)
> +	if (node->analyzed && (gimple_has_body_p (node->decl) && !in_lto_p)
>   	    && (!node->clone_of || node->decl != node->clone_of->decl))
>   	  callback (DECL_STRUCT_FUNCTION (node->decl), data);
>       }
> diff --git a/gcc/varpool.c b/gcc/varpool.c
> index ff67127..5cc558e 100644
> --- a/gcc/varpool.c
> +++ b/gcc/varpool.c
> @@ -293,6 +293,7 @@ ctor_for_folding (tree decl)
>     if (decl != real_decl)
>       {
>         gcc_assert (!DECL_INITIAL (decl)
> +		  || (node->alias && varpool_alias_target (node) == real_node)
>   		  || DECL_INITIAL (decl) == error_mark_node);
>         if (lookup_attribute ("weakref", DECL_ATTRIBUTES (decl)))
>   	{
>

Hi.

Following patch enhances API functions to be ready for main patch of this patchset.

Ready for thunk?

Thank you,
Martin

[-- Attachment #2: ipa-icf-api-enhancement.changelog --]
[-- Type: text/plain, Size: 447 bytes --]

gcc/ChangeLog:

2014-09-21  Martin Liška  <mliska@suse.cz>

	* cgraph.c (cgraph_node::release_body): New argument keep_arguments
	introduced.
	* cgraph.h: Likewise.
	* cgraphunit.c (cgraph_node::create_wrapper): Usage of new argument introduced.
	* ipa-devirt.c (polymorphic_type_binfo_p): Safe check for binfos created by Java.
	* tree-ssa-alias.c (ao_ref_base_alias_set): Static function transformed to global.
	* tree-ssa-alias.h: Likewise.

[-- Attachment #3: ipa-icf-api-enhancement.patch --]
[-- Type: text/x-patch, Size: 3036 bytes --]

diff --git a/gcc/cgraph.c b/gcc/cgraph.c
index 8f04284..d40a2922 100644
--- a/gcc/cgraph.c
+++ b/gcc/cgraph.c
@@ -1637,13 +1637,15 @@ release_function_body (tree decl)
    are free'd in final.c via free_after_compilation().  */
 
 void
-cgraph_node::release_body (void)
+cgraph_node::release_body (bool keep_arguments)
 {
   ipa_transforms_to_apply.release ();
   if (!used_as_abstract_origin && symtab->state != PARSING)
     {
       DECL_RESULT (decl) = NULL;
-      DECL_ARGUMENTS (decl) = NULL;
+
+      if (!keep_arguments)
+	DECL_ARGUMENTS (decl) = NULL;
     }
   /* If the node is abstract and needed, then do not clear DECL_INITIAL
      of its associated function function declaration because it's
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index a316e40..19ce3b8 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -915,7 +915,7 @@ public:
      Use this only for functions that are released before being translated to
      target code (i.e. RTL).  Functions that are compiled to RTL and beyond
      are free'd in final.c via free_after_compilation().  */
-  void release_body (void);
+  void release_body (bool keep_arguments = false);
 
   /* cgraph_node is no longer nested function; update cgraph accordingly.  */
   void unnest (void);
diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c
index 3e3b8d2..c4597e2 100644
--- a/gcc/cgraphunit.c
+++ b/gcc/cgraphunit.c
@@ -2300,7 +2300,7 @@ cgraph_node::create_wrapper (cgraph_node *target)
     tree decl_result = DECL_RESULT (decl);
 
     /* Remove the function's body.  */
-    release_body ();
+    release_body (true);
     reset ();
 
     DECL_RESULT (decl) = decl_result;
diff --git a/gcc/ipa-devirt.c b/gcc/ipa-devirt.c
index af42c6d..f374933 100644
--- a/gcc/ipa-devirt.c
+++ b/gcc/ipa-devirt.c
@@ -225,7 +225,7 @@ static inline bool
 polymorphic_type_binfo_p (tree binfo)
 {
   /* See if BINFO's type has an virtual table associtated with it.  */
-  return BINFO_VTABLE (TYPE_BINFO (BINFO_TYPE (binfo)));
+  return BINFO_TYPE (binfo) && BINFO_VTABLE (TYPE_BINFO (BINFO_TYPE (binfo)));
 }
 
 /* Return TRUE if all derived types of T are known and thus
diff --git a/gcc/tree-ssa-alias.c b/gcc/tree-ssa-alias.c
index 442112a..1bf88e2 100644
--- a/gcc/tree-ssa-alias.c
+++ b/gcc/tree-ssa-alias.c
@@ -559,7 +559,7 @@ ao_ref_base (ao_ref *ref)
 
 /* Returns the base object alias set of the memory reference *REF.  */
 
-static alias_set_type
+alias_set_type
 ao_ref_base_alias_set (ao_ref *ref)
 {
   tree base_ref;
diff --git a/gcc/tree-ssa-alias.h b/gcc/tree-ssa-alias.h
index 436381a..0d35283 100644
--- a/gcc/tree-ssa-alias.h
+++ b/gcc/tree-ssa-alias.h
@@ -98,6 +98,7 @@ extern void ao_ref_init (ao_ref *, tree);
 extern void ao_ref_init_from_ptr_and_size (ao_ref *, tree, tree);
 extern tree ao_ref_base (ao_ref *);
 extern alias_set_type ao_ref_alias_set (ao_ref *);
+extern alias_set_type ao_ref_base_alias_set (ao_ref *);
 extern bool ptr_deref_may_alias_global_p (tree);
 extern bool ptr_derefs_may_alias_p (tree, tree);
 extern bool ref_may_alias_global_p (tree);

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 2/5] Existing call graph infrastructure enhancement
  2014-09-24 14:23   ` Martin Liška
@ 2014-09-24 15:01     ` Jan Hubicka
  2014-09-26 10:19       ` Martin Liška
  0 siblings, 1 reply; 70+ messages in thread
From: Jan Hubicka @ 2014-09-24 15:01 UTC (permalink / raw)
  To: Martin Liška; +Cc: gcc-patches, hubicka

> Hi.
> 
> Following patch enhances API functions to be ready for main patch of this patchset.
> 
> Ready for thunk?
> 
> Thank you,
> Martin

> gcc/ChangeLog:
> 
> 2014-09-21  Martin Liška  <mliska@suse.cz>
> 
> 	* cgraph.c (cgraph_node::release_body): New argument keep_arguments
> 	introduced.
> 	* cgraph.h: Likewise.
> 	* cgraphunit.c (cgraph_node::create_wrapper): Usage of new argument introduced.
> 	* ipa-devirt.c (polymorphic_type_binfo_p): Safe check for binfos created by Java.
> 	* tree-ssa-alias.c (ao_ref_base_alias_set): Static function transformed to global.
> 	* tree-ssa-alias.h: Likewise.

> diff --git a/gcc/cgraph.c b/gcc/cgraph.c
> index 8f04284..d40a2922 100644
> --- a/gcc/cgraph.c
> +++ b/gcc/cgraph.c
> @@ -1637,13 +1637,15 @@ release_function_body (tree decl)
>     are free'd in final.c via free_after_compilation().  */
>  
>  void
> -cgraph_node::release_body (void)
> +cgraph_node::release_body (bool keep_arguments)
>  {
>    ipa_transforms_to_apply.release ();
>    if (!used_as_abstract_origin && symtab->state != PARSING)
>      {
>        DECL_RESULT (decl) = NULL;
> -      DECL_ARGUMENTS (decl) = NULL;
> +
> +      if (!keep_arguments)
> +	DECL_ARGUMENTS (decl) = NULL;
>      }
>    /* If the node is abstract and needed, then do not clear DECL_INITIAL
>       of its associated function function declaration because it's
> diff --git a/gcc/cgraph.h b/gcc/cgraph.h
> index a316e40..19ce3b8 100644
> --- a/gcc/cgraph.h
> +++ b/gcc/cgraph.h
> @@ -915,7 +915,7 @@ public:
>       Use this only for functions that are released before being translated to
>       target code (i.e. RTL).  Functions that are compiled to RTL and beyond
>       are free'd in final.c via free_after_compilation().  */
> -  void release_body (void);
> +  void release_body (bool keep_arguments = false);

Please add documentation for KEEP_ARGUMENTS explaining that it is useful only if you want to
rebuild body as thunk.
>  
>    /* cgraph_node is no longer nested function; update cgraph accordingly.  */
>    void unnest (void);
> diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c
> index 3e3b8d2..c4597e2 100644
> --- a/gcc/cgraphunit.c
> +++ b/gcc/cgraphunit.c
> @@ -2300,7 +2300,7 @@ cgraph_node::create_wrapper (cgraph_node *target)
>      tree decl_result = DECL_RESULT (decl);
>  
>      /* Remove the function's body.  */
I would say Remove the function's body but keep arguments to be reused for thunk.
> -    release_body ();
> +    release_body (true);
>      reset ();
>  
>      DECL_RESULT (decl) = decl_result;
> diff --git a/gcc/ipa-devirt.c b/gcc/ipa-devirt.c
> index af42c6d..f374933 100644
> --- a/gcc/ipa-devirt.c
> +++ b/gcc/ipa-devirt.c
> @@ -225,7 +225,7 @@ static inline bool
>  polymorphic_type_binfo_p (tree binfo)
>  {
>    /* See if BINFO's type has an virtual table associtated with it.  */
> -  return BINFO_VTABLE (TYPE_BINFO (BINFO_TYPE (binfo)));
> +  return BINFO_TYPE (binfo) && BINFO_VTABLE (TYPE_BINFO (BINFO_TYPE (binfo)));

Aha, this change was for Java, right? Please add comment that Java produces
BINFOs without BINFO_TYPE set.
>  }
>  
>  /* Return TRUE if all derived types of T are known and thus
> diff --git a/gcc/tree-ssa-alias.c b/gcc/tree-ssa-alias.c
> index 442112a..1bf88e2 100644
> --- a/gcc/tree-ssa-alias.c
> +++ b/gcc/tree-ssa-alias.c
> @@ -559,7 +559,7 @@ ao_ref_base (ao_ref *ref)
>  
>  /* Returns the base object alias set of the memory reference *REF.  */
>  
> -static alias_set_type
> +alias_set_type
>  ao_ref_base_alias_set (ao_ref *ref)
>  {
>    tree base_ref;
> diff --git a/gcc/tree-ssa-alias.h b/gcc/tree-ssa-alias.h
> index 436381a..0d35283 100644
> --- a/gcc/tree-ssa-alias.h
> +++ b/gcc/tree-ssa-alias.h
> @@ -98,6 +98,7 @@ extern void ao_ref_init (ao_ref *, tree);
>  extern void ao_ref_init_from_ptr_and_size (ao_ref *, tree, tree);
>  extern tree ao_ref_base (ao_ref *);
>  extern alias_set_type ao_ref_alias_set (ao_ref *);
> +extern alias_set_type ao_ref_base_alias_set (ao_ref *);

I can not approve this change, but I suppose it is what Richard suggested?

Patch is OK except for the tree-ssa-alias bits.
Honza
>  extern bool ptr_deref_may_alias_global_p (tree);
>  extern bool ptr_derefs_may_alias_p (tree, tree);
>  extern bool ref_may_alias_global_p (tree);

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 2/5] Existing call graph infrastructure enhancement
  2014-09-24 15:01     ` Jan Hubicka
@ 2014-09-26 10:19       ` Martin Liška
  0 siblings, 0 replies; 70+ messages in thread
From: Martin Liška @ 2014-09-26 10:19 UTC (permalink / raw)
  To: Jan Hubicka; +Cc: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 4636 bytes --]

On 09/24/2014 05:01 PM, Jan Hubicka wrote:
>> Hi.
>>
>> Following patch enhances API functions to be ready for main patch of this patchset.
>>
>> Ready for thunk?
>>
>> Thank you,
>> Martin
>
>> gcc/ChangeLog:
>>
>> 2014-09-21  Martin Liška  <mliska@suse.cz>
>>
>> 	* cgraph.c (cgraph_node::release_body): New argument keep_arguments
>> 	introduced.
>> 	* cgraph.h: Likewise.
>> 	* cgraphunit.c (cgraph_node::create_wrapper): Usage of new argument introduced.
>> 	* ipa-devirt.c (polymorphic_type_binfo_p): Safe check for binfos created by Java.
>> 	* tree-ssa-alias.c (ao_ref_base_alias_set): Static function transformed to global.
>> 	* tree-ssa-alias.h: Likewise.
>
>> diff --git a/gcc/cgraph.c b/gcc/cgraph.c
>> index 8f04284..d40a2922 100644
>> --- a/gcc/cgraph.c
>> +++ b/gcc/cgraph.c
>> @@ -1637,13 +1637,15 @@ release_function_body (tree decl)
>>      are free'd in final.c via free_after_compilation().  */
>>
>>   void
>> -cgraph_node::release_body (void)
>> +cgraph_node::release_body (bool keep_arguments)
>>   {
>>     ipa_transforms_to_apply.release ();
>>     if (!used_as_abstract_origin && symtab->state != PARSING)
>>       {
>>         DECL_RESULT (decl) = NULL;
>> -      DECL_ARGUMENTS (decl) = NULL;
>> +
>> +      if (!keep_arguments)
>> +	DECL_ARGUMENTS (decl) = NULL;
>>       }
>>     /* If the node is abstract and needed, then do not clear DECL_INITIAL
>>        of its associated function function declaration because it's
>> diff --git a/gcc/cgraph.h b/gcc/cgraph.h
>> index a316e40..19ce3b8 100644
>> --- a/gcc/cgraph.h
>> +++ b/gcc/cgraph.h
>> @@ -915,7 +915,7 @@ public:
>>        Use this only for functions that are released before being translated to
>>        target code (i.e. RTL).  Functions that are compiled to RTL and beyond
>>        are free'd in final.c via free_after_compilation().  */
>> -  void release_body (void);
>> +  void release_body (bool keep_arguments = false);
>
> Please add documentation for KEEP_ARGUMENTS explaining that it is useful only if you want to
> rebuild body as thunk.
>>
>>     /* cgraph_node is no longer nested function; update cgraph accordingly.  */
>>     void unnest (void);
>> diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c
>> index 3e3b8d2..c4597e2 100644
>> --- a/gcc/cgraphunit.c
>> +++ b/gcc/cgraphunit.c
>> @@ -2300,7 +2300,7 @@ cgraph_node::create_wrapper (cgraph_node *target)
>>       tree decl_result = DECL_RESULT (decl);
>>
>>       /* Remove the function's body.  */
> I would say Remove the function's body but keep arguments to be reused for thunk.
>> -    release_body ();
>> +    release_body (true);
>>       reset ();
>>
>>       DECL_RESULT (decl) = decl_result;
>> diff --git a/gcc/ipa-devirt.c b/gcc/ipa-devirt.c
>> index af42c6d..f374933 100644
>> --- a/gcc/ipa-devirt.c
>> +++ b/gcc/ipa-devirt.c
>> @@ -225,7 +225,7 @@ static inline bool
>>   polymorphic_type_binfo_p (tree binfo)
>>   {
>>     /* See if BINFO's type has an virtual table associtated with it.  */
>> -  return BINFO_VTABLE (TYPE_BINFO (BINFO_TYPE (binfo)));
>> +  return BINFO_TYPE (binfo) && BINFO_VTABLE (TYPE_BINFO (BINFO_TYPE (binfo)));
>
> Aha, this change was for Java, right? Please add comment that Java produces
> BINFOs without BINFO_TYPE set.
>>   }
>>
>>   /* Return TRUE if all derived types of T are known and thus
>> diff --git a/gcc/tree-ssa-alias.c b/gcc/tree-ssa-alias.c
>> index 442112a..1bf88e2 100644
>> --- a/gcc/tree-ssa-alias.c
>> +++ b/gcc/tree-ssa-alias.c
>> @@ -559,7 +559,7 @@ ao_ref_base (ao_ref *ref)
>>
>>   /* Returns the base object alias set of the memory reference *REF.  */
>>
>> -static alias_set_type
>> +alias_set_type
>>   ao_ref_base_alias_set (ao_ref *ref)
>>   {
>>     tree base_ref;
>> diff --git a/gcc/tree-ssa-alias.h b/gcc/tree-ssa-alias.h
>> index 436381a..0d35283 100644
>> --- a/gcc/tree-ssa-alias.h
>> +++ b/gcc/tree-ssa-alias.h
>> @@ -98,6 +98,7 @@ extern void ao_ref_init (ao_ref *, tree);
>>   extern void ao_ref_init_from_ptr_and_size (ao_ref *, tree, tree);
>>   extern tree ao_ref_base (ao_ref *);
>>   extern alias_set_type ao_ref_alias_set (ao_ref *);
>> +extern alias_set_type ao_ref_base_alias_set (ao_ref *);
>
> I can not approve this change, but I suppose it is what Richard suggested?
>

There's updated version of the patch that deals with Honza's notes.
Yes, I explicitly asked Richard if we can mark the function as global.

I will commit the patch soon.

Thank you,
Martin

> Patch is OK except for the tree-ssa-alias bits.
> Honza
>>   extern bool ptr_deref_may_alias_global_p (tree);
>>   extern bool ptr_derefs_may_alias_p (tree, tree);
>>   extern bool ref_may_alias_global_p (tree);
>


[-- Attachment #2: ipa-icf-api-enhancement2.patch --]
[-- Type: text/x-patch, Size: 3690 bytes --]

diff --git a/gcc/cgraph.c b/gcc/cgraph.c
index 1cfc783..fdcaf79 100644
--- a/gcc/cgraph.c
+++ b/gcc/cgraph.c
@@ -1625,16 +1625,19 @@ release_function_body (tree decl)
 /* Release memory used to represent body of function.
    Use this only for functions that are released before being translated to
    target code (i.e. RTL).  Functions that are compiled to RTL and beyond
-   are free'd in final.c via free_after_compilation().  */
+   are free'd in final.c via free_after_compilation().
+   KEEP_ARGUMENTS are useful only if you want to rebuild body as thunk.  */
 
 void
-cgraph_node::release_body (void)
+cgraph_node::release_body (bool keep_arguments)
 {
   ipa_transforms_to_apply.release ();
   if (!used_as_abstract_origin && symtab->state != PARSING)
     {
       DECL_RESULT (decl) = NULL;
-      DECL_ARGUMENTS (decl) = NULL;
+
+      if (!keep_arguments)
+	DECL_ARGUMENTS (decl) = NULL;
     }
   /* If the node is abstract and needed, then do not clear DECL_INITIAL
      of its associated function function declaration because it's
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index 7481906..4fd58a5 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -917,7 +917,7 @@ public:
      Use this only for functions that are released before being translated to
      target code (i.e. RTL).  Functions that are compiled to RTL and beyond
      are free'd in final.c via free_after_compilation().  */
-  void release_body (void);
+  void release_body (bool keep_arguments = false);
 
   /* cgraph_node is no longer nested function; update cgraph accordingly.  */
   void unnest (void);
diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c
index b854e4b..d463505 100644
--- a/gcc/cgraphunit.c
+++ b/gcc/cgraphunit.c
@@ -2294,8 +2294,9 @@ cgraph_node::create_wrapper (cgraph_node *target)
     /* Preserve DECL_RESULT so we get right by reference flag.  */
     tree decl_result = DECL_RESULT (decl);
 
-    /* Remove the function's body.  */
-    release_body ();
+    /* Remove the function's body but keep arguments to be reused
+       for thunk.  */
+    release_body (true);
     reset ();
 
     DECL_RESULT (decl) = decl_result;
diff --git a/gcc/ipa-utils.h b/gcc/ipa-utils.h
index 029f39a..465bc26 100644
--- a/gcc/ipa-utils.h
+++ b/gcc/ipa-utils.h
@@ -179,8 +179,10 @@ odr_type_p (const_tree t)
 inline bool
 polymorphic_type_binfo_p (const_tree binfo)
 {
-  /* See if BINFO's type has an virtual table associtated with it.  */
-  return BINFO_VTABLE (TYPE_BINFO (BINFO_TYPE (binfo)));
+  /* See if BINFO's type has an virtual table associtated with it.
+     Check is defensive because of Java FE produces BINFOs
+     without BINFO_TYPE set.   */
+  return BINFO_TYPE (binfo) && BINFO_VTABLE (TYPE_BINFO (BINFO_TYPE (binfo)));
 }
 #endif  /* GCC_IPA_UTILS_H  */
 
diff --git a/gcc/tree-ssa-alias.c b/gcc/tree-ssa-alias.c
index 442112a..1bf88e2 100644
--- a/gcc/tree-ssa-alias.c
+++ b/gcc/tree-ssa-alias.c
@@ -559,7 +559,7 @@ ao_ref_base (ao_ref *ref)
 
 /* Returns the base object alias set of the memory reference *REF.  */
 
-static alias_set_type
+alias_set_type
 ao_ref_base_alias_set (ao_ref *ref)
 {
   tree base_ref;
diff --git a/gcc/tree-ssa-alias.h b/gcc/tree-ssa-alias.h
index 436381a..0d35283 100644
--- a/gcc/tree-ssa-alias.h
+++ b/gcc/tree-ssa-alias.h
@@ -98,6 +98,7 @@ extern void ao_ref_init (ao_ref *, tree);
 extern void ao_ref_init_from_ptr_and_size (ao_ref *, tree, tree);
 extern tree ao_ref_base (ao_ref *);
 extern alias_set_type ao_ref_alias_set (ao_ref *);
+extern alias_set_type ao_ref_base_alias_set (ao_ref *);
 extern bool ptr_deref_may_alias_global_p (tree);
 extern bool ptr_derefs_may_alias_p (tree, tree);
 extern bool ref_may_alias_global_p (tree);

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 3/5] IPA ICF pass
  2014-07-17 15:23         ` Martin Liška
@ 2014-09-26 12:20           ` Martin Liška
  2014-09-26 14:44             ` Markus Trippelsdorf
                               ` (2 more replies)
  0 siblings, 3 replies; 70+ messages in thread
From: Martin Liška @ 2014-09-26 12:20 UTC (permalink / raw)
  To: gcc-patches; +Cc: Jan Hubicka

[-- Attachment #1: Type: text/plain, Size: 3485 bytes --]

On 07/17/2014 05:05 PM, Martin Liška wrote:
>
> On 07/06/2014 12:53 AM, Jan Hubicka wrote:
>>> On Fri, 20 Jun 2014, Trevor Saunders wrote:
>>>>> +@item -fipa-icf
>>>>> +@opindex fipa-icf
>>>>> +Perform Identical Code Folding for functions and read-only variables.
>> I would perhaps explicitly say that the optimizations reduce code size
>> and may disturb unwind stacks by replacing a function by equivalent
>> one with different name.
>>>>> +Behavior is similar to Gold Linker ICF optimization. Symbols proved
>> Perhaps tell a bit more here. The optimization works more effectively with link
>> time optimization enabled and that the Gold and GCC ICF works on different
>> levels and thus are not equivalent optimizations - there are equivallences that
>> are found only by GCC and equivalences found only by Gold.
>>
>>>> +as semantically equivalent are redirected to corresponding symbol. The pass
>>>>> +sensitively decides for usage of alias, thunk or local redirection.
>>>>> +This flag is enabled by default at @option{-O2}.
>> Probably at -Os too.
>>> I found this a bit hard to read/understand.
>>>
>>> Perhaps first describe what it does and then, before "This flag is
>>> enabled..." note that "This is similar to the ICF optimization performed
>>> by the Gold linker".
>>> "Symbols proved" (plural) vs "to corresponding symbol" seems to miss
>>> an an "a" as in "a corresponding symbol".  Alas, how is that one
>>> determined?  Is this more "...are merged into one", from the user's
>>> perspective?
>>>
>>> What does it mean to "sensitively decide for usage of alias, thunk,
>>> or local redirection"?
>> I think this is just a technical detail of the implementation.  I would not put that
>> into user manual.  It means that for some functions you can make alias, for others
>> you need thunk (so addresses stay different)
>>> Gerald
>
> Hello,
>     there's updated version of patch that newly uses devirtualization machinery to identify polymorphic types that can potentially break ICF (There are such examples in Firefox).
>
> Apart from that, I did many small updates, incorporated Trevor's comments and I tried to improve documentation entry for the pass.
> Patch has been tested for Firefox and Inkscape with LTO.
>
> Thanks,
> Martin

Hello.

After couple of weeks I spent with fixing new issues connected to the pass:
1) Inliner failed in case I created a thunk and release body of a function. In such situation we need to preserve DECL_ARGUMENTS. I added new argument for: cgraph_node::release_body.
2) Awkward error was hidden in libstdc++ test for trees, there were two functions having one argument that differs in one sub-template. Thank to Richard who helped me to fix alias set accuracy.
3) There was missing comparison for FIELD_DECLS (DECL_FIELD_BIT_OFFSET) which caused me miscompilation.
4) After discussion with Honza, we introduced new cgraph_node flag called icf_merged. The flag helps to fix verifier in cgraph_node::verify.

Current version of the patch can bootstrap on x86_64-linux. With following patch applied, there's not testcase regression.
I tried to build Firefox, Inkscape, GIMP and Chromium with LTO and patch applied and no regression has been observed.

Moreover, I discussed with Richard and the pass is capable of playing role in tree-ssa-tail-merge (according to first experiments). It can replace current usage of value numbering.

I hope we can apply the patch to the mainline in a short-term time window?

Thank you,
Martin


[-- Attachment #2: 0001-IPA-ICF-patch1.patch --]
[-- Type: text/x-patch, Size: 121131 bytes --]

From 53d20d0b0c209b50d385ee8d85d5a7ed4594d477 Mon Sep 17 00:00:00 2001
From: mliska <mliska@suse.cz>
Date: Fri, 26 Sep 2014 13:51:47 +0200
Subject: [PATCH 1/3] IPA ICF: patch1

---
 gcc/Makefile.in      |    2 +
 gcc/cgraph.c         |   20 +-
 gcc/cgraph.h         |    2 +
 gcc/cgraphunit.c     |    2 +-
 gcc/common.opt       |   12 +
 gcc/doc/invoke.texi  |   16 +-
 gcc/ipa-icf-gimple.c |  384 +++++++
 gcc/ipa-icf.c        | 2841 ++++++++++++++++++++++++++++++++++++++++++++++++++
 gcc/ipa-icf.h        |  803 ++++++++++++++
 gcc/lto-cgraph.c     |    2 +
 gcc/lto-section-in.c |    3 +-
 gcc/lto-streamer.h   |    1 +
 gcc/opts.c           |    6 +
 gcc/passes.def       |    1 +
 gcc/timevar.def      |    1 +
 gcc/tree-pass.h      |    1 +
 16 files changed, 4089 insertions(+), 8 deletions(-)
 create mode 100644 gcc/ipa-icf-gimple.c
 create mode 100644 gcc/ipa-icf.c
 create mode 100644 gcc/ipa-icf.h

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 3dd9d8f..8d02425 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1265,6 +1265,8 @@ OBJS = \
 	ipa-profile.o \
 	ipa-prop.o \
 	ipa-pure-const.o \
+	ipa-icf.o \
+	ipa-icf-gimple.o \
 	ipa-reference.o \
 	ipa-ref.o \
 	ipa-utils.o \
diff --git a/gcc/cgraph.c b/gcc/cgraph.c
index fdcaf79..439db49 100644
--- a/gcc/cgraph.c
+++ b/gcc/cgraph.c
@@ -1913,6 +1913,8 @@ cgraph_node::dump (FILE *f)
     fprintf (f, " only_called_at_exit");
   if (tm_clone)
     fprintf (f, " tm_clone");
+  if (icf_merged)
+    fprintf (f, " icf_merged");
   if (DECL_STATIC_CONSTRUCTOR (decl))
     fprintf (f," static_constructor (priority:%i)", get_init_priority ());
   if (DECL_STATIC_DESTRUCTOR (decl))
@@ -2827,11 +2829,19 @@ cgraph_node::verify_node (void)
 			    {
 			      if (verify_edge_corresponds_to_fndecl (e, decl))
 				{
-				  error ("edge points to wrong declaration:");
-				  debug_tree (e->callee->decl);
-				  fprintf (stderr," Instead of:");
-				  debug_tree (decl);
-				  error_found = true;
+				  /* The edge can be redirected in WPA by IPA ICF.
+				     Following check really ensures that it's
+				     not the case.  */
+
+				  cgraph_node *current_node = cgraph_node::get (decl);
+				  if (!current_node || !current_node->icf_merged)
+				  {
+				    error ("edge points to wrong declaration:");
+				    debug_tree (e->callee->decl);
+				    fprintf (stderr," Instead of:");
+				    debug_tree (decl);
+				    error_found = true;
+				  }
 				}
 			    }
 			  else if (decl)
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index 4fd58a5..3bc14a0 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -1230,6 +1230,8 @@ public:
   /* True if this decl calls a COMDAT-local function.  This is set up in
      compute_inline_parameters and inline_call.  */
   unsigned calls_comdat_local : 1;
+  /* True if node has been created by merge operation in IPA-ICF.  */
+  unsigned icf_merged: 1;
 };
 
 /* A cgraph node set is a collection of cgraph nodes.  A cgraph node
diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c
index d463505..7100cd7 100644
--- a/gcc/cgraphunit.c
+++ b/gcc/cgraphunit.c
@@ -1500,7 +1500,7 @@ cgraph_node::expand_thunk (bool output_asm_thunks, bool force_gimple_thunk)
       if (in_lto_p)
 	get_body ();
       a = DECL_ARGUMENTS (thunk_fndecl);
-      
+
       current_function_decl = thunk_fndecl;
 
       /* Ensure thunks are emitted in their correct sections.  */
diff --git a/gcc/common.opt b/gcc/common.opt
index b4f0ed4..5db5e1e 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -1443,6 +1443,18 @@ fipa-pure-const
 Common Report Var(flag_ipa_pure_const) Init(0) Optimization
 Discover pure and const functions
 
+fipa-icf
+Common Report Var(flag_ipa_icf) Optimization
+Perform Identical Code Folding for functions and read-only variables
+
+fipa-icf-functions
+Common Report Var(flag_ipa_icf_functions) Optimization
+Perform Identical Code Folding for functions
+
+fipa-icf-variables
+Common Report Var(flag_ipa_icf_variables) Optimization
+Perform Identical Code Folding for variables
+
 fipa-reference
 Common Report Var(flag_ipa_reference) Init(0) Optimization
 Discover readonly and non addressable static variables
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index f6c3b42..4d8d3e8 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -382,7 +382,7 @@ Objective-C and Objective-C++ Dialects}.
 -fif-conversion2 -findirect-inlining @gol
 -finline-functions -finline-functions-called-once -finline-limit=@var{n} @gol
 -finline-small-functions -fipa-cp -fipa-cp-clone @gol
--fipa-pta -fipa-profile -fipa-pure-const -fipa-reference @gol
+-fipa-pta -fipa-profile -fipa-pure-const -fipa-reference -fipa-icf @gol
 -fira-algorithm=@var{algorithm} @gol
 -fira-region=@var{region} -fira-hoist-pressure @gol
 -fira-loop-pressure -fno-ira-share-save-slots @gol
@@ -7123,6 +7123,7 @@ also turns on the following optimization flags:
 -findirect-inlining @gol
 -fipa-cp @gol
 -fipa-sra @gol
+-fipa-icf @gol
 -fisolate-erroneous-paths-dereference @gol
 -foptimize-sibling-calls @gol
 -foptimize-strlen @gol
@@ -8068,6 +8069,19 @@ it may significantly increase code size
 (see @option{--param ipcp-unit-growth=@var{value}}).
 This flag is enabled by default at @option{-O3}.
 
+@item -fipa-icf
+@opindex fipa-icf
+Perform Identical Code Folding for functions and read-only variables.
+The optimization reduces code size and may disturb unwind stacks by replacing
+a function by equivalent one with a different name. The optimization works
+more effectively with link time optimization enabled.
+
+Nevertheless the behavior is similar to Gold Linker ICF optimization, GCC ICF
+works on different levels and thus the optimizations are not same - there are
+equivalences that are found only by GCC and equivalences found only by Gold.
+
+This flag is enabled by default at @option{-O2}.
+
 @item -fisolate-erroneous-paths-dereference
 Detect paths which trigger erroneous or undefined behaviour due to
 dereferencing a NULL pointer.  Isolate those paths from the main control
diff --git a/gcc/ipa-icf-gimple.c b/gcc/ipa-icf-gimple.c
new file mode 100644
index 0000000..7031eaa
--- /dev/null
+++ b/gcc/ipa-icf-gimple.c
@@ -0,0 +1,384 @@
+/* Interprocedural Identical Code Folding pass
+   Copyright (C) 2014 Free Software Foundation, Inc.
+
+   Contributed by Jan Hubicka <hubicka@ucw.cz> and Martin Liska <mliska@suse.cz>
+
+This file is part of GCC.
+
+GCC 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, or (at your option) any later
+version.
+
+GCC 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 GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tree.h"
+#include "basic-block.h"
+#include "tree-ssa-alias.h"
+#include "internal-fn.h"
+#include "gimple-expr.h"
+#include "is-a.h"
+#include "gimple.h"
+#include "expr.h"
+#include "gimple-iterator.h"
+#include "gimple-ssa.h"
+#include "tree-cfg.h"
+#include "stringpool.h"
+#include "tree-dfa.h"
+#include "tree-pass.h"
+#include "gimple-pretty-print.h"
+#include "cfgloop.h"
+#include "except.h"
+#include "data-streamer.h"
+#include "ipa-utils.h"
+#include "ipa-icf.h"
+
+namespace ipa_icf {
+
+/* Basic block equivalence comparison function that returns true if
+   basic blocks BB1 and BB2 (from functions FUNC1 and FUNC2) correspond.  */
+
+bool
+func_checker::compare_bb (sem_bb *bb1, sem_bb *bb2)
+{
+  unsigned i;
+  gimple_stmt_iterator gsi1, gsi2;
+  gimple s1, s2;
+
+  if (bb1->nondbg_stmt_count != bb2->nondbg_stmt_count
+      || bb1->edge_count != bb2->edge_count)
+    return RETURN_FALSE ();
+
+  gsi1 = gsi_start_bb (bb1->bb);
+  gsi2 = gsi_start_bb (bb2->bb);
+
+  for (i = 0; i < bb1->nondbg_stmt_count; i++)
+    {
+      if (is_gimple_debug (gsi_stmt (gsi1)))
+	gsi_next_nondebug (&gsi1);
+
+      if (is_gimple_debug (gsi_stmt (gsi2)))
+	gsi_next_nondebug (&gsi2);
+
+      s1 = gsi_stmt (gsi1);
+      s2 = gsi_stmt (gsi2);
+
+      if (gimple_code (s1) != gimple_code (s2))
+	return RETURN_FALSE_WITH_MSG ("gimple codes are different");
+
+      switch (gimple_code (s1))
+	{
+	case GIMPLE_CALL:
+	  if (!compare_gimple_call (s1, s2))
+	    return RETURN_DIFFERENT_STMTS (s1, s2, "GIMPLE_CALL");
+	  break;
+	case GIMPLE_ASSIGN:
+	  if (!compare_gimple_assign (s1, s2))
+	    return RETURN_DIFFERENT_STMTS (s1, s2, "GIMPLE_ASSIGN");
+	  break;
+	case GIMPLE_COND:
+	  if (!compare_gimple_cond (s1, s2))
+	    return RETURN_DIFFERENT_STMTS (s1, s2, "GIMPLE_COND");
+	  break;
+	case GIMPLE_SWITCH:
+	  if (!compare_gimple_switch (s1, s2))
+	    return RETURN_DIFFERENT_STMTS (s1, s2, "GIMPLE_SWITCH");
+	  break;
+	case GIMPLE_DEBUG:
+	case GIMPLE_EH_DISPATCH:
+	  break;
+	case GIMPLE_RESX:
+	  if (!compare_gimple_resx (s1, s2))
+	    return RETURN_DIFFERENT_STMTS (s1, s2, "GIMPLE_RESX");
+	  break;
+	case GIMPLE_LABEL:
+	  if (!compare_gimple_label (s1, s2))
+	    return RETURN_DIFFERENT_STMTS (s1, s2, "GIMPLE_LABEL");
+	  break;
+	case GIMPLE_RETURN:
+	  if (!compare_gimple_return (s1, s2))
+	    return RETURN_DIFFERENT_STMTS (s1, s2, "GIMPLE_RETURN");
+	  break;
+	case GIMPLE_GOTO:
+	  if (!compare_gimple_goto (s1, s2))
+	    return RETURN_DIFFERENT_STMTS (s1, s2, "GIMPLE_GOTO");
+	  break;
+	case GIMPLE_ASM:
+	  if (!compare_gimple_asm (s1, s2))
+	    return RETURN_DIFFERENT_STMTS (s1, s2, "GIMPLE_ASM");
+	  break;
+	case GIMPLE_PREDICT:
+	case GIMPLE_NOP:
+	  return true;
+	default:
+	  return RETURN_FALSE_WITH_MSG ("Unknown GIMPLE code reached");
+	}
+
+      gsi_next (&gsi1);
+      gsi_next (&gsi2);
+    }
+
+  return true;
+}
+
+/* Verifies for given GIMPLEs S1 and S2 that
+   call statements are semantically equivalent.  */
+
+bool
+func_checker::compare_gimple_call (gimple s1, gimple s2)
+{
+  unsigned i;
+  tree t1, t2;
+
+  if (gimple_call_num_args (s1) != gimple_call_num_args (s2))
+    return false;
+
+  t1 = gimple_call_fndecl (s1);
+  t2 = gimple_call_fndecl (s2);
+
+  /* Function pointer variables are not supported yet.  */
+  if (t1 == NULL || t2 == NULL)
+    {
+      if (!compare_operand (t1, t2))
+	return RETURN_FALSE();
+    }
+  else if (!compare_function_decl (t1, t2))
+    return false;
+
+  /* Checking of argument.  */
+  for (i = 0; i < gimple_call_num_args (s1); ++i)
+    {
+      t1 = gimple_call_arg (s1, i);
+      t2 = gimple_call_arg (s2, i);
+
+      if (!compare_operand (t1, t2))
+	return false;
+    }
+
+  /* Return value checking.  */
+  t1 = gimple_get_lhs (s1);
+  t2 = gimple_get_lhs (s2);
+
+  return compare_operand (t1, t2);
+}
+
+
+/* Verifies for given GIMPLEs S1 and S2 that
+   assignment statements are semantically equivalent.  */
+
+bool
+func_checker::compare_gimple_assign (gimple s1, gimple s2)
+{
+  tree arg1, arg2;
+  tree_code code1, code2;
+  unsigned i;
+
+  code1 = gimple_expr_code (s1);
+  code2 = gimple_expr_code (s2);
+
+  if (code1 != code2)
+    return false;
+
+  code1 = gimple_assign_rhs_code (s1);
+  code2 = gimple_assign_rhs_code (s2);
+
+  if (code1 != code2)
+    return false;
+
+  for (i = 0; i < gimple_num_ops (s1); i++)
+    {
+      arg1 = gimple_op (s1, i);
+      arg2 = gimple_op (s2, i);
+
+      if (!compare_operand (arg1, arg2))
+	return false;
+    }
+
+
+  return true;
+}
+
+/* Verifies for given GIMPLEs S1 and S2 that
+   condition statements are semantically equivalent.  */
+
+bool
+func_checker::compare_gimple_cond (gimple s1, gimple s2)
+{
+  tree t1, t2;
+  tree_code code1, code2;
+
+  code1 = gimple_expr_code (s1);
+  code2 = gimple_expr_code (s2);
+
+  if (code1 != code2)
+    return false;
+
+  t1 = gimple_cond_lhs (s1);
+  t2 = gimple_cond_lhs (s2);
+
+  if (!compare_operand (t1, t2))
+    return false;
+
+  t1 = gimple_cond_rhs (s1);
+  t2 = gimple_cond_rhs (s2);
+
+  return compare_operand (t1, t2);
+}
+
+/* Verifies that tree labels T1 and T2 correspond in FUNC1 and FUNC2.  */
+
+bool
+func_checker::compare_tree_ssa_label (tree t1, tree t2)
+{
+  return compare_operand (t1, t2);
+}
+
+/* Verifies for given GIMPLEs S1 and S2 that
+   label statements are semantically equivalent.  */
+
+bool
+func_checker::compare_gimple_label (gimple g1, gimple g2)
+{
+  if (m_ignore_labels)
+    return true;
+
+  tree t1 = gimple_label_label (g1);
+  tree t2 = gimple_label_label (g2);
+
+  return compare_tree_ssa_label (t1, t2);
+}
+
+/* Verifies for given GIMPLEs S1 and S2 that
+   switch statements are semantically equivalent.  */
+
+bool
+func_checker::compare_gimple_switch (gimple g1, gimple g2)
+{
+  unsigned lsize1, lsize2, i;
+  tree t1, t2, low1, low2, high1, high2;
+
+  lsize1 = gimple_switch_num_labels (g1);
+  lsize2 = gimple_switch_num_labels (g2);
+
+  if (lsize1 != lsize2)
+    return false;
+
+  t1 = gimple_switch_index (g1);
+  t2 = gimple_switch_index (g2);
+
+  if (TREE_CODE (t1) != SSA_NAME || TREE_CODE(t2) != SSA_NAME)
+    return false;
+
+  if (!compare_operand (t1, t2))
+    return false;
+
+  for (i = 0; i < lsize1; i++)
+    {
+      low1 = CASE_LOW (gimple_switch_label (g1, i));
+      low2 = CASE_LOW (gimple_switch_label (g2, i));
+
+      if ((low1 != NULL) != (low2 != NULL)
+	  || (low1 && low2 && TREE_INT_CST_LOW (low1) != TREE_INT_CST_LOW (low2)))
+	return false;
+
+      high1 = CASE_HIGH (gimple_switch_label (g1, i));
+      high2 = CASE_HIGH (gimple_switch_label (g2, i));
+
+      if ((high1 != NULL) != (high2 != NULL)
+	  || (high1 && high2
+	      && TREE_INT_CST_LOW (high1) != TREE_INT_CST_LOW (high2)))
+	return false;
+    }
+
+  return true;
+}
+
+/* Verifies for given GIMPLEs S1 and S2 that
+   return statements are semantically equivalent.  */
+
+bool
+func_checker::compare_gimple_return (gimple g1, gimple g2)
+{
+  tree t1, t2;
+
+  t1 = gimple_return_retval (g1);
+  t2 = gimple_return_retval (g2);
+
+  /* Void return type.  */
+  if (t1 == NULL && t2 == NULL)
+    return true;
+  else
+    return compare_operand (t1, t2);
+}
+
+/* Verifies for given GIMPLEs S1 and S2 that
+   goto statements are semantically equivalent.  */
+
+bool
+func_checker::compare_gimple_goto (gimple g1, gimple g2)
+{
+  tree dest1, dest2;
+
+  dest1 = gimple_goto_dest (g1);
+  dest2 = gimple_goto_dest (g2);
+
+  if (TREE_CODE (dest1) != TREE_CODE (dest2) || TREE_CODE (dest1) != SSA_NAME)
+    return false;
+
+  return compare_operand (dest1, dest2);
+}
+
+/* Verifies for given GIMPLEs S1 and S2 that
+   resx statements are semantically equivalent.  */
+
+bool
+func_checker::compare_gimple_resx (gimple g1, gimple g2)
+{
+  return gimple_resx_region (g1) == gimple_resx_region (g2);
+}
+
+/* Verifies for given GIMPLEs S1 and S2 that ASM statements are equivalent.
+   For the beginning, the pass only supports equality for
+   '__asm__ __volatile__ ("", "", "", "memory")'.  */
+
+bool
+func_checker::compare_gimple_asm (gimple g1, gimple g2)
+{
+  if (gimple_asm_volatile_p (g1) != gimple_asm_volatile_p (g2))
+    return false;
+
+  if (gimple_asm_ninputs (g1) || gimple_asm_ninputs (g2))
+    return false;
+
+  if (gimple_asm_noutputs (g1) || gimple_asm_noutputs (g2))
+    return false;
+
+  if (gimple_asm_nlabels (g1) || gimple_asm_nlabels (g2))
+    return false;
+
+  if (gimple_asm_nclobbers (g1) != gimple_asm_nclobbers (g2))
+    return false;
+
+  for (unsigned i = 0; i < gimple_asm_nclobbers (g1); i++)
+    {
+      tree clobber1 = TREE_VALUE (gimple_asm_clobber_op (g1, i));
+      tree clobber2 = TREE_VALUE (gimple_asm_clobber_op (g2, i));
+
+      if (!operand_equal_p (clobber1, clobber2, OEP_ONLY_CONST))
+	return false;
+    }
+
+  return true;
+}
+
+} // ipa_icf namespace
diff --git a/gcc/ipa-icf.c b/gcc/ipa-icf.c
new file mode 100644
index 0000000..f3472fe
--- /dev/null
+++ b/gcc/ipa-icf.c
@@ -0,0 +1,2841 @@
+/* Interprocedural Identical Code Folding pass
+   Copyright (C) 2014 Free Software Foundation, Inc.
+
+   Contributed by Jan Hubicka <hubicka@ucw.cz> and Martin Liska <mliska@suse.cz>
+
+This file is part of GCC.
+
+GCC 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, or (at your option) any later
+version.
+
+GCC 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 GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+/* Interprocedural Identical Code Folding for functions and
+   read-only variables.
+
+   The goal of this transformation is to discover functions and read-only
+   variables which do have exactly the same semantics.
+
+   In case of functions,
+   we could either create a virtual clone or do a simple function wrapper
+   that will call equivalent function. If the function is just locally visible,
+   all function calls can be redirected. For read-only variables, we create
+   aliases if possible.
+
+   Optimization pass arranges as follows:
+   1) All functions and read-only variables are visited and internal
+      data structure, either sem_function or sem_variables is created.
+   2) For every symbol from the previous step, VAR_DECL and FUNCTION_DECL are
+      saved and matched to corresponding sem_items.
+   3) These declaration are ignored for equality check and are solved
+      by Value Numbering algorithm published by Alpert, Zadeck in 1992.
+   4) We compute hash value for each symbol.
+   5) Congruence classes are created based on hash value. If hash value are
+      equal, equals function is called and symbols are deeply compared.
+      We must prove that all SSA names, declarations and other items
+      correspond.
+   6) Value Numbering is executed for these classes. At the end of the process
+      all symbol members in remaining classes can be merged.
+   7) Merge operation creates alias in case of read-only variables. For
+      callgraph node, we must decide if we can redirect local calls,
+      create an alias or a thunk.
+
+*/
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tree.h"
+#include "basic-block.h"
+#include "tree-ssa-alias.h"
+#include "internal-fn.h"
+#include "gimple-expr.h"
+#include "is-a.h"
+#include "gimple.h"
+#include "expr.h"
+#include "gimple-iterator.h"
+#include "gimple-ssa.h"
+#include "tree-cfg.h"
+#include "tree-phinodes.h"
+#include "stringpool.h"
+#include "tree-ssanames.h"
+#include "tree-dfa.h"
+#include "tree-pass.h"
+#include "gimple-pretty-print.h"
+#include "ipa-inline.h"
+#include "cfgloop.h"
+#include "except.h"
+#include "hash-table.h"
+#include "coverage.h"
+#include "attribs.h"
+#include "print-tree.h"
+#include "lto-streamer.h"
+#include "data-streamer.h"
+#include "ipa-utils.h"
+#include "ipa-icf.h"
+
+namespace ipa_icf {
+
+/* Initialize internal structures for a given SOURCE_FUNC_DECL and
+   TARGET_FUNC_DECL. Strict polymorphic comparison is processed if
+   an option COMPARE_POLYMORPHIC is true. For special cases, one can
+   set IGNORE_LABELS to skip label comparison.
+   Similarly, IGNORE_SOURCE_DECLS and IGNORE_TARGET_DECLS are sets
+   of declarations that can be skipped.  */
+
+func_checker::func_checker (tree source_func_decl, tree target_func_decl,
+			    bool compare_polymorphic,
+			    bool ignore_labels,
+			    hash_set<tree> *ignored_source_decls, hash_set<tree> *ignored_target_decls)
+  : m_source_func_decl (source_func_decl), m_target_func_decl (target_func_decl),
+    m_ignored_source_decls (ignored_source_decls),
+    m_ignored_target_decls (ignored_target_decls),
+    m_compare_polymorphic (compare_polymorphic),
+    m_ignore_labels (ignore_labels)
+{
+  function *source_func = DECL_STRUCT_FUNCTION (source_func_decl);
+  function *target_func = DECL_STRUCT_FUNCTION (target_func_decl);
+
+  unsigned ssa_source = SSANAMES (source_func)->length ();
+  unsigned ssa_target = SSANAMES (target_func)->length ();
+
+  m_source_ssa_names.create (ssa_source);
+  m_target_ssa_names.create (ssa_target);
+
+  for (unsigned i = 0; i < ssa_source; i++)
+    m_source_ssa_names.safe_push (-1);
+
+  for (unsigned i = 0; i < ssa_target; i++)
+    m_target_ssa_names.safe_push (-1);
+}
+
+/* Memory release routine.  */
+
+func_checker::~func_checker ()
+{
+  m_source_ssa_names.release();
+  m_target_ssa_names.release();
+}
+
+/* Verifies that trees T1 and T2 are equivalent from perspective of ICF.  */
+
+bool
+func_checker::compare_ssa_name (tree t1, tree t2)
+{
+  unsigned i1 = SSA_NAME_VERSION (t1);
+  unsigned i2 = SSA_NAME_VERSION (t2);
+
+  if (m_source_ssa_names[i1] == -1)
+    m_source_ssa_names[i1] = i2;
+  else if (m_source_ssa_names[i1] != (int) i2)
+    return false;
+
+  if(m_target_ssa_names[i2] == -1)
+    m_target_ssa_names[i2] = i1;
+  else if (m_target_ssa_names[i2] != (int) i1)
+    return false;
+
+  return true;
+}
+
+/* Verification function for edges E1 and E2.  */
+
+bool
+func_checker::compare_edge (edge e1, edge e2)
+{
+  if (e1->flags != e2->flags)
+    return false;
+
+  bool existed_p;
+
+  edge &slot = m_edge_map.get_or_insert (e1, &existed_p);
+  if (existed_p)
+    return RETURN_WITH_DEBUG (slot == e2);
+  else
+    slot = e2;
+
+  return true;
+}
+
+/* Verification function for declaration trees T1 and T2 that
+   come from functions FUNC1 and FUNC2.  */
+
+bool
+func_checker::compare_decl (tree t1, tree t2)
+{
+  if (!auto_var_in_fn_p (t1, m_source_func_decl)
+      || !auto_var_in_fn_p (t2, m_target_func_decl))
+    return RETURN_WITH_DEBUG (t1 == t2);
+
+  if (!types_are_compatible_p (TREE_TYPE (t1), TREE_TYPE (t2),
+			       m_compare_polymorphic))
+    return RETURN_FALSE ();
+
+  bool existed_p;
+
+  tree &slot = m_decl_map.get_or_insert (t1, &existed_p);
+  if (existed_p)
+    return RETURN_WITH_DEBUG (slot == t2);
+  else
+    slot = t2;
+
+  return true;
+}
+
+/* Constructor for key value pair, where _ITEM is key and _INDEX is a target.  */
+
+sem_usage_pair::sem_usage_pair (sem_item *_item, unsigned int _index):
+  item (_item), index (_index)
+{
+}
+
+/* Semantic item constructor for a node of _TYPE, where STACK is used
+   for bitmap memory allocation.  */
+
+sem_item::sem_item (sem_item_type _type,
+		    bitmap_obstack *stack): type(_type), hash(0)
+{
+  setup (stack);
+}
+
+/* Semantic item constructor for a node of _TYPE, where STACK is used
+   for bitmap memory allocation. The item is based on symtab node _NODE
+   with computed _HASH.  */
+
+sem_item::sem_item (sem_item_type _type, symtab_node *_node,
+		    hashval_t _hash, bitmap_obstack *stack): type(_type),
+  node (_node), hash (_hash)
+{
+  decl = node->decl;
+  setup (stack);
+}
+
+/* Initialize internal data structures. Bitmap STACK is used for
+   bitmap memory allocation process.  */
+
+void
+sem_item::setup (bitmap_obstack *stack)
+{
+  gcc_checking_assert (node);
+
+  refs.create (0);
+  tree_refs.create (0);
+  usages.create (0);
+  usage_index_bitmap = BITMAP_ALLOC (stack);
+}
+
+sem_item::~sem_item ()
+{
+  for (unsigned i = 0; i < usages.length (); i++)
+    delete usages[i];
+
+  refs.release ();
+  tree_refs.release ();
+  usages.release ();
+
+  BITMAP_FREE (usage_index_bitmap);
+}
+
+/* Dump function for debugging purpose.  */
+
+DEBUG_FUNCTION void
+sem_item::dump (void)
+{
+  if (dump_file)
+    {
+      fprintf (dump_file, "[%s] %s (%u) (tree:%p)\n", type == FUNC ? "func" : "var",
+	       name(), node->order, (void *) node->decl);
+      fprintf (dump_file, "  hash: %u\n", get_hash ());
+      fprintf (dump_file, "  references: ");
+
+      for (unsigned i = 0; i < refs.length (); i++)
+	fprintf (dump_file, "%s%s ", refs[i]->name (),
+		 i < refs.length() - 1 ? "," : "");
+
+      fprintf (dump_file, "\n");
+    }
+}
+
+/* Return true if types are compatible from perspective of ICF.  */
+bool func_checker::types_are_compatible_p (tree t1, tree t2,
+    bool compare_polymorphic,
+    bool first_argument)
+{
+  if (TREE_CODE (t1) != TREE_CODE (t2))
+    return RETURN_FALSE_WITH_MSG ("different tree types");
+
+  if (!types_compatible_p (t1, t2))
+    return RETURN_FALSE_WITH_MSG ("types are not compatible");
+
+  if (get_alias_set (t1) != get_alias_set (t2))
+    return RETURN_FALSE_WITH_MSG ("alias sets are different");
+
+  /* We call contains_polymorphic_type_p with this pointer type.  */
+  if (first_argument && TREE_CODE (t1) == POINTER_TYPE)
+    {
+      t1 = TREE_TYPE (t1);
+      t2 = TREE_TYPE (t2);
+    }
+
+  if (compare_polymorphic
+      && (contains_polymorphic_type_p (t1) || contains_polymorphic_type_p (t2)))
+    {
+      if (!contains_polymorphic_type_p (t1) || !contains_polymorphic_type_p (t2))
+	return RETURN_FALSE_WITH_MSG ("one type is not polymorphic");
+
+      if (TYPE_MAIN_VARIANT (t1) != TYPE_MAIN_VARIANT (t2))
+	return RETURN_FALSE_WITH_MSG ("type variants are different for "
+				      "polymorphic type");
+    }
+
+  return true;
+}
+
+/* Semantic function constructor that uses STACK as bitmap memory stack.  */
+
+sem_function::sem_function (bitmap_obstack *stack): sem_item (FUNC, stack),
+  m_checker (NULL), m_compared_func (NULL)
+{
+  arg_types.create (0);
+  bb_sizes.create (0);
+  bb_sorted.create (0);
+}
+
+/*  Constructor based on callgraph node _NODE with computed hash _HASH.
+    Bitmap STACK is used for memory allocation.  */
+sem_function::sem_function (cgraph_node *node, hashval_t hash,
+			    bitmap_obstack *stack):
+  sem_item (FUNC, node, hash, stack),
+  m_checker (NULL), m_compared_func (NULL)
+{
+  arg_types.create (0);
+  bb_sizes.create (0);
+  bb_sorted.create (0);
+}
+
+sem_function::~sem_function ()
+{
+  for (unsigned i = 0; i < bb_sorted.length (); i++)
+    free (bb_sorted[i]);
+
+  arg_types.release ();
+  bb_sizes.release ();
+  bb_sorted.release ();
+}
+
+/* Calculates hash value based on a BASIC_BLOCK.  */
+
+hashval_t
+sem_function::get_bb_hash (const sem_bb *basic_block)
+{
+  inchash::hash hstate;
+
+  hstate.add_int (basic_block->nondbg_stmt_count);
+  hstate.add_int (basic_block->edge_count);
+
+  return hstate.end ();
+}
+
+/* References independent hash function.  */
+
+hashval_t
+sem_function::get_hash (void)
+{
+  if(!hash)
+    {
+      inchash::hash hstate;
+      hstate.add_int (177454); /* Random number for function type.  */
+
+      hstate.add_int (arg_count);
+      hstate.add_int (cfg_checksum);
+      hstate.add_int (gcode_hash);
+
+      for (unsigned i = 0; i < bb_sorted.length (); i++)
+	hstate.merge_hash (get_bb_hash (bb_sorted[i]));
+
+      for (unsigned i = 0; i < bb_sizes.length (); i++)
+	hstate.add_int (bb_sizes[i]);
+
+      hash = hstate.end ();
+    }
+
+  return hash;
+}
+
+/* Fast equality function based on knowledge known in WPA.  */
+
+bool
+sem_function::equals_wpa (sem_item *item)
+{
+  gcc_assert (item->type == FUNC);
+
+  m_compared_func = static_cast<sem_function *> (item);
+
+  if (arg_types.length () != m_compared_func->arg_types.length ())
+    return RETURN_FALSE_WITH_MSG ("different number of arguments");
+
+  /* Checking types of arguments.  */
+  for (unsigned i = 0; i < arg_types.length (); i++)
+    {
+      /* This guard is here for function pointer with attributes (pr59927.c).  */
+      if (!arg_types[i] || !m_compared_func->arg_types[i])
+	return RETURN_FALSE_WITH_MSG ("NULL argument type");
+
+      if (!func_checker::types_are_compatible_p (arg_types[i],
+	  m_compared_func->arg_types[i],
+	  true, i == 0))
+	return RETURN_FALSE_WITH_MSG ("argument type is different");
+    }
+
+  /* Result type checking.  */
+  if (!func_checker::types_are_compatible_p (result_type,
+      m_compared_func->result_type))
+    return RETURN_FALSE_WITH_MSG ("result types are different");
+
+  return true;
+}
+
+/* Returns true if the item equals to ITEM given as argument.  */
+
+bool
+sem_function::equals (sem_item *item)
+{
+  gcc_assert (item->type == FUNC);
+  bool eq = equals_private (item);
+
+  if (m_checker != NULL)
+    {
+      delete m_checker;
+      m_checker = NULL;
+    }
+
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    fprintf (dump_file,
+	     "Equals called for:%s:%s (%u:%u) (%s:%s) with result: %s\n\n",
+	     name(), item->name (), node->order, item->node->order, asm_name (),
+	     item->asm_name (), eq ? "true" : "false");
+
+  return eq;
+}
+
+/* Processes function equality comparison.  */
+
+bool
+sem_function::equals_private (sem_item *item)
+{
+  if (item->type != FUNC)
+    return false;
+
+  basic_block bb1, bb2;
+  edge e1, e2;
+  edge_iterator ei1, ei2;
+  int *bb_dict = NULL;
+  bool result = true;
+  tree arg1, arg2;
+
+  m_compared_func = static_cast<sem_function *> (item);
+
+  gcc_assert (decl != item->decl);
+
+  if (bb_sorted.length () != m_compared_func->bb_sorted.length ()
+      || edge_count != m_compared_func->edge_count
+      || cfg_checksum != m_compared_func->cfg_checksum)
+    return RETURN_FALSE ();
+
+  if (!equals_wpa (item))
+    return false;
+
+  /* Checking function arguments.  */
+  tree decl1 = DECL_ATTRIBUTES (decl);
+  tree decl2 = DECL_ATTRIBUTES (m_compared_func->decl);
+
+  m_checker = new func_checker (decl, m_compared_func->decl,
+				compare_polymorphic_p (),
+				false,
+				&tree_refs_set,
+				&m_compared_func->tree_refs_set);
+  while (decl1)
+    {
+      if (decl2 == NULL)
+	return RETURN_FALSE ();
+
+      if (get_attribute_name (decl1) != get_attribute_name (decl2))
+	return RETURN_FALSE ();
+
+      tree attr_value1 = TREE_VALUE (decl1);
+      tree attr_value2 = TREE_VALUE (decl2);
+
+      if (attr_value1 && attr_value2)
+	{
+	  bool ret = m_checker->compare_operand (TREE_VALUE (attr_value1),
+						 TREE_VALUE (attr_value2));
+	  if (!ret)
+	    return RETURN_FALSE_WITH_MSG ("attribute values are different");
+	}
+      else if (!attr_value1 && !attr_value2)
+	{}
+      else
+	return RETURN_FALSE ();
+
+      decl1 = TREE_CHAIN (decl1);
+      decl2 = TREE_CHAIN (decl2);
+    }
+
+  if (decl1 != decl2)
+    return RETURN_FALSE();
+
+
+  for (arg1 = DECL_ARGUMENTS (decl),
+       arg2 = DECL_ARGUMENTS (m_compared_func->decl);
+       arg1; arg1 = DECL_CHAIN (arg1), arg2 = DECL_CHAIN (arg2))
+    if (!m_checker->compare_decl (arg1, arg2))
+      return RETURN_FALSE ();
+
+  /* Exception handling regions comparison.  */
+  if (!compare_eh_region (region_tree, m_compared_func->region_tree))
+    return RETURN_FALSE();
+
+  /* Checking all basic blocks.  */
+  for (unsigned i = 0; i < bb_sorted.length (); ++i)
+    if(!m_checker->compare_bb (bb_sorted[i], m_compared_func->bb_sorted[i]))
+      return RETURN_FALSE();
+
+  DUMP_MESSAGE ("All BBs are equal\n");
+
+  /* Basic block edges check.  */
+  for (unsigned i = 0; i < bb_sorted.length (); ++i)
+    {
+      bb_dict = XNEWVEC (int, bb_sorted.length () + 2);
+      memset (bb_dict, -1, (bb_sorted.length () + 2) * sizeof (int));
+
+      bb1 = bb_sorted[i]->bb;
+      bb2 = m_compared_func->bb_sorted[i]->bb;
+
+      ei2 = ei_start (bb2->preds);
+
+      for (ei1 = ei_start (bb1->preds); ei_cond (ei1, &e1); ei_next (&ei1))
+	{
+	  ei_cond (ei2, &e2);
+
+	  if (e1->flags != e2->flags)
+	    return RETURN_FALSE_WITH_MSG ("flags comparison returns false");
+
+	  if (!bb_dict_test (bb_dict, e1->src->index, e2->src->index))
+	    return RETURN_FALSE_WITH_MSG ("edge comparison returns false");
+
+	  if (!bb_dict_test (bb_dict, e1->dest->index, e2->dest->index))
+	    return RETURN_FALSE_WITH_MSG ("BB comparison returns false");
+
+	  if (!m_checker->compare_edge (e1, e2))
+	    return RETURN_FALSE_WITH_MSG ("edge comparison returns false");
+
+	  ei_next (&ei2);
+	}
+    }
+
+  /* Basic block PHI nodes comparison.  */
+  for (unsigned i = 0; i < bb_sorted.length (); i++)
+    if (!compare_phi_node (bb_sorted[i]->bb, m_compared_func->bb_sorted[i]->bb))
+      return RETURN_FALSE_WITH_MSG ("PHI node comparison returns false");
+
+  return result;
+}
+
+/* Initialize references to another sem_item for tree T.  */
+
+void
+sem_function::init_refs_for_tree (tree t)
+{
+  switch (TREE_CODE (t))
+    {
+    case VAR_DECL:
+    case FUNCTION_DECL:
+      tree_refs.safe_push (t);
+      break;
+    case MEM_REF:
+    case ADDR_EXPR:
+    case OBJ_TYPE_REF:
+      init_refs_for_tree (TREE_OPERAND (t, 0));
+      break;
+    case FIELD_DECL:
+      init_refs_for_tree (DECL_FCONTEXT (t));
+      break;
+    default:
+      break;
+    }
+}
+
+/* Initialize references to another sem_item for gimple STMT of type assign.  */
+
+void
+sem_function::init_refs_for_assign (gimple stmt)
+{
+  if (gimple_num_ops (stmt) != 2)
+    return;
+
+  tree rhs = gimple_op (stmt, 1);
+
+  init_refs_for_tree (rhs);
+}
+
+/* Initialize references to other semantic functions/variables.  */
+
+void
+sem_function::init_refs (void)
+{
+  for (unsigned i = 0; i < bb_sorted.length (); i++)
+    {
+      basic_block bb = bb_sorted[i]->bb;
+
+      for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi);
+	   gsi_next (&gsi))
+	{
+	  gimple stmt = gsi_stmt (gsi);
+	  hashval_t code = (hashval_t) gimple_code (stmt);
+
+	  switch (code)
+	    {
+	    case GIMPLE_CALL:
+	      {
+		tree funcdecl = gimple_call_fndecl (stmt);
+
+		/* Function pointer variables are not supported yet.  */
+		if (funcdecl)
+		  tree_refs.safe_push (funcdecl);
+
+		break;
+	      }
+	    case GIMPLE_ASSIGN:
+	      init_refs_for_assign (stmt);
+	      break;
+	    default:
+	      break;
+	    }
+	}
+    }
+}
+
+/* Merges instance with an ALIAS_ITEM, where alias, thunk or redirection can
+   be applied.  */
+bool
+sem_function::merge (sem_item *alias_item)
+{
+  gcc_assert (alias_item->type == FUNC);
+
+  sem_function *alias_func = static_cast<sem_function *> (alias_item);
+
+  cgraph_node *original = get_node ();
+  cgraph_node *local_original = original;
+  cgraph_node *alias = alias_func->get_node ();
+  bool original_address_matters;
+  bool alias_address_matters;
+
+  bool create_thunk = false;
+  bool create_alias = false;
+  bool redirect_callers = false;
+  bool original_discardable = false;
+
+  /* Do not attempt to mix functions from different user sections;
+     we do not know what user intends with those.  */
+  if (((DECL_SECTION_NAME (original->decl) && !original->implicit_section)
+       || (DECL_SECTION_NAME (alias->decl) && !alias->implicit_section))
+      && DECL_SECTION_NAME (original->decl) != DECL_SECTION_NAME (alias->decl))
+    {
+      if (dump_file)
+	fprintf (dump_file,
+		 "Not unifying; original and alias are in different sections.\n\n");
+      return false;
+    }
+
+  /* See if original is in a section that can be discarded if the main
+     symbol is not used.  */
+  if (DECL_EXTERNAL (original->decl))
+    original_discardable = true;
+  if (original->resolution == LDPR_PREEMPTED_REG
+      || original->resolution == LDPR_PREEMPTED_IR)
+    original_discardable = true;
+  if (original->can_be_discarded_p ())
+    original_discardable = true;
+
+  /* See if original and/or alias address can be compared for equality.  */
+  original_address_matters
+    = (!DECL_VIRTUAL_P (original->decl)
+       && (original->externally_visible
+	   || original->address_taken_from_non_vtable_p ()));
+  alias_address_matters
+    = (!DECL_VIRTUAL_P (alias->decl)
+       && (alias->externally_visible
+	   || alias->address_taken_from_non_vtable_p ()));
+
+  /* If alias and original can be compared for address equality, we need
+     to create a thunk.  Also we can not create extra aliases into discardable
+     section (or we risk link failures when section is discarded).  */
+  if ((original_address_matters
+       && alias_address_matters)
+      || original_discardable)
+    {
+      create_thunk = !stdarg_p (TREE_TYPE (alias->decl));
+      create_alias = false;
+      /* When both alias and original are not overwritable, we can save
+         the extra thunk wrapper for direct calls.  */
+      redirect_callers
+	= (!original_discardable
+	   && alias->get_availability () > AVAIL_INTERPOSABLE
+	   && original->get_availability () > AVAIL_INTERPOSABLE);
+    }
+  else
+    {
+      create_alias = true;
+      create_thunk = false;
+      redirect_callers = false;
+    }
+
+  if (create_alias && DECL_COMDAT_GROUP (alias->decl))
+    {
+      create_alias = false;
+      create_thunk = true;
+    }
+
+  /* We want thunk to always jump to the local function body
+     unless the body is comdat and may be optimized out.  */
+  if ((create_thunk || redirect_callers)
+      && (!original_discardable
+	  || (DECL_COMDAT_GROUP (original->decl)
+	      && (DECL_COMDAT_GROUP (original->decl)
+		  == DECL_COMDAT_GROUP (alias->decl)))))
+    local_original
+      = dyn_cast <cgraph_node *> (original->noninterposable_alias ());
+
+  if (redirect_callers)
+    {
+      /* If alias is non-overwritable then
+         all direct calls are safe to be redirected to the original.  */
+      bool redirected = false;
+      while (alias->callers)
+	{
+	  cgraph_edge *e = alias->callers;
+	  e->redirect_callee (local_original);
+	  push_cfun (DECL_STRUCT_FUNCTION (e->caller->decl));
+
+	  if (e->call_stmt)
+	    e->redirect_call_stmt_to_callee ();
+
+	  pop_cfun ();
+	  redirected = true;
+	}
+
+      alias->icf_merged = true;
+
+      /* The alias function is removed if symbol address
+         does not matter.  */
+      if (!alias_address_matters)
+	alias->remove ();
+
+      if (dump_file && redirected)
+	fprintf (dump_file, "Callgraph local calls have been redirected.\n\n");
+    }
+  /* If the condtion above is not met, we are lucky and can turn the
+     function into real alias.  */
+  else if (create_alias)
+    {
+      alias->icf_merged = true;
+
+      /* Remove the function's body.  */
+      ipa_merge_profiles (original, alias);
+      alias->release_body (true);
+      alias->reset ();
+
+      /* Create the alias.  */
+      cgraph_node::create_alias (alias_func->decl, decl);
+      alias->resolve_alias (original);
+
+      if (dump_file)
+	fprintf (dump_file, "Callgraph alias has been created.\n\n");
+    }
+  else if (create_thunk)
+    {
+      if (DECL_COMDAT_GROUP (alias->decl))
+	{
+	  if (dump_file)
+	    fprintf (dump_file, "Callgraph thunk cannot be created because of COMDAT\n");
+
+	  return 0;
+	}
+
+      alias->icf_merged = true;
+      ipa_merge_profiles (local_original, alias);
+      alias->create_wrapper (local_original);
+
+      if (dump_file)
+	fprintf (dump_file, "Callgraph thunk has been created.\n\n");
+    }
+  else if (dump_file)
+    fprintf (dump_file, "Callgraph merge operation cannot be performed.\n\n");
+
+  return true;
+}
+
+/* Semantic item initialization function.  */
+
+void
+sem_function::init (void)
+{
+  if (in_lto_p)
+    get_node ()->get_body ();
+
+  tree fndecl = node->decl;
+  function *func = DECL_STRUCT_FUNCTION (fndecl);
+
+  gcc_assert (func);
+  gcc_assert (SSANAMES (func));
+
+  ssa_names_size = SSANAMES (func)->length ();
+  node = node;
+
+  decl = fndecl;
+  region_tree = func->eh->region_tree;
+
+  /* iterating all function arguments.  */
+  arg_count = count_formal_params (fndecl);
+
+  edge_count = n_edges_for_fn (func);
+  cfg_checksum = coverage_compute_cfg_checksum (func);
+
+  inchash::hash hstate;
+
+  basic_block bb;
+  FOR_EACH_BB_FN (bb, func)
+  {
+    unsigned nondbg_stmt_count = 0;
+
+    edge e;
+    for (edge_iterator ei = ei_start (bb->preds); ei_cond (ei, &e); ei_next (&ei))
+      cfg_checksum = iterative_hash_host_wide_int (e->flags,
+		     cfg_checksum);
+
+    for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi);
+	 gsi_next (&gsi))
+      {
+	gimple stmt = gsi_stmt (gsi);
+
+	if (gimple_code (stmt) != GIMPLE_DEBUG)
+	  {
+	    improve_hash (&hstate, stmt);
+	    nondbg_stmt_count++;
+	  }
+      }
+
+    gcode_hash = hstate.end ();
+    bb_sizes.safe_push (nondbg_stmt_count);
+
+    /* Inserting basic block to hash table.  */
+    sem_bb *semantic_bb = new sem_bb (bb, nondbg_stmt_count,
+				      EDGE_COUNT (bb->preds) + EDGE_COUNT (bb->succs));
+
+    bb_sorted.safe_push (semantic_bb);
+  }
+
+  parse_tree_args ();
+}
+
+/* Improve accumulated hash for HSTATE based on a gimple statement STMT.  */
+
+void
+sem_function::improve_hash (inchash::hash *hstate, gimple stmt)
+{
+  enum gimple_code code = gimple_code (stmt);
+
+  hstate->add_int (code);
+
+  if (code == GIMPLE_CALL)
+    {
+      /* Checking of argument.  */
+      for (unsigned i = 0; i < gimple_call_num_args (stmt); ++i)
+	{
+	  tree argument = gimple_call_arg (stmt, i);
+
+	  switch (TREE_CODE (argument))
+	    {
+	    case INTEGER_CST:
+	      if (tree_fits_shwi_p (argument))
+		hstate->add_wide_int (tree_to_shwi (argument));
+	      else if (tree_fits_uhwi_p (argument))
+		hstate->add_wide_int (tree_to_uhwi (argument));
+	      break;
+	    case ADDR_EXPR:
+	      {
+		tree addr_operand = TREE_OPERAND (argument, 0);
+
+		if (TREE_CODE (addr_operand) == STRING_CST)
+		  hstate->add (TREE_STRING_POINTER (addr_operand),
+			       TREE_STRING_LENGTH (addr_operand));
+		break;
+	      }
+	    default:
+	      break;
+	    }
+	}
+    }
+}
+
+
+/* Return true if polymorphic comparison must be processed.  */
+
+bool
+sem_function::compare_polymorphic_p (void)
+{
+  return get_node ()->callees != NULL
+	 || m_compared_func->get_node ()->callees != NULL;
+}
+
+/* For a given call graph NODE, the function constructs new
+   semantic function item.  */
+
+sem_function *
+sem_function::parse (cgraph_node *node, bitmap_obstack *stack)
+{
+  tree fndecl = node->decl;
+  function *func = DECL_STRUCT_FUNCTION (fndecl);
+
+  if (!func || !node->has_gimple_body_p ())
+    return NULL;
+
+  if (lookup_attribute_by_prefix ("omp ", DECL_ATTRIBUTES (node->decl)) != NULL)
+    return NULL;
+
+  sem_function *f = new sem_function (node, 0, stack);
+
+  f->init ();
+
+  return f;
+}
+
+/* Parses function arguments and result type.  */
+
+void
+sem_function::parse_tree_args (void)
+{
+  tree result;
+
+  if (arg_types.exists ())
+    arg_types.release ();
+
+  arg_types.create (4);
+  tree fnargs = DECL_ARGUMENTS (decl);
+
+  for (tree parm = fnargs; parm; parm = DECL_CHAIN (parm))
+    arg_types.safe_push (DECL_ARG_TYPE (parm));
+
+  /* Function result type.  */
+  result = DECL_RESULT (decl);
+  result_type = result ? TREE_TYPE (result) : NULL;
+
+  /* During WPA, we can get arguments by following method.  */
+  if (!fnargs)
+    {
+      tree type = TYPE_ARG_TYPES (TREE_TYPE (decl));
+      for (tree parm = type; parm; parm = TREE_CHAIN (parm))
+	arg_types.safe_push (TYPE_CANONICAL (TREE_VALUE (parm)));
+
+      result_type = TREE_TYPE (TREE_TYPE (decl));
+    }
+}
+
+/* For given basic blocks BB1 and BB2 (from functions FUNC1 and FUNC),
+   return true if phi nodes are semantically equivalent in these blocks .  */
+
+bool
+sem_function::compare_phi_node (basic_block bb1, basic_block bb2)
+{
+  gimple_stmt_iterator si1, si2;
+  gimple phi1, phi2;
+  unsigned size1, size2, i;
+  tree t1, t2;
+  edge e1, e2;
+
+  gcc_assert (bb1 != NULL);
+  gcc_assert (bb2 != NULL);
+
+  si2 = gsi_start_phis (bb2);
+  for (si1 = gsi_start_phis (bb1); !gsi_end_p (si1);
+       gsi_next (&si1))
+    {
+      gsi_next_nonvirtual_phi (&si1);
+      gsi_next_nonvirtual_phi (&si2);
+
+      if (gsi_end_p (si1) && gsi_end_p (si2))
+	break;
+
+      if (gsi_end_p (si1) || gsi_end_p (si2))
+	return RETURN_FALSE();
+
+      phi1 = gsi_stmt (si1);
+      phi2 = gsi_stmt (si2);
+
+      size1 = gimple_phi_num_args (phi1);
+      size2 = gimple_phi_num_args (phi2);
+
+      if (size1 != size2)
+	return RETURN_FALSE ();
+
+      for (i = 0; i < size1; ++i)
+	{
+	  t1 = gimple_phi_arg (phi1, i)->def;
+	  t2 = gimple_phi_arg (phi2, i)->def;
+
+	  if (!m_checker->compare_operand (t1, t2))
+	    return RETURN_FALSE ();
+
+	  e1 = gimple_phi_arg_edge (phi1, i);
+	  e2 = gimple_phi_arg_edge (phi2, i);
+
+	  if (!m_checker->compare_edge (e1, e2))
+	    return RETURN_FALSE ();
+	}
+
+      gsi_next (&si2);
+    }
+
+  return true;
+}
+
+/* For given basic blocks BB1 and BB2 (from functions FUNC1 and FUNC),
+   true value is returned if exception handling regions are equivalent
+   in these blocks.  */
+
+bool
+sem_function::compare_eh_region (eh_region r1, eh_region r2)
+{
+  eh_landing_pad lp1, lp2;
+  eh_catch c1, c2;
+  tree t1, t2;
+
+  while (1)
+    {
+      if (!r1 && !r2)
+	return true;
+
+      if (!r1 || !r2)
+	return false;
+
+      if (r1->index != r2->index || r1->type != r2->type)
+	return false;
+
+      /* Landing pads comparison */
+      lp1 = r1->landing_pads;
+      lp2 = r2->landing_pads;
+
+      while (lp1 && lp2)
+	{
+	  if (lp1->index != lp2->index)
+	    return false;
+
+	  /* Comparison of post landing pads. */
+	  if (lp1->post_landing_pad && lp2->post_landing_pad)
+	    {
+	      t1 = lp1->post_landing_pad;
+	      t2 = lp2->post_landing_pad;
+
+	      gcc_assert (TREE_CODE (t1) == LABEL_DECL);
+	      gcc_assert (TREE_CODE (t2) == LABEL_DECL);
+
+	      if (!m_checker->compare_tree_ssa_label (t1, t2))
+		return false;
+	    }
+	  else if (lp1->post_landing_pad || lp2->post_landing_pad)
+	    return false;
+
+	  lp1 = lp1->next_lp;
+	  lp2 = lp2->next_lp;
+	}
+
+      if (lp1 || lp2)
+	return false;
+
+      switch (r1->type)
+	{
+	case ERT_TRY:
+	  c1 = r1->u.eh_try.first_catch;
+	  c2 = r2->u.eh_try.first_catch;
+
+	  while (c1 && c2)
+	    {
+	      /* Catch label checking */
+	      if (c1->label && c2->label)
+		{
+		  if (!m_checker->compare_tree_ssa_label (c1->label, c2->label))
+		    return false;
+		}
+	      else if (c1->label || c2->label)
+		return false;
+
+	      /* Type list checking */
+	      if (!compare_type_list (c1->type_list, c2->type_list,
+				      compare_polymorphic_p ()))
+		return false;
+
+	      c1 = c1->next_catch;
+	      c2 = c2->next_catch;
+	    }
+
+	  break;
+
+	case ERT_ALLOWED_EXCEPTIONS:
+	  if (r1->u.allowed.filter != r2->u.allowed.filter)
+	    return false;
+
+	  if (!compare_type_list (r1->u.allowed.type_list,
+				  r2->u.allowed.type_list,
+				  compare_polymorphic_p ()))
+	    return false;
+
+	  break;
+	case ERT_CLEANUP:
+	  break;
+	case ERT_MUST_NOT_THROW:
+	  if (r1->u.must_not_throw.failure_decl != r1->u.must_not_throw.failure_decl)
+	    return false;
+	  break;
+	default:
+	  gcc_unreachable ();
+	  break;
+	}
+
+      /* If there are sub-regions, process them.  */
+      if ((!r1->inner && r2->inner) || (!r1->next_peer && r2->next_peer))
+	return false;
+
+      if (r1->inner)
+	{
+	  r1 = r1->inner;
+	  r2 = r2->inner;
+	}
+
+      /* If there are peers, process them.  */
+      else if (r1->next_peer)
+	{
+	  r1 = r1->next_peer;
+	  r2 = r2->next_peer;
+	}
+      /* Otherwise, step back up the tree to the next peer.  */
+      else
+	{
+	  do
+	    {
+	      r1 = r1->outer;
+	      r2 = r2->outer;
+
+	      /* All nodes have been visited. */
+	      if (!r1 && !r2)
+		return true;
+	    }
+	  while (r1->next_peer == NULL);
+
+	  r1 = r1->next_peer;
+	  r2 = r2->next_peer;
+	}
+    }
+
+  return false;
+}
+
+/* Verifies that trees T1 and T2, representing function declarations
+   are equivalent from perspective of ICF.  */
+
+bool
+func_checker::compare_function_decl (tree t1, tree t2)
+{
+  bool ret = false;
+
+  if (t1 == t2)
+    return true;
+
+  if (m_ignored_source_decls != NULL && m_ignored_target_decls != NULL)
+    {
+      ret = m_ignored_source_decls->contains (t1)
+	    && m_ignored_target_decls->contains (t2);
+
+      if (ret)
+	return true;
+    }
+
+  /* If function decl is WEAKREF, we compare targets.  */
+  cgraph_node *f1 = cgraph_node::get (t1);
+  cgraph_node *f2 = cgraph_node::get (t2);
+
+  if(f1 && f2 && f1->weakref && f2->weakref)
+    ret = f1->alias_target == f2->alias_target;
+
+  return ret;
+}
+
+/* Verifies that trees T1 and T2 do correspond.  */
+
+bool
+func_checker::compare_variable_decl (tree t1, tree t2)
+{
+  bool ret = false;
+
+  if (t1 == t2)
+    return true;
+
+  if (m_ignored_source_decls != NULL && m_ignored_target_decls != NULL)
+    {
+      ret = m_ignored_source_decls->contains (t1)
+	    && m_ignored_target_decls->contains (t2);
+
+      if (ret)
+	return true;
+    }
+
+  ret = compare_decl (t1, t2);
+
+  return RETURN_WITH_DEBUG (ret);
+}
+
+
+/* Returns true if tree T can be compared as a handled component.  */
+
+bool
+sem_function::icf_handled_component_p (tree t)
+{
+  tree_code tc = TREE_CODE (t);
+
+  return ((handled_component_p (t))
+	  || tc == ADDR_EXPR || tc == MEM_REF || tc == REALPART_EXPR
+	  || tc == IMAGPART_EXPR || tc == OBJ_TYPE_REF);
+}
+
+/* Basic blocks dictionary BB_DICT returns true if SOURCE index BB
+   corresponds to TARGET.  */
+
+bool
+sem_function::bb_dict_test (int* bb_dict, int source, int target)
+{
+  if (bb_dict[source] == -1)
+    {
+      bb_dict[source] = target;
+      return true;
+    }
+  else
+    return bb_dict[source] == target;
+}
+
+/* Function responsible for comparison of handled components T1 and T2.
+   If these components, from functions FUNC1 and FUNC2, are equal, true
+   is returned.  */
+
+bool
+func_checker::compare_operand (tree t1, tree t2)
+{
+  tree base1, base2, x1, x2, y1, y2, z1, z2;
+  HOST_WIDE_INT offset1 = 0, offset2 = 0;
+  bool ret;
+
+  if (!t1 && !t2)
+    return true;
+  else if (!t1 || !t2)
+    return false;
+
+  tree tt1 = TREE_TYPE (t1);
+  tree tt2 = TREE_TYPE (t2);
+
+  if (!func_checker::types_are_compatible_p (tt1, tt2))
+    return false;
+
+  base1 = get_addr_base_and_unit_offset (t1, &offset1);
+  base2 = get_addr_base_and_unit_offset (t2, &offset2);
+
+  if (base1 && base2)
+    {
+      if (offset1 != offset2)
+	return RETURN_FALSE_WITH_MSG ("base offsets are different");
+
+      t1 = base1;
+      t2 = base2;
+    }
+
+  if (TREE_CODE (t1) != TREE_CODE (t2))
+    return RETURN_FALSE ();
+
+  switch (TREE_CODE (t1))
+    {
+    case CONSTRUCTOR:
+      {
+	unsigned length1 = vec_safe_length (CONSTRUCTOR_ELTS (t1));
+	unsigned length2 = vec_safe_length (CONSTRUCTOR_ELTS (t2));
+
+	if (length1 != length2)
+	  return RETURN_FALSE ();
+
+	for (unsigned i = 0; i < length1; i++)
+	  if (!compare_operand (CONSTRUCTOR_ELT (t1, i)->value,
+				CONSTRUCTOR_ELT (t2, i)->value))
+	    return RETURN_FALSE();
+
+	return true;
+      }
+    case ARRAY_REF:
+    case ARRAY_RANGE_REF:
+      {
+	x1 = TREE_OPERAND (t1, 0);
+	x2 = TREE_OPERAND (t2, 0);
+	y1 = TREE_OPERAND (t1, 1);
+	y2 = TREE_OPERAND (t2, 1);
+
+	if (!compare_operand (array_ref_low_bound (t1),
+			      array_ref_low_bound (t2)))
+	  return RETURN_FALSE_WITH_MSG ("");
+	if (!compare_operand (array_ref_element_size (t1),
+			      array_ref_element_size (t2)))
+	  return RETURN_FALSE_WITH_MSG ("");
+	if (!compare_operand (x1, x2))
+	  return RETURN_FALSE_WITH_MSG ("");
+	return compare_operand (y1, y2);
+      }
+
+    case MEM_REF:
+      {
+	x1 = TREE_OPERAND (t1, 0);
+	x2 = TREE_OPERAND (t2, 0);
+	y1 = TREE_OPERAND (t1, 1);
+	y2 = TREE_OPERAND (t2, 1);
+
+	/* See if operand is an memory access (the test originate from
+	 gimple_load_p).
+
+	In this case the alias set of the function being replaced must
+	be subset of the alias set of the other function.  At the moment
+	we seek for equivalency classes, so simply require inclussion in
+	both directions.  */
+
+	if (!func_checker::types_are_compatible_p (TREE_TYPE (x1), TREE_TYPE (x2)))
+	  return RETURN_FALSE ();
+
+	if (!compare_operand (x1, x2))
+	  return RETURN_FALSE_WITH_MSG ("");
+
+	if (get_alias_set (TREE_TYPE (y1)) != get_alias_set (TREE_TYPE (y2)))
+	  return RETURN_FALSE_WITH_MSG ("alias set for MEM_REF offsets are different");
+
+	ao_ref r1, r2;
+	ao_ref_init (&r1, t1);
+	ao_ref_init (&r2, t2);
+	if (ao_ref_alias_set (&r1) != ao_ref_alias_set (&r2)
+	    || ao_ref_base_alias_set (&r1) != ao_ref_base_alias_set (&r2))
+	  return RETURN_FALSE_WITH_MSG ("ao alias sets are different");
+
+	/* Type of the offset on MEM_REF does not matter.  */
+	return wi::to_offset  (y1) == wi::to_offset  (y2);
+      }
+    case COMPONENT_REF:
+      {
+	x1 = TREE_OPERAND (t1, 0);
+	x2 = TREE_OPERAND (t2, 0);
+	y1 = TREE_OPERAND (t1, 1);
+	y2 = TREE_OPERAND (t2, 1);
+
+	ret = compare_operand (x1, x2)
+	      && compare_operand (y1, y2);
+
+	return RETURN_WITH_DEBUG (ret);
+      }
+    /* Virtual table call.  */
+    case OBJ_TYPE_REF:
+      {
+	x1 = TREE_OPERAND (t1, 0);
+	x2 = TREE_OPERAND (t2, 0);
+	y1 = TREE_OPERAND (t1, 1);
+	y2 = TREE_OPERAND (t2, 1);
+	z1 = TREE_OPERAND (t1, 2);
+	z2 = TREE_OPERAND (t2, 2);
+
+	ret = compare_operand (x1, x2)
+	      && compare_operand (y1, y2)
+	      && compare_operand (z1, z2);
+
+	return RETURN_WITH_DEBUG (ret);
+      }
+    case ADDR_EXPR:
+      {
+	x1 = TREE_OPERAND (t1, 0);
+	x2 = TREE_OPERAND (t2, 0);
+
+	ret = compare_operand (x1, x2);
+	return RETURN_WITH_DEBUG (ret);
+      }
+    case SSA_NAME:
+      {
+	ret = compare_ssa_name (t1, t2);
+
+	if (!ret)
+	  return RETURN_WITH_DEBUG (ret);
+
+	if (SSA_NAME_IS_DEFAULT_DEF (t1))
+	  {
+	    tree b1 = SSA_NAME_VAR (t1);
+	    tree b2 = SSA_NAME_VAR (t2);
+
+	    if (b1 == NULL && b2 == NULL)
+	      return true;
+
+	    if (b1 == NULL || b2 == NULL || TREE_CODE (b1) != TREE_CODE (b2))
+	      return RETURN_FALSE ();
+
+	    switch (TREE_CODE (b1))
+	      {
+	      case VAR_DECL:
+		return RETURN_WITH_DEBUG (compare_variable_decl (t1, t2));
+	      case PARM_DECL:
+	      case RESULT_DECL:
+		ret = compare_decl (b1, b2);
+		return RETURN_WITH_DEBUG (ret);
+	      default:
+		return RETURN_FALSE_WITH_MSG ("Unknown TREE code reached");
+	      }
+	  }
+	else
+	  return true;
+      }
+    case INTEGER_CST:
+      {
+	ret = types_are_compatible_p (TREE_TYPE (t1), TREE_TYPE (t2))
+	      && wi::to_offset  (t1) == wi::to_offset  (t2);
+
+	return RETURN_WITH_DEBUG (ret);
+      }
+    case COMPLEX_CST:
+    case VECTOR_CST:
+    case STRING_CST:
+    case REAL_CST:
+      {
+	ret = operand_equal_p (t1, t2, OEP_ONLY_CONST);
+	return RETURN_WITH_DEBUG (ret);
+      }
+    case FUNCTION_DECL:
+      {
+	ret = compare_function_decl (t1, t2);
+	return RETURN_WITH_DEBUG (ret);
+      }
+    case VAR_DECL:
+      return RETURN_WITH_DEBUG (compare_variable_decl (t1, t2));
+    case FIELD_DECL:
+      {
+	tree fctx1 = DECL_FCONTEXT (t1);
+	tree fctx2 = DECL_FCONTEXT (t2);
+
+	tree offset1 = DECL_FIELD_OFFSET (t1);
+	tree offset2 = DECL_FIELD_OFFSET (t2);
+
+	tree bit_offset1 = DECL_FIELD_BIT_OFFSET (t1);
+	tree bit_offset2 = DECL_FIELD_BIT_OFFSET (t2);
+
+	ret = compare_operand (fctx1, fctx2)
+	      && compare_operand (offset1, offset2)
+	      && compare_operand (bit_offset1, bit_offset2);
+
+	return RETURN_WITH_DEBUG (ret);
+      }
+    case PARM_DECL:
+    case LABEL_DECL:
+    case RESULT_DECL:
+    case CONST_DECL:
+    case BIT_FIELD_REF:
+      {
+	ret = compare_decl (t1, t2);
+	return RETURN_WITH_DEBUG (ret);
+      }
+    default:
+      return RETURN_FALSE_WITH_MSG ("Unknown TREE code reached");
+    }
+}
+
+/* Iterates all tree types in T1 and T2 and returns true if all types
+   are compatible. If COMPARE_POLYMORPHIC is set to true,
+   more strict comparison is executed.  */
+
+bool
+sem_function::compare_type_list (tree t1, tree t2, bool compare_polymorphic)
+{
+  tree tv1, tv2;
+  tree_code tc1, tc2;
+
+  if (!t1 && !t2)
+    return true;
+
+  while (t1 != NULL && t2 != NULL)
+    {
+      tv1 = TREE_VALUE (t1);
+      tv2 = TREE_VALUE (t2);
+
+      tc1 = TREE_CODE (tv1);
+      tc2 = TREE_CODE (tv2);
+
+      if (tc1 == NOP_EXPR && tc2 == NOP_EXPR)
+	{}
+      else if (tc1 == NOP_EXPR || tc2 == NOP_EXPR)
+	return false;
+      else if (!func_checker::types_are_compatible_p (tv1, tv2, compare_polymorphic))
+	return false;
+
+      t1 = TREE_CHAIN (t1);
+      t2 = TREE_CHAIN (t2);
+    }
+
+  return !(t1 || t2);
+}
+
+
+/* Semantic variable constructor that uses STACK as bitmap memory stack.  */
+
+sem_variable::sem_variable (bitmap_obstack *stack): sem_item (VAR, stack)
+{
+}
+
+/*  Constructor based on varpool node _NODE with computed hash _HASH.
+    Bitmap STACK is used for memory allocation.  */
+
+sem_variable::sem_variable (varpool_node *node, hashval_t _hash,
+			    bitmap_obstack *stack): sem_item(VAR,
+				  node, _hash, stack)
+{
+  gcc_checking_assert (node);
+  gcc_checking_assert (get_node ());
+}
+
+/* Returns true if the item equals to ITEM given as argument.  */
+
+bool
+sem_variable::equals (sem_item *item)
+{
+  gcc_assert (item->type == VAR);
+
+  return sem_variable::equals (ctor, static_cast<sem_variable *>(item)->ctor);
+}
+
+/* Compares trees T1 and T2 for semantic equality.  */
+
+bool
+sem_variable::equals (tree t1, tree t2)
+{
+  tree_code tc1 = TREE_CODE (t1);
+  tree_code tc2 = TREE_CODE (t2);
+
+  if (tc1 != tc2)
+    return false;
+
+  switch (tc1)
+    {
+    case CONSTRUCTOR:
+      {
+	unsigned len1 = vec_safe_length (CONSTRUCTOR_ELTS (t1));
+	unsigned len2 = vec_safe_length (CONSTRUCTOR_ELTS (t2));
+
+	if (len1 != len2)
+	  return false;
+
+	for (unsigned i = 0; i < len1; i++)
+	  if (!sem_variable::equals (CONSTRUCTOR_ELT (t1, i)->value,
+				     CONSTRUCTOR_ELT (t2, i)->value))
+	    return false;
+
+	return true;
+      }
+    case MEM_REF:
+      {
+	tree x1 = TREE_OPERAND (t1, 0);
+	tree x2 = TREE_OPERAND (t2, 0);
+	tree y1 = TREE_OPERAND (t1, 1);
+	tree y2 = TREE_OPERAND (t2, 1);
+
+	if (!func_checker::types_are_compatible_p (TREE_TYPE (x1), TREE_TYPE (x2),
+	    true))
+	  return RETURN_FALSE ();
+
+	/* Type of the offset on MEM_REF does not matter.  */
+	return sem_variable::equals (x1, x2)
+	       && wi::to_offset  (y1) == wi::to_offset  (y2);
+      }
+    case NOP_EXPR:
+    case ADDR_EXPR:
+      {
+	tree op1 = TREE_OPERAND (t1, 0);
+	tree op2 = TREE_OPERAND (t2, 0);
+	return sem_variable::equals (op1, op2);
+      }
+    case FUNCTION_DECL:
+    case VAR_DECL:
+    case FIELD_DECL:
+    case LABEL_DECL:
+      return t1 == t2;
+    case INTEGER_CST:
+      return func_checker::types_are_compatible_p (TREE_TYPE (t1), TREE_TYPE (t2),
+	     true)
+	     && wi::to_offset (t1) == wi::to_offset (t2);
+    case STRING_CST:
+    case REAL_CST:
+    case COMPLEX_CST:
+      return operand_equal_p (t1, t2, OEP_ONLY_CONST);
+    case COMPONENT_REF:
+    case ARRAY_REF:
+    case POINTER_PLUS_EXPR:
+      {
+	tree x1 = TREE_OPERAND (t1, 0);
+	tree x2 = TREE_OPERAND (t2, 0);
+	tree y1 = TREE_OPERAND (t1, 1);
+	tree y2 = TREE_OPERAND (t2, 1);
+
+	return sem_variable::equals (x1, x2) && sem_variable::equals (y1, y2);
+      }
+    case ERROR_MARK:
+      return RETURN_FALSE_WITH_MSG ("ERROR_MARK");
+    default:
+      return RETURN_FALSE_WITH_MSG ("Unknown TREE code reached");
+    }
+}
+
+/* Parser function that visits a varpool NODE.  */
+
+sem_variable *
+sem_variable::parse (varpool_node *node, bitmap_obstack *stack)
+{
+  tree decl = node->decl;
+
+  bool readonly = TYPE_P (decl) ? TYPE_READONLY (decl) : TREE_READONLY (decl);
+  bool can_handle = readonly && (DECL_VIRTUAL_P (decl)
+				 || !TREE_ADDRESSABLE (decl));
+
+  if (!can_handle)
+    return NULL;
+
+  tree ctor = ctor_for_folding (decl);
+  if (!ctor)
+    return NULL;
+
+  sem_variable *v = new sem_variable (node, 0, stack);
+
+  v->init ();
+
+  return v;
+}
+
+/* References independent hash function.  */
+
+hashval_t
+sem_variable::get_hash (void)
+{
+  if (hash)
+    return hash;
+
+  inchash::hash hstate;
+
+  hstate.add_int (456346417);
+  hstate.add_int (TREE_CODE (ctor));
+
+  if (TREE_CODE (ctor) == CONSTRUCTOR)
+    {
+      unsigned length = vec_safe_length (CONSTRUCTOR_ELTS (ctor));
+      hstate.add_int (length);
+    }
+
+  hash = hstate.end ();
+
+  return hash;
+}
+
+/* Merges instance with an ALIAS_ITEM, where alias, thunk or redirection can
+   be applied.  */
+
+bool
+sem_variable::merge (sem_item *alias_item)
+{
+  gcc_assert (alias_item->type == VAR);
+
+  sem_variable *alias_var = static_cast<sem_variable *> (alias_item);
+
+  varpool_node *original = get_node ();
+  varpool_node *alias = alias_var->get_node ();
+  bool original_discardable = false;
+
+  /* See if original is in a section that can be discarded if the main
+     symbol is not used.  */
+  if (DECL_EXTERNAL (original->decl))
+    original_discardable = true;
+  if (original->resolution == LDPR_PREEMPTED_REG
+      || original->resolution == LDPR_PREEMPTED_IR)
+    original_discardable = true;
+  if (original->can_be_discarded_p ())
+    original_discardable = true;
+
+  gcc_assert (!TREE_ASM_WRITTEN (alias->decl));
+
+  if (original_discardable || DECL_EXTERNAL (alias_var->decl) ||
+      !compare_sections (alias_var))
+    {
+      if (dump_file)
+	fprintf (dump_file, "Varpool alias cannot be created\n\n");
+
+      return false;
+    }
+  else
+    {
+      // alias cycle creation check
+      varpool_node *n = original;
+
+      while (n->alias)
+	{
+	  n = n->get_alias_target ();
+	  if (n == alias)
+	    {
+	      if (dump_file)
+		fprintf (dump_file, "Varpool alias cannot be created (alias cycle).\n\n");
+
+	      return false;
+	    }
+	}
+
+      alias->analyzed = false;
+
+      DECL_INITIAL (alias->decl) = NULL;
+      alias->remove_all_references ();
+
+      varpool_node::create_alias (alias_var->decl, decl);
+      alias->resolve_alias (original);
+
+      if (dump_file)
+	fprintf (dump_file, "Varpool alias has been created.\n\n");
+
+      return true;
+    }
+}
+
+bool
+sem_variable::compare_sections (sem_variable *alias)
+{
+  const char *source = node->get_section ();
+  const char *target = alias->node->get_section();
+
+  if (source == NULL && target == NULL)
+    return true;
+  else if(!source || !target)
+    return false;
+  else
+    return strcmp (source, target) == 0;
+}
+
+/* Dump symbol to FILE.  */
+
+void
+sem_variable::dump_to_file (FILE *file)
+{
+  gcc_assert (file);
+
+  print_node (file, "", decl, 0);
+  fprintf (file, "\n\n");
+}
+
+/* Iterates though a constructor and identifies tree references
+   we are interested in semantic function equality.  */
+
+void
+sem_variable::parse_tree_refs (tree t)
+{
+  switch (TREE_CODE (t))
+    {
+    case CONSTRUCTOR:
+      {
+	unsigned length = vec_safe_length (CONSTRUCTOR_ELTS (t));
+
+	for (unsigned i = 0; i < length; i++)
+	  parse_tree_refs(CONSTRUCTOR_ELT (t, i)->value);
+
+	break;
+      }
+    case NOP_EXPR:
+    case ADDR_EXPR:
+      {
+	tree op = TREE_OPERAND (t, 0);
+	parse_tree_refs (op);
+	break;
+      }
+    case FUNCTION_DECL:
+      {
+	tree_refs.safe_push (t);
+	break;
+      }
+    default:
+      break;
+    }
+}
+
+unsigned int sem_item_optimizer::class_id = 0;
+
+sem_item_optimizer::sem_item_optimizer (): worklist (0), m_classes (0),
+  m_classes_count (0), m_cgraph_node_hooks (NULL), m_varpool_node_hooks (NULL)
+{
+  m_items.create (0);
+  bitmap_obstack_initialize (&m_bmstack);
+}
+
+sem_item_optimizer::~sem_item_optimizer ()
+{
+  for (unsigned int i = 0; i < m_items.length (); i++)
+    delete m_items[i];
+
+  for (hash_table<congruence_class_group_hash>::iterator it = m_classes.begin ();
+       it != m_classes.end (); ++it)
+    {
+      for (unsigned int i = 0; i < (*it)->classes.length (); i++)
+	delete (*it)->classes[i];
+
+      (*it)->classes.release ();
+    }
+
+  m_items.release ();
+
+  bitmap_obstack_release (&m_bmstack);
+}
+
+/* Write IPA ICF summary for symbols.  */
+
+void
+sem_item_optimizer::write_summary (void)
+{
+  unsigned int count = 0;
+
+  output_block *ob = create_output_block (LTO_section_ipa_icf);
+  lto_symtab_encoder_t encoder = ob->decl_state->symtab_node_encoder;
+  ob->symbol = NULL;
+
+  /* Calculate number of symbols to be serialized.  */
+  for (lto_symtab_encoder_iterator lsei = lsei_start_in_partition (encoder);
+       !lsei_end_p (lsei);
+       lsei_next_in_partition (&lsei))
+    {
+      symtab_node *node = lsei_node (lsei);
+
+      if (m_symtab_node_map.get (node))
+	count++;
+    }
+
+  streamer_write_uhwi (ob, count);
+
+  /* Process all of the symbols.  */
+  for (lto_symtab_encoder_iterator lsei = lsei_start_in_partition (encoder);
+       !lsei_end_p (lsei);
+       lsei_next_in_partition (&lsei))
+    {
+      symtab_node *node = lsei_node (lsei);
+
+      sem_item **item = m_symtab_node_map.get (node);
+
+      if (item && *item)
+	{
+	  int node_ref = lto_symtab_encoder_encode (encoder, node);
+	  streamer_write_uhwi_stream (ob->main_stream, node_ref);
+
+	  streamer_write_uhwi (ob, (*item)->get_hash ());
+	}
+    }
+
+  streamer_write_char_stream (ob->main_stream, 0);
+  produce_asm (ob, NULL);
+  destroy_output_block (ob);
+}
+
+/* Reads a section from LTO stream file FILE_DATA. Input block for DATA
+   contains LEN bytes.  */
+
+void
+sem_item_optimizer::read_section (lto_file_decl_data *file_data,
+				  const char *data, size_t len)
+{
+  const lto_function_header *header =
+    (const lto_function_header *) data;
+  const int cfg_offset = sizeof (lto_function_header);
+  const int main_offset = cfg_offset + header->cfg_size;
+  const int string_offset = main_offset + header->main_size;
+  data_in *data_in;
+  unsigned int i;
+  unsigned int count;
+
+  lto_input_block ib_main ((const char *) data + main_offset, 0,
+			   header->main_size);
+
+  data_in =
+    lto_data_in_create (file_data, (const char *) data + string_offset,
+			header->string_size, vNULL);
+
+  count = streamer_read_uhwi (&ib_main);
+
+  for (i = 0; i < count; i++)
+    {
+      unsigned int index;
+      symtab_node *node;
+      lto_symtab_encoder_t encoder;
+
+      index = streamer_read_uhwi (&ib_main);
+      encoder = file_data->symtab_node_encoder;
+      node = lto_symtab_encoder_deref (encoder, index);
+
+      hashval_t hash = streamer_read_uhwi (&ib_main);
+
+      gcc_assert (node->definition);
+
+      if (dump_file)
+	fprintf (dump_file, "Symbol added:%s (tree: %p, uid:%u)\n", node->asm_name (),
+		 (void *) node->decl, node->order);
+
+      if (is_a<cgraph_node *> (node))
+	{
+	  cgraph_node *cnode = dyn_cast <cgraph_node *> (node);
+
+	  m_items.safe_push (new sem_function (cnode, hash, &m_bmstack));
+	}
+      else
+	{
+	  varpool_node *vnode = dyn_cast <varpool_node *> (node);
+
+	  m_items.safe_push (new sem_variable (vnode, hash, &m_bmstack));
+	}
+    }
+
+  lto_free_section_data (file_data, LTO_section_ipa_icf, NULL, data,
+			 len);
+  lto_data_in_delete (data_in);
+}
+
+/* Read IPA IPA ICF summary for symbols.  */
+
+void
+sem_item_optimizer::read_summary (void)
+{
+  lto_file_decl_data **file_data_vec = lto_get_file_decl_data ();
+  lto_file_decl_data *file_data;
+  unsigned int j = 0;
+
+  while ((file_data = file_data_vec[j++]))
+    {
+      size_t len;
+      const char *data = lto_get_section_data (file_data,
+			 LTO_section_ipa_icf, NULL, &len);
+
+      if (data)
+	read_section (file_data, data, len);
+    }
+}
+
+/* Register callgraph and varpool hooks.  */
+
+void
+sem_item_optimizer::register_hooks (void)
+{
+  m_cgraph_node_hooks = symtab->add_cgraph_removal_hook
+			(&sem_item_optimizer::cgraph_removal_hook, this);
+
+  m_varpool_node_hooks = symtab->add_varpool_removal_hook
+			 (&sem_item_optimizer::varpool_removal_hook, this);
+}
+
+/* Unregister callgraph and varpool hooks.  */
+
+void
+sem_item_optimizer::unregister_hooks (void)
+{
+  if (m_cgraph_node_hooks)
+    symtab->remove_cgraph_removal_hook (m_cgraph_node_hooks);
+
+  if (m_varpool_node_hooks)
+    symtab->remove_varpool_removal_hook (m_varpool_node_hooks);
+}
+
+/* Adds a CLS to hashtable associated by hash value.  */
+
+void
+sem_item_optimizer::add_class (congruence_class *cls)
+{
+  gcc_assert (cls->members.length ());
+
+  congruence_class_group *group = get_group_by_hash (
+				    cls->members[0]->get_hash (),
+				    cls->members[0]->type);
+  group->classes.safe_push (cls);
+}
+
+/* Gets a congruence class group based on given HASH value and TYPE.  */
+
+congruence_class_group *
+sem_item_optimizer::get_group_by_hash (hashval_t hash, sem_item_type type)
+{
+  congruence_class_group *item = XNEW (congruence_class_group);
+  item->hash = hash;
+  item->type = type;
+
+  congruence_class_group **slot = m_classes.find_slot (item, INSERT);
+
+  if (*slot)
+    free (item);
+  else
+    {
+      item->classes.create (1);
+      *slot = item;
+    }
+
+  return *slot;
+}
+
+/* Callgraph removal hook called for a NODE with a custom DATA.  */
+
+void
+sem_item_optimizer::cgraph_removal_hook (cgraph_node *node, void *data)
+{
+  sem_item_optimizer *optimizer = (sem_item_optimizer *) data;
+  optimizer->remove_symtab_node (node);
+}
+
+/* Varpool removal hook called for a NODE with a custom DATA.  */
+
+void
+sem_item_optimizer::varpool_removal_hook (varpool_node *node, void *data)
+{
+  sem_item_optimizer *optimizer = (sem_item_optimizer *) data;
+  optimizer->remove_symtab_node (node);
+}
+
+/* Remove symtab NODE triggered by symtab removal hooks.  */
+
+void
+sem_item_optimizer::remove_symtab_node (symtab_node *node)
+{
+  gcc_assert (!m_classes.elements());
+
+  m_removed_items_set.add (node);
+}
+
+/* Removes all callgraph and varpool nodes that are marked by symtab
+   as deleted.  */
+
+void
+sem_item_optimizer::filter_removed_items (void)
+{
+  auto_vec <sem_item *> filtered;
+
+  for (unsigned int i = 0; i < m_items.length(); i++)
+    {
+      sem_item *item = m_items[i];
+
+      if (!flag_ipa_icf_functions && item->type == FUNC)
+	continue;
+
+      if (!flag_ipa_icf_variables && item->type == VAR)
+	continue;
+
+      bool no_body_function = false;
+
+      if (item->type == FUNC)
+	{
+	  cgraph_node *cnode = static_cast <sem_function *>(item)->get_node ();
+
+	  no_body_function = in_lto_p && (cnode->alias || cnode->body_removed);
+	}
+
+      if(!m_removed_items_set.contains (m_items[i]->node)
+	  && !no_body_function)
+	{
+	  if (item->type == VAR || (!DECL_CXX_CONSTRUCTOR_P (item->decl)
+				    && !DECL_CXX_DESTRUCTOR_P (item->decl)))
+	    filtered.safe_push (m_items[i]);
+	}
+    }
+
+  m_items.release ();
+  for (unsigned int i = 0; i < filtered.length(); i++)
+    m_items.safe_push (filtered[i]);
+}
+
+/* Optimizer entry point.  */
+
+void
+sem_item_optimizer::execute (void)
+{
+  filter_removed_items ();
+  build_hash_based_classes ();
+
+  if (dump_file)
+    fprintf (dump_file, "Dump after hash based groups\n");
+  dump_cong_classes ();
+
+  for (unsigned int i = 0; i < m_items.length(); i++)
+    m_items[i]->init_wpa ();
+
+  subdivide_classes_by_equality (true);
+
+  if (dump_file)
+    fprintf (dump_file, "Dump after WPA based types groups\n");
+  dump_cong_classes ();
+
+  parse_nonsingleton_classes ();
+  subdivide_classes_by_equality ();
+
+  if (dump_file)
+    fprintf (dump_file, "Dump after full equality comparison of groups\n");
+
+  dump_cong_classes ();
+
+  unsigned int prev_class_count = m_classes_count;
+
+  process_cong_reduction ();
+  dump_cong_classes ();
+  merge_classes (prev_class_count);
+
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    symtab_node::dump_table (dump_file);
+}
+
+/* Function responsible for visiting all potential functions and
+   read-only variables that can be merged.  */
+
+void
+sem_item_optimizer::parse_funcs_and_vars (void)
+{
+  cgraph_node *cnode;
+
+  if (flag_ipa_icf_functions)
+    FOR_EACH_DEFINED_FUNCTION (cnode)
+    {
+      sem_function *f = sem_function::parse (cnode, &m_bmstack);
+      if (f)
+	{
+	  m_items.safe_push (f);
+	  m_symtab_node_map.put (cnode, f);
+
+	  if (dump_file)
+	    fprintf (dump_file, "Parsed function:%s\n", f->asm_name ());
+
+	  if (dump_file && (dump_flags & TDF_DETAILS))
+	    f->dump_to_file (dump_file);
+	}
+      else if (dump_file)
+	fprintf (dump_file, "Not parsed function:%s\n", cnode->asm_name ());
+    }
+
+  varpool_node *vnode;
+
+  if (flag_ipa_icf_variables)
+    FOR_EACH_DEFINED_VARIABLE (vnode)
+    {
+      sem_variable *v = sem_variable::parse (vnode, &m_bmstack);
+
+      if (v)
+	{
+	  m_items.safe_push (v);
+	  m_symtab_node_map.put (vnode, v);
+	}
+    }
+}
+
+/* Makes pairing between a congruence class CLS and semantic ITEM.  */
+
+void
+sem_item_optimizer::add_item_to_class (congruence_class *cls, sem_item *item)
+{
+  item->index_in_class = cls->members.length ();
+  cls->members.safe_push (item);
+  item->cls = cls;
+}
+
+/* Congruence classes are built by hash value.  */
+
+void
+sem_item_optimizer::build_hash_based_classes (void)
+{
+  for (unsigned i = 0; i < m_items.length (); i++)
+    {
+      sem_item *item = m_items[i];
+
+      congruence_class_group *group = get_group_by_hash (item->get_hash (),
+				      item->type);
+
+      if (!group->classes.length ())
+	{
+	  m_classes_count++;
+	  group->classes.safe_push (new congruence_class (class_id++));
+	}
+
+      add_item_to_class (group->classes[0], item);
+    }
+}
+
+/* Semantic items in classes having more than one element and initialized.
+   In case of WPA, we load function body.  */
+
+void
+sem_item_optimizer::parse_nonsingleton_classes (void)
+{
+  for (hash_table <congruence_class_group_hash>::iterator it = m_classes.begin ();
+       it != m_classes.end (); ++it)
+    {
+      for (unsigned i = 0; i < (*it)->classes.length (); i++)
+	{
+	  congruence_class *c = (*it)->classes [i];
+
+	  if (c->members.length() > 1)
+	    for (unsigned j = 0; j < c->members.length (); j++)
+	      {
+		sem_item *item = c->members[j];
+		m_decl_map.put (item->decl, item);
+	      }
+	}
+    }
+
+  unsigned int init_called_count = 0;
+
+  for (hash_table <congruence_class_group_hash>::iterator it = m_classes.begin ();
+       it != m_classes.end (); ++it)
+    {
+      /* We fill in all declarations for sem_items.  */
+      for (unsigned i = 0; i < (*it)->classes.length (); i++)
+	{
+	  congruence_class *c = (*it)->classes [i];
+
+	  if (c->members.length() > 1)
+	    for (unsigned j = 0; j < c->members.length (); j++)
+	      {
+		sem_item *item = c->members[j];
+
+		item->init ();
+		item->init_refs ();
+
+		init_called_count++;
+
+		for (unsigned j = 0; j < item->tree_refs.length (); j++)
+		  {
+		    sem_item **result = m_decl_map.get (item->tree_refs[j]);
+
+		    if(result)
+		      {
+			sem_item *target = *result;
+			item->refs.safe_push (target);
+
+			unsigned index = item->refs.length ();
+			target->usages.safe_push (new sem_usage_pair(item, index));
+			bitmap_set_bit (target->usage_index_bitmap, index);
+			item->tree_refs_set.add (item->tree_refs[j]);
+		      }
+		  }
+	      }
+	}
+    }
+
+  if (dump_file)
+    fprintf (dump_file, "Init called for %u items (%.2f%%).\n", init_called_count,
+	     100.0f * init_called_count / m_items.length ());
+}
+
+/* Equality function for semantic items is used to subdivide existing
+   classes. If IN_WPA, fast equality function is invoked.  */
+
+void
+sem_item_optimizer::subdivide_classes_by_equality (bool in_wpa)
+{
+  for (hash_table <congruence_class_group_hash>::iterator it = m_classes.begin ();
+       it != m_classes.end (); ++it)
+    {
+      unsigned int class_count = (*it)->classes.length ();
+
+      for (unsigned i = 0; i < class_count; i++)
+	{
+	  congruence_class *c = (*it)->classes [i];
+
+	  if (c->members.length() > 1)
+	    {
+	      auto_vec <sem_item *> new_vector;
+
+	      sem_item *first = c->members[0];
+	      new_vector.safe_push (first);
+
+	      unsigned class_split_first = (*it)->classes.length ();
+
+	      for (unsigned j = 1; j < c->members.length (); j++)
+		{
+		  sem_item *item = c->members[j];
+
+		  bool equals = in_wpa ? first->equals_wpa (item) : first->equals (item);
+
+		  if (equals)
+		    new_vector.safe_push (item);
+		  else
+		    {
+		      bool integrated = false;
+
+		      for (unsigned k = class_split_first; k < (*it)->classes.length (); k++)
+			{
+			  sem_item *x = (*it)->classes[k]->members[0];
+			  bool equals = in_wpa ? x->equals_wpa (item) : x->equals (item);
+
+			  if (equals)
+			    {
+			      integrated = true;
+			      add_item_to_class ((*it)->classes[k], item);
+
+			      break;
+			    }
+			}
+
+		      if (!integrated)
+			{
+			  congruence_class *c = new congruence_class (class_id++);
+			  m_classes_count++;
+			  add_item_to_class (c, item);
+
+			  (*it)->classes.safe_push (c);
+			}
+		    }
+		}
+
+	      // we replace newly created new_vector for the class we've just splitted
+	      c->members.release ();
+	      c->members.create (new_vector.length ());
+
+	      for (unsigned int j = 0; j < new_vector.length (); j++)
+		add_item_to_class (c, new_vector[j]);
+	    }
+	}
+    }
+
+  verify_classes ();
+}
+
+/* Verify congruence classes if checking is enabled.  */
+
+void
+sem_item_optimizer::verify_classes (void)
+{
+#if ENABLE_CHECKING
+  for (hash_table <congruence_class_group_hash>::iterator it = m_classes.begin ();
+       it != m_classes.end (); ++it)
+    {
+      for (unsigned int i = 0; i < (*it)->classes.length (); i++)
+	{
+	  congruence_class *cls = (*it)->classes[i];
+
+	  gcc_checking_assert (cls);
+	  gcc_checking_assert (cls->members.length () > 0);
+
+	  for (unsigned int j = 0; j < cls->members.length (); j++)
+	    {
+	      sem_item *item = cls->members[j];
+
+	      gcc_checking_assert (item);
+
+	      for (unsigned k = 0; k < item->usages.length (); k++)
+		{
+		  sem_usage_pair *usage = item->usages[k];
+		  gcc_checking_assert (usage->item->index_in_class <
+				       usage->item->cls->members.length ());
+		}
+	    }
+	}
+    }
+#endif
+}
+
+/* Disposes split map traverse function. CLS_PTR is pointer to congruence
+   class, BSLOT is bitmap slot we want to release. DATA is mandatory,
+   but unused argument.  */
+
+bool
+sem_item_optimizer::release_split_map (congruence_class * const &,
+				       bitmap const &b, traverse_split_pair *)
+{
+  bitmap bmp = b;
+
+  BITMAP_FREE (bmp);
+
+  return true;
+}
+
+/* Process split operation for a class given as pointer CLS_PTR,
+   where bitmap B splits congruence class members. DATA is used
+   as argument of split pair.  */
+
+bool
+sem_item_optimizer::traverse_congruence_split (congruence_class * const &cls,
+    bitmap const &b, traverse_split_pair *pair)
+{
+  sem_item_optimizer *optimizer = pair->optimizer;
+  const congruence_class *splitter_cls = pair->cls;
+
+  /* If counted bits are greater than zero and less than the number of members
+     a group will be splitted.  */
+  unsigned popcount = bitmap_count_bits (b);
+
+  if (popcount > 0 && popcount < cls->members.length ())
+    {
+      congruence_class* newclasses[2] = { new congruence_class (class_id++), new congruence_class (class_id++) };
+
+      for (unsigned int i = 0; i < cls->members.length (); i++)
+	{
+	  int target = bitmap_bit_p (b, i);
+	  congruence_class *tc = newclasses[target];
+
+	  add_item_to_class (tc, cls->members[i]);
+	}
+
+#ifdef ENABLE_CHECKING
+      for (unsigned int i = 0; i < 2; i++)
+	gcc_checking_assert (newclasses[i]->members.length ());
+#endif
+
+      if (splitter_cls == cls)
+	optimizer->splitter_class_removed = true;
+
+      /* Remove old class from worklist if presented.  */
+      bool in_work_list = optimizer->worklist_contains (cls);
+
+      if (in_work_list)
+	optimizer->worklist_remove (cls);
+
+      congruence_class_group g;
+      g.hash = cls->members[0]->get_hash ();
+      g.type = cls->members[0]->type;
+
+      congruence_class_group *slot = optimizer->m_classes.find(&g);
+
+      for (unsigned int i = 0; i < slot->classes.length (); i++)
+	if (slot->classes[i] == cls)
+	  {
+	    slot->classes.ordered_remove (i);
+	    break;
+	  }
+
+      /* New class will be inserted and integrated to work list.  */
+      for (unsigned int i = 0; i < 2; i++)
+	optimizer->add_class (newclasses[i]);
+
+      /* Two classes replace one, so that increment just by one.  */
+      optimizer->m_classes_count++;
+
+      /* If OLD class was presented in the worklist, we remove the class
+         are replace it will both newly created classes.  */
+      if (in_work_list)
+	for (unsigned int i = 0; i < 2; i++)
+	  optimizer->worklist_push (newclasses[i]);
+      else /* Just smaller class is inserted.  */
+	{
+	  unsigned int smaller_index = newclasses[0]->members.length () <
+				       newclasses[1]->members.length () ?
+				       0 : 1;
+	  optimizer->worklist_push (newclasses[smaller_index]);
+	}
+
+      if (dump_file && (dump_flags & TDF_DETAILS))
+	{
+	  fprintf (dump_file, "  congruence class splitted:\n");
+	  cls->dump (dump_file, 4);
+
+	  fprintf (dump_file, "  newly created groups:\n");
+	  for (unsigned int i = 0; i < 2; i++)
+	    newclasses[i]->dump (dump_file, 4);
+	}
+
+      delete cls;
+    }
+
+
+  return true;
+}
+
+/* Tests if a class CLS used as INDEXth splits any congruence classes.
+   Bitmap stack BMSTACK is used for bitmap allocation.  */
+
+void
+sem_item_optimizer::do_congruence_step_for_index (congruence_class *cls,
+    unsigned int index)
+{
+  hash_map <congruence_class *, bitmap> split_map;
+
+  for (unsigned int i = 0; i < cls->members.length (); i++)
+    {
+      sem_item *item = cls->members[i];
+
+      /* Iterate all usages that have INDEX as usage of the item.  */
+      for (unsigned int j = 0; j < item->usages.length (); j++)
+	{
+	  sem_usage_pair *usage = item->usages[j];
+
+	  if (usage->index != index)
+	    continue;
+
+	  bitmap *slot = split_map.get (usage->item->cls);
+	  bitmap b;
+
+	  if(!slot)
+	    {
+	      b = BITMAP_ALLOC (&m_bmstack);
+	      split_map.put (usage->item->cls, b);
+	    }
+	  else
+	    b = *slot;
+
+#if ENABLE_CHECKING
+	  gcc_checking_assert (usage->item->cls);
+	  gcc_checking_assert (usage->item->index_in_class <
+			       usage->item->cls->members.length ());
+#endif
+
+	  bitmap_set_bit (b, usage->item->index_in_class);
+	}
+    }
+
+  traverse_split_pair pair;
+  pair.optimizer = this;
+  pair.cls = cls;
+
+  splitter_class_removed = false;
+  split_map.traverse
+  <traverse_split_pair *, sem_item_optimizer::traverse_congruence_split> (&pair);
+
+  /* Bitmap clean-up.  */
+  split_map.traverse
+  <traverse_split_pair *, sem_item_optimizer::release_split_map> (NULL);
+}
+
+/* Every usage of a congruence class CLS is a candidate that can split the
+   collection of classes. Bitmap stack BMSTACK is used for bitmap
+   allocation.  */
+
+void
+sem_item_optimizer::do_congruence_step (congruence_class *cls)
+{
+  bitmap_iterator bi;
+  unsigned int i;
+
+  bitmap usage = BITMAP_ALLOC (&m_bmstack);
+
+  for (unsigned int i = 0; i < cls->members.length (); i++)
+    bitmap_ior_into (usage, cls->members[i]->usage_index_bitmap);
+
+  EXECUTE_IF_SET_IN_BITMAP (usage, 0, i, bi)
+  {
+    if (dump_file && (dump_flags & TDF_DETAILS))
+      fprintf (dump_file, "  processing congruece step for class: %u, index: %u\n",
+	       cls->id, i);
+
+    do_congruence_step_for_index (cls, i);
+
+    if (splitter_class_removed)
+      break;
+  }
+
+  BITMAP_FREE (usage);
+}
+
+/* Adds a newly created congruence class CLS to worklist.  */
+
+void
+sem_item_optimizer::worklist_push (congruence_class *cls)
+{
+  congruence_class **slot = worklist.find_slot (cls, INSERT);
+
+  if (*slot)
+    return;
+
+  *slot = cls;
+}
+
+/* Pops a class from worklist. */
+
+congruence_class *
+sem_item_optimizer::worklist_pop (void)
+{
+  gcc_assert (worklist.elements ());
+
+  congruence_class *cls = *worklist.begin ();
+  worklist.remove_elt (cls);
+
+  return cls;
+}
+
+/* Iterative congruence reduction function.  */
+
+void
+sem_item_optimizer::process_cong_reduction (void)
+{
+  for (hash_table<congruence_class_group_hash>::iterator it = m_classes.begin ();
+       it != m_classes.end (); ++it)
+    for (unsigned i = 0; i < (*it)->classes.length (); i++)
+      if ((*it)->classes[i]->is_class_used ())
+	worklist_push ((*it)->classes[i]);
+
+  if (dump_file)
+    fprintf (dump_file, "Worklist has been filled with: %lu\n",
+	     worklist.elements ());
+
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    fprintf (dump_file, "Congruence class reduction\n");
+
+  while (worklist.elements ())
+    {
+      congruence_class *cls = worklist_pop ();
+      do_congruence_step (cls);
+    }
+}
+
+/* Debug function prints all informations about congruence classes.  */
+
+void
+sem_item_optimizer::dump_cong_classes (void)
+{
+  if (!dump_file)
+    return;
+
+  fprintf (dump_file,
+	   "Congruence classes: %u (unique hash values: %lu), with total: %u items\n",
+	   m_classes_count, m_classes.elements(), m_items.length ());
+
+  /* Histogram calculation.  */
+  unsigned int max_index = 0;
+  unsigned int* histogram = XCNEWVEC (unsigned int, m_items.length () + 1);
+
+  for (hash_table<congruence_class_group_hash>::iterator it = m_classes.begin ();
+       it != m_classes.end (); ++it)
+
+    for (unsigned i = 0; i < (*it)->classes.length (); i++)
+      {
+	unsigned int c = (*it)->classes[i]->members.length ();
+	histogram[c]++;
+
+	if (c > max_index)
+	  max_index = c;
+      }
+
+  fprintf (dump_file,
+	   "Class size histogram [num of members]: number of classe number of classess\n");
+
+  for (unsigned int i = 0; i <= max_index; i++)
+    if (histogram[i])
+      fprintf (dump_file, "[%u]: %u classes\n", i, histogram[i]);
+
+  fprintf (dump_file, "\n\n");
+
+
+  if (dump_flags & TDF_DETAILS)
+    for (hash_table<congruence_class_group_hash>::iterator it = m_classes.begin ();
+	 it != m_classes.end (); ++it)
+      {
+	fprintf (dump_file, "  group: with %u classes:\n", (*it)->classes.length ());
+
+	for (unsigned i = 0; i < (*it)->classes.length (); i++)
+	  {
+	    (*it)->classes[i]->dump (dump_file, 4);
+
+	    if(i < (*it)->classes.length () - 1)
+	      fprintf (dump_file, " ");
+	  }
+      }
+
+  free (histogram);
+}
+
+/* After reduction is done, we can declare all items in a group
+   to be equal. PREV_CLASS_COUNT is start number of classes
+   before reduction.  */
+
+void
+sem_item_optimizer::merge_classes (unsigned int prev_class_count)
+{
+  unsigned int item_count = m_items.length ();
+  unsigned int class_count = m_classes_count;
+  unsigned int equal_items = item_count - class_count;
+
+  if (dump_file)
+    {
+      fprintf (dump_file, "\nItem count: %u\n", item_count);
+      fprintf (dump_file, "Congruent classes before: %u, after: %u\n",
+	       prev_class_count, class_count);
+      fprintf (dump_file, "Average class size before: %.2f, after: %.2f\n",
+	       1.0f * item_count / prev_class_count,
+	       1.0f * item_count / class_count);
+      fprintf (dump_file, "Equal symbols: %u\n", equal_items);
+      fprintf (dump_file, "Fraction of visited symbols: %.2f%%\n\n",
+	       100.0f * equal_items / item_count);
+    }
+
+  for (hash_table<congruence_class_group_hash>::iterator it = m_classes.begin ();
+       it != m_classes.end (); ++it)
+    for (unsigned int i = 0; i < (*it)->classes.length (); i++)
+      {
+	congruence_class *c = (*it)->classes[i];
+
+	if (c->members.length () == 1)
+	  continue;
+
+	gcc_assert (c->members.length ());
+
+	sem_item *source = c->members[0];
+
+	for (unsigned int j = 1; j < c->members.length (); j++)
+	  {
+	    sem_item *alias = c->members[j];
+	    source->equals (alias);
+
+	    if (dump_file)
+	      {
+		fprintf (dump_file, "Semantic equality hit:%s->%s\n",
+			 source->name (), alias->name ());
+		fprintf (dump_file, "Assembler symbol names:%s->%s\n",
+			 source->asm_name (), alias->asm_name ());
+	      }
+
+	    if (dump_file && (dump_flags & TDF_DETAILS))
+	      {
+		source->dump_to_file (dump_file);
+		alias->dump_to_file (dump_file);
+	      }
+
+	    source->merge (alias);
+	  }
+      }
+}
+
+/* Dump function prints all class members to a FILE with an INDENT.  */
+
+void
+congruence_class::dump (FILE *file, unsigned int indent) const
+{
+  FPRINTF_SPACES (file, indent, "class with id: %u, hash: %u, items: %u\n",
+		  id, members[0]->get_hash (), members.length ());
+
+  FPUTS_SPACES (file, indent + 2, "");
+  for (unsigned i = 0; i < members.length (); i++)
+    fprintf (file, "%s(%p/%u) ", members[i]->asm_name (), (void *) members[i]->decl,
+	     members[i]->node->order);
+
+  fprintf (file, "\n");
+}
+
+/* Returns true if there's a member that is used from another group.  */
+
+bool
+congruence_class::is_class_used (void)
+{
+  for (unsigned int i = 0; i < members.length (); i++)
+    if (members[i]->usages.length ())
+      return true;
+
+  return false;
+}
+
+/* Initialization and computation of symtab node hash, there data
+   are propagated later on.  */
+
+static sem_item_optimizer *optimizer = NULL;
+
+/* Generate pass summary for IPA ICF pass.  */
+
+static void
+ipa_icf_generate_summary (void)
+{
+  if (!optimizer)
+    optimizer = new sem_item_optimizer ();
+
+  optimizer->parse_funcs_and_vars ();
+}
+
+/* Write pass summary for IPA ICF pass.  */
+
+static void
+ipa_icf_write_summary (void)
+{
+  gcc_assert (optimizer);
+
+  optimizer->write_summary ();
+}
+
+/* Read pass summary for IPA ICF pass.  */
+
+static void
+ipa_icf_read_summary (void)
+{
+  if (!optimizer)
+    optimizer = new sem_item_optimizer ();
+
+  optimizer->read_summary ();
+  optimizer->register_hooks ();
+}
+
+/* Semantic equality exection function.  */
+
+static unsigned int
+ipa_icf_driver (void)
+{
+  gcc_assert (optimizer);
+
+  optimizer->execute ();
+  optimizer->unregister_hooks ();
+
+  delete optimizer;
+
+  return 0;
+}
+
+const pass_data pass_data_ipa_icf =
+{
+  IPA_PASS,		    /* type */
+  "icf",		    /* name */
+  OPTGROUP_IPA,             /* optinfo_flags */
+  TV_IPA_ICF,		    /* tv_id */
+  0,                        /* properties_required */
+  0,                        /* properties_provided */
+  0,                        /* properties_destroyed */
+  0,                        /* todo_flags_start */
+  0,                        /* todo_flags_finish */
+};
+
+class pass_ipa_icf : public ipa_opt_pass_d
+{
+public:
+  pass_ipa_icf (gcc::context *ctxt)
+    : ipa_opt_pass_d (pass_data_ipa_icf, ctxt,
+		      ipa_icf_generate_summary, /* generate_summary */
+		      ipa_icf_write_summary, /* write_summary */
+		      ipa_icf_read_summary, /* read_summary */
+		      NULL, /*
+		      write_optimization_summary */
+		      NULL, /*
+		      read_optimization_summary */
+		      NULL, /* stmt_fixup */
+		      0, /* function_transform_todo_flags_start */
+		      NULL, /* function_transform */
+		      NULL) /* variable_transform */
+  {}
+
+  /* opt_pass methods: */
+  virtual bool gate (function *)
+  {
+    return flag_ipa_icf_variables || flag_ipa_icf_functions;
+  }
+
+  virtual unsigned int execute (function *)
+  {
+    return ipa_icf_driver();
+  }
+}; // class pass_ipa_icf
+
+} // ipa_icf namespace
+
+ipa_opt_pass_d *
+make_pass_ipa_icf (gcc::context *ctxt)
+{
+  return new ipa_icf::pass_ipa_icf (ctxt);
+}
diff --git a/gcc/ipa-icf.h b/gcc/ipa-icf.h
new file mode 100644
index 0000000..5fb1b07
--- /dev/null
+++ b/gcc/ipa-icf.h
@@ -0,0 +1,803 @@
+/* Interprocedural semantic function equality pass
+   Copyright (C) 2014 Free Software Foundation, Inc.
+
+   Contributed by Jan Hubicka <hubicka@ucw.cz> and Martin Liska <mliska@suse.cz>
+
+This file is part of GCC.
+
+GCC 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, or (at your option) any later
+version.
+
+GCC 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 GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+/* Prints string STRING to a FILE with a given number of SPACE_COUNT.  */
+#define FPUTS_SPACES(file, space_count, string) \
+  fprintf (file, "%*s" string, space_count, " ");
+
+/* fprintf function wrapper that transforms given FORMAT to follow given
+   number for SPACE_COUNT and call fprintf for a FILE.  */
+#define FPRINTF_SPACES(file, space_count, format, ...) \
+  fprintf (file, "%*s" format, space_count, " ", ##__VA_ARGS__);
+
+/* Prints a MESSAGE to dump_file if exists. FUNC is name of function and
+   LINE is location in the source file.  */
+
+static inline void
+dump_message (const char *message, const char *func, unsigned int line)
+{
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    fprintf (dump_file, "  debug message: %s (%s:%u)\n", message, func, line);
+}
+
+/* Prints a MESSAGE to dump_file if exists.  */
+#define DUMP_MESSAGE(message) dump_message (message, __func__, __LINE__)
+
+/* Logs a MESSAGE to dump_file if exists and returns false. FUNC is name
+   of function and LINE is location in the source file.  */
+
+static inline bool
+return_false_with_message (const char *message, const char *func,
+			   unsigned int line)
+{
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    fprintf (dump_file, "  false returned: '%s' (%s:%u)\n", message, func, line);
+  return false;
+}
+
+/* Logs a MESSAGE to dump_file if exists and returns false.  */
+#define RETURN_FALSE_WITH_MSG(message) \
+  return_false_with_message (message, __func__, __LINE__)
+
+/* Return false and log that false value is returned.  */
+#define RETURN_FALSE() RETURN_FALSE_WITH_MSG ("")
+
+/* Logs return value if RESULT is false. FUNC is name of function and LINE
+   is location in the source file.  */
+
+static inline bool
+return_with_result (bool result, const char *func, unsigned int line)
+{
+  if (!result && dump_file && (dump_flags & TDF_DETAILS))
+    fprintf (dump_file, "  false returned (%s:%u)\n", func, line);
+
+  return result;
+}
+
+/* Logs return value if RESULT is false.  */
+#define RETURN_WITH_DEBUG(result) return_with_result (result, __func__, __LINE__)
+
+/* Verbose logging function logging statements S1 and S2 of a CODE.
+   FUNC is name of function and LINE is location in the source file.  */
+
+static inline bool
+return_different_stmts (gimple s1, gimple s2, const char *code,
+			const char *func, unsigned int line)
+{
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    {
+      fprintf (dump_file, "  different statement for code: %s (%s:%u):\n",
+	       code, func, line);
+
+      print_gimple_stmt (dump_file, s1, 3, TDF_DETAILS);
+      print_gimple_stmt (dump_file, s2, 3, TDF_DETAILS);
+    }
+
+  return false;
+}
+
+/* Verbose logging function logging statements S1 and S2 of a CODE.  */
+#define RETURN_DIFFERENT_STMTS(s1, s2, code) \
+  return_different_stmts (s1, s2, code, __func__, __LINE__)
+
+namespace ipa_icf {
+
+class sem_item;
+class sem_bb;
+
+/* A class aggregating all connections and semantic equivalents
+   for a given pair of semantic function candidates.  */
+class func_checker
+{
+public:
+  /* Initialize internal structures for a given SOURCE_FUNC_DECL and
+     TARGET_FUNC_DECL. Strict polymorphic comparison is processed if
+     an option COMPARE_POLYMORPHIC is true. For special cases, one can
+     set IGNORE_LABELS to skip label comparison.
+     Similarly, IGNORE_SOURCE_DECLS and IGNORE_TARGET_DECLS are sets
+     of declarations that can be skipped.  */
+  func_checker (tree source_func_decl, tree target_func_decl,
+		bool compare_polymorphic,
+		bool ignore_labels = false,
+		hash_set<tree> *ignored_source_decls = NULL,
+		hash_set<tree> *ignored_target_decls = NULL);
+
+  /* Memory release routine.  */
+  ~func_checker();
+
+  /* Basic block equivalence comparison function that returns true if
+     basic blocks BB1 and BB2 correspond.  */
+  bool compare_bb (sem_bb *bb1, sem_bb *bb2);
+
+  /* Verifies that trees T1 and T2 are equivalent from perspective of ICF.  */
+  bool compare_ssa_name (tree t1, tree t2);
+
+  /* Verification function for edges E1 and E2.  */
+  bool compare_edge (edge e1, edge e2);
+
+  /* Verifies for given GIMPLEs S1 and S2 that
+     call statements are semantically equivalent.  */
+  bool compare_gimple_call (gimple s1, gimple s2);
+
+  /* Verifies for given GIMPLEs S1 and S2 that
+     assignment statements are semantically equivalent.  */
+  bool compare_gimple_assign (gimple s1, gimple s2);
+
+  /* Verifies for given GIMPLEs S1 and S2 that
+     condition statements are semantically equivalent.  */
+  bool compare_gimple_cond (gimple s1, gimple s2);
+
+  /* Verifies for given GIMPLEs S1 and S2 that
+     label statements are semantically equivalent.  */
+  bool compare_gimple_label (gimple s1, gimple s2);
+
+  /* Verifies for given GIMPLEs S1 and S2 that
+     switch statements are semantically equivalent.  */
+  bool compare_gimple_switch (gimple s1, gimple s2);
+
+  /* Verifies for given GIMPLEs S1 and S2 that
+     return statements are semantically equivalent.  */
+  bool compare_gimple_return (gimple s1, gimple s2);
+
+  /* Verifies for given GIMPLEs S1 and S2 that
+     goto statements are semantically equivalent.  */
+  bool compare_gimple_goto (gimple s1, gimple s2);
+
+  /* Verifies for given GIMPLEs S1 and S2 that
+     resx statements are semantically equivalent.  */
+  bool compare_gimple_resx (gimple s1, gimple s2);
+
+  /* Verifies for given GIMPLEs S1 and S2 that ASM statements are equivalent.
+     For the beginning, the pass only supports equality for
+     '__asm__ __volatile__ ("", "", "", "memory")'.  */
+  bool compare_gimple_asm (gimple s1, gimple s2);
+
+  /* Verification function for declaration trees T1 and T2.  */
+  bool compare_decl (tree t1, tree t2);
+
+  /* Verifies that tree labels T1 and T2 correspond.  */
+  bool compare_tree_ssa_label (tree t1, tree t2);
+
+  /* Function compares two operands T1 and T2 and returns true if these
+     two trees are semantically equivalent.  */
+  bool compare_operand (tree t1, tree t2);
+
+  /* Verifies that trees T1 and T2, representing function declarations
+     are equivalent from perspective of ICF.  */
+  bool compare_function_decl (tree t1, tree t2);
+
+  /* Verifies that trees T1 and T2 do correspond.  */
+  bool compare_variable_decl (tree t1, tree t2);
+
+  /* Return true if types are compatible from perspective of ICF.
+     FIRST_ARGUMENT indicates if the comparison is called for
+     first parameter of a function.  */
+  static bool types_are_compatible_p (tree t1, tree t2,
+				      bool compare_polymorphic = true,
+				      bool first_argument = false);
+
+
+private:
+  /* Vector mapping source SSA names to target ones.  */
+  vec <int> m_source_ssa_names;
+
+  /* Vector mapping target SSA names to source ones.  */
+  vec <int> m_target_ssa_names;
+
+  /* Source TREE function declaration.  */
+  tree m_source_func_decl;
+
+  /* Target TREE function declaration.  */
+  tree m_target_func_decl;
+
+  /* Source function declarations that should be skipped by
+     declaration comparison.  */
+  hash_set<tree> *m_ignored_source_decls;
+
+  /* Target function declarations that should be skipped by
+     declaration comparison.  */
+  hash_set<tree> *m_ignored_target_decls;
+
+  /* Source to target edge map.  */
+  hash_map <edge, edge> m_edge_map;
+
+  /* Source to target declaration map.  */
+  hash_map <tree, tree> m_decl_map;
+
+  /* Flag if polymorphic comparison should be executed.  */
+  bool m_compare_polymorphic;
+
+  /* Flag if ignore labels in comparison.  */
+  bool m_ignore_labels;
+};
+
+/* Congruence class encompasses a collection of either functions or
+   read-only variables. These items are considered to be equivalent
+   if not proved the oposite.  */
+class congruence_class
+{
+public:
+  /* Congruence class constructor for a new class with _ID.  */
+  congruence_class (unsigned int _id): id(_id)
+  {
+    members.create (2);
+  }
+
+  /* Destructor.  */
+  ~congruence_class ()
+  {
+    members.release ();
+  }
+
+  /* Dump function prints all class members to a FILE with an INDENT.  */
+  void dump (FILE *file, unsigned int indent = 0) const;
+
+  /* Returns true if there's a member that is used from another group.  */
+  bool is_class_used (void);
+
+  /* Vector of all group members.  */
+  vec <sem_item *> members;
+
+  /* Global unique class identifier.  */
+  unsigned int id;
+};
+
+/* Semantic item type enum.  */
+enum sem_item_type
+{
+  FUNC,
+  VAR
+};
+
+/* Semantic item usage pair.  */
+class sem_usage_pair
+{
+public:
+  /* Constructor for key value pair, where _ITEM is key and _INDEX is a target.  */
+  sem_usage_pair (sem_item *_item, unsigned int _index);
+
+  /* Target semantic item where an item is used.  */
+  sem_item *item;
+
+  /* Index of usage of such an item.  */
+  unsigned int index;
+};
+
+/* Basic block struct for semantic equality pass.  */
+class sem_bb
+{
+public:
+  sem_bb (basic_block bb_, unsigned nondbg_stmt_count_, unsigned edge_count_):
+    bb (bb_), nondbg_stmt_count (nondbg_stmt_count_), edge_count (edge_count_) {}
+
+  /* Basic block the structure belongs to.  */
+  basic_block bb;
+
+  /* Number of non-debug statements in the basic block.  */
+  unsigned nondbg_stmt_count;
+
+  /* Number of edges connected to the block.  */
+  unsigned edge_count;
+};
+
+/* Semantic item is a base class that encapsulates all shared functionality
+   for both semantic function and variable items.  */
+class sem_item
+{
+public:
+  /* Semantic item constructor for a node of _TYPE, where STACK is used
+     for bitmap memory allocation.  */
+  sem_item (sem_item_type _type, bitmap_obstack *stack);
+
+  /* Semantic item constructor for a node of _TYPE, where STACK is used
+     for bitmap memory allocation. The item is based on symtab node _NODE
+     with computed _HASH.  */
+  sem_item (sem_item_type _type, symtab_node *_node, hashval_t _hash,
+	    bitmap_obstack *stack);
+
+  virtual ~sem_item ();
+
+  /* Dump function for debugging purpose.  */
+  DEBUG_FUNCTION void dump (void);
+
+  /* Initialize semantic item by info reachable during LTO WPA phase.  */
+  virtual void init_wpa (void) = 0;
+
+  /* Semantic item initialization function.  */
+  virtual void init (void) = 0;
+
+  /* Gets symbol name of the item.  */
+  const char *name (void)
+  {
+    return node->name ();
+  }
+
+  /* Gets assembler name of the item.  */
+  const char *asm_name (void)
+  {
+    return node->asm_name ();
+  }
+
+  /* Initialize references to other semantic functions/variables.  */
+  virtual void init_refs () = 0;
+
+  /* Fast equality function based on knowledge known in WPA.  */
+  virtual bool equals_wpa (sem_item *item) = 0;
+
+  /* Returns true if the item equals to ITEM given as arguemnt.  */
+  virtual bool equals (sem_item *item) = 0;
+
+  /* References independent hash function.  */
+  virtual hashval_t get_hash (void) = 0;
+
+  /* Merges instance with an ALIAS_ITEM, where alias, thunk or redirection can
+     be applied.  */
+  virtual bool merge (sem_item *alias_item) = 0;
+
+  /* Dump symbol to FILE.  */
+  virtual void dump_to_file (FILE *file) = 0;
+
+  /* Return base tree that can be used for types_compatible_p and
+     contains_polymorphic_type_p comparison.  */
+
+  static bool get_base_types (tree *t1, tree *t2);
+
+  /* Item type.  */
+  sem_item_type type;
+
+  /* Global unique function index.  */
+  unsigned int index;
+
+  /* Symtab node.  */
+  symtab_node *node;
+
+  /* Declaration tree node.  */
+  tree decl;
+
+  /* Semantic references used that generate congruence groups.  */
+  vec <sem_item *> refs;
+
+  /* Pointer to a congruence class the item belongs to.  */
+  congruence_class *cls;
+
+  /* Index of the item in a class belonging to.  */
+  unsigned int index_in_class;
+
+  /* List of semantic items where the instance is used.  */
+  vec <sem_usage_pair *> usages;
+
+  /* A bitmap with indices of all classes referencing this item.  */
+  bitmap usage_index_bitmap;
+
+  /* List of tree references (either FUNC_DECL or VAR_DECL).  */
+  vec <tree> tree_refs;
+
+  /* A set with tree references (either FUNC_DECL or VAR_DECL).  */
+  hash_set <tree> tree_refs_set;
+
+protected:
+  /* Cached, once calculated hash for the item.  */
+  hashval_t hash;
+
+private:
+  /* Initialize internal data structures. Bitmap STACK is used for
+     bitmap memory allocation process.  */
+  void setup (bitmap_obstack *stack);
+}; // class sem_item
+
+class sem_function: public sem_item
+{
+public:
+  /* Semantic function constructor that uses STACK as bitmap memory stack.  */
+  sem_function (bitmap_obstack *stack);
+
+  /*  Constructor based on callgraph node _NODE with computed hash _HASH.
+      Bitmap STACK is used for memory allocation.  */
+  sem_function (cgraph_node *_node, hashval_t _hash, bitmap_obstack *stack);
+
+  ~sem_function ();
+
+  inline virtual void init_wpa (void)
+  {
+    parse_tree_args ();
+  }
+
+  virtual void init (void);
+  virtual bool equals_wpa (sem_item *item);
+  virtual hashval_t get_hash (void);
+  virtual bool equals (sem_item *item);
+  virtual void init_refs ();
+  virtual bool merge (sem_item *alias_item);
+
+  /* Dump symbol to FILE.  */
+  virtual void dump_to_file (FILE *file)
+  {
+    gcc_assert (file);
+    dump_function_to_file (decl, file, TDF_DETAILS);
+  }
+
+  /* Parses function arguments and result type.  */
+  void parse_tree_args (void);
+
+  /* Returns cgraph_node.  */
+  inline cgraph_node *get_node (void)
+  {
+    return dyn_cast <cgraph_node *> (node);
+  }
+
+  /* Improve accumulated hash for HSTATE based on a gimple statement STMT.  */
+  void improve_hash (inchash::hash *inchash, gimple stmt);
+
+  /* Return true if polymorphic comparison must be processed.  */
+  bool compare_polymorphic_p (void);
+
+  /* For a given call graph NODE, the function constructs new
+     semantic function item.  */
+  static sem_function *parse (cgraph_node *node, bitmap_obstack *stack);
+
+  /* Exception handling region tree.  */
+  eh_region region_tree;
+
+  /* Result type tree node.  */
+  tree result_type;
+
+  /* Array of argument tree types.  */
+  vec <tree> arg_types;
+
+  /* Number of function arguments.  */
+  unsigned int arg_count;
+
+  /* Total amount of edges in the function.  */
+  unsigned int edge_count;
+
+  /* Vector of sizes of all basic blocks.  */
+  vec <unsigned int> bb_sizes;
+
+  /* Control flow graph checksum.  */
+  hashval_t cfg_checksum;
+
+  /* GIMPLE codes hash value.  */
+  hashval_t gcode_hash;
+
+  /* Total number of SSA names used in the function.  */
+  unsigned ssa_names_size;
+
+  /* Array of structures for all basic blocks.  */
+  vec <sem_bb *> bb_sorted;
+
+private:
+  /* Calculates hash value based on a BASIC_BLOCK.  */
+  hashval_t get_bb_hash (const sem_bb *basic_block);
+
+  /* For given basic blocks BB1 and BB2 (from functions FUNC1 and FUNC),
+     true value is returned if phi nodes are semantically
+     equivalent in these blocks .  */
+  bool compare_phi_node (basic_block bb1, basic_block bb2);
+
+  /* For given basic blocks BB1 and BB2 (from functions FUNC1 and FUNC),
+     true value is returned if exception handling regions are equivalent
+     in these blocks.  */
+  bool compare_eh_region (eh_region r1, eh_region r2);
+
+  /* Basic blocks dictionary BB_DICT returns true if SOURCE index BB
+     corresponds to TARGET.  */
+  bool bb_dict_test (int* bb_dict, int source, int target);
+
+  /* Iterates all tree types in T1 and T2 and returns true if all types
+     are compatible. If COMPARE_POLYMORPHIC is set to true,
+     more strict comparison is executed.  */
+  bool compare_type_list (tree t1, tree t2, bool compare_polymorphic);
+
+  /* Processes function equality comparison.  */
+  bool equals_private (sem_item *item);
+
+  /* Initialize references to another sem_item for gimple STMT of type assign.  */
+  void init_refs_for_assign (gimple stmt);
+
+  /* Initialize references to another sem_item for tree T.  */
+  void init_refs_for_tree (tree t);
+
+  /* Returns true if tree T can be compared as a handled component.  */
+  static bool icf_handled_component_p (tree t);
+
+  /* Function checker stores binding between functions.   */
+  func_checker *m_checker;
+
+  /* COMPARED_FUNC is a function that we compare to.  */
+  sem_function *m_compared_func;
+}; // class sem_function
+
+class sem_variable: public sem_item
+{
+public:
+  /* Semantic variable constructor that uses STACK as bitmap memory stack.  */
+  sem_variable (bitmap_obstack *stack);
+
+  /*  Constructor based on callgraph node _NODE with computed hash _HASH.
+      Bitmap STACK is used for memory allocation.  */
+
+  sem_variable (varpool_node *_node, hashval_t _hash, bitmap_obstack *stack);
+
+  inline virtual void init_wpa (void) {}
+
+  /* Semantic variable initialization function.  */
+  inline virtual void init (void)
+  {
+    decl = get_node ()->decl;
+    ctor = ctor_for_folding (decl);
+  }
+
+  /* Initialize references to other semantic functions/variables.  */
+  inline virtual void init_refs ()
+  {
+    parse_tree_refs (ctor);
+  }
+
+  virtual hashval_t get_hash (void);
+  virtual bool merge (sem_item *alias_item);
+  virtual void dump_to_file (FILE *file);
+  virtual bool equals (sem_item *item);
+
+  /* Fast equality variable based on knowledge known in WPA.  */
+  inline virtual bool equals_wpa (sem_item *item)
+  {
+    gcc_assert (item->type == VAR);
+    return true;
+  }
+
+  /* Returns varpool_node.  */
+  inline varpool_node *get_node (void)
+  {
+    return dyn_cast <varpool_node *> (node);
+  }
+
+  /* Parser function that visits a varpool NODE.  */
+  static sem_variable *parse (varpool_node *node, bitmap_obstack *stack);
+
+  /* Variable constructor.  */
+  tree ctor;
+
+private:
+  /* Iterates though a constructor and identifies tree references
+     we are interested in semantic function equality.  */
+  void parse_tree_refs (tree t);
+
+  /* Compares trees T1 and T2 for semantic equality.  */
+  static bool equals (tree t1, tree t2);
+
+  /* Compare that symbol sections are either NULL or have same name.  */
+  bool compare_sections (sem_variable *alias);
+
+}; // class sem_variable
+
+class sem_item_optimizer;
+
+/* Congruence class set structure.  */
+struct congruence_class_var_hash: typed_noop_remove <congruence_class>
+{
+  typedef congruence_class value_type;
+  typedef congruence_class compare_type;
+
+  static inline hashval_t hash (const value_type *item)
+  {
+    return htab_hash_pointer (item);
+  }
+
+  static inline int equal (const value_type *item1, const compare_type *item2)
+  {
+    return item1 == item2;
+  }
+};
+
+struct congruence_class_group
+{
+  hashval_t hash;
+  sem_item_type type;
+  vec <congruence_class *> classes;
+};
+
+/* Congruence class set structure.  */
+struct congruence_class_group_hash: typed_noop_remove <congruence_class_group>
+{
+  typedef congruence_class_group value_type;
+  typedef congruence_class_group compare_type;
+
+  static inline hashval_t hash (const value_type *item)
+  {
+    return item->hash;
+  }
+
+  static inline int equal (const value_type *item1, const compare_type *item2)
+  {
+    return item1->hash == item2->hash && item1->type == item2->type;
+  }
+};
+
+struct traverse_split_pair
+{
+  sem_item_optimizer *optimizer;
+  class congruence_class *cls;
+};
+
+/* Semantic item optimizer includes all top-level logic
+   related to semantic equality comparison.  */
+class sem_item_optimizer
+{
+public:
+  sem_item_optimizer ();
+  ~sem_item_optimizer ();
+
+  /* Function responsible for visiting all potential functions and
+     read-only variables that can be merged.  */
+  void parse_funcs_and_vars (void);
+
+  /* Optimizer entry point.  */
+  void execute (void);
+
+  /* Dump function. */
+  void dump (void);
+
+  /* Verify congruence classes if checking is enabled.  */
+  void verify_classes (void);
+
+  /* Write IPA ICF summary for symbols.  */
+  void write_summary (void);
+
+  /* Read IPA IPA ICF summary for symbols.  */
+  void read_summary (void);
+
+  /* Callgraph removal hook called for a NODE with a custom DATA.  */
+  static void cgraph_removal_hook (cgraph_node *node, void *data);
+
+  /* Varpool removal hook called for a NODE with a custom DATA.  */
+  static void varpool_removal_hook (varpool_node *node, void *data);
+
+  /* Worklist of congruence classes that can potentially
+     refine classes of congruence.  */
+  hash_table <congruence_class_var_hash> worklist;
+
+  /* Remove symtab NODE triggered by symtab removal hooks.  */
+  void remove_symtab_node (symtab_node *node);
+
+  /* Register callgraph and varpool hooks.  */
+  void register_hooks (void);
+
+  /* Unregister callgraph and varpool hooks.  */
+  void unregister_hooks (void);
+
+  /* Adds a CLS to hashtable associated by hash value.  */
+  void add_class (congruence_class *cls);
+
+  /* Gets a congruence class group based on given HASH value and TYPE.  */
+  congruence_class_group *get_group_by_hash (hashval_t hash,
+      sem_item_type type);
+
+private:
+
+  /* Congruence classes are built by hash value.  */
+  void build_hash_based_classes (void);
+
+  /* Semantic items in classes having more than one element and initialized.
+     In case of WPA, we load function body.  */
+  void parse_nonsingleton_classes (void);
+
+  /* Equality function for semantic items is used to subdivide existing
+     classes. If IN_WPA, fast equality function is invoked.  */
+  void subdivide_classes_by_equality (bool in_wpa = false);
+
+  /* Debug function prints all informations about congruence classes.  */
+  void dump_cong_classes (void);
+
+  /* Iterative congruence reduction function.  */
+  void process_cong_reduction (void);
+
+  /* After reduction is done, we can declare all items in a group
+     to be equal. PREV_CLASS_COUNT is start number of classes
+     before reduction.  */
+  void merge_classes (unsigned int prev_class_count);
+
+  /* Adds a newly created congruence class CLS to worklist.  */
+  void worklist_push (congruence_class *cls);
+
+  /* Pops a class from worklist. */
+  congruence_class *worklist_pop ();
+
+  /* Returns true if a congruence class CLS is present in worklist.  */
+  inline bool worklist_contains (const congruence_class *cls)
+  {
+    return worklist.find (cls);
+  }
+
+  /* Removes given congruence class CLS from worklist.  */
+  inline void worklist_remove (const congruence_class *cls)
+  {
+    worklist.remove_elt (cls);
+  }
+
+  /* Every usage of a congruence class CLS is a candidate that can split the
+     collection of classes. Bitmap stack BMSTACK is used for bitmap
+     allocation.  */
+  void do_congruence_step (congruence_class *cls);
+
+  /* Tests if a class CLS used as INDEXth splits any congruence classes.
+     Bitmap stack BMSTACK is used for bitmap allocation.  */
+  void do_congruence_step_for_index (congruence_class *cls, unsigned int index);
+
+  /* Makes pairing between a congruence class CLS and semantic ITEM.  */
+  static void add_item_to_class (congruence_class *cls, sem_item *item);
+
+  /* Disposes split map traverse function. CLS is congruence
+     class, BSLOT is bitmap slot we want to release. DATA is mandatory,
+     but unused argument.  */
+  static bool release_split_map (congruence_class * const &cls, bitmap const &b,
+				 traverse_split_pair *pair);
+
+  /* Process split operation for a cognruence class CLS,
+     where bitmap B splits congruence class members. DATA is used
+     as argument of split pair.  */
+  static bool traverse_congruence_split (congruence_class * const &cls,
+					 bitmap const &b,
+					 traverse_split_pair *pair);
+
+  /* Reads a section from LTO stream file FILE_DATA. Input block for DATA
+     contains LEN bytes.  */
+  void read_section (lto_file_decl_data *file_data, const char *data,
+		     size_t len);
+
+  /* Removes all callgraph and varpool nodes that are marked by symtab
+     as deleted.  */
+  void filter_removed_items (void);
+
+  /* Vector of semantic items.  */
+  vec <sem_item *> m_items;
+
+  /* A set containing all items removed by hooks.  */
+  hash_set <symtab_node *> m_removed_items_set;
+
+  /* Hashtable of congruence classes */
+  hash_table <congruence_class_group_hash> m_classes;
+
+  /* Count of congruence classes.  */
+  unsigned int m_classes_count;
+
+  /* Map data structure maps trees to semantic items.  */
+  hash_map <tree, sem_item *> m_decl_map;
+
+  /* Map data structure maps symtab nodes to semantic items.  */
+  hash_map <symtab_node *, sem_item *> m_symtab_node_map;
+
+  /* Set to true if a splitter class is removed.  */
+  bool splitter_class_removed;
+
+  /* Global unique class id counter.  */
+  static unsigned int class_id;
+
+  /* Callgraph node removal hook holder.  */
+  cgraph_node_hook_list *m_cgraph_node_hooks;
+
+  /* Varpool node removal hook holder.  */
+  varpool_node_hook_list *m_varpool_node_hooks;
+
+  /* Bitmap stack.  */
+  bitmap_obstack m_bmstack;
+}; // class sem_item_optimizer
+
+} // ipa_icf namespace
diff --git a/gcc/lto-cgraph.c b/gcc/lto-cgraph.c
index 0584946..c5aa797 100644
--- a/gcc/lto-cgraph.c
+++ b/gcc/lto-cgraph.c
@@ -539,6 +539,7 @@ lto_output_node (struct lto_simple_output_block *ob, struct cgraph_node *node,
   bp_pack_value (&bp, node->only_called_at_exit, 1);
   bp_pack_value (&bp, node->tm_clone, 1);
   bp_pack_value (&bp, node->calls_comdat_local, 1);
+  bp_pack_value (&bp, node->icf_merged, 1);
   bp_pack_value (&bp, node->thunk.thunk_p && !boundary_p, 1);
   bp_pack_enum (&bp, ld_plugin_symbol_resolution,
 	        LDPR_NUM_KNOWN, node->resolution);
@@ -1079,6 +1080,7 @@ input_overwrite_node (struct lto_file_decl_data *file_data,
   node->only_called_at_exit = bp_unpack_value (bp, 1);
   node->tm_clone = bp_unpack_value (bp, 1);
   node->calls_comdat_local = bp_unpack_value (bp, 1);
+  node->icf_merged = bp_unpack_value (bp, 1);
   node->thunk.thunk_p = bp_unpack_value (bp, 1);
   node->resolution = bp_unpack_enum (bp, ld_plugin_symbol_resolution,
 				     LDPR_NUM_KNOWN);
diff --git a/gcc/lto-section-in.c b/gcc/lto-section-in.c
index 5623706..c053545 100644
--- a/gcc/lto-section-in.c
+++ b/gcc/lto-section-in.c
@@ -60,7 +60,8 @@ const char *lto_section_name[LTO_N_SECTION_TYPES] =
   "opts",
   "cgraphopt",
   "inline",
-  "ipcp_trans"
+  "ipcp_trans",
+  "icf"
 };
 
 
diff --git a/gcc/lto-streamer.h b/gcc/lto-streamer.h
index 4bec969..63e4b32 100644
--- a/gcc/lto-streamer.h
+++ b/gcc/lto-streamer.h
@@ -247,6 +247,7 @@ enum lto_section_type
   LTO_section_cgraph_opt_sum,
   LTO_section_inline_summary,
   LTO_section_ipcp_transform,
+  LTO_section_ipa_icf,
   LTO_N_SECTION_TYPES		/* Must be last.  */
 };
 
diff --git a/gcc/opts.c b/gcc/opts.c
index 0a49bc0..28bf684 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -497,6 +497,7 @@ static const struct default_options default_options_table[] =
     { OPT_LEVELS_2_PLUS, OPT_fvect_cost_model_, NULL, VECT_COST_MODEL_CHEAP },
     { OPT_LEVELS_2_PLUS_SPEED_ONLY, OPT_foptimize_strlen, NULL, 1 },
     { OPT_LEVELS_2_PLUS, OPT_fhoist_adjacent_loads, NULL, 1 },
+    { OPT_LEVELS_2_PLUS, OPT_fipa_icf, NULL, 1 },
     { OPT_LEVELS_2_PLUS, OPT_fisolate_erroneous_paths_dereference, NULL, 1 },
     { OPT_LEVELS_2_PLUS, OPT_fuse_caller_save, NULL, 1 },
 
@@ -1969,6 +1970,11 @@ common_handle_option (struct gcc_options *opts,
 	opts->x_flag_wrapv = 0;
       break;
 
+    case OPT_fipa_icf:
+	opts->x_flag_ipa_icf_functions = value;
+	opts->x_flag_ipa_icf_variables = value;
+      break;
+
     default:
       /* If the flag was handled in a standard way, assume the lack of
 	 processing here is intentional.  */
diff --git a/gcc/passes.def b/gcc/passes.def
index 334c670..4b7547a 100644
--- a/gcc/passes.def
+++ b/gcc/passes.def
@@ -104,6 +104,7 @@ along with GCC; see the file COPYING3.  If not see
   NEXT_PASS (pass_ipa_whole_program_visibility);
   NEXT_PASS (pass_ipa_profile);
   NEXT_PASS (pass_ipa_devirt);
+  NEXT_PASS (pass_ipa_icf);
   NEXT_PASS (pass_ipa_cp);
   NEXT_PASS (pass_ipa_cdtor_merge);
   NEXT_PASS (pass_ipa_inline);
diff --git a/gcc/timevar.def b/gcc/timevar.def
index a04d05c..55a230b 100644
--- a/gcc/timevar.def
+++ b/gcc/timevar.def
@@ -90,6 +90,7 @@ DEFTIMEVAR (TV_WHOPR_LTRANS          , "whopr ltrans")
 DEFTIMEVAR (TV_IPA_REFERENCE         , "ipa reference")
 DEFTIMEVAR (TV_IPA_PROFILE           , "ipa profile")
 DEFTIMEVAR (TV_IPA_PURE_CONST        , "ipa pure const")
+DEFTIMEVAR (TV_IPA_ICF		     , "ipa icf")
 DEFTIMEVAR (TV_IPA_PTA               , "ipa points-to")
 DEFTIMEVAR (TV_IPA_SRA               , "ipa SRA")
 DEFTIMEVAR (TV_IPA_FREE_LANG_DATA    , "ipa free lang data")
diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h
index ed109c3..aac6703 100644
--- a/gcc/tree-pass.h
+++ b/gcc/tree-pass.h
@@ -461,6 +461,7 @@ extern simple_ipa_opt_pass *make_pass_ipa_free_lang_data (gcc::context *ctxt);
 extern simple_ipa_opt_pass *make_pass_ipa_free_inline_summary (gcc::context
 							       *ctxt);
 extern ipa_opt_pass_d *make_pass_ipa_cp (gcc::context *ctxt);
+extern ipa_opt_pass_d *make_pass_ipa_icf (gcc::context *ctxt);
 extern ipa_opt_pass_d *make_pass_ipa_devirt (gcc::context *ctxt);
 extern ipa_opt_pass_d *make_pass_ipa_reference (gcc::context *ctxt);
 extern ipa_opt_pass_d *make_pass_ipa_pure_const (gcc::context *ctxt);
-- 
1.8.4.5


^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 4/5] Existing tests fix
  2014-06-30 12:12     ` Martin Liška
@ 2014-09-26 12:21       ` Martin Liška
  0 siblings, 0 replies; 70+ messages in thread
From: Martin Liška @ 2014-09-26 12:21 UTC (permalink / raw)
  To: gcc-patches; +Cc: Jan Hubicka

[-- Attachment #1: Type: text/plain, Size: 4754 bytes --]

On 06/30/2014 02:11 PM, Martin Liška wrote:
>
> On 06/17/2014 09:52 PM, Jeff Law wrote:
>> On 06/13/14 04:48, mliska wrote:
>>> Hi,
>>>    many tests rely on a precise number of scanned functions in a dump file. If IPA ICF decides to merge some function and(or) read-only variables, counts do not match.
>>>
>>> Martin
>>>
>>> Changelog:
>>>
>>> 2014-06-13  Martin Liska  <mliska@suse.cz>
>>>         Honza Hubicka  <hubicka@ucw.cz>
>>>
>>>     * c-c++-common/rotate-1.c: Text
>>>     * c-c++-common/rotate-2.c: New test.
>>>     * c-c++-common/rotate-3.c: Likewise.
>>>     * c-c++-common/rotate-4.c: Likewise.
>>>     * g++.dg/cpp0x/rv-return.C: Likewise.
>>>     * g++.dg/cpp0x/rv1n.C: Likewise.
>>>     * g++.dg/cpp0x/rv1p.C: Likewise.
>>>     * g++.dg/cpp0x/rv2n.C: Likewise.
>>>     * g++.dg/cpp0x/rv3n.C: Likewise.
>>>     * g++.dg/cpp0x/rv4n.C: Likewise.
>>>     * g++.dg/cpp0x/rv5n.C: Likewise.
>>>     * g++.dg/cpp0x/rv6n.C: Likewise.
>>>     * g++.dg/cpp0x/rv7n.C: Likewise.
>>>     * gcc.dg/ipa/ipacost-1.c: Likewise.
>>>     * gcc.dg/ipa/ipacost-2.c: Likewise.
>>>     * gcc.dg/ipa/ipcp-agg-6.c: Likewise.
>>>     * gcc.dg/ipa/remref-2a.c: Likewise.
>>>     * gcc.dg/ipa/remref-2b.c: Likewise.
>>>     * gcc.dg/pr46309-2.c: Likewise.
>>>     * gcc.dg/torture/ipa-pta-1.c: Likewise.
>>>     * gcc.dg/tree-ssa/andor-3.c: Likewise.
>>>     * gcc.dg/tree-ssa/andor-4.c: Likewise.
>>>     * gcc.dg/tree-ssa/andor-5.c: Likewise.
>>>     * gcc.dg/vect/no-vfa-pr29145.c: Likewise.
>>>     * gcc.dg/vect/vect-cond-10.c: Likewise.
>>>     * gcc.dg/vect/vect-cond-9.c: Likewise.
>>>     * gcc.dg/vect/vect-widen-mult-const-s16.c: Likewise.
>>>     * gcc.dg/vect/vect-widen-mult-const-u16.c: Likewise.
>>>     * gcc.dg/vect/vect-widen-mult-half-u8.c: Likewise.
>>>     * gcc.target/i386/bmi-1.c: Likewise.
>>>     * gcc.target/i386/bmi-2.c: Likewise.
>>>     * gcc.target/i386/pr56564-2.c: Likewise.
>>>     * g++.dg/opt/pr30965.C: Likewise.
>>>     * g++.dg/tree-ssa/pr19637.C: Likewise.
>>>     * gcc.dg/guality/csttest.c: Likewise.
>>>     * gcc.dg/ipa/iinline-4.c: Likewise.
>>>     * gcc.dg/ipa/iinline-7.c: Likewise.
>>>     * gcc.dg/ipa/ipa-pta-13.c: Likewise.
>> I know this is the least interesting part of your changes, but it's also simple and mechanical and thus trivial to review. Approved, but obviously don't install until the rest of your patch has been approved.
>>
>> Similar changes for recently added tests or cases where you might improve ICF requiring similar tweaks to existing tests are pre-approved as well.
>>
>> jeff
>>
> Hello,
>     I fixed few more tests and added correct ChangeLog message.
>
> gcc/testsuite/ChangeLog
>
> 2014-06-30  Martin Liska  <mliska@suse.cz>
>          Honza Hubicka  <hubicka@ucw.cz>
>
>      * c-c++-common/rotate-1.c: Test fixed.
>      * c-c++-common/rotate-2.c: Likewise.
>      * c-c++-common/rotate-3.c: Likewise.
>      * c-c++-common/rotate-4.c: Likewise.
>      * g++.dg/cpp0x/rv-return.C: Likewise.
>      * g++.dg/cpp0x/rv1n.C: Likewise.
>      * g++.dg/cpp0x/rv1p.C: Likewise.
>      * g++.dg/cpp0x/rv2n.C: Likewise.
>      * g++.dg/cpp0x/rv3n.C: Likewise.
>      * g++.dg/cpp0x/rv4n.C: Likewise.
>      * g++.dg/cpp0x/rv5n.C: Likewise.
>      * g++.dg/cpp0x/rv6n.C: Likewise.
>      * g++.dg/cpp0x/rv7n.C: Likewise.
>      * g++.dg/ipa/devirt-g-1.C: Likewise.
>      * g++.dg/ipa/inline-1.C: Likewise.
>      * g++.dg/ipa/inline-2.C: Likewise.
>      * g++.dg/ipa/inline-3.C: Likewise.
>      * g++.dg/opt/pr30965.C: Likewise.
>      * g++.dg/tree-ssa/pr19637.C: Likewise.
>      * gcc.dg/guality/csttest.c: Likewise.
>      * gcc.dg/ipa/iinline-4.c: Likewise.
>      * gcc.dg/ipa/iinline-7.c: Likewise.
>      * gcc.dg/ipa/ipa-pta-13.c: Likewise.
>      * gcc.dg/ipa/ipacost-1.c: Likewise.
>      * gcc.dg/ipa/ipacost-2.c: Likewise.
>      * gcc.dg/ipa/ipcp-agg-6.c: Likewise.
>      * gcc.dg/ipa/remref-2a.c: Likewise.
>      * gcc.dg/ipa/remref-2b.c: Likewise.
>      * gcc.dg/pr46309-2.c: Likewise.
>      * gcc.dg/torture/ipa-pta-1.c: Likewise.
>      * gcc.dg/tree-ssa/andor-3.c: Likewise.
>      * gcc.dg/tree-ssa/andor-4.c: Likewise.
>      * gcc.dg/tree-ssa/andor-5.c: Likewise.
>      * gcc.dg/vect/no-vfa-pr29145.c: Likewise.
>      * gcc.dg/vect/vect-cond-10.c: Likewise.
>      * gcc.dg/vect/vect-cond-9.c: Likewise.
>      * gcc.dg/vect/vect-widen-mult-const-s16.c: Likewise.
>      * gcc.dg/vect/vect-widen-mult-const-u16.c: Likewise.
>      * gcc.dg/vect/vect-widen-mult-half-u8.c: Likewise.
>      * gcc.target/i386/bmi-1.c: Likewise.
>      * gcc.target/i386/bmi-2.c: Likewise.
>      * gcc.target/i386/pr56564-2.c: Likewise.
>
> Thank you,
> Martin
>

Hello.

There's updated version of the patch that fixes another issued connected to test suite.

Thanks,
Martin

[-- Attachment #2: 0002-IPA-ICF-patch2.patch --]
[-- Type: text/x-patch, Size: 22463 bytes --]

From e7818e646687c05e13a68828ef70fb41716a267c Mon Sep 17 00:00:00 2001
From: mliska <mliska@suse.cz>
Date: Fri, 26 Sep 2014 13:52:29 +0200
Subject: [PATCH 2/3] IPA ICF: patch2.

---
 gcc/testsuite/c-c++-common/rotate-1.c                 | 2 +-
 gcc/testsuite/c-c++-common/rotate-2.c                 | 2 +-
 gcc/testsuite/c-c++-common/rotate-3.c                 | 2 +-
 gcc/testsuite/c-c++-common/rotate-4.c                 | 2 +-
 gcc/testsuite/g++.dg/cpp0x/rv-return.C                | 1 +
 gcc/testsuite/g++.dg/cpp0x/rv1n.C                     | 2 ++
 gcc/testsuite/g++.dg/cpp0x/rv1p.C                     | 1 +
 gcc/testsuite/g++.dg/cpp0x/rv2n.C                     | 2 +-
 gcc/testsuite/g++.dg/cpp0x/rv3n.C                     | 2 +-
 gcc/testsuite/g++.dg/cpp0x/rv4n.C                     | 2 +-
 gcc/testsuite/g++.dg/cpp0x/rv5n.C                     | 2 +-
 gcc/testsuite/g++.dg/cpp0x/rv6n.C                     | 2 +-
 gcc/testsuite/g++.dg/cpp0x/rv7n.C                     | 2 +-
 gcc/testsuite/g++.dg/ipa/devirt-g-1.C                 | 2 +-
 gcc/testsuite/g++.dg/ipa/inline-1.C                   | 2 +-
 gcc/testsuite/g++.dg/ipa/inline-2.C                   | 2 +-
 gcc/testsuite/g++.dg/ipa/inline-3.C                   | 2 +-
 gcc/testsuite/g++.dg/opt/pr30965.C                    | 2 +-
 gcc/testsuite/g++.dg/tree-ssa/pr19637.C               | 2 +-
 gcc/testsuite/gcc.dg/guality/csttest.c                | 2 +-
 gcc/testsuite/gcc.dg/ipa/iinline-4.c                  | 2 +-
 gcc/testsuite/gcc.dg/ipa/iinline-7.c                  | 2 +-
 gcc/testsuite/gcc.dg/ipa/ipa-pta-13.c                 | 2 +-
 gcc/testsuite/gcc.dg/ipa/ipacost-1.c                  | 2 +-
 gcc/testsuite/gcc.dg/ipa/ipacost-2.c                  | 2 +-
 gcc/testsuite/gcc.dg/ipa/ipcp-agg-6.c                 | 2 +-
 gcc/testsuite/gcc.dg/ipa/remref-2a.c                  | 2 +-
 gcc/testsuite/gcc.dg/ipa/remref-2b.c                  | 2 +-
 gcc/testsuite/gcc.dg/pr46309-2.c                      | 2 +-
 gcc/testsuite/gcc.dg/torture/ipa-pta-1.c              | 2 +-
 gcc/testsuite/gcc.dg/tree-ssa/andor-3.c               | 2 +-
 gcc/testsuite/gcc.dg/tree-ssa/andor-4.c               | 2 +-
 gcc/testsuite/gcc.dg/tree-ssa/andor-5.c               | 2 +-
 gcc/testsuite/gcc.dg/vect/no-vfa-pr29145.c            | 1 +
 gcc/testsuite/gcc.dg/vect/vect-cond-10.c              | 1 +
 gcc/testsuite/gcc.dg/vect/vect-cond-9.c               | 1 +
 gcc/testsuite/gcc.dg/vect/vect-widen-mult-const-s16.c | 1 +
 gcc/testsuite/gcc.dg/vect/vect-widen-mult-const-u16.c | 1 +
 gcc/testsuite/gcc.dg/vect/vect-widen-mult-half-u8.c   | 1 +
 gcc/testsuite/gcc.target/i386/bmi-1.c                 | 2 +-
 gcc/testsuite/gcc.target/i386/bmi-2.c                 | 2 +-
 gcc/testsuite/gcc.target/i386/pr56564-2.c             | 2 +-
 42 files changed, 43 insertions(+), 33 deletions(-)

diff --git a/gcc/testsuite/c-c++-common/rotate-1.c b/gcc/testsuite/c-c++-common/rotate-1.c
index afdaa28..bca9dd8 100644
--- a/gcc/testsuite/c-c++-common/rotate-1.c
+++ b/gcc/testsuite/c-c++-common/rotate-1.c
@@ -1,6 +1,6 @@
 /* Check rotate pattern detection.  */
 /* { dg-do compile } */
-/* { dg-options "-O2 -fdump-tree-optimized" } */
+/* { dg-options "-O2 -fno-ipa-icf -fdump-tree-optimized" } */
 /* { dg-final { scan-tree-dump-times "r\[<>]\[<>]" 96 "optimized" } } */
 /* { dg-final { cleanup-tree-dump "optimized" } } */
 
diff --git a/gcc/testsuite/c-c++-common/rotate-2.c b/gcc/testsuite/c-c++-common/rotate-2.c
index 109fd32..4ffa218 100644
--- a/gcc/testsuite/c-c++-common/rotate-2.c
+++ b/gcc/testsuite/c-c++-common/rotate-2.c
@@ -1,6 +1,6 @@
 /* Check rotate pattern detection.  */
 /* { dg-do compile } */
-/* { dg-options "-O2 -fdump-tree-optimized" } */
+/* { dg-options "-O2 -fno-ipa-icf -fdump-tree-optimized" } */
 /* Rotates should be recognized only in functions with | instead of + or ^,
    or in functions that have constant shift counts (unused attribute on y).  */
 /* { dg-final { scan-tree-dump-times "r\[<>]\[<>]" 48 "optimized" } } */
diff --git a/gcc/testsuite/c-c++-common/rotate-3.c b/gcc/testsuite/c-c++-common/rotate-3.c
index 8dc8313..aaa9f50 100644
--- a/gcc/testsuite/c-c++-common/rotate-3.c
+++ b/gcc/testsuite/c-c++-common/rotate-3.c
@@ -1,6 +1,6 @@
 /* Check rotate pattern detection.  */
 /* { dg-do compile } */
-/* { dg-options "-O2 -fdump-tree-optimized" } */
+/* { dg-options "-O2 -fno-ipa-icf -fdump-tree-optimized" } */
 /* { dg-final { scan-tree-dump-times "r\[<>]\[<>]" 96 "optimized" } } */
 /* { dg-final { cleanup-tree-dump "optimized" } } */
 
diff --git a/gcc/testsuite/c-c++-common/rotate-4.c b/gcc/testsuite/c-c++-common/rotate-4.c
index 2f433b3..0a21177 100644
--- a/gcc/testsuite/c-c++-common/rotate-4.c
+++ b/gcc/testsuite/c-c++-common/rotate-4.c
@@ -1,6 +1,6 @@
 /* Check rotate pattern detection.  */
 /* { dg-do compile } */
-/* { dg-options "-O2 -fdump-tree-optimized" } */
+/* { dg-options "-O2 -fno-ipa-icf -fdump-tree-optimized" } */
 /* Rotates should be recognized only in functions with | instead of + or ^,
    or in functions that have constant shift counts (unused attribute on y).  */
 /* { dg-final { scan-tree-dump-times "r\[<>]\[<>]" 48 "optimized" } } */
diff --git a/gcc/testsuite/g++.dg/cpp0x/rv-return.C b/gcc/testsuite/g++.dg/cpp0x/rv-return.C
index 12a15aa..6d57209 100644
--- a/gcc/testsuite/g++.dg/cpp0x/rv-return.C
+++ b/gcc/testsuite/g++.dg/cpp0x/rv-return.C
@@ -1,5 +1,6 @@
 // PR c++/41815
 // { dg-do compile { target c++11 } }
+// { dg-options "-fno-ipa-icf" }
 
 template<typename T, typename U> struct same_type;
 template<typename T> struct same_type<T, T> {};
diff --git a/gcc/testsuite/g++.dg/cpp0x/rv1n.C b/gcc/testsuite/g++.dg/cpp0x/rv1n.C
index 204ca31..f5e7568 100644
--- a/gcc/testsuite/g++.dg/cpp0x/rv1n.C
+++ b/gcc/testsuite/g++.dg/cpp0x/rv1n.C
@@ -1,8 +1,10 @@
 // I, Howard Hinnant, hereby place this code in the public domain.
+/* { dg-additional-options "-fno-ipa-icf" } */
 
 // Test overload resolution among reference types
 
 // { dg-do compile { target c++11 } }
+// { dg-additional-options "-fno-ipa-icf" }
 
 template <bool> struct sa;
 template <> struct sa<true> {};
diff --git a/gcc/testsuite/g++.dg/cpp0x/rv1p.C b/gcc/testsuite/g++.dg/cpp0x/rv1p.C
index e4c0ab1..e87ec0e 100644
--- a/gcc/testsuite/g++.dg/cpp0x/rv1p.C
+++ b/gcc/testsuite/g++.dg/cpp0x/rv1p.C
@@ -4,6 +4,7 @@
 
 // { dg-do compile { target c++11 } }
 // { dg-skip-if "packed attribute missing for struct one/three/five/seven" { "epiphany-*-*" } { "*" } { "" } }
+// { dg-additional-options "-fno-ipa-icf" }
 
 template <bool> struct sa;
 template <> struct sa<true> {};
diff --git a/gcc/testsuite/g++.dg/cpp0x/rv2n.C b/gcc/testsuite/g++.dg/cpp0x/rv2n.C
index 9677f58..663a66b 100644
--- a/gcc/testsuite/g++.dg/cpp0x/rv2n.C
+++ b/gcc/testsuite/g++.dg/cpp0x/rv2n.C
@@ -3,7 +3,7 @@
 // Test overload resolution among reference types
 
 // { dg-do compile { target c++11 } }
-// { dg-options "" }
+// { dg-options "-fno-ipa-icf" }
 
 template <bool> struct sa;
 template <> struct sa<true> {};
diff --git a/gcc/testsuite/g++.dg/cpp0x/rv3n.C b/gcc/testsuite/g++.dg/cpp0x/rv3n.C
index 8a1730b..b7c1d7a 100644
--- a/gcc/testsuite/g++.dg/cpp0x/rv3n.C
+++ b/gcc/testsuite/g++.dg/cpp0x/rv3n.C
@@ -3,7 +3,7 @@
 // Test overload resolution among reference types
 
 // { dg-do compile { target c++11 } }
-// { dg-options "" }
+// { dg-options "-fno-ipa-icf" }
 
 template <bool> struct sa;
 template <> struct sa<true> {};
diff --git a/gcc/testsuite/g++.dg/cpp0x/rv4n.C b/gcc/testsuite/g++.dg/cpp0x/rv4n.C
index e64856d..29deb3f 100644
--- a/gcc/testsuite/g++.dg/cpp0x/rv4n.C
+++ b/gcc/testsuite/g++.dg/cpp0x/rv4n.C
@@ -3,7 +3,7 @@
 // Test overload resolution among reference types
 
 // { dg-do compile { target c++11 } }
-// { dg-options "" }
+// { dg-options "-fno-ipa-icf" }
 
 template <bool> struct sa;
 template <> struct sa<true> {};
diff --git a/gcc/testsuite/g++.dg/cpp0x/rv5n.C b/gcc/testsuite/g++.dg/cpp0x/rv5n.C
index 90d5418..f11d07a 100644
--- a/gcc/testsuite/g++.dg/cpp0x/rv5n.C
+++ b/gcc/testsuite/g++.dg/cpp0x/rv5n.C
@@ -3,7 +3,7 @@
 // Test overload resolution among reference types
 
 // { dg-do compile { target c++11 } }
-// { dg-options "" }
+// { dg-options "-fno-ipa-icf" }
 
 template <bool> struct sa;
 template <> struct sa<true> {};
diff --git a/gcc/testsuite/g++.dg/cpp0x/rv6n.C b/gcc/testsuite/g++.dg/cpp0x/rv6n.C
index 5bd9a23..0ebbe33 100644
--- a/gcc/testsuite/g++.dg/cpp0x/rv6n.C
+++ b/gcc/testsuite/g++.dg/cpp0x/rv6n.C
@@ -3,7 +3,7 @@
 // Test overload resolution among reference types
 
 // { dg-do compile { target c++11 } }
-// { dg-options "" }
+// { dg-options "-fno-ipa-icf" }
 
 template <bool> struct sa;
 template <> struct sa<true> {};
diff --git a/gcc/testsuite/g++.dg/cpp0x/rv7n.C b/gcc/testsuite/g++.dg/cpp0x/rv7n.C
index 38ca7b8..d9e371b 100644
--- a/gcc/testsuite/g++.dg/cpp0x/rv7n.C
+++ b/gcc/testsuite/g++.dg/cpp0x/rv7n.C
@@ -3,7 +3,7 @@
 // Test overload resolution among reference types
 
 // { dg-do compile { target c++11 } }
-// { dg-options "" }
+// { dg-options "-fno-ipa-icf" }
 
 template <bool> struct sa;
 template <> struct sa<true> {};
diff --git a/gcc/testsuite/g++.dg/ipa/devirt-g-1.C b/gcc/testsuite/g++.dg/ipa/devirt-g-1.C
index 175f24e..1530fdb 100644
--- a/gcc/testsuite/g++.dg/ipa/devirt-g-1.C
+++ b/gcc/testsuite/g++.dg/ipa/devirt-g-1.C
@@ -1,5 +1,5 @@
 // { dg-do compile }
-// { dg-options "-O2 -fdump-ipa-cp -fdump-tree-optimized" }
+// { dg-options "-O2 -fdump-ipa-cp -fno-ipa-icf -fdump-tree-optimized" }
 
 struct S { S(); virtual void xyzzy(); void otherstuff(); };
 struct R { int a; S s; R(); };
diff --git a/gcc/testsuite/g++.dg/ipa/inline-1.C b/gcc/testsuite/g++.dg/ipa/inline-1.C
index dbbfb4e..3a6a041 100644
--- a/gcc/testsuite/g++.dg/ipa/inline-1.C
+++ b/gcc/testsuite/g++.dg/ipa/inline-1.C
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O2 -fdump-ipa-inline --param max-early-inliner-iterations=1" } */
+/* { dg-options "-O2 -fdump-ipa-inline -fno-ipa-icf --param max-early-inliner-iterations=1" } */
 /* { dg-add-options bind_pic_locally } */
 
 namespace std {
diff --git a/gcc/testsuite/g++.dg/ipa/inline-2.C b/gcc/testsuite/g++.dg/ipa/inline-2.C
index fd284a1e2..d1e46c0 100644
--- a/gcc/testsuite/g++.dg/ipa/inline-2.C
+++ b/gcc/testsuite/g++.dg/ipa/inline-2.C
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O2 -fdump-ipa-inline --param max-early-inliner-iterations=1" } */
+/* { dg-options "-O2 -fdump-ipa-inline -fno-ipa-icf --param max-early-inliner-iterations=1" } */
 /* { dg-add-options bind_pic_locally } */
 
 namespace std {
diff --git a/gcc/testsuite/g++.dg/ipa/inline-3.C b/gcc/testsuite/g++.dg/ipa/inline-3.C
index 8d5f905..7315bf5 100644
--- a/gcc/testsuite/g++.dg/ipa/inline-3.C
+++ b/gcc/testsuite/g++.dg/ipa/inline-3.C
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O2 -fdump-ipa-inline --param max-early-inliner-iterations=1" } */
+/* { dg-options "-O2 -fdump-ipa-inline -fno-ipa-icf --param max-early-inliner-iterations=1" } */
 /* { dg-add-options bind_pic_locally } */
 
 #include <algorithm>
diff --git a/gcc/testsuite/g++.dg/opt/pr30965.C b/gcc/testsuite/g++.dg/opt/pr30965.C
index 91bb55c..45393fd 100644
--- a/gcc/testsuite/g++.dg/opt/pr30965.C
+++ b/gcc/testsuite/g++.dg/opt/pr30965.C
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O3 -fdump-tree-optimized" } */
+/* { dg-options "-O3 -fno-ipa-icf -fdump-tree-optimized" } */
 
 #include <tr1/functional>
 #include <algorithm>
diff --git a/gcc/testsuite/g++.dg/tree-ssa/pr19637.C b/gcc/testsuite/g++.dg/tree-ssa/pr19637.C
index 2d1dcce..92f673f 100644
--- a/gcc/testsuite/g++.dg/tree-ssa/pr19637.C
+++ b/gcc/testsuite/g++.dg/tree-ssa/pr19637.C
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O2 -fdump-tree-dom1" } */
+/* { dg-options "-O2 -fno-ipa-icf -fdump-tree-dom1" } */
 
 #include <new>
 
diff --git a/gcc/testsuite/gcc.dg/guality/csttest.c b/gcc/testsuite/gcc.dg/guality/csttest.c
index 4480c71..05e1ad7 100644
--- a/gcc/testsuite/gcc.dg/guality/csttest.c
+++ b/gcc/testsuite/gcc.dg/guality/csttest.c
@@ -1,6 +1,6 @@
 /* PR debug/49676 */
 /* { dg-do run { target lp64 } } */
-/* { dg-options "-g" } */
+/* { dg-options "-g -fno-ipa-icf" } */
 
 volatile int v;
 
diff --git a/gcc/testsuite/gcc.dg/ipa/iinline-4.c b/gcc/testsuite/gcc.dg/ipa/iinline-4.c
index 71faae2..3f295df 100644
--- a/gcc/testsuite/gcc.dg/ipa/iinline-4.c
+++ b/gcc/testsuite/gcc.dg/ipa/iinline-4.c
@@ -1,7 +1,7 @@
 /* Verify that simple indirect calls are inlined even without early
    inlining..  */
 /* { dg-do compile } */
-/* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining"  } */
+/* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining -fno-ipa-icf"  } */
 
 struct S
 {
diff --git a/gcc/testsuite/gcc.dg/ipa/iinline-7.c b/gcc/testsuite/gcc.dg/ipa/iinline-7.c
index c95d374..61e8d0b 100644
--- a/gcc/testsuite/gcc.dg/ipa/iinline-7.c
+++ b/gcc/testsuite/gcc.dg/ipa/iinline-7.c
@@ -1,7 +1,7 @@
 /* Verify that simple indirect calls are inlined even without early
    inlining..  */
 /* { dg-do run } */
-/* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining"  } */
+/* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining -fno-ipa-icf"  } */
 
 extern void abort (void);
 
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-pta-13.c b/gcc/testsuite/gcc.dg/ipa/ipa-pta-13.c
index 0f46e98..f7f95f4 100644
--- a/gcc/testsuite/gcc.dg/ipa/ipa-pta-13.c
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-pta-13.c
@@ -1,5 +1,5 @@
 /* { dg-do link } */
-/* { dg-options "-O2 -fipa-pta -fdump-ipa-pta-details -fdump-tree-fre2" } */
+/* { dg-options "-O2 -fipa-pta -fdump-ipa-pta-details -fdump-tree-fre2 -fno-ipa-icf" } */
 
 static int x, y;
 
diff --git a/gcc/testsuite/gcc.dg/ipa/ipacost-1.c b/gcc/testsuite/gcc.dg/ipa/ipacost-1.c
index 4fce41e..9603afe 100644
--- a/gcc/testsuite/gcc.dg/ipa/ipacost-1.c
+++ b/gcc/testsuite/gcc.dg/ipa/ipacost-1.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-Os -fipa-cp -fdump-ipa-cp -fno-early-inlining -fdump-tree-optimized"  } */
+/* { dg-options "-Os -fipa-cp -fdump-ipa-cp -fno-early-inlining -fdump-tree-optimized -fno-ipa-icf"  } */
 
 int array[100];
 
diff --git a/gcc/testsuite/gcc.dg/ipa/ipacost-2.c b/gcc/testsuite/gcc.dg/ipa/ipacost-2.c
index ceb524e..e7074f1 100644
--- a/gcc/testsuite/gcc.dg/ipa/ipacost-2.c
+++ b/gcc/testsuite/gcc.dg/ipa/ipacost-2.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O3 -fipa-cp -fipa-cp-clone -fdump-ipa-cp -fno-early-inlining -fdump-tree-optimized"  } */
+/* { dg-options "-O3 -fipa-cp -fipa-cp-clone -fdump-ipa-cp -fno-early-inlining -fdump-tree-optimized -fno-ipa-icf"  } */
 /* { dg-add-options bind_pic_locally } */
 
 int array[100];
diff --git a/gcc/testsuite/gcc.dg/ipa/ipcp-agg-6.c b/gcc/testsuite/gcc.dg/ipa/ipcp-agg-6.c
index 050e13b..5d6425b 100644
--- a/gcc/testsuite/gcc.dg/ipa/ipcp-agg-6.c
+++ b/gcc/testsuite/gcc.dg/ipa/ipcp-agg-6.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O3 -fno-ipa-sra -fdump-ipa-cp-details -fdump-tree-optimized-slim"  } */
+/* { dg-options "-O3 -fno-ipa-sra -fdump-ipa-cp-details -fdump-tree-optimized-slim -fno-ipa-icf"  } */
 /* { dg-add-options bind_pic_locally } */
 
 struct S
diff --git a/gcc/testsuite/gcc.dg/ipa/remref-2a.c b/gcc/testsuite/gcc.dg/ipa/remref-2a.c
index 1e0df2e..4697a18 100644
--- a/gcc/testsuite/gcc.dg/ipa/remref-2a.c
+++ b/gcc/testsuite/gcc.dg/ipa/remref-2a.c
@@ -1,7 +1,7 @@
 /* Verify that indirect inlining can also remove references of the functions it
    discovers calls for.  */
 /* { dg-do compile } */
-/* { dg-options "-O3 -fno-early-inlining -fno-ipa-cp -fdump-ipa-inline -fdump-tree-optimized"  } */
+/* { dg-options "-O3 -fno-early-inlining -fno-ipa-cp -fdump-ipa-inline -fdump-tree-optimized -fno-ipa-icf"  } */
 
 int global;
 
diff --git a/gcc/testsuite/gcc.dg/ipa/remref-2b.c b/gcc/testsuite/gcc.dg/ipa/remref-2b.c
index 554f306..7799033 100644
--- a/gcc/testsuite/gcc.dg/ipa/remref-2b.c
+++ b/gcc/testsuite/gcc.dg/ipa/remref-2b.c
@@ -2,7 +2,7 @@
    discovers calls for, even when nodes being inlined are virtual IPA-CP
    clones.  */
 /* { dg-do compile } */
-/* { dg-options "-O3 -fno-early-inlining -fdump-ipa-cp-details -fdump-ipa-inline -fdump-tree-optimized"  } */
+/* { dg-options "-O3 -fno-early-inlining -fdump-ipa-cp-details -fdump-ipa-inline -fdump-tree-optimized -fno-ipa-icf"  } */
 
 
 int global;
diff --git a/gcc/testsuite/gcc.dg/pr46309-2.c b/gcc/testsuite/gcc.dg/pr46309-2.c
index f407e66..00ffee1 100644
--- a/gcc/testsuite/gcc.dg/pr46309-2.c
+++ b/gcc/testsuite/gcc.dg/pr46309-2.c
@@ -1,6 +1,6 @@
 /* PR tree-optimization/46309 */
 /* { dg-do compile } */
-/* { dg-options "-O2 -fdump-tree-reassoc-details" } */
+/* { dg-options "-O2 -fno-ipa-icf -fdump-tree-reassoc-details" } */
 
 int foo (void);
 
diff --git a/gcc/testsuite/gcc.dg/torture/ipa-pta-1.c b/gcc/testsuite/gcc.dg/torture/ipa-pta-1.c
index aae987d..80303a5 100644
--- a/gcc/testsuite/gcc.dg/torture/ipa-pta-1.c
+++ b/gcc/testsuite/gcc.dg/torture/ipa-pta-1.c
@@ -1,5 +1,5 @@
 /* { dg-do compile { target { nonpic } } } */
-/* { dg-options "-fipa-pta -fdump-ipa-pta" } */
+/* { dg-options "-fipa-pta -fdump-ipa-pta -fno-ipa-icf" } */
 /* { dg-skip-if "" { *-*-* } { "-O0" "-fno-fat-lto-objects" } { "" } } */
 
 struct X { char x; char y; };
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/andor-3.c b/gcc/testsuite/gcc.dg/tree-ssa/andor-3.c
index a1401c0..8b2f206 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/andor-3.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/andor-3.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O2 -fdump-tree-optimized" } */
+/* { dg-options "-O2 -fno-ipa-icf -fdump-tree-optimized" } */
 
 int f(int y, int x)
 {
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/andor-4.c b/gcc/testsuite/gcc.dg/tree-ssa/andor-4.c
index 1dbdca7..46a4826 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/andor-4.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/andor-4.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O2 -fdump-tree-optimized" } */
+/* { dg-options "-O2 -fno-ipa-icf -fdump-tree-optimized" } */
 
 int f(int y, int x)
 {
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/andor-5.c b/gcc/testsuite/gcc.dg/tree-ssa/andor-5.c
index 1509727..929851c 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/andor-5.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/andor-5.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O2 -fdump-tree-optimized" } */
+/* { dg-options "-O2 -fno-ipa-icf -fdump-tree-optimized" } */
 
 int f(int y, int x)
 {
diff --git a/gcc/testsuite/gcc.dg/vect/no-vfa-pr29145.c b/gcc/testsuite/gcc.dg/vect/no-vfa-pr29145.c
index e475fff..5896271 100644
--- a/gcc/testsuite/gcc.dg/vect/no-vfa-pr29145.c
+++ b/gcc/testsuite/gcc.dg/vect/no-vfa-pr29145.c
@@ -1,4 +1,5 @@
 /* { dg-require-effective-target vect_int } */
+/* { dg-additional-options "-fno-ipa-icf" } */
 
 #include <stdarg.h>
 #include "tree-vect.h"
diff --git a/gcc/testsuite/gcc.dg/vect/vect-cond-10.c b/gcc/testsuite/gcc.dg/vect/vect-cond-10.c
index 687d42f..da2eb05 100644
--- a/gcc/testsuite/gcc.dg/vect/vect-cond-10.c
+++ b/gcc/testsuite/gcc.dg/vect/vect-cond-10.c
@@ -1,4 +1,5 @@
 /* { dg-require-effective-target vect_cond_mixed } */
+/* { dg-additional-options "-fno-ipa-icf" } */
 
 #include "tree-vect.h"
 
diff --git a/gcc/testsuite/gcc.dg/vect/vect-cond-9.c b/gcc/testsuite/gcc.dg/vect/vect-cond-9.c
index cfa0363..de88fc5 100644
--- a/gcc/testsuite/gcc.dg/vect/vect-cond-9.c
+++ b/gcc/testsuite/gcc.dg/vect/vect-cond-9.c
@@ -1,4 +1,5 @@
 /* { dg-require-effective-target vect_cond_mixed } */
+/* { dg-additional-options "-fno-ipa-icf" } */
 
 #include "tree-vect.h"
 
diff --git a/gcc/testsuite/gcc.dg/vect/vect-widen-mult-const-s16.c b/gcc/testsuite/gcc.dg/vect/vect-widen-mult-const-s16.c
index a2fe975..895bbd0 100644
--- a/gcc/testsuite/gcc.dg/vect/vect-widen-mult-const-s16.c
+++ b/gcc/testsuite/gcc.dg/vect/vect-widen-mult-const-s16.c
@@ -1,4 +1,5 @@
 /* { dg-require-effective-target vect_int } */
+/* { dg-additional-options "-fno-ipa-icf" } */
 
 #include "tree-vect.h"
 #include <stdlib.h>
diff --git a/gcc/testsuite/gcc.dg/vect/vect-widen-mult-const-u16.c b/gcc/testsuite/gcc.dg/vect/vect-widen-mult-const-u16.c
index e712da9..70219bb 100644
--- a/gcc/testsuite/gcc.dg/vect/vect-widen-mult-const-u16.c
+++ b/gcc/testsuite/gcc.dg/vect/vect-widen-mult-const-u16.c
@@ -1,4 +1,5 @@
 /* { dg-require-effective-target vect_int } */
+/* { dg-additional-options "-fno-ipa-icf" } */
 
 #include "tree-vect.h"
 #include <stdlib.h>
diff --git a/gcc/testsuite/gcc.dg/vect/vect-widen-mult-half-u8.c b/gcc/testsuite/gcc.dg/vect/vect-widen-mult-half-u8.c
index 39078df..3f07585 100644
--- a/gcc/testsuite/gcc.dg/vect/vect-widen-mult-half-u8.c
+++ b/gcc/testsuite/gcc.dg/vect/vect-widen-mult-half-u8.c
@@ -1,4 +1,5 @@
 /* { dg-require-effective-target vect_int } */
+/* { dg-additional-options "-fno-ipa-icf" } */
 
 #include "tree-vect.h"
 #include <stdlib.h>
diff --git a/gcc/testsuite/gcc.target/i386/bmi-1.c b/gcc/testsuite/gcc.target/i386/bmi-1.c
index c66a9d8..738705e 100644
--- a/gcc/testsuite/gcc.target/i386/bmi-1.c
+++ b/gcc/testsuite/gcc.target/i386/bmi-1.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O2 -mbmi " } */
+/* { dg-options "-O2 -fno-ipa-icf -mbmi " } */
 /* { dg-final { scan-assembler "andn\[^\\n]*eax" } } */
 /* { dg-final { scan-assembler-times "bextr\[ \\t]+\[^\\n]*eax" 2 } } */
 /* { dg-final { scan-assembler-times "blsi\[^\\n]*eax" 2 } } */
diff --git a/gcc/testsuite/gcc.target/i386/bmi-2.c b/gcc/testsuite/gcc.target/i386/bmi-2.c
index 6eea66a..25fb86b 100644
--- a/gcc/testsuite/gcc.target/i386/bmi-2.c
+++ b/gcc/testsuite/gcc.target/i386/bmi-2.c
@@ -1,5 +1,5 @@
 /* { dg-do compile { target { ! { ia32 }  } } } */
-/* { dg-options "-O2 -mbmi " } */
+/* { dg-options "-O2 -fno-ipa-icf -mbmi " } */
 /* { dg-final { scan-assembler "andn\[^\\n]*rax" } } */
 /* { dg-final { scan-assembler-times "bextr\[ \\t]+\[^\\n]*rax" 2 } } */
 /* { dg-final { scan-assembler-times "blsi\[^\\n]*rax" 2 } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr56564-2.c b/gcc/testsuite/gcc.target/i386/pr56564-2.c
index fc89a4c..c42bfae 100644
--- a/gcc/testsuite/gcc.target/i386/pr56564-2.c
+++ b/gcc/testsuite/gcc.target/i386/pr56564-2.c
@@ -1,6 +1,6 @@
 /* PR target/56564 */
 /* { dg-do compile { target { *-*-linux* && lp64 } } } */
-/* { dg-options "-O3 -fno-pic -fdump-tree-optimized" } */
+/* { dg-options "-O3 -fno-pic -fno-ipa-icf -fdump-tree-optimized" } */
 
 struct S { long a, b; } s = { 5, 6 };
 char t[16] = { 7 };
-- 
1.8.4.5


^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 3/5] IPA ICF pass
  2014-09-26 12:20           ` Martin Liška
@ 2014-09-26 14:44             ` Markus Trippelsdorf
  2014-09-26 23:27               ` Jan Hubicka
  2014-09-26 20:47             ` Jan Hubicka
  2014-09-26 22:27             ` Jan Hubicka
  2 siblings, 1 reply; 70+ messages in thread
From: Markus Trippelsdorf @ 2014-09-26 14:44 UTC (permalink / raw)
  To: Martin Liška; +Cc: gcc-patches, Jan Hubicka

On 2014.09.26 at 14:20 +0200, Martin Liška wrote:
> After couple of weeks I spent with fixing new issues connected to the
> pass: 1) Inliner failed in case I created a thunk and release body of
> a function. In such situation we need to preserve DECL_ARGUMENTS. I
> added new argument for: cgraph_node::release_body.  2) Awkward error
> was hidden in libstdc++ test for trees, there were two functions
> having one argument that differs in one sub-template. Thank to Richard
> who helped me to fix alias set accuracy.  3) There was missing
> comparison for FIELD_DECLS (DECL_FIELD_BIT_OFFSET) which caused me
> miscompilation.  4) After discussion with Honza, we introduced new
> cgraph_node flag called icf_merged. The flag helps to fix verifier in
> cgraph_node::verify.
> 
> Current version of the patch can bootstrap on x86_64-linux. With
> following patch applied, there's not testcase regression.  I tried to
> build Firefox, Inkscape, GIMP and Chromium with LTO and patch applied
> and no regression has been observed.

While a plain Firefox -flto build works fine. LTO/PGO build fails with:

lto1: internal compiler error: in ipa_merge_profiles, at ipa-utils.c:540
0x7d6165 ipa_merge_profiles(cgraph_node*, cgraph_node*)
        ../../gcc/gcc/ipa-utils.c:540
0xf10c41 ipa_icf::sem_function::merge(ipa_icf::sem_item*)
        ../../gcc/gcc/ipa-icf.c:753
0xf15206 ipa_icf::sem_item_optimizer::merge_classes(unsigned int)
        ../../gcc/gcc/ipa-icf.c:2706
0xf1c1f4 ipa_icf::sem_item_optimizer::execute()
        ../../gcc/gcc/ipa-icf.c:2098
0xf1d3f1 ipa_icf_driver
        ../../gcc/gcc/ipa-icf.c:2784
0xf1d3f1 ipa_icf::pass_ipa_icf::execute(function*)
        ../../gcc/gcc/ipa-icf.c:2831


The pass is also very memory hungry (from 3GB without ICF to 4GB during
libxul link), while the code size savings are in the 1% range.

-- 
Markus

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 3/5] IPA ICF pass
  2014-09-26 12:20           ` Martin Liška
  2014-09-26 14:44             ` Markus Trippelsdorf
@ 2014-09-26 20:47             ` Jan Hubicka
  2014-10-11  0:08               ` Martin Liška
  2014-09-26 22:27             ` Jan Hubicka
  2 siblings, 1 reply; 70+ messages in thread
From: Jan Hubicka @ 2014-09-26 20:47 UTC (permalink / raw)
  To: Martin Liška; +Cc: gcc-patches, Jan Hubicka

Hi,
this is on ipa-icf-gimple.c

@@ -2827,11 +2829,19 @@ cgraph_node::verify_node (void)
 			    {
 			      if (verify_edge_corresponds_to_fndecl (e, decl))
 				{
-				  error ("edge points to wrong declaration:");
-				  debug_tree (e->callee->decl);
-				  fprintf (stderr," Instead of:");
-				  debug_tree (decl);
-				  error_found = true;
+				  /* The edge can be redirected in WPA by IPA ICF.
+				     Following check really ensures that it's
+				     not the case.  */
+
+				  cgraph_node *current_node = cgraph_node::get (decl);
+				  if (!current_node || !current_node->icf_merged)

I would move this into verify_edge_corresponds_to_fndecl.

diff --git a/gcc/ipa-icf-gimple.c b/gcc/ipa-icf-gimple.c
new file mode 100644
index 0000000..7031eaa
--- /dev/null
+++ b/gcc/ipa-icf-gimple.c
@@ -0,0 +1,384 @@
+/* Interprocedural Identical Code Folding pass
+   Copyright (C) 2014 Free Software Foundation, Inc.
+
+   Contributed by Jan Hubicka <hubicka@ucw.cz> and Martin Liska <mliska@suse.cz>
+
+This file is part of GCC.
+
+GCC 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, or (at your option) any later
+version.
+
+GCC 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 GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */

Please add toplevel comment about what the code does and how to use it.

+namespace ipa_icf {
+
+/* Basic block equivalence comparison function that returns true if
+   basic blocks BB1 and BB2 (from functions FUNC1 and FUNC2) correspond.  */
... to each other?
I would add short comment that as comparsion goes you build voclabulary
of equivalences of variables/ssanames etc.
So people reading the code do not get lost at very beggining.

+
+bool
+func_checker::compare_bb (sem_bb *bb1, sem_bb *bb2)
+{
+  unsigned i;
+  gimple_stmt_iterator gsi1, gsi2;
+  gimple s1, s2;
+
+  if (bb1->nondbg_stmt_count != bb2->nondbg_stmt_count
+      || bb1->edge_count != bb2->edge_count)
+    return RETURN_FALSE ();

The UPPERCASE looks ugly.  I see that RETURN_FALSE is a warpper for return_false_with_msg
that outputs line and file information.

I would make it lowercase even if it is macro. You may consider using
CXX_MEM_STAT_INFO style default argument to avoid function macro completely.
Probably not big win given that it won't save you from preprocesor mess.
+
+  gsi1 = gsi_start_bb (bb1->bb);
+  gsi2 = gsi_start_bb (bb2->bb);
+
+  for (i = 0; i < bb1->nondbg_stmt_count; i++)
+    {
+      if (is_gimple_debug (gsi_stmt (gsi1)))
+	gsi_next_nondebug (&gsi1);
+
+      if (is_gimple_debug (gsi_stmt (gsi2)))
+	gsi_next_nondebug (&gsi2);
+
+      s1 = gsi_stmt (gsi1);
+      s2 = gsi_stmt (gsi2);
+
+      if (gimple_code (s1) != gimple_code (s2))
+	return RETURN_FALSE_WITH_MSG ("gimple codes are different");

I think you need to compare EH here.  Consider case where one unit
is compiled with -fno-exception and thus all EH regions are removed,
while other function has EH regions in it.  Those are not equivalent.

EH region is obtained by lookup_stmt_eh and then you need to comapre
them for match as you do with gimple_resx_regoin.

+  t1 = gimple_call_fndecl (s1);
+  t2 = gimple_call_fndecl (s2);
+
+  /* Function pointer variables are not supported yet.  */

They seems to be, compare_operand seems just right.

+
+/* Verifies for given GIMPLEs S1 and S2 that
+   label statements are semantically equivalent.  */
+
+bool
+func_checker::compare_gimple_label (gimple g1, gimple g2)
+{
+  if (m_ignore_labels)
+    return true;
+
+  tree t1 = gimple_label_label (g1);
+  tree t2 = gimple_label_label (g2);
+
+  return compare_tree_ssa_label (t1, t2);
+}

I would expect the main BB loop to record BB in which label belongs to
and the BB assciatio neing checked here.
Otherwise I do not see how switch statements are compared to not have
different permutations of targets. Also note that one BB may have
multiple labels in them and they are equivalent.

Also I would punt on occurence of FORCED_LABEL. Those are tricky as they
may be passed around and compared for address and no one really defines
what should happen.  Better to avoid those.

+
+/* Verifies for given GIMPLEs S1 and S2 that
+   switch statements are semantically equivalent.  */
+
+bool
+func_checker::compare_gimple_switch (gimple g1, gimple g2)
+{
+  unsigned lsize1, lsize2, i;
+  tree t1, t2, low1, low2, high1, high2;
+
+  lsize1 = gimple_switch_num_labels (g1);
+  lsize2 = gimple_switch_num_labels (g2);
+
+  if (lsize1 != lsize2)
+    return false;
+
+  t1 = gimple_switch_index (g1);
+  t2 = gimple_switch_index (g2);
+
+  if (TREE_CODE (t1) != SSA_NAME || TREE_CODE(t2) != SSA_NAME)
+    return false;

Why non-SSA_NAMES are excluded? I see that constants should be optimized out,
but I do not see a need for specific test here.
+
+  if (!compare_operand (t1, t2))
+    return false;
+
+  for (i = 0; i < lsize1; i++)
+    {
+      low1 = CASE_LOW (gimple_switch_label (g1, i));
+      low2 = CASE_LOW (gimple_switch_label (g2, i));
+
+      if ((low1 != NULL) != (low2 != NULL)
+	  || (low1 && low2 && TREE_INT_CST_LOW (low1) != TREE_INT_CST_LOW (low2)))
+	return false;

You want to compare integers for equivality.  Just use tree_int_cst_equal.
+
+      high1 = CASE_HIGH (gimple_switch_label (g1, i));
+      high2 = CASE_HIGH (gimple_switch_label (g2, i));
+
+      if ((high1 != NULL) != (high2 != NULL)
+	  || (high1 && high2
+	      && TREE_INT_CST_LOW (high1) != TREE_INT_CST_LOW (high2)))
+	return false;

Same here.
+    }
+
+  return true;
+}
+
+/* Verifies for given GIMPLEs S1 and S2 that
+   return statements are semantically equivalent.  */
+
+bool
+func_checker::compare_gimple_return (gimple g1, gimple g2)
+{
+  tree t1, t2;
+
+  t1 = gimple_return_retval (g1);
+  t2 = gimple_return_retval (g2);
+
+  /* Void return type.  */
+  if (t1 == NULL && t2 == NULL)
+    return true;
+  else
+    return compare_operand (t1, t2);

Do you check somewhere DECL_BY_REFERENCE (DECL_RESULT (struct function))? Those needs to match, too.
Perhaps it is always the case that SSA form differs, but I would test it.
+}
+
+/* Verifies for given GIMPLEs S1 and S2 that
+   goto statements are semantically equivalent.  */
+
+bool
+func_checker::compare_gimple_goto (gimple g1, gimple g2)
+{
+  tree dest1, dest2;
+
+  dest1 = gimple_goto_dest (g1);
+  dest2 = gimple_goto_dest (g2);
+
+  if (TREE_CODE (dest1) != TREE_CODE (dest2) || TREE_CODE (dest1) != SSA_NAME)
+    return false;
+
+  return compare_operand (dest1, dest2);

You probably need to care only about indirect gotos, the direct ones are checked by
CFG compare.  So is the condtional jump.
+
+/* Verifies for given GIMPLEs S1 and S2 that ASM statements are equivalent.
+   For the beginning, the pass only supports equality for
+   '__asm__ __volatile__ ("", "", "", "memory")'.  */
+
+bool
+func_checker::compare_gimple_asm (gimple g1, gimple g2)
+{
+  if (gimple_asm_volatile_p (g1) != gimple_asm_volatile_p (g2))
+    return false;
+
+  if (gimple_asm_ninputs (g1) || gimple_asm_ninputs (g2))
+    return false;
+
+  if (gimple_asm_noutputs (g1) || gimple_asm_noutputs (g2))
+    return false;
+
+  if (gimple_asm_nlabels (g1) || gimple_asm_nlabels (g2))
+    return false;
+
+  if (gimple_asm_nclobbers (g1) != gimple_asm_nclobbers (g2))
+    return false;
+
+  for (unsigned i = 0; i < gimple_asm_nclobbers (g1); i++)
+    {
+      tree clobber1 = TREE_VALUE (gimple_asm_clobber_op (g1, i));
+      tree clobber2 = TREE_VALUE (gimple_asm_clobber_op (g2, i));
+
+      if (!operand_equal_p (clobber1, clobber2, OEP_ONLY_CONST))
+	return false;
+    }
+

Even asm statements with no inputs or outputs can differ by the actual
asm statement. Compare it too.

Comparing inputs/outputs/labels should be very easy to do.

Compare all gimple_asm_n* for equivalency.
At the end walk operands and watch the case they are TREE_LIST.
THen compare TREE_VALUE (op) of the list for operand_equal_p
and TREE_VALUE (TREE_PURPOSE (op)) for equivalency
(those are the constraints)

If they are not (clobbers are not, those are just strings), operand_equal_p
should do.

+  return true;
+}
+
+} // ipa_icf namespace

Otherwise I think ipa-gimple-icf is quite fine now.
Please send updated version and I think it can go to mainline before the actual ipa-icf.

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 3/5] IPA ICF pass
  2014-09-26 12:20           ` Martin Liška
  2014-09-26 14:44             ` Markus Trippelsdorf
  2014-09-26 20:47             ` Jan Hubicka
@ 2014-09-26 22:27             ` Jan Hubicka
  2014-10-11  0:23               ` Martin Liška
  2 siblings, 1 reply; 70+ messages in thread
From: Jan Hubicka @ 2014-09-26 22:27 UTC (permalink / raw)
  To: Martin Liška; +Cc: gcc-patches, Jan Hubicka

> diff --git a/gcc/ipa-icf.c b/gcc/ipa-icf.c
> new file mode 100644
> index 0000000..f3472fe
> --- /dev/null
> +++ b/gcc/ipa-icf.c
> @@ -0,0 +1,2841 @@
> +/* Interprocedural Identical Code Folding pass
> +   Copyright (C) 2014 Free Software Foundation, Inc.
> +
> +   Contributed by Jan Hubicka <hubicka@ucw.cz> and Martin Liska <mliska@suse.cz>
> +
> +This file is part of GCC.
> +
> +GCC 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, or (at your option) any later
> +version.
> +
> +GCC 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 GCC; see the file COPYING3.  If not see
> +<http://www.gnu.org/licenses/>.  */
> +
> +/* Interprocedural Identical Code Folding for functions and
> +   read-only variables.
> +
> +   The goal of this transformation is to discover functions and read-only
> +   variables which do have exactly the same semantics.
(or value)
> +
> +   In case of functions,
> +   we could either create a virtual clone or do a simple function wrapper
> +   that will call equivalent function. If the function is just locally visible,
> +   all function calls can be redirected. For read-only variables, we create
> +   aliases if possible.
> +
> +   Optimization pass arranges as follows:

The optimization pass is arranged as follows: (I guess)

I also wonder if the gimple equality code should be in ipa_icf namespace, it is intended
to be shared with tail merging pass, so what about just calling it gimple_sem_equality?

> +/* Verification function for edges E1 and E2.  */
> +
> +bool
> +func_checker::compare_edge (edge e1, edge e2)
> +{
> +  if (e1->flags != e2->flags)
> +    return false;

In future we may want to experiment with checking that edge probabilities with
profile feedback match and refuse to merge BBs with different outgoing probabilities
(i.e. +-5%).
Just add it as TODO there, please.
> +
> +/* Return true if types are compatible from perspective of ICF.  */
> +bool func_checker::types_are_compatible_p (tree t1, tree t2,

Perhaps dropping _are_ would make sense, so we do not have two names
for essentially same thing.
> +    bool compare_polymorphic,
> +    bool first_argument)
> +{
> +  if (TREE_CODE (t1) != TREE_CODE (t2))
> +    return RETURN_FALSE_WITH_MSG ("different tree types");
> +
> +  if (!types_compatible_p (t1, t2))
> +    return RETURN_FALSE_WITH_MSG ("types are not compatible");
> +
> +  if (get_alias_set (t1) != get_alias_set (t2))
> +    return RETURN_FALSE_WITH_MSG ("alias sets are different");

You do not need to compare alias sets except for memory operations IMO.
> +
> +  /* We call contains_polymorphic_type_p with this pointer type.  */
> +  if (first_argument && TREE_CODE (t1) == POINTER_TYPE)
> +    {
> +      t1 = TREE_TYPE (t1);
> +      t2 = TREE_TYPE (t2);
> +    }
> +
> +  if (compare_polymorphic
> +      && (contains_polymorphic_type_p (t1) || contains_polymorphic_type_p (t2)))
> +    {
> +      if (!contains_polymorphic_type_p (t1) || !contains_polymorphic_type_p (t2))
> +	return RETURN_FALSE_WITH_MSG ("one type is not polymorphic");
> +
> +      if (TYPE_MAIN_VARIANT (t1) != TYPE_MAIN_VARIANT (t2))
> +	return RETURN_FALSE_WITH_MSG ("type variants are different for "
> +				      "polymorphic type");

I added types_must_be_same_for_odr (t1,t2) for you here.
> +/* Fast equality function based on knowledge known in WPA.  */
> +
> +bool
> +sem_function::equals_wpa (sem_item *item)
> +{
> +  gcc_assert (item->type == FUNC);
> +
> +  m_compared_func = static_cast<sem_function *> (item);
> +
> +  if (arg_types.length () != m_compared_func->arg_types.length ())
> +    return RETURN_FALSE_WITH_MSG ("different number of arguments");
> +
> +  /* Checking types of arguments.  */
> +  for (unsigned i = 0; i < arg_types.length (); i++)
> +    {
> +      /* This guard is here for function pointer with attributes (pr59927.c).  */
> +      if (!arg_types[i] || !m_compared_func->arg_types[i])
> +	return RETURN_FALSE_WITH_MSG ("NULL argument type");
> +
> +      if (!func_checker::types_are_compatible_p (arg_types[i],
> +	  m_compared_func->arg_types[i],
> +	  true, i == 0))
> +	return RETURN_FALSE_WITH_MSG ("argument type is different");
> +    }
> +
> +  /* Result type checking.  */
> +  if (!func_checker::types_are_compatible_p (result_type,
> +      m_compared_func->result_type))
> +    return RETURN_FALSE_WITH_MSG ("result types are different");

You may want to compare ECF flags, such as nothrow/const/pure.  We do not
want to merge non-pure function into pure as it may not be pure in the context
it is used.

Do you compare attributes? I think optimize attribute needs to be matched.
> +  /* Checking function arguments.  */
attributes.
> +  tree decl1 = DECL_ATTRIBUTES (decl);
> +  tree decl2 = DECL_ATTRIBUTES (m_compared_func->decl);
> +
> +  m_checker = new func_checker (decl, m_compared_func->decl,
> +				compare_polymorphic_p (),
> +				false,
> +				&tree_refs_set,
> +				&m_compared_func->tree_refs_set);
> +  while (decl1)
> +    {
> +      if (decl2 == NULL)
> +	return RETURN_FALSE ();
> +
> +      if (get_attribute_name (decl1) != get_attribute_name (decl2))
> +	return RETURN_FALSE ();
> +
> +      tree attr_value1 = TREE_VALUE (decl1);
> +      tree attr_value2 = TREE_VALUE (decl2);
> +
> +      if (attr_value1 && attr_value2)
> +	{
> +	  bool ret = m_checker->compare_operand (TREE_VALUE (attr_value1),
> +						 TREE_VALUE (attr_value2));
> +	  if (!ret)
> +	    return RETURN_FALSE_WITH_MSG ("attribute values are different");
> +	}
> +      else if (!attr_value1 && !attr_value2)
> +	{}
> +      else
> +	return RETURN_FALSE ();
> +
> +      decl1 = TREE_CHAIN (decl1);
> +      decl2 = TREE_CHAIN (decl2);
> +    }
> +
> +  if (decl1 != decl2)
> +    return RETURN_FALSE();
> +
> +
> +  for (arg1 = DECL_ARGUMENTS (decl),
> +       arg2 = DECL_ARGUMENTS (m_compared_func->decl);
> +       arg1; arg1 = DECL_CHAIN (arg1), arg2 = DECL_CHAIN (arg2))
> +    if (!m_checker->compare_decl (arg1, arg2))
> +      return RETURN_FALSE ();

I think you want to compare PARM_DECL flags, such as DECL_BY_REFERENCE
> +
> +/* Improve accumulated hash for HSTATE based on a gimple statement STMT.  */
> +
> +void
> +sem_function::improve_hash (inchash::hash *hstate, gimple stmt)

Hehe, I think simple hash_stmt would be easier to read - it took me some time
to figure out what you mean by improving.
> +{
> +  enum gimple_code code = gimple_code (stmt);
> +
> +  hstate->add_int (code);
> +
> +  if (code == GIMPLE_CALL)
> +    {
> +      /* Checking of argument.  */
> +      for (unsigned i = 0; i < gimple_call_num_args (stmt); ++i)
> +	{
> +	  tree argument = gimple_call_arg (stmt, i);
> +
> +	  switch (TREE_CODE (argument))
> +	    {
> +	    case INTEGER_CST:
> +	      if (tree_fits_shwi_p (argument))
> +		hstate->add_wide_int (tree_to_shwi (argument));
> +	      else if (tree_fits_uhwi_p (argument))
> +		hstate->add_wide_int (tree_to_uhwi (argument));
> +	      break;
> +	    case ADDR_EXPR:
> +	      {
> +		tree addr_operand = TREE_OPERAND (argument, 0);
> +
> +		if (TREE_CODE (addr_operand) == STRING_CST)
> +		  hstate->add (TREE_STRING_POINTER (addr_operand),
> +			       TREE_STRING_LENGTH (addr_operand));

It may be nice to reuse some of the hash_tree code, but yep, i guess
this is good first cut. Perhaps adding also REAL_CST
> +
> +/* Return true if polymorphic comparison must be processed.  */
> +
> +bool
> +sem_function::compare_polymorphic_p (void)
> +{
> +  return get_node ()->callees != NULL
> +	 || m_compared_func->get_node ()->callees != NULL;
This is somewhat kludgy, but probably OK for start.  I do not see how
local declaration can leak out after inlining.
You also want to check for no indirect calls.
> +
> +  if (!func || !node->has_gimple_body_p ())
> +    return NULL;

Do you somewhere handle thunks and aliases?
(again someting that can be done later, but TODO would be nice.)
> +    case INTEGER_CST:
> +      {
> +	ret = types_are_compatible_p (TREE_TYPE (t1), TREE_TYPE (t2))
> +	      && wi::to_offset  (t1) == wi::to_offset  (t2);

  tree_int_cst_equal

> +    case FIELD_DECL:
> +      {
> +	tree fctx1 = DECL_FCONTEXT (t1);
> +	tree fctx2 = DECL_FCONTEXT (t2);

DECL_FCONTEXT has no semantic meaning; so you can skip comparing it.
> +
> +	tree offset1 = DECL_FIELD_OFFSET (t1);
> +	tree offset2 = DECL_FIELD_OFFSET (t2);
> +
> +	tree bit_offset1 = DECL_FIELD_BIT_OFFSET (t1);
> +	tree bit_offset2 = DECL_FIELD_BIT_OFFSET (t2);
> +
> +	ret = compare_operand (fctx1, fctx2)
> +	      && compare_operand (offset1, offset2)
> +	      && compare_operand (bit_offset1, bit_offset2);

You probably want to compare type here?
> +    case CONSTRUCTOR:
> +      {
> +	unsigned len1 = vec_safe_length (CONSTRUCTOR_ELTS (t1));
> +	unsigned len2 = vec_safe_length (CONSTRUCTOR_ELTS (t2));
> +
> +	if (len1 != len2)
> +	  return false;
> +
> +	for (unsigned i = 0; i < len1; i++)
> +	  if (!sem_variable::equals (CONSTRUCTOR_ELT (t1, i)->value,
> +				     CONSTRUCTOR_ELT (t2, i)->value))
> +	    return false;

You want to compare ->index, too.
> +    case INTEGER_CST:
> +      return func_checker::types_are_compatible_p (TREE_TYPE (t1), TREE_TYPE (t2),
> +	     true)
> +	     && wi::to_offset (t1) == wi::to_offset (t2);
again ;)

This is where I stopped for now.  Generally the patch seems OK to me with few of these
details fixed.

Honza

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 3/5] IPA ICF pass
  2014-09-26 14:44             ` Markus Trippelsdorf
@ 2014-09-26 23:27               ` Jan Hubicka
  2014-09-27  5:59                 ` Markus Trippelsdorf
  2014-09-27 10:32                 ` Martin Liška
  0 siblings, 2 replies; 70+ messages in thread
From: Jan Hubicka @ 2014-09-26 23:27 UTC (permalink / raw)
  To: Markus Trippelsdorf; +Cc: Martin Liška, gcc-patches, Jan Hubicka

> While a plain Firefox -flto build works fine. LTO/PGO build fails with:
> 
> lto1: internal compiler error: in ipa_merge_profiles, at ipa-utils.c:540
> 0x7d6165 ipa_merge_profiles(cgraph_node*, cgraph_node*)
>         ../../gcc/gcc/ipa-utils.c:540
> 0xf10c41 ipa_icf::sem_function::merge(ipa_icf::sem_item*)
>         ../../gcc/gcc/ipa-icf.c:753
> 0xf15206 ipa_icf::sem_item_optimizer::merge_classes(unsigned int)
>         ../../gcc/gcc/ipa-icf.c:2706
> 0xf1c1f4 ipa_icf::sem_item_optimizer::execute()
>         ../../gcc/gcc/ipa-icf.c:2098
> 0xf1d3f1 ipa_icf_driver
>         ../../gcc/gcc/ipa-icf.c:2784
> 0xf1d3f1 ipa_icf::pass_ipa_icf::execute(function*)
>         ../../gcc/gcc/ipa-icf.c:2831
> 
> 
> The pass is also very memory hungry (from 3GB without ICF to 4GB during
> libxul link), while the code size savings are in the 1% range.

Thnks for checking. I was just thinking about doing that myself.  Would
you mind posting -ftime-report of firefox WPA stage?

It seems that in this case we reject too many of equality candidates?
It think the original numbers was about 4-5% but later some equivalences was
disabled because of devirt/aliasing issues. Do you compare it with gold ICF
enabled? There are quite few obvious improvements to the analysis that can
be done, but I guess we need to analyze the interesting cases one by one.

One thing that Martin can try is to hook into lto-symtab and try to check
that the COMDAT functions that are known to be same pass the equality check.
I suppose we will learn interesting things this way.

I think the patch adds quite important infrastructure for gimple semantic
equality checking and function merging. I went through the majority of code and
I think it is mostly ready to mainline (i.e. cleaner than what we have in
tree-ssa-tailmerge) so hope we can finish the review process next week.
We will need to get better cost/benefits ratio to enable it for -O2 that is
someting I would really like to see for 5.0, but it seems to be easier to
handle this incrementally....

Honza

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 3/5] IPA ICF pass
  2014-09-26 23:27               ` Jan Hubicka
@ 2014-09-27  5:59                 ` Markus Trippelsdorf
  2014-09-27  7:47                   ` Markus Trippelsdorf
  2014-09-27 10:37                   ` Martin Liška
  2014-09-27 10:32                 ` Martin Liška
  1 sibling, 2 replies; 70+ messages in thread
From: Markus Trippelsdorf @ 2014-09-27  5:59 UTC (permalink / raw)
  To: Jan Hubicka; +Cc: Martin Liška, gcc-patches

On 2014.09.27 at 01:27 +0200, Jan Hubicka wrote:
> > While a plain Firefox -flto build works fine. LTO/PGO build fails with:
> > 
> > lto1: internal compiler error: in ipa_merge_profiles, at ipa-utils.c:540
> > 0x7d6165 ipa_merge_profiles(cgraph_node*, cgraph_node*)
> >         ../../gcc/gcc/ipa-utils.c:540
> > 0xf10c41 ipa_icf::sem_function::merge(ipa_icf::sem_item*)
> >         ../../gcc/gcc/ipa-icf.c:753
> > 0xf15206 ipa_icf::sem_item_optimizer::merge_classes(unsigned int)
> >         ../../gcc/gcc/ipa-icf.c:2706
> > 0xf1c1f4 ipa_icf::sem_item_optimizer::execute()
> >         ../../gcc/gcc/ipa-icf.c:2098
> > 0xf1d3f1 ipa_icf_driver
> >         ../../gcc/gcc/ipa-icf.c:2784
> > 0xf1d3f1 ipa_icf::pass_ipa_icf::execute(function*)
> >         ../../gcc/gcc/ipa-icf.c:2831
> > 
> > 
> > The pass is also very memory hungry (from 3GB without ICF to 4GB during
> > libxul link), while the code size savings are in the 1% range.
> 
> Thnks for checking. I was just thinking about doing that myself.  Would
> you mind posting -ftime-report of firefox WPA stage?

(without ICF)
Execution times (seconds)
 phase setup             :   0.00 ( 0%) usr   0.00 ( 0%) sys   0.01 ( 0%) wall    1412 kB ( 0%) ggc
 phase opt and generate  :  58.38 (63%) usr   2.00 (47%) sys  60.37 (40%) wall  403069 kB (12%) ggc
 phase stream in         :  30.24 (33%) usr   0.97 (23%) sys  33.90 (22%) wall 2944210 kB (88%) ggc
 phase stream out        :   4.29 ( 5%) usr   1.32 (31%) sys  57.32 (38%) wall       0 kB ( 0%) ggc
 phase finalize          :   0.00 ( 0%) usr   0.00 ( 0%) sys   0.13 ( 0%) wall       0 kB ( 0%) ggc
 garbage collection      :   3.68 ( 4%) usr   0.00 ( 0%) sys   3.68 ( 2%) wall       0 kB ( 0%) ggc
 callgraph optimization  :   0.50 ( 1%) usr   0.00 ( 0%) sys   0.50 ( 0%) wall     166 kB ( 0%) ggc
 ipa dead code removal   :   6.91 ( 7%) usr   0.08 ( 2%) sys   7.25 ( 5%) wall       0 kB ( 0%) ggc
 ipa virtual call target :   7.08 ( 8%) usr   0.04 ( 1%) sys   6.93 ( 5%) wall       0 kB ( 0%) ggc
 ipa devirtualization    :   0.27 ( 0%) usr   0.00 ( 0%) sys   0.27 ( 0%) wall   10365 kB ( 0%) ggc
 ipa cp                  :   1.81 ( 2%) usr   0.06 ( 1%) sys   3.40 ( 2%) wall  173701 kB ( 5%) ggc
 ipa inlining heuristics :  16.60 (18%) usr   0.27 ( 6%) sys  17.48 (12%) wall  532704 kB (16%) ggc
 ipa comdats             :   0.19 ( 0%) usr   0.00 ( 0%) sys   0.19 ( 0%) wall       0 kB ( 0%) ggc
 ipa lto gimple out      :   0.21 ( 0%) usr   0.04 ( 1%) sys   0.97 ( 1%) wall       0 kB ( 0%) ggc
 ipa lto decl in         :  18.29 (20%) usr   0.54 (13%) sys  18.96 (12%) wall 2226088 kB (66%) ggc
 ipa lto decl out        :   3.93 ( 4%) usr   0.13 ( 3%) sys   4.06 ( 3%) wall       0 kB ( 0%) ggc
 ipa lto constructors in :   0.24 ( 0%) usr   0.03 ( 1%) sys   0.59 ( 0%) wall   14226 kB ( 0%) ggc
 ipa lto constructors out:   0.08 ( 0%) usr   0.04 ( 1%) sys   0.15 ( 0%) wall       0 kB ( 0%) ggc
 ipa lto cgraph I/O      :   0.89 ( 1%) usr   0.12 ( 3%) sys   1.02 ( 1%) wall  364151 kB (11%) ggc
 ipa lto decl merge      :   2.14 ( 2%) usr   0.01 ( 0%) sys   2.14 ( 1%) wall    8196 kB ( 0%) ggc
 ipa lto cgraph merge    :   1.59 ( 2%) usr   0.00 ( 0%) sys   1.60 ( 1%) wall   12716 kB ( 0%) ggc
 whopr wpa               :   1.54 ( 2%) usr   0.03 ( 1%) sys   1.55 ( 1%) wall       1 kB ( 0%) ggc
 whopr wpa I/O           :   0.04 ( 0%) usr   1.11 (26%) sys  52.10 (34%) wall       0 kB ( 0%) ggc
 whopr partitioning      :   5.02 ( 5%) usr   0.01 ( 0%) sys   5.03 ( 3%) wall    4938 kB ( 0%) ggc
 ipa reference           :   2.04 ( 2%) usr   0.02 ( 0%) sys   2.08 ( 1%) wall       0 kB ( 0%) ggc
 ipa profile             :   0.32 ( 0%) usr   0.00 ( 0%) sys   0.33 ( 0%) wall       0 kB ( 0%) ggc
 ipa pure const          :   2.43 ( 3%) usr   0.02 ( 0%) sys   2.49 ( 2%) wall       0 kB ( 0%) ggc
 tree STMT verifier      :   0.01 ( 0%) usr   0.00 ( 0%) sys   0.00 ( 0%) wall       0 kB ( 0%) ggc
 callgraph verifier      :  16.31 (18%) usr   1.69 (39%) sys  17.96 (12%) wall       0 kB ( 0%) ggc
 dominance computation   :   0.01 ( 0%) usr   0.00 ( 0%) sys   0.02 ( 0%) wall       0 kB ( 0%) ggc
 varconst                :   0.01 ( 0%) usr   0.03 ( 1%) sys   0.05 ( 0%) wall       0 kB ( 0%) ggc
 unaccounted todo        :   0.69 ( 1%) usr   0.00 ( 0%) sys   0.69 ( 0%) wall       0 kB ( 0%) ggc
 TOTAL                 :  92.91             4.29           151.73            3348693 kB
Extra diagnostic checks enabled; compiler may run slowly.
Configure with --enable-checking=release to disable checks.

(with ICF)
Execution times (seconds)
 phase setup             :   0.00 ( 0%) usr   0.00 ( 0%) sys   0.01 ( 0%) wall    1412 kB ( 0%) ggc
 phase opt and generate  :  82.70 (70%) usr   3.31 (53%) sys  86.17 (45%) wall 1468975 kB (33%) ggc
 phase stream in         :  30.46 (26%) usr   1.02 (16%) sys  31.48 (16%) wall 2944210 kB (67%) ggc
 phase stream out        :   4.52 ( 4%) usr   1.90 (30%) sys  73.47 (38%) wall      12 kB ( 0%) ggc
 phase finalize          :   0.00 ( 0%) usr   0.00 ( 0%) sys   0.02 ( 0%) wall       0 kB ( 0%) ggc
 garbage collection      :   7.01 ( 6%) usr   0.00 ( 0%) sys   6.99 ( 4%) wall       0 kB ( 0%) ggc
 callgraph optimization  :   0.49 ( 0%) usr   0.00 ( 0%) sys   0.50 ( 0%) wall     166 kB ( 0%) ggc
 ipa dead code removal   :   6.98 ( 6%) usr   0.13 ( 2%) sys   6.89 ( 4%) wall       0 kB ( 0%) ggc
 ipa virtual call target :   6.93 ( 6%) usr   0.03 ( 0%) sys   7.20 ( 4%) wall       6 kB ( 0%) ggc
 ipa devirtualization    :   0.27 ( 0%) usr   0.00 ( 0%) sys   0.18 ( 0%) wall   10365 kB ( 0%) ggc
 ipa cp                  :   1.87 ( 2%) usr   0.11 ( 2%) sys   2.00 ( 1%) wall  167204 kB ( 4%) ggc
 ipa inlining heuristics :  17.15 (15%) usr   0.21 ( 3%) sys  17.35 ( 9%) wall  512636 kB (12%) ggc
 ipa comdats             :   0.19 ( 0%) usr   0.00 ( 0%) sys   0.19 ( 0%) wall       0 kB ( 0%) ggc
 ipa lto gimple in       :   5.17 ( 4%) usr   1.04 (17%) sys   6.51 ( 3%) wall  855058 kB (19%) ggc
 ipa lto gimple out      :   0.38 ( 0%) usr   0.08 ( 1%) sys   3.07 ( 2%) wall      12 kB ( 0%) ggc
 ipa lto decl in         :  18.38 (16%) usr   0.56 ( 9%) sys  18.95 (10%) wall 2226088 kB (50%) ggc
 ipa lto decl out        :   3.95 ( 3%) usr   0.08 ( 1%) sys   4.03 ( 2%) wall       0 kB ( 0%) ggc
 ipa lto constructors in :   0.29 ( 0%) usr   0.01 ( 0%) sys   0.29 ( 0%) wall   14389 kB ( 0%) ggc
 ipa lto constructors out:   0.10 ( 0%) usr   0.03 ( 0%) sys   0.58 ( 0%) wall       0 kB ( 0%) ggc
 ipa lto cgraph I/O      :   0.91 ( 1%) usr   0.10 ( 2%) sys   1.02 ( 1%) wall  364151 kB ( 8%) ggc
 ipa lto decl merge      :   2.14 ( 2%) usr   0.00 ( 0%) sys   2.14 ( 1%) wall    8196 kB ( 0%) ggc
 ipa lto cgraph merge    :   1.65 ( 1%) usr   0.01 ( 0%) sys   1.66 ( 1%) wall   12716 kB ( 0%) ggc
 whopr wpa               :   1.81 ( 2%) usr   0.01 ( 0%) sys   1.85 ( 1%) wall       1 kB ( 0%) ggc
 whopr wpa I/O           :   0.05 ( 0%) usr   1.71 (27%) sys  65.75 (34%) wall       0 kB ( 0%) ggc
 whopr partitioning      :   5.05 ( 4%) usr   0.00 ( 0%) sys   5.06 ( 3%) wall    5012 kB ( 0%) ggc
 ipa reference           :   2.13 ( 2%) usr   0.03 ( 0%) sys   2.16 ( 1%) wall       0 kB ( 0%) ggc
 ipa profile             :   0.32 ( 0%) usr   0.01 ( 0%) sys   0.33 ( 0%) wall       0 kB ( 0%) ggc
 ipa pure const          :   2.57 ( 2%) usr   0.00 ( 0%) sys   2.56 ( 1%) wall       0 kB ( 0%) ggc
 ipa icf                 :   6.88 ( 6%) usr   0.08 ( 1%) sys   7.01 ( 4%) wall     855 kB ( 0%) ggc
 tree SSA rewrite        :   0.23 ( 0%) usr   0.06 ( 1%) sys   0.28 ( 0%) wall   33946 kB ( 1%) ggc
 tree SSA incremental    :   0.42 ( 0%) usr   0.05 ( 1%) sys   0.53 ( 0%) wall   21099 kB ( 0%) ggc
 tree operand scan       :   0.47 ( 0%) usr   0.08 ( 1%) sys   0.34 ( 0%) wall  181275 kB ( 4%) ggc
 tree STMT verifier      :   0.00 ( 0%) usr   0.00 ( 0%) sys   0.01 ( 0%) wall       0 kB ( 0%) ggc
 callgraph verifier      :  22.76 (19%) usr   1.68 (27%) sys  24.44 (13%) wall       0 kB ( 0%) ggc
 dominance frontiers     :   0.02 ( 0%) usr   0.01 ( 0%) sys   0.04 ( 0%) wall       0 kB ( 0%) ggc
 dominance computation   :   0.19 ( 0%) usr   0.05 ( 1%) sys   0.25 ( 0%) wall       0 kB ( 0%) ggc
 varconst                :   0.04 ( 0%) usr   0.01 ( 0%) sys   0.05 ( 0%) wall       0 kB ( 0%) ggc
 loop fini               :   0.03 ( 0%) usr   0.00 ( 0%) sys   0.01 ( 0%) wall       0 kB ( 0%) ggc
 unaccounted todo        :   0.82 ( 1%) usr   0.00 ( 0%) sys   0.81 ( 0%) wall       0 kB ( 0%) ggc
 TOTAL                 : 117.68             6.23           191.15            4414612 kB
Extra diagnostic checks enabled; compiler may run slowly.
Configure with --enable-checking=release to disable checks.

> It seems that in this case we reject too many of equality candidates?
> It think the original numbers was about 4-5% but later some equivalences was
> disabled because of devirt/aliasing issues. Do you compare it with gold ICF
> enabled? There are quite few obvious improvements to the analysis that can
> be done, but I guess we need to analyze the interesting cases one by one.

Gold ICF was enabled (-Wl,--icf=all,--icf-iterations=3).

-- 
Markus

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 3/5] IPA ICF pass
  2014-09-27  5:59                 ` Markus Trippelsdorf
@ 2014-09-27  7:47                   ` Markus Trippelsdorf
  2014-09-27 10:46                     ` Martin Liška
  2014-09-27 10:37                   ` Martin Liška
  1 sibling, 1 reply; 70+ messages in thread
From: Markus Trippelsdorf @ 2014-09-27  7:47 UTC (permalink / raw)
  To: Jan Hubicka; +Cc: Martin Liška, gcc-patches

On 2014.09.27 at 07:59 +0200, Markus Trippelsdorf wrote:
> 
> > It seems that in this case we reject too many of equality candidates?
> > It think the original numbers was about 4-5% but later some equivalences was
> > disabled because of devirt/aliasing issues. Do you compare it with gold ICF
> > enabled? There are quite few obvious improvements to the analysis that can
> > be done, but I guess we need to analyze the interesting cases one by one.

Forgot to post the binary size numbers (in bytes):

              | gold's icf off | gold's icf on  |
--------------+----------------+----------------+
gcc's icf off |    79793880    |    74881040    |
--------------+---------------------------------+
gcc's icf on  |    78043608    |    73612800    |
--------------+----------------+----------------+

-- 
Markus

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 3/5] IPA ICF pass
  2014-09-26 23:27               ` Jan Hubicka
  2014-09-27  5:59                 ` Markus Trippelsdorf
@ 2014-09-27 10:32                 ` Martin Liška
  1 sibling, 0 replies; 70+ messages in thread
From: Martin Liška @ 2014-09-27 10:32 UTC (permalink / raw)
  To: gcc-patches

On 09/27/2014 01:27 AM, Jan Hubicka wrote:
>> While a plain Firefox -flto build works fine. LTO/PGO build fails with:
>>
>> lto1: internal compiler error: in ipa_merge_profiles, at ipa-utils.c:540
>> 0x7d6165 ipa_merge_profiles(cgraph_node*, cgraph_node*)
>>         ../../gcc/gcc/ipa-utils.c:540
>> 0xf10c41 ipa_icf::sem_function::merge(ipa_icf::sem_item*)
>>         ../../gcc/gcc/ipa-icf.c:753
>> 0xf15206 ipa_icf::sem_item_optimizer::merge_classes(unsigned int)
>>         ../../gcc/gcc/ipa-icf.c:2706
>> 0xf1c1f4 ipa_icf::sem_item_optimizer::execute()
>>         ../../gcc/gcc/ipa-icf.c:2098
>> 0xf1d3f1 ipa_icf_driver
>>         ../../gcc/gcc/ipa-icf.c:2784
>> 0xf1d3f1 ipa_icf::pass_ipa_icf::execute(function*)
>>         ../../gcc/gcc/ipa-icf.c:2831
>>
>>
>> The pass is also very memory hungry (from 3GB without ICF to 4GB during
>> libxul link), while the code size savings are in the 1% range.


The majority of the problem are groups of candidates that are built according to hash.
The hash value is based on a number of arguments, number of BB, number of gimple statements and types of these statements.
It groups function into classes. In WPA (before a body of any function is loaded) I get following histogram:

Dump after WPA based types groups
Congruence classes: 97204 (unique hash values: 88725), with total: 191457 items
Class size histogram [num of members]: number of classe number of classess
[1]: 86453 classes
[2]: 5680 classes
[3]: 1541 classes
[4]: 915 classes
[5]: 446 classes
[6]: 346 classes
[7]: 200 classes
[8]: 181 classes
[9]: 154 classes
[10]: 109 classes
[11]: 87 classes
[12]: 87 classes
[13]: 68 classes
[14]: 58 classes
[15]: 58 classes
[16]: 41 classes
[17]: 25 classes
[18]: 33 classes
[19]: 28 classes
[20]: 25 classes
[21]: 19 classes
[22]: 30 classes
[23]: 24 classes
[24]: 33 classes
[25]: 17 classes
[26]: 15 classes
[27]: 10 classes
[28]: 13 classes
[29]: 18 classes
[30]: 10 classes

It means that each class with more than one member needs to be iterated and these functions are compared. And yes, there's the root of the problem.
I have to load function body to process deep function comparison. As you can see, we have almost 200k function, where more than half each situated
in a group with more that one member. So that 1GB extra memory usage is caused by these bodies:

Init called for 105004 items (54.84%).

Memory footprint can be significantly reduced if one can load the body and release it and the memory is freed. I asked Honza about it, but it looks
GGC mechanism cannot be easily forced to release it.

> 
> Thnks for checking. I was just thinking about doing that myself.  Would
> you mind posting -ftime-report of firefox WPA stage?
> 
> It seems that in this case we reject too many of equality candidates?
> It think the original numbers was about 4-5% but later some equivalences was
> disabled because of devirt/aliasing issues. Do you compare it with gold ICF
> enabled? There are quite few obvious improvements to the analysis that can
> be done, but I guess we need to analyze the interesting cases one by one.

You are right, the number were quite promising, but during the time, I had to
reduce the "aggressivity" of the pass. As Honza said, it can be improved step-by-step.

> 
> One thing that Martin can try is to hook into lto-symtab and try to check
> that the COMDAT functions that are known to be same pass the equality check.
> I suppose we will learn interesting things this way.
 
Good point, I will try it.

Martin


> I think the patch adds quite important infrastructure for gimple semantic
> equality checking and function merging. I went through the majority of code and
> I think it is mostly ready to mainline (i.e. cleaner than what we have in
> tree-ssa-tailmerge) so hope we can finish the review process next week.
> We will need to get better cost/benefits ratio to enable it for -O2 that is
> someting I would really like to see for 5.0, but it seems to be easier to
> handle this incrementally....

Thank you for the review,
Martin

> 
> Honza
> 

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 3/5] IPA ICF pass
  2014-09-27  5:59                 ` Markus Trippelsdorf
  2014-09-27  7:47                   ` Markus Trippelsdorf
@ 2014-09-27 10:37                   ` Martin Liška
  2014-09-28  2:21                     ` Jan Hubicka
  1 sibling, 1 reply; 70+ messages in thread
From: Martin Liška @ 2014-09-27 10:37 UTC (permalink / raw)
  To: gcc-patches

On 09/27/2014 07:59 AM, Markus Trippelsdorf wrote:
> On 2014.09.27 at 01:27 +0200, Jan Hubicka wrote:
>>> While a plain Firefox -flto build works fine. LTO/PGO build fails with:
>>>
>>> lto1: internal compiler error: in ipa_merge_profiles, at ipa-utils.c:540
>>> 0x7d6165 ipa_merge_profiles(cgraph_node*, cgraph_node*)
>>>         ../../gcc/gcc/ipa-utils.c:540
>>> 0xf10c41 ipa_icf::sem_function::merge(ipa_icf::sem_item*)
>>>         ../../gcc/gcc/ipa-icf.c:753
>>> 0xf15206 ipa_icf::sem_item_optimizer::merge_classes(unsigned int)
>>>         ../../gcc/gcc/ipa-icf.c:2706
>>> 0xf1c1f4 ipa_icf::sem_item_optimizer::execute()
>>>         ../../gcc/gcc/ipa-icf.c:2098
>>> 0xf1d3f1 ipa_icf_driver
>>>         ../../gcc/gcc/ipa-icf.c:2784
>>> 0xf1d3f1 ipa_icf::pass_ipa_icf::execute(function*)
>>>         ../../gcc/gcc/ipa-icf.c:2831
>>>
>>>
>>> The pass is also very memory hungry (from 3GB without ICF to 4GB during
>>> libxul link), while the code size savings are in the 1% range.
>>
>> Thnks for checking. I was just thinking about doing that myself.  Would
>> you mind posting -ftime-report of firefox WPA stage?
> 
> (without ICF)
> Execution times (seconds)
>  phase setup             :   0.00 ( 0%) usr   0.00 ( 0%) sys   0.01 ( 0%) wall    1412 kB ( 0%) ggc
>  phase opt and generate  :  58.38 (63%) usr   2.00 (47%) sys  60.37 (40%) wall  403069 kB (12%) ggc
>  phase stream in         :  30.24 (33%) usr   0.97 (23%) sys  33.90 (22%) wall 2944210 kB (88%) ggc
>  phase stream out        :   4.29 ( 5%) usr   1.32 (31%) sys  57.32 (38%) wall       0 kB ( 0%) ggc
>  phase finalize          :   0.00 ( 0%) usr   0.00 ( 0%) sys   0.13 ( 0%) wall       0 kB ( 0%) ggc
>  garbage collection      :   3.68 ( 4%) usr   0.00 ( 0%) sys   3.68 ( 2%) wall       0 kB ( 0%) ggc
>  callgraph optimization  :   0.50 ( 1%) usr   0.00 ( 0%) sys   0.50 ( 0%) wall     166 kB ( 0%) ggc
>  ipa dead code removal   :   6.91 ( 7%) usr   0.08 ( 2%) sys   7.25 ( 5%) wall       0 kB ( 0%) ggc
>  ipa virtual call target :   7.08 ( 8%) usr   0.04 ( 1%) sys   6.93 ( 5%) wall       0 kB ( 0%) ggc
>  ipa devirtualization    :   0.27 ( 0%) usr   0.00 ( 0%) sys   0.27 ( 0%) wall   10365 kB ( 0%) ggc
>  ipa cp                  :   1.81 ( 2%) usr   0.06 ( 1%) sys   3.40 ( 2%) wall  173701 kB ( 5%) ggc
>  ipa inlining heuristics :  16.60 (18%) usr   0.27 ( 6%) sys  17.48 (12%) wall  532704 kB (16%) ggc
>  ipa comdats             :   0.19 ( 0%) usr   0.00 ( 0%) sys   0.19 ( 0%) wall       0 kB ( 0%) ggc
>  ipa lto gimple out      :   0.21 ( 0%) usr   0.04 ( 1%) sys   0.97 ( 1%) wall       0 kB ( 0%) ggc
>  ipa lto decl in         :  18.29 (20%) usr   0.54 (13%) sys  18.96 (12%) wall 2226088 kB (66%) ggc
>  ipa lto decl out        :   3.93 ( 4%) usr   0.13 ( 3%) sys   4.06 ( 3%) wall       0 kB ( 0%) ggc
>  ipa lto constructors in :   0.24 ( 0%) usr   0.03 ( 1%) sys   0.59 ( 0%) wall   14226 kB ( 0%) ggc
>  ipa lto constructors out:   0.08 ( 0%) usr   0.04 ( 1%) sys   0.15 ( 0%) wall       0 kB ( 0%) ggc
>  ipa lto cgraph I/O      :   0.89 ( 1%) usr   0.12 ( 3%) sys   1.02 ( 1%) wall  364151 kB (11%) ggc
>  ipa lto decl merge      :   2.14 ( 2%) usr   0.01 ( 0%) sys   2.14 ( 1%) wall    8196 kB ( 0%) ggc
>  ipa lto cgraph merge    :   1.59 ( 2%) usr   0.00 ( 0%) sys   1.60 ( 1%) wall   12716 kB ( 0%) ggc
>  whopr wpa               :   1.54 ( 2%) usr   0.03 ( 1%) sys   1.55 ( 1%) wall       1 kB ( 0%) ggc
>  whopr wpa I/O           :   0.04 ( 0%) usr   1.11 (26%) sys  52.10 (34%) wall       0 kB ( 0%) ggc
>  whopr partitioning      :   5.02 ( 5%) usr   0.01 ( 0%) sys   5.03 ( 3%) wall    4938 kB ( 0%) ggc
>  ipa reference           :   2.04 ( 2%) usr   0.02 ( 0%) sys   2.08 ( 1%) wall       0 kB ( 0%) ggc
>  ipa profile             :   0.32 ( 0%) usr   0.00 ( 0%) sys   0.33 ( 0%) wall       0 kB ( 0%) ggc
>  ipa pure const          :   2.43 ( 3%) usr   0.02 ( 0%) sys   2.49 ( 2%) wall       0 kB ( 0%) ggc
>  tree STMT verifier      :   0.01 ( 0%) usr   0.00 ( 0%) sys   0.00 ( 0%) wall       0 kB ( 0%) ggc
>  callgraph verifier      :  16.31 (18%) usr   1.69 (39%) sys  17.96 (12%) wall       0 kB ( 0%) ggc
>  dominance computation   :   0.01 ( 0%) usr   0.00 ( 0%) sys   0.02 ( 0%) wall       0 kB ( 0%) ggc
>  varconst                :   0.01 ( 0%) usr   0.03 ( 1%) sys   0.05 ( 0%) wall       0 kB ( 0%) ggc
>  unaccounted todo        :   0.69 ( 1%) usr   0.00 ( 0%) sys   0.69 ( 0%) wall       0 kB ( 0%) ggc
>  TOTAL                 :  92.91             4.29           151.73            3348693 kB
> Extra diagnostic checks enabled; compiler may run slowly.
> Configure with --enable-checking=release to disable checks.
> 
> (with ICF)
> Execution times (seconds)
>  phase setup             :   0.00 ( 0%) usr   0.00 ( 0%) sys   0.01 ( 0%) wall    1412 kB ( 0%) ggc
>  phase opt and generate  :  82.70 (70%) usr   3.31 (53%) sys  86.17 (45%) wall 1468975 kB (33%) ggc
>  phase stream in         :  30.46 (26%) usr   1.02 (16%) sys  31.48 (16%) wall 2944210 kB (67%) ggc
>  phase stream out        :   4.52 ( 4%) usr   1.90 (30%) sys  73.47 (38%) wall      12 kB ( 0%) ggc
>  phase finalize          :   0.00 ( 0%) usr   0.00 ( 0%) sys   0.02 ( 0%) wall       0 kB ( 0%) ggc
>  garbage collection      :   7.01 ( 6%) usr   0.00 ( 0%) sys   6.99 ( 4%) wall       0 kB ( 0%) ggc
>  callgraph optimization  :   0.49 ( 0%) usr   0.00 ( 0%) sys   0.50 ( 0%) wall     166 kB ( 0%) ggc
>  ipa dead code removal   :   6.98 ( 6%) usr   0.13 ( 2%) sys   6.89 ( 4%) wall       0 kB ( 0%) ggc
>  ipa virtual call target :   6.93 ( 6%) usr   0.03 ( 0%) sys   7.20 ( 4%) wall       6 kB ( 0%) ggc
>  ipa devirtualization    :   0.27 ( 0%) usr   0.00 ( 0%) sys   0.18 ( 0%) wall   10365 kB ( 0%) ggc
>  ipa cp                  :   1.87 ( 2%) usr   0.11 ( 2%) sys   2.00 ( 1%) wall  167204 kB ( 4%) ggc
>  ipa inlining heuristics :  17.15 (15%) usr   0.21 ( 3%) sys  17.35 ( 9%) wall  512636 kB (12%) ggc
>  ipa comdats             :   0.19 ( 0%) usr   0.00 ( 0%) sys   0.19 ( 0%) wall       0 kB ( 0%) ggc
>  ipa lto gimple in       :   5.17 ( 4%) usr   1.04 (17%) sys   6.51 ( 3%) wall  855058 kB (19%) ggc
>  ipa lto gimple out      :   0.38 ( 0%) usr   0.08 ( 1%) sys   3.07 ( 2%) wall      12 kB ( 0%) ggc
>  ipa lto decl in         :  18.38 (16%) usr   0.56 ( 9%) sys  18.95 (10%) wall 2226088 kB (50%) ggc
>  ipa lto decl out        :   3.95 ( 3%) usr   0.08 ( 1%) sys   4.03 ( 2%) wall       0 kB ( 0%) ggc
>  ipa lto constructors in :   0.29 ( 0%) usr   0.01 ( 0%) sys   0.29 ( 0%) wall   14389 kB ( 0%) ggc
>  ipa lto constructors out:   0.10 ( 0%) usr   0.03 ( 0%) sys   0.58 ( 0%) wall       0 kB ( 0%) ggc
>  ipa lto cgraph I/O      :   0.91 ( 1%) usr   0.10 ( 2%) sys   1.02 ( 1%) wall  364151 kB ( 8%) ggc
>  ipa lto decl merge      :   2.14 ( 2%) usr   0.00 ( 0%) sys   2.14 ( 1%) wall    8196 kB ( 0%) ggc
>  ipa lto cgraph merge    :   1.65 ( 1%) usr   0.01 ( 0%) sys   1.66 ( 1%) wall   12716 kB ( 0%) ggc
>  whopr wpa               :   1.81 ( 2%) usr   0.01 ( 0%) sys   1.85 ( 1%) wall       1 kB ( 0%) ggc
>  whopr wpa I/O           :   0.05 ( 0%) usr   1.71 (27%) sys  65.75 (34%) wall       0 kB ( 0%) ggc
>  whopr partitioning      :   5.05 ( 4%) usr   0.00 ( 0%) sys   5.06 ( 3%) wall    5012 kB ( 0%) ggc
>  ipa reference           :   2.13 ( 2%) usr   0.03 ( 0%) sys   2.16 ( 1%) wall       0 kB ( 0%) ggc
>  ipa profile             :   0.32 ( 0%) usr   0.01 ( 0%) sys   0.33 ( 0%) wall       0 kB ( 0%) ggc
>  ipa pure const          :   2.57 ( 2%) usr   0.00 ( 0%) sys   2.56 ( 1%) wall       0 kB ( 0%) ggc
>  ipa icf                 :   6.88 ( 6%) usr   0.08 ( 1%) sys   7.01 ( 4%) wall     855 kB ( 0%) ggc
>  tree SSA rewrite        :   0.23 ( 0%) usr   0.06 ( 1%) sys   0.28 ( 0%) wall   33946 kB ( 1%) ggc
>  tree SSA incremental    :   0.42 ( 0%) usr   0.05 ( 1%) sys   0.53 ( 0%) wall   21099 kB ( 0%) ggc
>  tree operand scan       :   0.47 ( 0%) usr   0.08 ( 1%) sys   0.34 ( 0%) wall  181275 kB ( 4%) ggc
>  tree STMT verifier      :   0.00 ( 0%) usr   0.00 ( 0%) sys   0.01 ( 0%) wall       0 kB ( 0%) ggc
>  callgraph verifier      :  22.76 (19%) usr   1.68 (27%) sys  24.44 (13%) wall       0 kB ( 0%) ggc
>  dominance frontiers     :   0.02 ( 0%) usr   0.01 ( 0%) sys   0.04 ( 0%) wall       0 kB ( 0%) ggc
>  dominance computation   :   0.19 ( 0%) usr   0.05 ( 1%) sys   0.25 ( 0%) wall       0 kB ( 0%) ggc
>  varconst                :   0.04 ( 0%) usr   0.01 ( 0%) sys   0.05 ( 0%) wall       0 kB ( 0%) ggc
>  loop fini               :   0.03 ( 0%) usr   0.00 ( 0%) sys   0.01 ( 0%) wall       0 kB ( 0%) ggc
>  unaccounted todo        :   0.82 ( 1%) usr   0.00 ( 0%) sys   0.81 ( 0%) wall       0 kB ( 0%) ggc
>  TOTAL                 : 117.68             6.23           191.15            4414612 kB
> Extra diagnostic checks enabled; compiler may run slowly.
> Configure with --enable-checking=release to disable checks.
> 
>> It seems that in this case we reject too many of equality candidates?
>> It think the original numbers was about 4-5% but later some equivalences was
>> disabled because of devirt/aliasing issues. Do you compare it with gold ICF
>> enabled? There are quite few obvious improvements to the analysis that can
>> be done, but I guess we need to analyze the interesting cases one by one.
> 
> Gold ICF was enabled (-Wl,--icf=all,--icf-iterations=3).
> 

Hi.

Thank you Markus for presenting numbers, it corresponds with I measured. If I see correctly, IPA ICF pass takes about 7 seconds,
the rest is distributed in verifier (not interesting for release version of the compiler) and 'phase opt and generate'. No idea
what can make the difference?

Martin

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 3/5] IPA ICF pass
  2014-09-27  7:47                   ` Markus Trippelsdorf
@ 2014-09-27 10:46                     ` Martin Liška
  0 siblings, 0 replies; 70+ messages in thread
From: Martin Liška @ 2014-09-27 10:46 UTC (permalink / raw)
  To: gcc-patches

On 09/27/2014 09:47 AM, Markus Trippelsdorf wrote:
> On 2014.09.27 at 07:59 +0200, Markus Trippelsdorf wrote:
>>
>>> It seems that in this case we reject too many of equality candidates?
>>> It think the original numbers was about 4-5% but later some equivalences was
>>> disabled because of devirt/aliasing issues. Do you compare it with gold ICF
>>> enabled? There are quite few obvious improvements to the analysis that can
>>> be done, but I guess we need to analyze the interesting cases one by one.
> 
> Forgot to post the binary size numbers (in bytes):
> 
>               | gold's icf off | gold's icf on  |
> --------------+----------------+----------------+
> gcc's icf off |    79793880    |    74881040    |
> --------------+---------------------------------+
> gcc's icf on  |    78043608    |    73612800    |
> --------------+----------------+----------------+
> 

Thanks once more!

Gold ICF is quite strong, I will verify what functions are not caught by IPA ICF.
These data present that IPA ICF can reduce the binary by 2.19%. I know that it's quite a small improvement,
but if you realize that the pass can reduce just the size of .text (and slightly related sections). There are
stats about libxul.so (please ignore last 3 columns):

Section name               Start   Size in B            Size    Portion Disk read in B   Disk read   Sec. portion
                               0           0          0.00 B      0.00%              0      0.00 B          0.00%
.note.gnu.build-i            512          36         36.00 B      0.00%              0      0.00 B          0.00%
.dynsym                      552       81192        79.29 KB      0.08%              0      0.00 B          0.00%
.dynstr                    81744       90859        88.73 KB      0.09%              0      0.00 B          0.00%
.hash                     172608       21752        21.24 KB      0.02%              0      0.00 B          0.00%
.gnu.version              194360        6766         6.61 KB      0.01%              0      0.00 B          0.00%
.gnu.version_d            201128          56         56.00 B      0.00%              0      0.00 B          0.00%
.gnu.version_r            201184        1216         1.19 KB      0.00%              0      0.00 B          0.00%
.rela.dyn                 202400     8198208         7.82 MB      8.56%              0      0.00 B          0.00%
.rela.plt                8400608       70272        68.62 KB      0.07%              0      0.00 B          0.00%
.init                    8470880          26         26.00 B      0.00%              0      0.00 B          0.00%
.plt                     8470912       46864        45.77 KB      0.05%              0      0.00 B          0.00%
.text                    8517776    39014333        37.21 MB     40.72%              0      0.00 B          0.00%
.fini                   47532112           9          9.00 B      0.00%              0      0.00 B          0.00%
.rodata                 47532288    15258560        14.55 MB     15.93%              0      0.00 B          0.00%
.eh_frame               62790848     6203564         5.92 MB      6.47%              0      0.00 B          0.00%
.eh_frame_hdr           68994412     1088012         1.04 MB      1.14%              0      0.00 B          0.00%
.tbss                   70082560           4          4.00 B      0.00%              0      0.00 B          0.00%
.dynamic                70082560        1104         1.08 KB      0.00%              0      0.00 B          0.00%
.got                    70083664        1384         1.35 KB      0.00%              0      0.00 B          0.00%
.got.plt                70085048       23448        22.90 KB      0.02%              0      0.00 B          0.00%
.data                   70108544      811616       792.59 KB      0.85%              0      0.00 B          0.00%
.jcr                    70920160           8          8.00 B      0.00%              0      0.00 B          0.00%
.tm_clone_table         70920168           0          0.00 B      0.00%              0      0.00 B          0.00%
.fini_array             70920168           8          8.00 B      0.00%              0      0.00 B          0.00%
.init_array             70920176          16         16.00 B      0.00%              0      0.00 B          0.00%
.data.rel.ro.loca       70920192     3938880         3.76 MB      4.11%              0      0.00 B          0.00%
.data.rel.ro            74859072      269216       262.91 KB      0.28%              0      0.00 B          0.00%
.bss                    75128320     1844246         1.76 MB      1.92%              0      0.00 B          0.00%
.debug_line             75128288         517        517.00 B      0.00%              0      0.00 B          0.00%
.debug_info             75128805         817        817.00 B      0.00%              0      0.00 B          0.00%
.debug_abbrev           75129622         438        438.00 B      0.00%              0      0.00 B          0.00%
.debug_aranges          75130064         224        224.00 B      0.00%              0      0.00 B          0.00%
.debug_ranges           75130288         128        128.00 B      0.00%              0      0.00 B          0.00%
.comment                75130416          42         42.00 B      0.00%              0      0.00 B          0.00%
.debug_loc              75130458         304        304.00 B      0.00%              0      0.00 B          0.00%
.debug_str              75130762         653        653.00 B      0.00%              0      0.00 B          0.00%
.note.gnu.gold-ve       75131416          28         28.00 B      0.00%              0      0.00 B          0.00%
.symtab                 75131448     6170112         5.88 MB      6.44%              0      0.00 B          0.00%
.strtab                 81301560    12666771        12.08 MB     13.22%              0      0.00 B          0.00%
.shstrtab               93968331         419        419.00 B      0.00%              0      0.00 B          0.00%
                                    95812108        91.37 MB                         0      0.00 B          0.00%


Martin

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 3/5] IPA ICF pass
  2014-09-27 10:37                   ` Martin Liška
@ 2014-09-28  2:21                     ` Jan Hubicka
  2014-10-10 23:54                       ` Fakturace
  2014-10-11  0:02                       ` Martin Liška
  0 siblings, 2 replies; 70+ messages in thread
From: Jan Hubicka @ 2014-09-28  2:21 UTC (permalink / raw)
  To: Martin Liška; +Cc: gcc-patches

> 
> Hi.
> 
> Thank you Markus for presenting numbers, it corresponds with I measured. If I see correctly, IPA ICF pass takes about 7 seconds,
> the rest is distributed in verifier (not interesting for release version of the compiler) and 'phase opt and generate'. No idea
> what can make the difference?

phase opt and generate just combine all the optimization times together, so it
is same 7 seconds as in the ICF pass :)
1GB of function bodies just to elimnate 2-3% of code seems quite alot. Do you
have any idea how many of those turns out to be different?
It would be nice to be able to release the duplicate bodies from memory after
the equivalency was stablished....

Honza

> 
> Martin

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 3/5] IPA ICF pass
  2014-09-28  2:21                     ` Jan Hubicka
@ 2014-10-10 23:54                       ` Fakturace
  2014-10-11  0:02                       ` Martin Liška
  1 sibling, 0 replies; 70+ messages in thread
From: Fakturace @ 2014-10-10 23:54 UTC (permalink / raw)
  To: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 5408 bytes --]

On 09/28/2014 04:20 AM, Jan Hubicka wrote:
>>
>> Hi.
>>
>> Thank you Markus for presenting numbers, it corresponds with I measured. If I see correctly, IPA ICF pass takes about 7 seconds,
>> the rest is distributed in verifier (not interesting for release version of the compiler) and 'phase opt and generate'. No idea
>> what can make the difference?
>
> phase opt and generate just combine all the optimization times together, so it
> is same 7 seconds as in the ICF pass :)
> 1GB of function bodies just to elimnate 2-3% of code seems quite alot. Do you
> have any idea how many of those turns out to be different?
> It would be nice to be able to release the duplicate bodies from memory after
> the equivalency was stablished....
>
> Honza
>
>>
>> Martin

Hello.

After few days of measurement and tuning, I was able to get numbers to the following shape:
Execution times (seconds)
 phase setup             :   0.00 ( 0%) usr   0.00 ( 0%) sys   0.00 ( 0%) wall    1412 kB ( 0%) ggc
 phase opt and generate  :  27.83 (59%) usr   0.66 (19%) sys  28.52 (37%) wall 1028813 kB (24%) ggc
 phase stream in         :  16.90 (36%) usr   0.63 (18%) sys  17.60 (23%) wall 3246453 kB (76%) ggc
 phase stream out        :   2.76 ( 6%) usr   2.19 (63%) sys  31.34 (40%) wall       2 kB ( 0%) ggc
 callgraph optimization  :   0.36 ( 1%) usr   0.00 ( 0%) sys   0.35 ( 0%) wall      40 kB ( 0%) ggc
 ipa dead code removal   :   3.31 ( 7%) usr   0.01 ( 0%) sys   3.25 ( 4%) wall       0 kB ( 0%) ggc
 ipa virtual call target :   3.69 ( 8%) usr   0.03 ( 1%) sys   3.80 ( 5%) wall      21 kB ( 0%) ggc
 ipa devirtualization    :   0.12 ( 0%) usr   0.00 ( 0%) sys   0.15 ( 0%) wall   13704 kB ( 0%) ggc
 ipa cp                  :   1.11 ( 2%) usr   0.07 ( 2%) sys   1.17 ( 2%) wall  188558 kB ( 4%) ggc
 ipa inlining heuristics :   8.17 (17%) usr   0.14 ( 4%) sys   8.27 (11%) wall  494738 kB (12%) ggc
 ipa comdats             :   0.12 ( 0%) usr   0.00 ( 0%) sys   0.12 ( 0%) wall       0 kB ( 0%) ggc
 ipa lto gimple in       :   1.86 ( 4%) usr   0.40 (11%) sys   2.20 ( 3%) wall  537970 kB (13%) ggc
 ipa lto gimple out      :   0.19 ( 0%) usr   0.08 ( 2%) sys   0.27 ( 0%) wall       2 kB ( 0%) ggc
 ipa lto decl in         :  12.20 (26%) usr   0.37 (11%) sys  12.64 (16%) wall 2441687 kB (57%) ggc
 ipa lto decl out        :   2.51 ( 5%) usr   0.21 ( 6%) sys   2.71 ( 3%) wall       0 kB ( 0%) ggc
 ipa lto constructors in :   0.13 ( 0%) usr   0.02 ( 1%) sys   0.17 ( 0%) wall   15692 kB ( 0%) ggc
 ipa lto constructors out:   0.03 ( 0%) usr   0.00 ( 0%) sys   0.03 ( 0%) wall       0 kB ( 0%) ggc
 ipa lto cgraph I/O      :   0.54 ( 1%) usr   0.09 ( 3%) sys   0.63 ( 1%) wall  407182 kB (10%) ggc
 ipa lto decl merge      :   1.34 ( 3%) usr   0.00 ( 0%) sys   1.34 ( 2%) wall    8220 kB ( 0%) ggc
 ipa lto cgraph merge    :   1.00 ( 2%) usr   0.00 ( 0%) sys   1.00 ( 1%) wall   14605 kB ( 0%) ggc
 whopr wpa               :   0.92 ( 2%) usr   0.00 ( 0%) sys   0.89 ( 1%) wall       1 kB ( 0%) ggc
 whopr wpa I/O           :   0.01 ( 0%) usr   1.90 (55%) sys  28.31 (37%) wall       0 kB ( 0%) ggc
 whopr partitioning      :   2.81 ( 6%) usr   0.01 ( 0%) sys   2.83 ( 4%) wall    4943 kB ( 0%) ggc
 ipa reference           :   1.34 ( 3%) usr   0.00 ( 0%) sys   1.35 ( 2%) wall       0 kB ( 0%) ggc
 ipa profile             :   0.20 ( 0%) usr   0.01 ( 0%) sys   0.21 ( 0%) wall       0 kB ( 0%) ggc
 ipa pure const          :   1.62 ( 3%) usr   0.00 ( 0%) sys   1.63 ( 2%) wall       0 kB ( 0%) ggc
 ipa icf                 :   2.65 ( 6%) usr   0.02 ( 1%) sys   2.68 ( 3%) wall    1352 kB ( 0%) ggc
 inline parameters       :   0.00 ( 0%) usr   0.01 ( 0%) sys   0.00 ( 0%) wall       0 kB ( 0%) ggc
 tree SSA rewrite        :   0.11 ( 0%) usr   0.01 ( 0%) sys   0.08 ( 0%) wall   18919 kB ( 0%) ggc
 tree SSA other          :   0.01 ( 0%) usr   0.00 ( 0%) sys   0.01 ( 0%) wall       0 kB ( 0%) ggc
 tree SSA incremental    :   0.24 ( 1%) usr   0.01 ( 0%) sys   0.32 ( 0%) wall   11325 kB ( 0%) ggc
 tree operand scan       :   0.15 ( 0%) usr   0.02 ( 1%) sys   0.18 ( 0%) wall  116283 kB ( 3%) ggc
 dominance frontiers     :   0.01 ( 0%) usr   0.00 ( 0%) sys   0.02 ( 0%) wall       0 kB ( 0%) ggc
 dominance computation   :   0.13 ( 0%) usr   0.01 ( 0%) sys   0.16 ( 0%) wall       0 kB ( 0%) ggc
 varconst                :   0.01 ( 0%) usr   0.02 ( 1%) sys   0.01 ( 0%) wall       0 kB ( 0%) ggc
 loop fini               :   0.02 ( 0%) usr   0.00 ( 0%) sys   0.04 ( 0%) wall       0 kB ( 0%) ggc
 unaccounted todo        :   0.55 ( 1%) usr   0.00 ( 0%) sys   0.56 ( 1%) wall       0 kB ( 0%) ggc
 TOTAL                 :  47.49             3.48            77.46            4276682 kB

and I was able to reduce function bodies loaded in WPA to 35% (from previous 55%). The main problem
with speed was hidden in work list for congruence classes, where hash_set was used. I chose the data
structure to support delete operation, but it was really slow. Thus, hash_set was replaced with linked list
and a flag is used to identify if a set is removed or not.

I have no clue who complicated can it be to implement release_body function to an operation that
really releases the memory?

Markus' problem with -fprofile-use has been removed, IPA-ICF is preceding devirtualization pass. I hope it is fine?

There's new version of the patch and I plan to comment both Honza's emails where he pointed some nits.

Thanks,
Martin


[-- Attachment #2: ipa-icf-3.patch --]
[-- Type: text/x-patch, Size: 122464 bytes --]

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index b38f8ef..f2580ba 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1265,6 +1265,8 @@ OBJS = \
 	ipa-profile.o \
 	ipa-prop.o \
 	ipa-pure-const.o \
+	ipa-icf.o \
+	ipa-icf-gimple.o \
 	ipa-reference.o \
 	ipa-ref.o \
 	ipa-utils.o \
diff --git a/gcc/cgraph.c b/gcc/cgraph.c
index fdcaf79..07e7ecb 100644
--- a/gcc/cgraph.c
+++ b/gcc/cgraph.c
@@ -1913,6 +1913,8 @@ cgraph_node::dump (FILE *f)
     fprintf (f, " only_called_at_exit");
   if (tm_clone)
     fprintf (f, " tm_clone");
+  if (icf_merged)
+    fprintf (f, " icf_merged");
   if (DECL_STATIC_CONSTRUCTOR (decl))
     fprintf (f," static_constructor (priority:%i)", get_init_priority ());
   if (DECL_STATIC_DESTRUCTOR (decl))
@@ -2561,6 +2563,7 @@ verify_edge_corresponds_to_fndecl (cgraph_edge *e, tree decl)
   if (!node
       || node->body_removed
       || node->in_other_partition
+      || node->icf_merged
       || e->callee->in_other_partition)
     return false;
 
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index fb41b01..2de98b4 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -172,6 +172,12 @@ public:
   /* Dump referring in list to FILE.  */
   void dump_referring (FILE *);
 
+  /* Get number of references for this node.  */
+  inline unsigned get_references_count (void)
+  {
+    return ref_list.references ? ref_list.references->length () : 0;
+  }
+
   /* Iterates I-th reference in the list, REF is also set.  */
   ipa_ref *iterate_reference (unsigned i, ipa_ref *&ref);
 
@@ -1230,6 +1236,8 @@ public:
   /* True if this decl calls a COMDAT-local function.  This is set up in
      compute_inline_parameters and inline_call.  */
   unsigned calls_comdat_local : 1;
+  /* True if node has been created by merge operation in IPA-ICF.  */
+  unsigned icf_merged: 1;
 };
 
 /* A cgraph node set is a collection of cgraph nodes.  A cgraph node
diff --git a/gcc/common.opt b/gcc/common.opt
index b4f0ed4..5db5e1e 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -1443,6 +1443,18 @@ fipa-pure-const
 Common Report Var(flag_ipa_pure_const) Init(0) Optimization
 Discover pure and const functions
 
+fipa-icf
+Common Report Var(flag_ipa_icf) Optimization
+Perform Identical Code Folding for functions and read-only variables
+
+fipa-icf-functions
+Common Report Var(flag_ipa_icf_functions) Optimization
+Perform Identical Code Folding for functions
+
+fipa-icf-variables
+Common Report Var(flag_ipa_icf_variables) Optimization
+Perform Identical Code Folding for variables
+
 fipa-reference
 Common Report Var(flag_ipa_reference) Init(0) Optimization
 Discover readonly and non addressable static variables
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 5fe7e15..38356e4 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -382,7 +382,7 @@ Objective-C and Objective-C++ Dialects}.
 -fif-conversion2 -findirect-inlining @gol
 -finline-functions -finline-functions-called-once -finline-limit=@var{n} @gol
 -finline-small-functions -fipa-cp -fipa-cp-clone @gol
--fipa-pta -fipa-profile -fipa-pure-const -fipa-reference @gol
+-fipa-pta -fipa-profile -fipa-pure-const -fipa-reference -fipa-icf @gol
 -fira-algorithm=@var{algorithm} @gol
 -fira-region=@var{region} -fira-hoist-pressure @gol
 -fira-loop-pressure -fno-ira-share-save-slots @gol
@@ -7123,6 +7123,7 @@ also turns on the following optimization flags:
 -findirect-inlining @gol
 -fipa-cp @gol
 -fipa-sra @gol
+-fipa-icf @gol
 -fisolate-erroneous-paths-dereference @gol
 -foptimize-sibling-calls @gol
 -foptimize-strlen @gol
@@ -8068,6 +8069,19 @@ it may significantly increase code size
 (see @option{--param ipcp-unit-growth=@var{value}}).
 This flag is enabled by default at @option{-O3}.
 
+@item -fipa-icf
+@opindex fipa-icf
+Perform Identical Code Folding for functions and read-only variables.
+The optimization reduces code size and may disturb unwind stacks by replacing
+a function by equivalent one with a different name. The optimization works
+more effectively with link time optimization enabled.
+
+Nevertheless the behavior is similar to Gold Linker ICF optimization, GCC ICF
+works on different levels and thus the optimizations are not same - there are
+equivalences that are found only by GCC and equivalences found only by Gold.
+
+This flag is enabled by default at @option{-O2}.
+
 @item -fisolate-erroneous-paths-dereference
 Detect paths which trigger erroneous or undefined behaviour due to
 dereferencing a NULL pointer.  Isolate those paths from the main control
diff --git a/gcc/ipa-icf-gimple.c b/gcc/ipa-icf-gimple.c
new file mode 100644
index 0000000..2f036fe
--- /dev/null
+++ b/gcc/ipa-icf-gimple.c
@@ -0,0 +1,889 @@
+/* Interprocedural Identical Code Folding pass
+   Copyright (C) 2014 Free Software Foundation, Inc.
+
+   Contributed by Jan Hubicka <hubicka@ucw.cz> and Martin Liska <mliska@suse.cz>
+
+This file is part of GCC.
+
+GCC 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, or (at your option) any later
+version.
+
+GCC 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 GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tree.h"
+#include "basic-block.h"
+#include "tree-ssa-alias.h"
+#include "internal-fn.h"
+#include "gimple-expr.h"
+#include "is-a.h"
+#include "gimple.h"
+#include "expr.h"
+#include "gimple-iterator.h"
+#include "gimple-ssa.h"
+#include "tree-cfg.h"
+#include "stringpool.h"
+#include "tree-dfa.h"
+#include "tree-pass.h"
+#include "gimple-pretty-print.h"
+#include "cfgloop.h"
+#include "except.h"
+#include "data-streamer.h"
+#include "ipa-utils.h"
+#include <list>
+#include "tree-ssanames.h"
+#include "tree-eh.h"
+
+#include "ipa-icf-gimple.h"
+#include "ipa-icf.h"
+
+namespace ipa_icf_gimple {
+
+/* Initialize internal structures for a given SOURCE_FUNC_DECL and
+   TARGET_FUNC_DECL. Strict polymorphic comparison is processed if
+   an option COMPARE_POLYMORPHIC is true. For special cases, one can
+   set IGNORE_LABELS to skip label comparison.
+   Similarly, IGNORE_SOURCE_DECLS and IGNORE_TARGET_DECLS are sets
+   of declarations that can be skipped.  */
+
+func_checker::func_checker (tree source_func_decl, tree target_func_decl,
+			    bool compare_polymorphic,
+			    bool ignore_labels,
+			    hash_set<symtab_node *> *ignored_source_nodes,
+			    hash_set<symtab_node *> *ignored_target_nodes)
+  : m_source_func_decl (source_func_decl), m_target_func_decl (target_func_decl),
+    m_ignored_source_nodes (ignored_source_nodes),
+    m_ignored_target_nodes (ignored_target_nodes),
+    m_compare_polymorphic (compare_polymorphic),
+    m_ignore_labels (ignore_labels)
+{
+  function *source_func = DECL_STRUCT_FUNCTION (source_func_decl);
+  function *target_func = DECL_STRUCT_FUNCTION (target_func_decl);
+
+  unsigned ssa_source = SSANAMES (source_func)->length ();
+  unsigned ssa_target = SSANAMES (target_func)->length ();
+
+  m_source_ssa_names.create (ssa_source);
+  m_target_ssa_names.create (ssa_target);
+
+  for (unsigned i = 0; i < ssa_source; i++)
+    m_source_ssa_names.safe_push (-1);
+
+  for (unsigned i = 0; i < ssa_target; i++)
+    m_target_ssa_names.safe_push (-1);
+}
+
+/* Memory release routine.  */
+
+func_checker::~func_checker ()
+{
+  m_source_ssa_names.release();
+  m_target_ssa_names.release();
+}
+
+/* Verifies that trees T1 and T2 are equivalent from perspective of ICF.  */
+
+bool
+func_checker::compare_ssa_name (tree t1, tree t2)
+{
+  unsigned i1 = SSA_NAME_VERSION (t1);
+  unsigned i2 = SSA_NAME_VERSION (t2);
+
+  if (m_source_ssa_names[i1] == -1)
+    m_source_ssa_names[i1] = i2;
+  else if (m_source_ssa_names[i1] != (int) i2)
+    return false;
+
+  if(m_target_ssa_names[i2] == -1)
+    m_target_ssa_names[i2] = i1;
+  else if (m_target_ssa_names[i2] != (int) i1)
+    return false;
+
+  return true;
+}
+
+/* Verification function for edges E1 and E2.  */
+
+bool
+func_checker::compare_edge (edge e1, edge e2)
+{
+  if (e1->flags != e2->flags)
+    return false;
+
+  bool existed_p;
+
+  edge &slot = m_edge_map.get_or_insert (e1, &existed_p);
+  if (existed_p)
+    return return_with_debug (slot == e2);
+  else
+    slot = e2;
+
+  /* TODO: filter edge probabilities for profile feedback match.  */
+
+  return true;
+}
+
+/* Verification function for declaration trees T1 and T2 that
+   come from functions FUNC1 and FUNC2.  */
+
+bool
+func_checker::compare_decl (tree t1, tree t2)
+{
+  if (!auto_var_in_fn_p (t1, m_source_func_decl)
+      || !auto_var_in_fn_p (t2, m_target_func_decl))
+    return return_with_debug (t1 == t2);
+
+  tree_code t = TREE_CODE (t1);
+  if ((t == VAR_DECL || t == PARM_DECL || t == RESULT_DECL)
+      && DECL_BY_REFERENCE (t1) != DECL_BY_REFERENCE (t2))
+    return return_false_with_msg ("DECL_BY_REFERENCE flags are different");
+
+  if (!compatible_types_p (TREE_TYPE (t1), TREE_TYPE (t2),
+			   m_compare_polymorphic))
+    return return_false ();
+
+  bool existed_p;
+
+  tree &slot = m_decl_map.get_or_insert (t1, &existed_p);
+  if (existed_p)
+    return return_with_debug (slot == t2);
+  else
+    slot = t2;
+
+  return true;
+}
+
+/* Return true if types are compatible from perspective of ICF.  */
+bool func_checker::compatible_types_p (tree t1, tree t2,
+				       bool compare_polymorphic,
+				       bool first_argument)
+{
+  if (TREE_CODE (t1) != TREE_CODE (t2))
+    return return_false_with_msg ("different tree types");
+
+  if (!types_compatible_p (t1, t2))
+    return return_false_with_msg ("types are not compatible");
+
+  if (get_alias_set (t1) != get_alias_set (t2))
+    return return_false_with_msg ("alias sets are different");
+
+  /* We call contains_polymorphic_type_p with this pointer type.  */
+  if (first_argument && TREE_CODE (t1) == POINTER_TYPE)
+    {
+      t1 = TREE_TYPE (t1);
+      t2 = TREE_TYPE (t2);
+    }
+
+  if (compare_polymorphic)
+    if (contains_polymorphic_type_p (t1) || contains_polymorphic_type_p (t2))
+      {
+	if (!contains_polymorphic_type_p (t1) || !contains_polymorphic_type_p (t2))
+	  return return_false_with_msg ("one type is not polymorphic");
+
+	if (!types_must_be_same_for_odr (t1, t2))
+	  return return_false_with_msg ("types are not same for ODR");
+      }
+
+  return true;
+}
+
+
+
+/* Function responsible for comparison of handled components T1 and T2.
+   If these components, from functions FUNC1 and FUNC2, are equal, true
+   is returned.  */
+
+bool
+func_checker::compare_operand (tree t1, tree t2)
+{
+  tree base1, base2, x1, x2, y1, y2, z1, z2;
+  HOST_WIDE_INT offset1 = 0, offset2 = 0;
+  bool ret;
+
+  if (!t1 && !t2)
+    return true;
+  else if (!t1 || !t2)
+    return false;
+
+  tree tt1 = TREE_TYPE (t1);
+  tree tt2 = TREE_TYPE (t2);
+
+  if (!func_checker::compatible_types_p (tt1, tt2))
+    return false;
+
+  base1 = get_addr_base_and_unit_offset (t1, &offset1);
+  base2 = get_addr_base_and_unit_offset (t2, &offset2);
+
+  if (base1 && base2)
+    {
+      if (offset1 != offset2)
+	return return_false_with_msg ("base offsets are different");
+
+      t1 = base1;
+      t2 = base2;
+    }
+
+  if (TREE_CODE (t1) != TREE_CODE (t2))
+    return return_false ();
+
+  switch (TREE_CODE (t1))
+    {
+    case CONSTRUCTOR:
+      {
+	unsigned length1 = vec_safe_length (CONSTRUCTOR_ELTS (t1));
+	unsigned length2 = vec_safe_length (CONSTRUCTOR_ELTS (t2));
+
+	if (length1 != length2)
+	  return return_false ();
+
+	for (unsigned i = 0; i < length1; i++)
+	  if (!compare_operand (CONSTRUCTOR_ELT (t1, i)->value,
+				CONSTRUCTOR_ELT (t2, i)->value))
+	    return return_false();
+
+	return true;
+      }
+    case ARRAY_REF:
+    case ARRAY_RANGE_REF:
+      {
+	x1 = TREE_OPERAND (t1, 0);
+	x2 = TREE_OPERAND (t2, 0);
+	y1 = TREE_OPERAND (t1, 1);
+	y2 = TREE_OPERAND (t2, 1);
+
+	if (!compare_operand (array_ref_low_bound (t1),
+			      array_ref_low_bound (t2)))
+	  return return_false_with_msg ("");
+	if (!compare_operand (array_ref_element_size (t1),
+			      array_ref_element_size (t2)))
+	  return return_false_with_msg ("");
+	if (!compare_operand (x1, x2))
+	  return return_false_with_msg ("");
+	return compare_operand (y1, y2);
+      }
+
+    case MEM_REF:
+      {
+	x1 = TREE_OPERAND (t1, 0);
+	x2 = TREE_OPERAND (t2, 0);
+	y1 = TREE_OPERAND (t1, 1);
+	y2 = TREE_OPERAND (t2, 1);
+
+	/* See if operand is an memory access (the test originate from
+	 gimple_load_p).
+
+	In this case the alias set of the function being replaced must
+	be subset of the alias set of the other function.  At the moment
+	we seek for equivalency classes, so simply require inclussion in
+	both directions.  */
+
+	if (!func_checker::compatible_types_p (TREE_TYPE (x1), TREE_TYPE (x2)))
+	  return return_false ();
+
+	if (!compare_operand (x1, x2))
+	  return return_false_with_msg ("");
+
+	if (get_alias_set (TREE_TYPE (y1)) != get_alias_set (TREE_TYPE (y2)))
+	  return return_false_with_msg ("alias set for MEM_REF offsets are different");
+
+	ao_ref r1, r2;
+	ao_ref_init (&r1, t1);
+	ao_ref_init (&r2, t2);
+	if (ao_ref_alias_set (&r1) != ao_ref_alias_set (&r2)
+	    || ao_ref_base_alias_set (&r1) != ao_ref_base_alias_set (&r2))
+	  return return_false_with_msg ("ao alias sets are different");
+
+	/* Type of the offset on MEM_REF does not matter.  */
+	return wi::to_offset  (y1) == wi::to_offset  (y2);
+      }
+    case COMPONENT_REF:
+      {
+	x1 = TREE_OPERAND (t1, 0);
+	x2 = TREE_OPERAND (t2, 0);
+	y1 = TREE_OPERAND (t1, 1);
+	y2 = TREE_OPERAND (t2, 1);
+
+	ret = compare_operand (x1, x2)
+	      && compare_operand (y1, y2);
+
+	return return_with_debug (ret);
+      }
+    /* Virtual table call.  */
+    case OBJ_TYPE_REF:
+      {
+	x1 = TREE_OPERAND (t1, 0);
+	x2 = TREE_OPERAND (t2, 0);
+	y1 = TREE_OPERAND (t1, 1);
+	y2 = TREE_OPERAND (t2, 1);
+	z1 = TREE_OPERAND (t1, 2);
+	z2 = TREE_OPERAND (t2, 2);
+
+	ret = compare_operand (x1, x2)
+	      && compare_operand (y1, y2)
+	      && compare_operand (z1, z2);
+
+	return return_with_debug (ret);
+      }
+    case ADDR_EXPR:
+      {
+	x1 = TREE_OPERAND (t1, 0);
+	x2 = TREE_OPERAND (t2, 0);
+
+	ret = compare_operand (x1, x2);
+	return return_with_debug (ret);
+      }
+    case SSA_NAME:
+      {
+	ret = compare_ssa_name (t1, t2);
+
+	if (!ret)
+	  return return_with_debug (ret);
+
+	if (SSA_NAME_IS_DEFAULT_DEF (t1))
+	  {
+	    tree b1 = SSA_NAME_VAR (t1);
+	    tree b2 = SSA_NAME_VAR (t2);
+
+	    if (b1 == NULL && b2 == NULL)
+	      return true;
+
+	    if (b1 == NULL || b2 == NULL || TREE_CODE (b1) != TREE_CODE (b2))
+	      return return_false ();
+
+	    switch (TREE_CODE (b1))
+	      {
+	      case VAR_DECL:
+		return return_with_debug (compare_variable_decl (t1, t2));
+	      case PARM_DECL:
+	      case RESULT_DECL:
+		ret = compare_decl (b1, b2);
+		return return_with_debug (ret);
+	      default:
+		return return_false_with_msg ("Unknown TREE code reached");
+	      }
+	  }
+	else
+	  return true;
+      }
+    case INTEGER_CST:
+      {
+	ret = compatible_types_p (TREE_TYPE (t1), TREE_TYPE (t2))
+	      && wi::to_offset  (t1) == wi::to_offset  (t2);
+
+	return return_with_debug (ret);
+      }
+    case COMPLEX_CST:
+    case VECTOR_CST:
+    case STRING_CST:
+    case REAL_CST:
+      {
+	ret = operand_equal_p (t1, t2, OEP_ONLY_CONST);
+	return return_with_debug (ret);
+      }
+    case FUNCTION_DECL:
+      {
+	ret = compare_function_decl (t1, t2);
+	return return_with_debug (ret);
+      }
+    case VAR_DECL:
+      return return_with_debug (compare_variable_decl (t1, t2));
+    case FIELD_DECL:
+      {
+	tree offset1 = DECL_FIELD_OFFSET (t1);
+	tree offset2 = DECL_FIELD_OFFSET (t2);
+
+	tree bit_offset1 = DECL_FIELD_BIT_OFFSET (t1);
+	tree bit_offset2 = DECL_FIELD_BIT_OFFSET (t2);
+
+	ret = compare_operand (offset1, offset2)
+	      && compare_operand (bit_offset1, bit_offset2);
+
+	return return_with_debug (ret);
+      }
+    case LABEL_DECL:
+      {
+	int *bb1 = m_label_bb_map.get (t1);
+	int *bb2 = m_label_bb_map.get (t2);
+
+	return return_with_debug (*bb1 == *bb2);
+      }
+    case PARM_DECL:
+    case RESULT_DECL:
+    case CONST_DECL:
+    case BIT_FIELD_REF:
+      {
+	ret = compare_decl (t1, t2);
+	return return_with_debug (ret);
+      }
+    default:
+      return return_false_with_msg ("Unknown TREE code reached");
+    }
+}
+
+
+/* Verifies that trees T1 and T2, representing function declarations
+   are equivalent from perspective of ICF.  */
+
+bool
+func_checker::compare_function_decl (tree t1, tree t2)
+{
+  bool ret = false;
+
+  if (t1 == t2)
+    return true;
+
+  symtab_node *n1 = symtab_node::get (t1);
+  symtab_node *n2 = symtab_node::get (t2);
+
+  if (m_ignored_source_nodes != NULL && m_ignored_target_nodes != NULL)
+    {
+      ret = m_ignored_source_nodes->contains (n1)
+	    && m_ignored_target_nodes->contains (n2);
+
+      if (ret)
+	return true;
+    }
+
+  /* If function decl is WEAKREF, we compare targets.  */
+  cgraph_node *f1 = cgraph_node::get (t1);
+  cgraph_node *f2 = cgraph_node::get (t2);
+
+  if(f1 && f2 && f1->weakref && f2->weakref)
+    ret = f1->alias_target == f2->alias_target;
+
+  return ret;
+}
+
+/* Verifies that trees T1 and T2 do correspond.  */
+
+bool
+func_checker::compare_variable_decl (tree t1, tree t2)
+{
+  bool ret = false;
+
+  if (t1 == t2)
+    return true;
+
+  if (TREE_CODE (t1) == VAR_DECL && (DECL_EXTERNAL (t1) || TREE_STATIC (t1)))
+    {
+      symtab_node *n1 = symtab_node::get (t1);
+      symtab_node *n2 = symtab_node::get (t2);
+
+      if (m_ignored_source_nodes != NULL && m_ignored_target_nodes != NULL)
+	{
+	  ret = m_ignored_source_nodes->contains (n1)
+		&& m_ignored_target_nodes->contains (n2);
+
+	  if (ret)
+	    return true;
+	}
+    }
+  ret = compare_decl (t1, t2);
+
+  return return_with_debug (ret);
+}
+
+void
+func_checker::parse_labels (sem_bb *bb)
+{
+  for (gimple_stmt_iterator gsi = gsi_start_bb (bb->bb); !gsi_end_p (gsi);
+       gsi_next (&gsi))
+    {
+      gimple stmt = gsi_stmt (gsi);
+
+      if (gimple_code (stmt) == GIMPLE_LABEL)
+	{
+	  tree t = gimple_label_label (stmt);
+	  gcc_assert (TREE_CODE (t) == LABEL_DECL);
+
+	  m_label_bb_map.put (t, bb->bb->index);
+	}
+    }
+}
+
+/* Basic block equivalence comparison function that returns true if
+   basic blocks BB1 and BB2 (from functions FUNC1 and FUNC2) correspond.
+
+   In general, a collection of equivalence dictionaries is built for types
+   like SSA names, declarations (VAR_DECL, PARM_DECL, ..). This infrastructure
+   is utilized by every statement-by-stament comparison function.  */
+
+bool
+func_checker::compare_bb (sem_bb *bb1, sem_bb *bb2)
+{
+  unsigned i;
+  gimple_stmt_iterator gsi1, gsi2;
+  gimple s1, s2;
+
+  if (bb1->nondbg_stmt_count != bb2->nondbg_stmt_count
+      || bb1->edge_count != bb2->edge_count)
+    return return_false ();
+
+  gsi1 = gsi_start_bb (bb1->bb);
+  gsi2 = gsi_start_bb (bb2->bb);
+
+  for (i = 0; i < bb1->nondbg_stmt_count; i++)
+    {
+      if (is_gimple_debug (gsi_stmt (gsi1)))
+	gsi_next_nondebug (&gsi1);
+
+      if (is_gimple_debug (gsi_stmt (gsi2)))
+	gsi_next_nondebug (&gsi2);
+
+      s1 = gsi_stmt (gsi1);
+      s2 = gsi_stmt (gsi2);
+
+      int eh1 = lookup_stmt_eh_lp_fn
+		(DECL_STRUCT_FUNCTION (m_source_func_decl), s1);
+      int eh2 = lookup_stmt_eh_lp_fn
+		(DECL_STRUCT_FUNCTION (m_target_func_decl), s2);
+
+      if (eh1 != eh2)
+	return return_false_with_msg ("EH regions are different");
+
+      if (gimple_code (s1) != gimple_code (s2))
+	return return_false_with_msg ("gimple codes are different");
+
+      switch (gimple_code (s1))
+	{
+	case GIMPLE_CALL:
+	  if (!compare_gimple_call (s1, s2))
+	    return return_different_stmts (s1, s2, "GIMPLE_CALL");
+	  break;
+	case GIMPLE_ASSIGN:
+	  if (!compare_gimple_assign (s1, s2))
+	    return return_different_stmts (s1, s2, "GIMPLE_ASSIGN");
+	  break;
+	case GIMPLE_COND:
+	  if (!compare_gimple_cond (s1, s2))
+	    return return_different_stmts (s1, s2, "GIMPLE_COND");
+	  break;
+	case GIMPLE_SWITCH:
+	  if (!compare_gimple_switch (s1, s2))
+	    return return_different_stmts (s1, s2, "GIMPLE_SWITCH");
+	  break;
+	case GIMPLE_DEBUG:
+	case GIMPLE_EH_DISPATCH:
+	  break;
+	case GIMPLE_RESX:
+	  if (!compare_gimple_resx (s1, s2))
+	    return return_different_stmts (s1, s2, "GIMPLE_RESX");
+	  break;
+	case GIMPLE_LABEL:
+	  if (!compare_gimple_label (s1, s2))
+	    return return_different_stmts (s1, s2, "GIMPLE_LABEL");
+	  break;
+	case GIMPLE_RETURN:
+	  if (!compare_gimple_return (s1, s2))
+	    return return_different_stmts (s1, s2, "GIMPLE_RETURN");
+	  break;
+	case GIMPLE_GOTO:
+	  if (!compare_gimple_goto (s1, s2))
+	    return return_different_stmts (s1, s2, "GIMPLE_GOTO");
+	  break;
+	case GIMPLE_ASM:
+	  if (!compare_gimple_asm (s1, s2))
+	    return return_different_stmts (s1, s2, "GIMPLE_ASM");
+	  break;
+	case GIMPLE_PREDICT:
+	case GIMPLE_NOP:
+	  return true;
+	default:
+	  return return_false_with_msg ("Unknown GIMPLE code reached");
+	}
+
+      gsi_next (&gsi1);
+      gsi_next (&gsi2);
+    }
+
+  return true;
+}
+
+/* Verifies for given GIMPLEs S1 and S2 that
+   call statements are semantically equivalent.  */
+
+bool
+func_checker::compare_gimple_call (gimple s1, gimple s2)
+{
+  unsigned i;
+  tree t1, t2;
+
+  if (gimple_call_num_args (s1) != gimple_call_num_args (s2))
+    return false;
+
+  t1 = gimple_call_fndecl (s1);
+  t2 = gimple_call_fndecl (s2);
+
+  /* Function pointer variables are not supported yet.  */
+  if (t1 == NULL || t2 == NULL)
+    {
+      if (!compare_operand (t1, t2))
+	return return_false();
+    }
+  else if (!compare_function_decl (t1, t2))
+    return false;
+
+  /* Checking of argument.  */
+  for (i = 0; i < gimple_call_num_args (s1); ++i)
+    {
+      t1 = gimple_call_arg (s1, i);
+      t2 = gimple_call_arg (s2, i);
+
+      if (!compare_operand (t1, t2))
+	return false;
+    }
+
+  /* Return value checking.  */
+  t1 = gimple_get_lhs (s1);
+  t2 = gimple_get_lhs (s2);
+
+  return compare_operand (t1, t2);
+}
+
+
+/* Verifies for given GIMPLEs S1 and S2 that
+   assignment statements are semantically equivalent.  */
+
+bool
+func_checker::compare_gimple_assign (gimple s1, gimple s2)
+{
+  tree arg1, arg2;
+  tree_code code1, code2;
+  unsigned i;
+
+  code1 = gimple_expr_code (s1);
+  code2 = gimple_expr_code (s2);
+
+  if (code1 != code2)
+    return false;
+
+  code1 = gimple_assign_rhs_code (s1);
+  code2 = gimple_assign_rhs_code (s2);
+
+  if (code1 != code2)
+    return false;
+
+  for (i = 0; i < gimple_num_ops (s1); i++)
+    {
+      arg1 = gimple_op (s1, i);
+      arg2 = gimple_op (s2, i);
+
+      if (!compare_operand (arg1, arg2))
+	return false;
+    }
+
+
+  return true;
+}
+
+/* Verifies for given GIMPLEs S1 and S2 that
+   condition statements are semantically equivalent.  */
+
+bool
+func_checker::compare_gimple_cond (gimple s1, gimple s2)
+{
+  tree t1, t2;
+  tree_code code1, code2;
+
+  code1 = gimple_expr_code (s1);
+  code2 = gimple_expr_code (s2);
+
+  if (code1 != code2)
+    return false;
+
+  t1 = gimple_cond_lhs (s1);
+  t2 = gimple_cond_lhs (s2);
+
+  if (!compare_operand (t1, t2))
+    return false;
+
+  t1 = gimple_cond_rhs (s1);
+  t2 = gimple_cond_rhs (s2);
+
+  return compare_operand (t1, t2);
+}
+
+/* Verifies that tree labels T1 and T2 correspond in FUNC1 and FUNC2.  */
+
+bool
+func_checker::compare_tree_ssa_label (tree t1, tree t2)
+{
+  return compare_operand (t1, t2);
+}
+
+/* Verifies for given GIMPLEs S1 and S2 that
+   label statements are semantically equivalent.  */
+
+bool
+func_checker::compare_gimple_label (gimple g1, gimple g2)
+{
+  if (m_ignore_labels)
+    return true;
+
+  tree t1 = gimple_label_label (g1);
+  tree t2 = gimple_label_label (g2);
+
+  return compare_tree_ssa_label (t1, t2);
+}
+
+/* Verifies for given GIMPLEs S1 and S2 that
+   switch statements are semantically equivalent.  */
+
+bool
+func_checker::compare_gimple_switch (gimple g1, gimple g2)
+{
+  unsigned lsize1, lsize2, i;
+
+  lsize1 = gimple_switch_num_labels (g1);
+  lsize2 = gimple_switch_num_labels (g2);
+
+  if (lsize1 != lsize2)
+    return false;
+
+  tree t1 = gimple_switch_index (g1);
+  tree t2 = gimple_switch_index (g2);
+
+  if (TREE_CODE (t1) != SSA_NAME || TREE_CODE(t2) != SSA_NAME)
+    gcc_unreachable ();
+
+  if (!compare_operand (t1, t2))
+    return false;
+
+  for (i = 0; i < lsize1; i++)
+    {
+      tree label1 = gimple_switch_label (g1, i);
+      tree label2 = gimple_switch_label (g2, i);
+
+      if (TREE_CODE (label1) == CASE_LABEL_EXPR
+	  && TREE_CODE (label2) == CASE_LABEL_EXPR)
+	{
+	  label1 = CASE_LABEL (label1);
+	  label2 = CASE_LABEL (label2);
+
+	  if (!compare_operand (label1, label2))
+	    return return_false_with_msg ("switch label_exprs are different");
+	}
+      else if (!tree_int_cst_equal (label1, label2))
+	return return_false_with_msg ("switch labels are different");
+    }
+
+  return true;
+}
+
+/* Verifies for given GIMPLEs S1 and S2 that
+   return statements are semantically equivalent.  */
+
+bool
+func_checker::compare_gimple_return (gimple g1, gimple g2)
+{
+  tree t1, t2;
+
+  t1 = gimple_return_retval (g1);
+  t2 = gimple_return_retval (g2);
+
+  /* Void return type.  */
+  if (t1 == NULL && t2 == NULL)
+    return true;
+  else
+    return compare_operand (t1, t2);
+}
+
+/* Verifies for given GIMPLEs S1 and S2 that
+   goto statements are semantically equivalent.  */
+
+bool
+func_checker::compare_gimple_goto (gimple g1, gimple g2)
+{
+  tree dest1, dest2;
+
+  dest1 = gimple_goto_dest (g1);
+  dest2 = gimple_goto_dest (g2);
+
+  if (TREE_CODE (dest1) != TREE_CODE (dest2) || TREE_CODE (dest1) != SSA_NAME)
+    return false;
+
+  return compare_operand (dest1, dest2);
+}
+
+/* Verifies for given GIMPLEs S1 and S2 that
+   resx statements are semantically equivalent.  */
+
+bool
+func_checker::compare_gimple_resx (gimple g1, gimple g2)
+{
+  return gimple_resx_region (g1) == gimple_resx_region (g2);
+}
+
+/* Verifies for given GIMPLEs S1 and S2 that ASM statements are equivalent.
+   For the beginning, the pass only supports equality for
+   '__asm__ __volatile__ ("", "", "", "memory")'.  */
+
+bool
+func_checker::compare_gimple_asm (gimple g1, gimple g2)
+{
+  if (gimple_asm_volatile_p (g1) != gimple_asm_volatile_p (g2))
+    return false;
+
+  if (gimple_asm_ninputs (g1) != gimple_asm_ninputs (g2))
+    return false;
+
+  if (gimple_asm_noutputs (g1) != gimple_asm_noutputs (g2))
+    return false;
+
+  if (gimple_asm_nlabels (g1) != gimple_asm_nlabels (g2))
+    return false;
+
+  if (gimple_asm_nclobbers (g1) != gimple_asm_nclobbers (g2))
+    return false;
+
+  for (unsigned i = 0; i < gimple_asm_ninputs (g1); i++)
+    {
+      tree input1 = TREE_VALUE (gimple_asm_input_op (g1, i));
+      tree input2 = TREE_VALUE (gimple_asm_input_op (g2, i));
+
+      if (!compare_operand (input1, input2))
+	return return_false_with_msg ("ASM input is different");
+    }
+
+  for (unsigned i = 0; i < gimple_asm_noutputs (g1); i++)
+    {
+      tree output1 = TREE_VALUE (gimple_asm_output_op (g1, i));
+      tree output2 = TREE_VALUE (gimple_asm_output_op (g2, i));
+
+      if (!compare_operand (output1, output2))
+	return return_false_with_msg ("ASM output is different");
+    }
+
+  for (unsigned i = 0; i < gimple_asm_nlabels (g1); i++)
+    {
+      tree label1 = TREE_VALUE (gimple_asm_label_op (g1, i));
+      tree label2 = TREE_VALUE (gimple_asm_label_op (g2, i));
+
+      if (!compare_operand (label1, label2))
+	return return_false_with_msg ("ASM label is different");
+    }
+
+  for (unsigned i = 0; i < gimple_asm_nclobbers (g1); i++)
+    {
+      tree clobber1 = TREE_VALUE (gimple_asm_clobber_op (g1, i));
+      tree clobber2 = TREE_VALUE (gimple_asm_clobber_op (g2, i));
+
+      if (!operand_equal_p (clobber1, clobber2, OEP_ONLY_CONST))
+	return return_false_with_msg ("ASM clobber is different");
+    }
+
+  return true;
+}
+
+} // ipa_icf_gimple namespace
diff --git a/gcc/ipa-icf-gimple.h b/gcc/ipa-icf-gimple.h
new file mode 100644
index 0000000..456edb6
--- /dev/null
+++ b/gcc/ipa-icf-gimple.h
@@ -0,0 +1,251 @@
+/* Interprocedural semantic function equality pass
+   Copyright (C) 2014 Free Software Foundation, Inc.
+
+   Contributed by Jan Hubicka <hubicka@ucw.cz> and Martin Liska <mliska@suse.cz>
+
+This file is part of GCC.
+
+GCC 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, or (at your option) any later
+version.
+
+GCC 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 GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+/* Prints string STRING to a FILE with a given number of SPACE_COUNT.  */
+#define FPUTS_SPACES(file, space_count, string) \
+  fprintf (file, "%*s" string, space_count, " ");
+
+/* fprintf function wrapper that transforms given FORMAT to follow given
+   number for SPACE_COUNT and call fprintf for a FILE.  */
+#define FPRINTF_SPACES(file, space_count, format, ...) \
+  fprintf (file, "%*s" format, space_count, " ", ##__VA_ARGS__);
+
+/* Prints a MESSAGE to dump_file if exists. FUNC is name of function and
+   LINE is location in the source file.  */
+
+static inline void
+dump_message_1 (const char *message, const char *func, unsigned int line)
+{
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    fprintf (dump_file, "  debug message: %s (%s:%u)\n", message, func, line);
+}
+
+/* Prints a MESSAGE to dump_file if exists.  */
+#define dump_message(message) dump_message_1 (message, __func__, __LINE__)
+
+/* Logs a MESSAGE to dump_file if exists and returns false. FUNC is name
+   of function and LINE is location in the source file.  */
+
+static inline bool
+return_false_with_message_1 (const char *message, const char *func,
+			     unsigned int line)
+{
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    fprintf (dump_file, "  false returned: '%s' (%s:%u)\n", message, func, line);
+  return false;
+}
+
+/* Logs a MESSAGE to dump_file if exists and returns false.  */
+#define return_false_with_msg(message) \
+  return_false_with_message_1 (message, __func__, __LINE__)
+
+/* Return false and log that false value is returned.  */
+#define return_false() return_false_with_msg ("")
+
+/* Logs return value if RESULT is false. FUNC is name of function and LINE
+   is location in the source file.  */
+
+static inline bool
+return_with_result (bool result, const char *func, unsigned int line)
+{
+  if (!result && dump_file && (dump_flags & TDF_DETAILS))
+    fprintf (dump_file, "  false returned (%s:%u)\n", func, line);
+
+  return result;
+}
+
+/* Logs return value if RESULT is false.  */
+#define return_with_debug(result) return_with_result (result, __func__, __LINE__)
+
+/* Verbose logging function logging statements S1 and S2 of a CODE.
+   FUNC is name of function and LINE is location in the source file.  */
+
+static inline bool
+return_different_stmts_1 (gimple s1, gimple s2, const char *code,
+			  const char *func, unsigned int line)
+{
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    {
+      fprintf (dump_file, "  different statement for code: %s (%s:%u):\n",
+	       code, func, line);
+
+      print_gimple_stmt (dump_file, s1, 3, TDF_DETAILS);
+      print_gimple_stmt (dump_file, s2, 3, TDF_DETAILS);
+    }
+
+  return false;
+}
+
+/* Verbose logging function logging statements S1 and S2 of a CODE.  */
+#define return_different_stmts(s1, s2, code) \
+  return_different_stmts_1 (s1, s2, code, __func__, __LINE__)
+
+namespace ipa_icf_gimple {
+
+/* Basic block struct for semantic equality pass.  */
+class sem_bb
+{
+public:
+  sem_bb (basic_block bb_, unsigned nondbg_stmt_count_, unsigned edge_count_):
+    bb (bb_), nondbg_stmt_count (nondbg_stmt_count_), edge_count (edge_count_) {}
+
+  /* Basic block the structure belongs to.  */
+  basic_block bb;
+
+  /* Number of non-debug statements in the basic block.  */
+  unsigned nondbg_stmt_count;
+
+  /* Number of edges connected to the block.  */
+  unsigned edge_count;
+};
+
+/* A class aggregating all connections and semantic equivalents
+   for a given pair of semantic function candidates.  */
+class func_checker
+{
+public:
+  /* Initialize internal structures for a given SOURCE_FUNC_DECL and
+     TARGET_FUNC_DECL. Strict polymorphic comparison is processed if
+     an option COMPARE_POLYMORPHIC is true. For special cases, one can
+     set IGNORE_LABELS to skip label comparison.
+     Similarly, IGNORE_SOURCE_DECLS and IGNORE_TARGET_DECLS are sets
+     of declarations that can be skipped.  */
+  func_checker (tree source_func_decl, tree target_func_decl,
+		bool compare_polymorphic,
+		bool ignore_labels = false,
+		hash_set<symtab_node *> *ignored_source_nodes = NULL,
+		hash_set<symtab_node *> *ignored_target_nodes = NULL);
+
+  /* Memory release routine.  */
+  ~func_checker();
+
+  void parse_labels (sem_bb *bb);
+
+  /* Basic block equivalence comparison function that returns true if
+     basic blocks BB1 and BB2 correspond.  */
+  bool compare_bb (sem_bb *bb1, sem_bb *bb2);
+
+  /* Verifies that trees T1 and T2 are equivalent from perspective of ICF.  */
+  bool compare_ssa_name (tree t1, tree t2);
+
+  /* Verification function for edges E1 and E2.  */
+  bool compare_edge (edge e1, edge e2);
+
+  /* Verifies for given GIMPLEs S1 and S2 that
+     call statements are semantically equivalent.  */
+  bool compare_gimple_call (gimple s1, gimple s2);
+
+  /* Verifies for given GIMPLEs S1 and S2 that
+     assignment statements are semantically equivalent.  */
+  bool compare_gimple_assign (gimple s1, gimple s2);
+
+  /* Verifies for given GIMPLEs S1 and S2 that
+     condition statements are semantically equivalent.  */
+  bool compare_gimple_cond (gimple s1, gimple s2);
+
+  /* Verifies for given GIMPLEs S1 and S2 that
+     label statements are semantically equivalent.  */
+  bool compare_gimple_label (gimple s1, gimple s2);
+
+  /* Verifies for given GIMPLEs S1 and S2 that
+     switch statements are semantically equivalent.  */
+  bool compare_gimple_switch (gimple s1, gimple s2);
+
+  /* Verifies for given GIMPLEs S1 and S2 that
+     return statements are semantically equivalent.  */
+  bool compare_gimple_return (gimple s1, gimple s2);
+
+  /* Verifies for given GIMPLEs S1 and S2 that
+     goto statements are semantically equivalent.  */
+  bool compare_gimple_goto (gimple s1, gimple s2);
+
+  /* Verifies for given GIMPLEs S1 and S2 that
+     resx statements are semantically equivalent.  */
+  bool compare_gimple_resx (gimple s1, gimple s2);
+
+  /* Verifies for given GIMPLEs S1 and S2 that ASM statements are equivalent.
+     For the beginning, the pass only supports equality for
+     '__asm__ __volatile__ ("", "", "", "memory")'.  */
+  bool compare_gimple_asm (gimple s1, gimple s2);
+
+  /* Verification function for declaration trees T1 and T2.  */
+  bool compare_decl (tree t1, tree t2);
+
+  /* Verifies that tree labels T1 and T2 correspond.  */
+  bool compare_tree_ssa_label (tree t1, tree t2);
+
+  /* Function compares two operands T1 and T2 and returns true if these
+     two trees are semantically equivalent.  */
+  bool compare_operand (tree t1, tree t2);
+
+  /* Verifies that trees T1 and T2, representing function declarations
+     are equivalent from perspective of ICF.  */
+  bool compare_function_decl (tree t1, tree t2);
+
+  /* Verifies that trees T1 and T2 do correspond.  */
+  bool compare_variable_decl (tree t1, tree t2);
+
+  /* Return true if types are compatible from perspective of ICF.
+     FIRST_ARGUMENT indicates if the comparison is called for
+     first parameter of a function.  */
+  static bool compatible_types_p (tree t1, tree t2,
+				  bool compare_polymorphic = true,
+				  bool first_argument = false);
+
+
+private:
+  /* Vector mapping source SSA names to target ones.  */
+  vec <int> m_source_ssa_names;
+
+  /* Vector mapping target SSA names to source ones.  */
+  vec <int> m_target_ssa_names;
+
+  /* Source TREE function declaration.  */
+  tree m_source_func_decl;
+
+  /* Target TREE function declaration.  */
+  tree m_target_func_decl;
+
+  /* Source symbol nodes that should be skipped by
+     declaration comparison.  */
+  hash_set<symtab_node *> *m_ignored_source_nodes;
+
+  /* Target symbol nodes that should be skipped by
+     declaration comparison.  */
+  hash_set<symtab_node *> *m_ignored_target_nodes;
+
+  /* Source to target edge map.  */
+  hash_map <edge, edge> m_edge_map;
+
+  /* Source to target declaration map.  */
+  hash_map <tree, tree> m_decl_map;
+
+  /* Label to basic block index mapping.  */
+  hash_map <tree, int> m_label_bb_map;
+
+  /* Flag if polymorphic comparison should be executed.  */
+  bool m_compare_polymorphic;
+
+  /* Flag if ignore labels in comparison.  */
+  bool m_ignore_labels;
+};
+
+} // ipa_icf_gimple namespace
diff --git a/gcc/ipa-icf.c b/gcc/ipa-icf.c
new file mode 100644
index 0000000..5184210
--- /dev/null
+++ b/gcc/ipa-icf.c
@@ -0,0 +1,2353 @@
+/* Interprocedural Identical Code Folding pass
+   Copyright (C) 2014 Free Software Foundation, Inc.
+
+   Contributed by Jan Hubicka <hubicka@ucw.cz> and Martin Liska <mliska@suse.cz>
+
+This file is part of GCC.
+
+GCC 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, or (at your option) any later
+version.
+
+GCC 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 GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+/* Interprocedural Identical Code Folding for functions and
+   read-only variables.
+
+   The goal of this transformation is to discover functions and read-only
+   variables which do have exactly the same semantics.
+
+   In case of functions,
+   we could either create a virtual clone or do a simple function wrapper
+   that will call equivalent function. If the function is just locally visible,
+   all function calls can be redirected. For read-only variables, we create
+   aliases if possible.
+
+   Optimization pass arranges as follows:
+   1) All functions and read-only variables are visited and internal
+      data structure, either sem_function or sem_variables is created.
+   2) For every symbol from the previous step, VAR_DECL and FUNCTION_DECL are
+      saved and matched to corresponding sem_items.
+   3) These declaration are ignored for equality check and are solved
+      by Value Numbering algorithm published by Alpert, Zadeck in 1992.
+   4) We compute hash value for each symbol.
+   5) Congruence classes are created based on hash value. If hash value are
+      equal, equals function is called and symbols are deeply compared.
+      We must prove that all SSA names, declarations and other items
+      correspond.
+   6) Value Numbering is executed for these classes. At the end of the process
+      all symbol members in remaining classes can be merged.
+   7) Merge operation creates alias in case of read-only variables. For
+      callgraph node, we must decide if we can redirect local calls,
+      create an alias or a thunk.
+
+*/
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tree.h"
+#include "basic-block.h"
+#include "tree-ssa-alias.h"
+#include "internal-fn.h"
+#include "gimple-expr.h"
+#include "is-a.h"
+#include "gimple.h"
+#include "expr.h"
+#include "gimple-iterator.h"
+#include "gimple-ssa.h"
+#include "tree-cfg.h"
+#include "tree-phinodes.h"
+#include "stringpool.h"
+#include "tree-ssanames.h"
+#include "tree-dfa.h"
+#include "tree-pass.h"
+#include "gimple-pretty-print.h"
+#include "ipa-inline.h"
+#include "cfgloop.h"
+#include "except.h"
+#include "hash-table.h"
+#include "coverage.h"
+#include "attribs.h"
+#include "print-tree.h"
+#include "lto-streamer.h"
+#include "data-streamer.h"
+#include "ipa-utils.h"
+#include <list>
+#include "ipa-icf-gimple.h"
+#include "ipa-icf.h"
+
+using namespace ipa_icf_gimple;
+
+namespace ipa_icf {
+/* Constructor for key value pair, where _ITEM is key and _INDEX is a target.  */
+
+sem_usage_pair::sem_usage_pair (sem_item *_item, unsigned int _index):
+  item (_item), index (_index)
+{
+}
+
+/* Semantic item constructor for a node of _TYPE, where STACK is used
+   for bitmap memory allocation.  */
+
+sem_item::sem_item (sem_item_type _type,
+		    bitmap_obstack *stack): type(_type), hash(0)
+{
+  setup (stack);
+}
+
+/* Semantic item constructor for a node of _TYPE, where STACK is used
+   for bitmap memory allocation. The item is based on symtab node _NODE
+   with computed _HASH.  */
+
+sem_item::sem_item (sem_item_type _type, symtab_node *_node,
+		    hashval_t _hash, bitmap_obstack *stack): type(_type),
+  node (_node), hash (_hash)
+{
+  decl = node->decl;
+  setup (stack);
+}
+
+/* Add reference to a semantic TARGET.  */
+
+void
+sem_item::add_reference (sem_item *target)
+{
+  refs.safe_push (target);
+  unsigned index = refs.length ();
+  target->usages.safe_push (new sem_usage_pair(this, index));
+  bitmap_set_bit (target->usage_index_bitmap, index);
+  refs_set.add (target->node);
+}
+
+/* Initialize internal data structures. Bitmap STACK is used for
+   bitmap memory allocation process.  */
+
+void
+sem_item::setup (bitmap_obstack *stack)
+{
+  gcc_checking_assert (node);
+
+  refs.create (0);
+  tree_refs.create (0);
+  usages.create (0);
+  usage_index_bitmap = BITMAP_ALLOC (stack);
+}
+
+sem_item::~sem_item ()
+{
+  for (unsigned i = 0; i < usages.length (); i++)
+    delete usages[i];
+
+  refs.release ();
+  tree_refs.release ();
+  usages.release ();
+
+  BITMAP_FREE (usage_index_bitmap);
+}
+
+/* Dump function for debugging purpose.  */
+
+DEBUG_FUNCTION void
+sem_item::dump (void)
+{
+  if (dump_file)
+    {
+      fprintf (dump_file, "[%s] %s (%u) (tree:%p)\n", type == FUNC ? "func" : "var",
+	       name(), node->order, (void *) node->decl);
+      fprintf (dump_file, "  hash: %u\n", get_hash ());
+      fprintf (dump_file, "  references: ");
+
+      for (unsigned i = 0; i < refs.length (); i++)
+	fprintf (dump_file, "%s%s ", refs[i]->name (),
+		 i < refs.length() - 1 ? "," : "");
+
+      fprintf (dump_file, "\n");
+    }
+}
+
+/* Semantic function constructor that uses STACK as bitmap memory stack.  */
+
+sem_function::sem_function (bitmap_obstack *stack): sem_item (FUNC, stack),
+  m_checker (NULL), m_compared_func (NULL)
+{
+  arg_types.create (0);
+  bb_sizes.create (0);
+  bb_sorted.create (0);
+}
+
+/*  Constructor based on callgraph node _NODE with computed hash _HASH.
+    Bitmap STACK is used for memory allocation.  */
+sem_function::sem_function (cgraph_node *node, hashval_t hash,
+			    bitmap_obstack *stack):
+  sem_item (FUNC, node, hash, stack),
+  m_checker (NULL), m_compared_func (NULL)
+{
+  arg_types.create (0);
+  bb_sizes.create (0);
+  bb_sorted.create (0);
+}
+
+sem_function::~sem_function ()
+{
+  for (unsigned i = 0; i < bb_sorted.length (); i++)
+    free (bb_sorted[i]);
+
+  arg_types.release ();
+  bb_sizes.release ();
+  bb_sorted.release ();
+}
+
+/* Calculates hash value based on a BASIC_BLOCK.  */
+
+hashval_t
+sem_function::get_bb_hash (const sem_bb *basic_block)
+{
+  inchash::hash hstate;
+
+  hstate.add_int (basic_block->nondbg_stmt_count);
+  hstate.add_int (basic_block->edge_count);
+
+  return hstate.end ();
+}
+
+/* References independent hash function.  */
+
+hashval_t
+sem_function::get_hash (void)
+{
+  if(!hash)
+    {
+      inchash::hash hstate;
+      hstate.add_int (177454); /* Random number for function type.  */
+
+      hstate.add_int (arg_count);
+      hstate.add_int (cfg_checksum);
+      hstate.add_int (gcode_hash);
+
+      for (unsigned i = 0; i < bb_sorted.length (); i++)
+	hstate.merge_hash (get_bb_hash (bb_sorted[i]));
+
+      for (unsigned i = 0; i < bb_sizes.length (); i++)
+	hstate.add_int (bb_sizes[i]);
+
+      hash = hstate.end ();
+    }
+
+  return hash;
+}
+
+/* For a given symbol table nodes N1 and N2, we check that FUNCTION_DECLs
+   point to a same function. Comparison can be skipped if IGNORED_NODES
+   contains these nodes.  */
+
+bool
+sem_function::compare_cgraph_references (hash_map <symtab_node *, sem_item *>
+    &ignored_nodes,
+    symtab_node *n1, symtab_node *n2)
+{
+  if (n1 == n2 || (ignored_nodes.get (n1) && ignored_nodes.get (n2)))
+    return true;
+
+  cgraph_node *cn1 = dyn_cast <cgraph_node *> (n1);
+  cgraph_node *cn2 = dyn_cast <cgraph_node *> (n2);
+
+  if (cn1 && cn2 && cn1->weakref && cn2->weakref
+      && cn1->alias_target == cn2->alias_target)
+    return true;
+
+  return return_false_with_msg ("different references");
+}
+
+/* If cgraph edges E1 and E2 are indirect calls, verify that
+   ECF flags are the same.  */
+
+bool sem_function::compare_edge_flags (cgraph_edge *e1, cgraph_edge *e2)
+{
+  if (e1->indirect_info && e2->indirect_info)
+    {
+      int e1_flags = e1->indirect_info->ecf_flags;
+      int e2_flags = e2->indirect_info->ecf_flags;
+
+      if (e1_flags != e2_flags)
+	return return_false_with_msg ("ICF flags are different");
+    }
+  else if (e1->indirect_info || e2->indirect_info)
+    return false;
+
+  return true;
+}
+
+/* Fast equality function based on knowledge known in WPA.  */
+
+bool
+sem_function::equals_wpa (sem_item *item,
+			  hash_map <symtab_node *, sem_item *> &ignored_nodes)
+{
+  gcc_assert (item->type == FUNC);
+
+  m_compared_func = static_cast<sem_function *> (item);
+
+  if (arg_types.length () != m_compared_func->arg_types.length ())
+    return return_false_with_msg ("different number of arguments");
+
+  /* Checking types of arguments.  */
+  for (unsigned i = 0; i < arg_types.length (); i++)
+    {
+      /* This guard is here for function pointer with attributes (pr59927.c).  */
+      if (!arg_types[i] || !m_compared_func->arg_types[i])
+	return return_false_with_msg ("NULL argument type");
+
+      /* Polymorphic comparison is executed just for non-leaf functions.  */
+      bool is_not_leaf = get_node ()->callees != NULL;
+
+      if (!func_checker::compatible_types_p (arg_types[i],
+					     m_compared_func->arg_types[i],
+					     is_not_leaf, i == 0))
+	return return_false_with_msg ("argument type is different");
+    }
+
+  /* Result type checking.  */
+  if (!func_checker::compatible_types_p (result_type,
+					 m_compared_func->result_type))
+    return return_false_with_msg ("result types are different");
+
+  if (node->get_references_count () != item->node->get_references_count ())
+    return return_false_with_msg ("different number of references");
+
+  ipa_ref *ref = NULL, *ref2 = NULL;
+  for (unsigned i = 0; node->iterate_reference (i, ref); i++)
+    {
+      item->node->iterate_reference (i, ref2);
+
+      if (!compare_cgraph_references (ignored_nodes, ref->referred, ref2->referred))
+	return false;
+    }
+
+  cgraph_edge *e1 = dyn_cast <cgraph_node *> (node)->callees;
+  cgraph_edge *e2 = dyn_cast <cgraph_node *> (item->node)->callees;
+
+  while (e1 && e2)
+    {
+      if (!compare_cgraph_references (ignored_nodes, e1->callee, e2->callee))
+	return false;
+
+      e1 = e1->next_callee;
+      e2 = e2->next_callee;
+    }
+
+  if (e1 || e2)
+    return return_false_with_msg ("different number of edges");
+
+  return true;
+}
+
+/* Returns true if the item equals to ITEM given as argument.  */
+
+bool
+sem_function::equals (sem_item *item,
+		      hash_map <symtab_node *, sem_item *> &ignored_nodes)
+{
+  gcc_assert (item->type == FUNC);
+  bool eq = equals_private (item, ignored_nodes);
+
+  if (m_checker != NULL)
+    {
+      delete m_checker;
+      m_checker = NULL;
+    }
+
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    fprintf (dump_file,
+	     "Equals called for:%s:%s (%u:%u) (%s:%s) with result: %s\n\n",
+	     name(), item->name (), node->order, item->node->order, asm_name (),
+	     item->asm_name (), eq ? "true" : "false");
+
+  return eq;
+}
+
+/* Processes function equality comparison.  */
+
+bool
+sem_function::equals_private (sem_item *item,
+			      hash_map <symtab_node *, sem_item *> &ignored_nodes)
+{
+  if (item->type != FUNC)
+    return false;
+
+  basic_block bb1, bb2;
+  edge e1, e2;
+  edge_iterator ei1, ei2;
+  int *bb_dict = NULL;
+  bool result = true;
+  tree arg1, arg2;
+
+  m_compared_func = static_cast<sem_function *> (item);
+
+  gcc_assert (decl != item->decl);
+
+  if (bb_sorted.length () != m_compared_func->bb_sorted.length ()
+      || edge_count != m_compared_func->edge_count
+      || cfg_checksum != m_compared_func->cfg_checksum)
+    return return_false ();
+
+  if (!equals_wpa (item, ignored_nodes))
+    return false;
+
+  /* Checking function arguments.  */
+  tree decl1 = DECL_ATTRIBUTES (decl);
+  tree decl2 = DECL_ATTRIBUTES (m_compared_func->decl);
+
+  m_checker = new func_checker (decl, m_compared_func->decl,
+				compare_polymorphic_p (),
+				false,
+				&refs_set,
+				&m_compared_func->refs_set);
+  while (decl1)
+    {
+      if (decl2 == NULL)
+	return return_false ();
+
+      if (get_attribute_name (decl1) != get_attribute_name (decl2))
+	return return_false ();
+
+      tree attr_value1 = TREE_VALUE (decl1);
+      tree attr_value2 = TREE_VALUE (decl2);
+
+      if (attr_value1 && attr_value2)
+	{
+	  bool ret = m_checker->compare_operand (TREE_VALUE (attr_value1),
+						 TREE_VALUE (attr_value2));
+	  if (!ret)
+	    return return_false_with_msg ("attribute values are different");
+	}
+      else if (!attr_value1 && !attr_value2)
+	{}
+      else
+	return return_false ();
+
+      decl1 = TREE_CHAIN (decl1);
+      decl2 = TREE_CHAIN (decl2);
+    }
+
+  if (decl1 != decl2)
+    return return_false();
+
+
+  for (arg1 = DECL_ARGUMENTS (decl),
+       arg2 = DECL_ARGUMENTS (m_compared_func->decl);
+       arg1; arg1 = DECL_CHAIN (arg1), arg2 = DECL_CHAIN (arg2))
+    if (!m_checker->compare_decl (arg1, arg2))
+      return return_false ();
+
+  /* Fill-up label dictionary.  */
+  for (unsigned i = 0; i < bb_sorted.length (); ++i)
+    {
+      m_checker->parse_labels (bb_sorted[i]);
+      m_checker->parse_labels (m_compared_func->bb_sorted[i]);
+    }
+
+  /* Checking all basic blocks.  */
+  for (unsigned i = 0; i < bb_sorted.length (); ++i)
+    if(!m_checker->compare_bb (bb_sorted[i], m_compared_func->bb_sorted[i]))
+      return return_false();
+
+  dump_message ("All BBs are equal\n");
+
+  /* Basic block edges check.  */
+  for (unsigned i = 0; i < bb_sorted.length (); ++i)
+    {
+      bb_dict = XNEWVEC (int, bb_sorted.length () + 2);
+      memset (bb_dict, -1, (bb_sorted.length () + 2) * sizeof (int));
+
+      bb1 = bb_sorted[i]->bb;
+      bb2 = m_compared_func->bb_sorted[i]->bb;
+
+      ei2 = ei_start (bb2->preds);
+
+      for (ei1 = ei_start (bb1->preds); ei_cond (ei1, &e1); ei_next (&ei1))
+	{
+	  ei_cond (ei2, &e2);
+
+	  if (e1->flags != e2->flags)
+	    return return_false_with_msg ("flags comparison returns false");
+
+	  if (!bb_dict_test (bb_dict, e1->src->index, e2->src->index))
+	    return return_false_with_msg ("edge comparison returns false");
+
+	  if (!bb_dict_test (bb_dict, e1->dest->index, e2->dest->index))
+	    return return_false_with_msg ("BB comparison returns false");
+
+	  if (!m_checker->compare_edge (e1, e2))
+	    return return_false_with_msg ("edge comparison returns false");
+
+	  ei_next (&ei2);
+	}
+    }
+
+  /* Basic block PHI nodes comparison.  */
+  for (unsigned i = 0; i < bb_sorted.length (); i++)
+    if (!compare_phi_node (bb_sorted[i]->bb, m_compared_func->bb_sorted[i]->bb))
+      return return_false_with_msg ("PHI node comparison returns false");
+
+  return result;
+}
+
+/* Merges instance with an ALIAS_ITEM, where alias, thunk or redirection can
+   be applied.  */
+bool
+sem_function::merge (sem_item *alias_item)
+{
+  gcc_assert (alias_item->type == FUNC);
+
+  sem_function *alias_func = static_cast<sem_function *> (alias_item);
+
+  cgraph_node *original = get_node ();
+  cgraph_node *local_original = original;
+  cgraph_node *alias = alias_func->get_node ();
+  bool original_address_matters;
+  bool alias_address_matters;
+
+  bool create_thunk = false;
+  bool create_alias = false;
+  bool redirect_callers = false;
+  bool original_discardable = false;
+
+  /* Do not attempt to mix functions from different user sections;
+     we do not know what user intends with those.  */
+  if (((DECL_SECTION_NAME (original->decl) && !original->implicit_section)
+       || (DECL_SECTION_NAME (alias->decl) && !alias->implicit_section))
+      && DECL_SECTION_NAME (original->decl) != DECL_SECTION_NAME (alias->decl))
+    {
+      if (dump_file)
+	fprintf (dump_file,
+		 "Not unifying; original and alias are in different sections.\n\n");
+      return false;
+    }
+
+  /* See if original is in a section that can be discarded if the main
+     symbol is not used.  */
+  if (DECL_EXTERNAL (original->decl))
+    original_discardable = true;
+  if (original->resolution == LDPR_PREEMPTED_REG
+      || original->resolution == LDPR_PREEMPTED_IR)
+    original_discardable = true;
+  if (original->can_be_discarded_p ())
+    original_discardable = true;
+
+  /* See if original and/or alias address can be compared for equality.  */
+  original_address_matters
+    = (!DECL_VIRTUAL_P (original->decl)
+       && (original->externally_visible
+	   || original->address_taken_from_non_vtable_p ()));
+  alias_address_matters
+    = (!DECL_VIRTUAL_P (alias->decl)
+       && (alias->externally_visible
+	   || alias->address_taken_from_non_vtable_p ()));
+
+  /* If alias and original can be compared for address equality, we need
+     to create a thunk.  Also we can not create extra aliases into discardable
+     section (or we risk link failures when section is discarded).  */
+  if ((original_address_matters
+       && alias_address_matters)
+      || original_discardable)
+    {
+      create_thunk = !stdarg_p (TREE_TYPE (alias->decl));
+      create_alias = false;
+      /* When both alias and original are not overwritable, we can save
+         the extra thunk wrapper for direct calls.  */
+      redirect_callers
+	= (!original_discardable
+	   && alias->get_availability () > AVAIL_INTERPOSABLE
+	   && original->get_availability () > AVAIL_INTERPOSABLE);
+    }
+  else
+    {
+      create_alias = true;
+      create_thunk = false;
+      redirect_callers = false;
+    }
+
+  if (create_alias && DECL_COMDAT_GROUP (alias->decl))
+    {
+      create_alias = false;
+      create_thunk = true;
+    }
+
+  /* We want thunk to always jump to the local function body
+     unless the body is comdat and may be optimized out.  */
+  if ((create_thunk || redirect_callers)
+      && (!original_discardable
+	  || (DECL_COMDAT_GROUP (original->decl)
+	      && (DECL_COMDAT_GROUP (original->decl)
+		  == DECL_COMDAT_GROUP (alias->decl)))))
+    local_original
+      = dyn_cast <cgraph_node *> (original->noninterposable_alias ());
+
+  if (redirect_callers)
+    {
+      /* If alias is non-overwritable then
+         all direct calls are safe to be redirected to the original.  */
+      bool redirected = false;
+      while (alias->callers)
+	{
+	  cgraph_edge *e = alias->callers;
+	  e->redirect_callee (local_original);
+	  push_cfun (DECL_STRUCT_FUNCTION (e->caller->decl));
+
+	  if (e->call_stmt)
+	    e->redirect_call_stmt_to_callee ();
+
+	  pop_cfun ();
+	  redirected = true;
+	}
+
+      alias->icf_merged = true;
+
+      /* The alias function is removed if symbol address
+         does not matter.  */
+      if (!alias_address_matters)
+	alias->remove ();
+
+      if (dump_file && redirected)
+	fprintf (dump_file, "Callgraph local calls have been redirected.\n\n");
+    }
+  /* If the condtion above is not met, we are lucky and can turn the
+     function into real alias.  */
+  else if (create_alias)
+    {
+      alias->icf_merged = true;
+
+      /* Remove the function's body.  */
+      ipa_merge_profiles (original, alias);
+      alias->release_body (true);
+      alias->reset ();
+
+      /* Create the alias.  */
+      cgraph_node::create_alias (alias_func->decl, decl);
+      alias->resolve_alias (original);
+
+      if (dump_file)
+	fprintf (dump_file, "Callgraph alias has been created.\n\n");
+    }
+  else if (create_thunk)
+    {
+      if (DECL_COMDAT_GROUP (alias->decl))
+	{
+	  if (dump_file)
+	    fprintf (dump_file, "Callgraph thunk cannot be created because of COMDAT\n");
+
+	  return 0;
+	}
+
+      alias->icf_merged = true;
+      ipa_merge_profiles (local_original, alias);
+      alias->create_wrapper (local_original);
+
+      if (dump_file)
+	fprintf (dump_file, "Callgraph thunk has been created.\n\n");
+    }
+  else if (dump_file)
+    fprintf (dump_file, "Callgraph merge operation cannot be performed.\n\n");
+
+  return true;
+}
+
+/* Semantic item initialization function.  */
+
+void
+sem_function::init (void)
+{
+  if (in_lto_p)
+    get_node ()->get_body ();
+
+  tree fndecl = node->decl;
+  function *func = DECL_STRUCT_FUNCTION (fndecl);
+
+  gcc_assert (func);
+  gcc_assert (SSANAMES (func));
+
+  ssa_names_size = SSANAMES (func)->length ();
+  node = node;
+
+  decl = fndecl;
+  region_tree = func->eh->region_tree;
+
+  /* iterating all function arguments.  */
+  arg_count = count_formal_params (fndecl);
+
+  edge_count = n_edges_for_fn (func);
+  cfg_checksum = coverage_compute_cfg_checksum (func);
+
+  inchash::hash hstate;
+
+  basic_block bb;
+  FOR_EACH_BB_FN (bb, func)
+  {
+    unsigned nondbg_stmt_count = 0;
+
+    edge e;
+    for (edge_iterator ei = ei_start (bb->preds); ei_cond (ei, &e); ei_next (&ei))
+      cfg_checksum = iterative_hash_host_wide_int (e->flags,
+		     cfg_checksum);
+
+    for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi);
+	 gsi_next (&gsi))
+      {
+	gimple stmt = gsi_stmt (gsi);
+
+	if (gimple_code (stmt) != GIMPLE_DEBUG)
+	  {
+	    hash_stmt (&hstate, stmt);
+	    nondbg_stmt_count++;
+	  }
+      }
+
+    gcode_hash = hstate.end ();
+    bb_sizes.safe_push (nondbg_stmt_count);
+
+    /* Inserting basic block to hash table.  */
+    sem_bb *semantic_bb = new sem_bb (bb, nondbg_stmt_count,
+				      EDGE_COUNT (bb->preds) + EDGE_COUNT (bb->succs));
+
+    bb_sorted.safe_push (semantic_bb);
+  }
+
+  parse_tree_args ();
+}
+
+/* Improve accumulated hash for HSTATE based on a gimple statement STMT.  */
+
+void
+sem_function::hash_stmt (inchash::hash *hstate, gimple stmt)
+{
+  enum gimple_code code = gimple_code (stmt);
+
+  hstate->add_int (code);
+
+  if (code == GIMPLE_CALL)
+    {
+      /* Checking of argument.  */
+      for (unsigned i = 0; i < gimple_call_num_args (stmt); ++i)
+	{
+	  tree argument = gimple_call_arg (stmt, i);
+
+	  switch (TREE_CODE (argument))
+	    {
+	    case INTEGER_CST:
+	      if (tree_fits_shwi_p (argument))
+		hstate->add_wide_int (tree_to_shwi (argument));
+	      else if (tree_fits_uhwi_p (argument))
+		hstate->add_wide_int (tree_to_uhwi (argument));
+	      break;
+	    case REAL_CST:
+	      REAL_VALUE_TYPE c;
+	      HOST_WIDE_INT n;
+
+	      c = TREE_REAL_CST (argument);
+	      n = real_to_integer (&c);
+
+	      hstate->add_wide_int (n);
+	      break;
+	    case ADDR_EXPR:
+	      {
+		tree addr_operand = TREE_OPERAND (argument, 0);
+
+		if (TREE_CODE (addr_operand) == STRING_CST)
+		  hstate->add (TREE_STRING_POINTER (addr_operand),
+			       TREE_STRING_LENGTH (addr_operand));
+		break;
+	      }
+	    default:
+	      break;
+	    }
+	}
+    }
+}
+
+
+/* Return true if polymorphic comparison must be processed.  */
+
+bool
+sem_function::compare_polymorphic_p (void)
+{
+  return get_node ()->callees != NULL
+	 || m_compared_func->get_node ()->callees != NULL;
+}
+
+/* For a given call graph NODE, the function constructs new
+   semantic function item.  */
+
+sem_function *
+sem_function::parse (cgraph_node *node, bitmap_obstack *stack)
+{
+  tree fndecl = node->decl;
+  function *func = DECL_STRUCT_FUNCTION (fndecl);
+
+  /* TODO: add support for thunks and aliases.  */
+
+  if (!func || !node->has_gimple_body_p ())
+    return NULL;
+
+  if (lookup_attribute_by_prefix ("omp ", DECL_ATTRIBUTES (node->decl)) != NULL)
+    return NULL;
+
+  sem_function *f = new sem_function (node, 0, stack);
+
+  f->init ();
+
+  return f;
+}
+
+/* Parses function arguments and result type.  */
+
+void
+sem_function::parse_tree_args (void)
+{
+  tree result;
+
+  if (arg_types.exists ())
+    arg_types.release ();
+
+  arg_types.create (4);
+  tree fnargs = DECL_ARGUMENTS (decl);
+
+  for (tree parm = fnargs; parm; parm = DECL_CHAIN (parm))
+    arg_types.safe_push (DECL_ARG_TYPE (parm));
+
+  /* Function result type.  */
+  result = DECL_RESULT (decl);
+  result_type = result ? TREE_TYPE (result) : NULL;
+
+  /* During WPA, we can get arguments by following method.  */
+  if (!fnargs)
+    {
+      tree type = TYPE_ARG_TYPES (TREE_TYPE (decl));
+      for (tree parm = type; parm; parm = TREE_CHAIN (parm))
+	arg_types.safe_push (TYPE_CANONICAL (TREE_VALUE (parm)));
+
+      result_type = TREE_TYPE (TREE_TYPE (decl));
+    }
+}
+
+/* For given basic blocks BB1 and BB2 (from functions FUNC1 and FUNC),
+   return true if phi nodes are semantically equivalent in these blocks .  */
+
+bool
+sem_function::compare_phi_node (basic_block bb1, basic_block bb2)
+{
+  gimple_stmt_iterator si1, si2;
+  gimple phi1, phi2;
+  unsigned size1, size2, i;
+  tree t1, t2;
+  edge e1, e2;
+
+  gcc_assert (bb1 != NULL);
+  gcc_assert (bb2 != NULL);
+
+  si2 = gsi_start_phis (bb2);
+  for (si1 = gsi_start_phis (bb1); !gsi_end_p (si1);
+       gsi_next (&si1))
+    {
+      gsi_next_nonvirtual_phi (&si1);
+      gsi_next_nonvirtual_phi (&si2);
+
+      if (gsi_end_p (si1) && gsi_end_p (si2))
+	break;
+
+      if (gsi_end_p (si1) || gsi_end_p (si2))
+	return return_false();
+
+      phi1 = gsi_stmt (si1);
+      phi2 = gsi_stmt (si2);
+
+      size1 = gimple_phi_num_args (phi1);
+      size2 = gimple_phi_num_args (phi2);
+
+      if (size1 != size2)
+	return return_false ();
+
+      for (i = 0; i < size1; ++i)
+	{
+	  t1 = gimple_phi_arg (phi1, i)->def;
+	  t2 = gimple_phi_arg (phi2, i)->def;
+
+	  if (!m_checker->compare_operand (t1, t2))
+	    return return_false ();
+
+	  e1 = gimple_phi_arg_edge (phi1, i);
+	  e2 = gimple_phi_arg_edge (phi2, i);
+
+	  if (!m_checker->compare_edge (e1, e2))
+	    return return_false ();
+	}
+
+      gsi_next (&si2);
+    }
+
+  return true;
+}
+
+/* Returns true if tree T can be compared as a handled component.  */
+
+bool
+sem_function::icf_handled_component_p (tree t)
+{
+  tree_code tc = TREE_CODE (t);
+
+  return ((handled_component_p (t))
+	  || tc == ADDR_EXPR || tc == MEM_REF || tc == REALPART_EXPR
+	  || tc == IMAGPART_EXPR || tc == OBJ_TYPE_REF);
+}
+
+/* Basic blocks dictionary BB_DICT returns true if SOURCE index BB
+   corresponds to TARGET.  */
+
+bool
+sem_function::bb_dict_test (int* bb_dict, int source, int target)
+{
+  if (bb_dict[source] == -1)
+    {
+      bb_dict[source] = target;
+      return true;
+    }
+  else
+    return bb_dict[source] == target;
+}
+
+/* Iterates all tree types in T1 and T2 and returns true if all types
+   are compatible. If COMPARE_POLYMORPHIC is set to true,
+   more strict comparison is executed.  */
+
+bool
+sem_function::compare_type_list (tree t1, tree t2, bool compare_polymorphic)
+{
+  tree tv1, tv2;
+  tree_code tc1, tc2;
+
+  if (!t1 && !t2)
+    return true;
+
+  while (t1 != NULL && t2 != NULL)
+    {
+      tv1 = TREE_VALUE (t1);
+      tv2 = TREE_VALUE (t2);
+
+      tc1 = TREE_CODE (tv1);
+      tc2 = TREE_CODE (tv2);
+
+      if (tc1 == NOP_EXPR && tc2 == NOP_EXPR)
+	{}
+      else if (tc1 == NOP_EXPR || tc2 == NOP_EXPR)
+	return false;
+      else if (!func_checker::compatible_types_p (tv1, tv2, compare_polymorphic))
+	return false;
+
+      t1 = TREE_CHAIN (t1);
+      t2 = TREE_CHAIN (t2);
+    }
+
+  return !(t1 || t2);
+}
+
+
+/* Semantic variable constructor that uses STACK as bitmap memory stack.  */
+
+sem_variable::sem_variable (bitmap_obstack *stack): sem_item (VAR, stack)
+{
+}
+
+/*  Constructor based on varpool node _NODE with computed hash _HASH.
+    Bitmap STACK is used for memory allocation.  */
+
+sem_variable::sem_variable (varpool_node *node, hashval_t _hash,
+			    bitmap_obstack *stack): sem_item(VAR,
+				  node, _hash, stack)
+{
+  gcc_checking_assert (node);
+  gcc_checking_assert (get_node ());
+}
+
+/* Returns true if the item equals to ITEM given as argument.  */
+
+bool
+sem_variable::equals (sem_item *item,
+		      hash_map <symtab_node *, sem_item *> & ARG_UNUSED (ignored_nodes))
+{
+  gcc_assert (item->type == VAR);
+
+  return sem_variable::equals (ctor, static_cast<sem_variable *>(item)->ctor);
+}
+
+/* Compares trees T1 and T2 for semantic equality.  */
+
+bool
+sem_variable::equals (tree t1, tree t2)
+{
+  tree_code tc1 = TREE_CODE (t1);
+  tree_code tc2 = TREE_CODE (t2);
+
+  if (tc1 != tc2)
+    return false;
+
+  switch (tc1)
+    {
+    case CONSTRUCTOR:
+      {
+	unsigned len1 = vec_safe_length (CONSTRUCTOR_ELTS (t1));
+	unsigned len2 = vec_safe_length (CONSTRUCTOR_ELTS (t2));
+
+	if (len1 != len2)
+	  return false;
+
+	for (unsigned i = 0; i < len1; i++)
+	  if (!sem_variable::equals (CONSTRUCTOR_ELT (t1, i)->value,
+				     CONSTRUCTOR_ELT (t2, i)->value)
+	      || CONSTRUCTOR_ELT (t1, i)->index != CONSTRUCTOR_ELT (t2, i)->index)
+	    return false;
+
+	return true;
+      }
+    case MEM_REF:
+      {
+	tree x1 = TREE_OPERAND (t1, 0);
+	tree x2 = TREE_OPERAND (t2, 0);
+	tree y1 = TREE_OPERAND (t1, 1);
+	tree y2 = TREE_OPERAND (t2, 1);
+
+	if (!func_checker::compatible_types_p (TREE_TYPE (x1), TREE_TYPE (x2),
+					       true))
+	  return return_false ();
+
+	/* Type of the offset on MEM_REF does not matter.  */
+	return sem_variable::equals (x1, x2)
+	       && wi::to_offset  (y1) == wi::to_offset  (y2);
+      }
+    case NOP_EXPR:
+    case ADDR_EXPR:
+      {
+	tree op1 = TREE_OPERAND (t1, 0);
+	tree op2 = TREE_OPERAND (t2, 0);
+	return sem_variable::equals (op1, op2);
+      }
+    case FUNCTION_DECL:
+    case VAR_DECL:
+    case FIELD_DECL:
+    case LABEL_DECL:
+      return t1 == t2;
+    case INTEGER_CST:
+      return func_checker::compatible_types_p (TREE_TYPE (t1), TREE_TYPE (t2),
+	     true)
+	     && wi::to_offset (t1) == wi::to_offset (t2);
+    case STRING_CST:
+    case REAL_CST:
+    case COMPLEX_CST:
+      return operand_equal_p (t1, t2, OEP_ONLY_CONST);
+    case COMPONENT_REF:
+    case ARRAY_REF:
+    case POINTER_PLUS_EXPR:
+      {
+	tree x1 = TREE_OPERAND (t1, 0);
+	tree x2 = TREE_OPERAND (t2, 0);
+	tree y1 = TREE_OPERAND (t1, 1);
+	tree y2 = TREE_OPERAND (t2, 1);
+
+	return sem_variable::equals (x1, x2) && sem_variable::equals (y1, y2);
+      }
+    case ERROR_MARK:
+      return return_false_with_msg ("ERROR_MARK");
+    default:
+      return return_false_with_msg ("Unknown TREE code reached");
+    }
+}
+
+/* Parser function that visits a varpool NODE.  */
+
+sem_variable *
+sem_variable::parse (varpool_node *node, bitmap_obstack *stack)
+{
+  tree decl = node->decl;
+
+  bool readonly = TYPE_P (decl) ? TYPE_READONLY (decl) : TREE_READONLY (decl);
+  bool can_handle = readonly && (DECL_VIRTUAL_P (decl)
+				 || !TREE_ADDRESSABLE (decl));
+
+  if (!can_handle)
+    return NULL;
+
+  tree ctor = ctor_for_folding (decl);
+  if (!ctor)
+    return NULL;
+
+  sem_variable *v = new sem_variable (node, 0, stack);
+
+  v->init ();
+
+  return v;
+}
+
+/* References independent hash function.  */
+
+hashval_t
+sem_variable::get_hash (void)
+{
+  if (hash)
+    return hash;
+
+  inchash::hash hstate;
+
+  hstate.add_int (456346417);
+  hstate.add_int (TREE_CODE (ctor));
+
+  if (TREE_CODE (ctor) == CONSTRUCTOR)
+    {
+      unsigned length = vec_safe_length (CONSTRUCTOR_ELTS (ctor));
+      hstate.add_int (length);
+    }
+
+  hash = hstate.end ();
+
+  return hash;
+}
+
+/* Merges instance with an ALIAS_ITEM, where alias, thunk or redirection can
+   be applied.  */
+
+bool
+sem_variable::merge (sem_item *alias_item)
+{
+  gcc_assert (alias_item->type == VAR);
+
+  sem_variable *alias_var = static_cast<sem_variable *> (alias_item);
+
+  varpool_node *original = get_node ();
+  varpool_node *alias = alias_var->get_node ();
+  bool original_discardable = false;
+
+  /* See if original is in a section that can be discarded if the main
+     symbol is not used.  */
+  if (DECL_EXTERNAL (original->decl))
+    original_discardable = true;
+  if (original->resolution == LDPR_PREEMPTED_REG
+      || original->resolution == LDPR_PREEMPTED_IR)
+    original_discardable = true;
+  if (original->can_be_discarded_p ())
+    original_discardable = true;
+
+  gcc_assert (!TREE_ASM_WRITTEN (alias->decl));
+
+  if (original_discardable || DECL_EXTERNAL (alias_var->decl) ||
+      !compare_sections (alias_var))
+    {
+      if (dump_file)
+	fprintf (dump_file, "Varpool alias cannot be created\n\n");
+
+      return false;
+    }
+  else
+    {
+      // alias cycle creation check
+      varpool_node *n = original;
+
+      while (n->alias)
+	{
+	  n = n->get_alias_target ();
+	  if (n == alias)
+	    {
+	      if (dump_file)
+		fprintf (dump_file, "Varpool alias cannot be created (alias cycle).\n\n");
+
+	      return false;
+	    }
+	}
+
+      alias->analyzed = false;
+
+      DECL_INITIAL (alias->decl) = NULL;
+      alias->remove_all_references ();
+
+      varpool_node::create_alias (alias_var->decl, decl);
+      alias->resolve_alias (original);
+
+      if (dump_file)
+	fprintf (dump_file, "Varpool alias has been created.\n\n");
+
+      return true;
+    }
+}
+
+bool
+sem_variable::compare_sections (sem_variable *alias)
+{
+  const char *source = node->get_section ();
+  const char *target = alias->node->get_section();
+
+  if (source == NULL && target == NULL)
+    return true;
+  else if(!source || !target)
+    return false;
+  else
+    return strcmp (source, target) == 0;
+}
+
+/* Dump symbol to FILE.  */
+
+void
+sem_variable::dump_to_file (FILE *file)
+{
+  gcc_assert (file);
+
+  print_node (file, "", decl, 0);
+  fprintf (file, "\n\n");
+}
+
+/* Iterates though a constructor and identifies tree references
+   we are interested in semantic function equality.  */
+
+void
+sem_variable::parse_tree_refs (tree t)
+{
+  switch (TREE_CODE (t))
+    {
+    case CONSTRUCTOR:
+      {
+	unsigned length = vec_safe_length (CONSTRUCTOR_ELTS (t));
+
+	for (unsigned i = 0; i < length; i++)
+	  parse_tree_refs(CONSTRUCTOR_ELT (t, i)->value);
+
+	break;
+      }
+    case NOP_EXPR:
+    case ADDR_EXPR:
+      {
+	tree op = TREE_OPERAND (t, 0);
+	parse_tree_refs (op);
+	break;
+      }
+    case FUNCTION_DECL:
+      {
+	tree_refs.safe_push (t);
+	break;
+      }
+    default:
+      break;
+    }
+}
+
+unsigned int sem_item_optimizer::class_id = 0;
+
+sem_item_optimizer::sem_item_optimizer (): worklist (0), m_classes (0),
+  m_classes_count (0), m_cgraph_node_hooks (NULL), m_varpool_node_hooks (NULL)
+{
+  m_items.create (0);
+  bitmap_obstack_initialize (&m_bmstack);
+}
+
+sem_item_optimizer::~sem_item_optimizer ()
+{
+  for (unsigned int i = 0; i < m_items.length (); i++)
+    delete m_items[i];
+
+  for (hash_table<congruence_class_group_hash>::iterator it = m_classes.begin ();
+       it != m_classes.end (); ++it)
+    {
+      for (unsigned int i = 0; i < (*it)->classes.length (); i++)
+	delete (*it)->classes[i];
+
+      (*it)->classes.release ();
+    }
+
+  m_items.release ();
+
+  bitmap_obstack_release (&m_bmstack);
+}
+
+/* Write IPA ICF summary for symbols.  */
+
+void
+sem_item_optimizer::write_summary (void)
+{
+  unsigned int count = 0;
+
+  output_block *ob = create_output_block (LTO_section_ipa_icf);
+  lto_symtab_encoder_t encoder = ob->decl_state->symtab_node_encoder;
+  ob->symbol = NULL;
+
+  /* Calculate number of symbols to be serialized.  */
+  for (lto_symtab_encoder_iterator lsei = lsei_start_in_partition (encoder);
+       !lsei_end_p (lsei);
+       lsei_next_in_partition (&lsei))
+    {
+      symtab_node *node = lsei_node (lsei);
+
+      if (m_symtab_node_map.get (node))
+	count++;
+    }
+
+  streamer_write_uhwi (ob, count);
+
+  /* Process all of the symbols.  */
+  for (lto_symtab_encoder_iterator lsei = lsei_start_in_partition (encoder);
+       !lsei_end_p (lsei);
+       lsei_next_in_partition (&lsei))
+    {
+      symtab_node *node = lsei_node (lsei);
+
+      sem_item **item = m_symtab_node_map.get (node);
+
+      if (item && *item)
+	{
+	  int node_ref = lto_symtab_encoder_encode (encoder, node);
+	  streamer_write_uhwi_stream (ob->main_stream, node_ref);
+
+	  streamer_write_uhwi (ob, (*item)->get_hash ());
+	}
+    }
+
+  streamer_write_char_stream (ob->main_stream, 0);
+  produce_asm (ob, NULL);
+  destroy_output_block (ob);
+}
+
+/* Reads a section from LTO stream file FILE_DATA. Input block for DATA
+   contains LEN bytes.  */
+
+void
+sem_item_optimizer::read_section (lto_file_decl_data *file_data,
+				  const char *data, size_t len)
+{
+  const lto_function_header *header =
+    (const lto_function_header *) data;
+  const int cfg_offset = sizeof (lto_function_header);
+  const int main_offset = cfg_offset + header->cfg_size;
+  const int string_offset = main_offset + header->main_size;
+  data_in *data_in;
+  unsigned int i;
+  unsigned int count;
+
+  lto_input_block ib_main ((const char *) data + main_offset, 0,
+			   header->main_size);
+
+  data_in =
+    lto_data_in_create (file_data, (const char *) data + string_offset,
+			header->string_size, vNULL);
+
+  count = streamer_read_uhwi (&ib_main);
+
+  for (i = 0; i < count; i++)
+    {
+      unsigned int index;
+      symtab_node *node;
+      lto_symtab_encoder_t encoder;
+
+      index = streamer_read_uhwi (&ib_main);
+      encoder = file_data->symtab_node_encoder;
+      node = lto_symtab_encoder_deref (encoder, index);
+
+      hashval_t hash = streamer_read_uhwi (&ib_main);
+
+      gcc_assert (node->definition);
+
+      if (dump_file)
+	fprintf (dump_file, "Symbol added:%s (tree: %p, uid:%u)\n", node->asm_name (),
+		 (void *) node->decl, node->order);
+
+      if (is_a<cgraph_node *> (node))
+	{
+	  cgraph_node *cnode = dyn_cast <cgraph_node *> (node);
+
+	  m_items.safe_push (new sem_function (cnode, hash, &m_bmstack));
+	}
+      else
+	{
+	  varpool_node *vnode = dyn_cast <varpool_node *> (node);
+
+	  m_items.safe_push (new sem_variable (vnode, hash, &m_bmstack));
+	}
+    }
+
+  lto_free_section_data (file_data, LTO_section_ipa_icf, NULL, data,
+			 len);
+  lto_data_in_delete (data_in);
+}
+
+/* Read IPA IPA ICF summary for symbols.  */
+
+void
+sem_item_optimizer::read_summary (void)
+{
+  lto_file_decl_data **file_data_vec = lto_get_file_decl_data ();
+  lto_file_decl_data *file_data;
+  unsigned int j = 0;
+
+  while ((file_data = file_data_vec[j++]))
+    {
+      size_t len;
+      const char *data = lto_get_section_data (file_data,
+			 LTO_section_ipa_icf, NULL, &len);
+
+      if (data)
+	read_section (file_data, data, len);
+    }
+}
+
+/* Register callgraph and varpool hooks.  */
+
+void
+sem_item_optimizer::register_hooks (void)
+{
+  m_cgraph_node_hooks = symtab->add_cgraph_removal_hook
+			(&sem_item_optimizer::cgraph_removal_hook, this);
+
+  m_varpool_node_hooks = symtab->add_varpool_removal_hook
+			 (&sem_item_optimizer::varpool_removal_hook, this);
+}
+
+/* Unregister callgraph and varpool hooks.  */
+
+void
+sem_item_optimizer::unregister_hooks (void)
+{
+  if (m_cgraph_node_hooks)
+    symtab->remove_cgraph_removal_hook (m_cgraph_node_hooks);
+
+  if (m_varpool_node_hooks)
+    symtab->remove_varpool_removal_hook (m_varpool_node_hooks);
+}
+
+/* Adds a CLS to hashtable associated by hash value.  */
+
+void
+sem_item_optimizer::add_class (congruence_class *cls)
+{
+  gcc_assert (cls->members.length ());
+
+  congruence_class_group *group = get_group_by_hash (
+				    cls->members[0]->get_hash (),
+				    cls->members[0]->type);
+  group->classes.safe_push (cls);
+}
+
+/* Gets a congruence class group based on given HASH value and TYPE.  */
+
+congruence_class_group *
+sem_item_optimizer::get_group_by_hash (hashval_t hash, sem_item_type type)
+{
+  congruence_class_group *item = XNEW (congruence_class_group);
+  item->hash = hash;
+  item->type = type;
+
+  congruence_class_group **slot = m_classes.find_slot (item, INSERT);
+
+  if (*slot)
+    free (item);
+  else
+    {
+      item->classes.create (1);
+      *slot = item;
+    }
+
+  return *slot;
+}
+
+/* Callgraph removal hook called for a NODE with a custom DATA.  */
+
+void
+sem_item_optimizer::cgraph_removal_hook (cgraph_node *node, void *data)
+{
+  sem_item_optimizer *optimizer = (sem_item_optimizer *) data;
+  optimizer->remove_symtab_node (node);
+}
+
+/* Varpool removal hook called for a NODE with a custom DATA.  */
+
+void
+sem_item_optimizer::varpool_removal_hook (varpool_node *node, void *data)
+{
+  sem_item_optimizer *optimizer = (sem_item_optimizer *) data;
+  optimizer->remove_symtab_node (node);
+}
+
+/* Remove symtab NODE triggered by symtab removal hooks.  */
+
+void
+sem_item_optimizer::remove_symtab_node (symtab_node *node)
+{
+  gcc_assert (!m_classes.elements());
+
+  m_removed_items_set.add (node);
+}
+
+void
+sem_item_optimizer::remove_item (sem_item *item)
+{
+  if (m_symtab_node_map.get (item->node))
+    m_symtab_node_map.remove (item->node);
+  delete item;
+}
+
+/* Removes all callgraph and varpool nodes that are marked by symtab
+   as deleted.  */
+
+void
+sem_item_optimizer::filter_removed_items (void)
+{
+  auto_vec <sem_item *> filtered;
+
+  for (unsigned int i = 0; i < m_items.length(); i++)
+    {
+      sem_item *item = m_items[i];
+
+      if (!flag_ipa_icf_functions && item->type == FUNC)
+	{
+	  remove_item (item);
+	  continue;
+	}
+
+      if (!flag_ipa_icf_variables && item->type == VAR)
+	{
+	  remove_item (item);
+	  continue;
+	}
+
+      bool no_body_function = false;
+
+      if (item->type == FUNC)
+	{
+	  cgraph_node *cnode = static_cast <sem_function *>(item)->get_node ();
+
+	  no_body_function = in_lto_p && (cnode->alias || cnode->body_removed);
+	}
+
+      if(!m_removed_items_set.contains (m_items[i]->node)
+	  && !no_body_function)
+	{
+	  if (item->type == VAR || (!DECL_CXX_CONSTRUCTOR_P (item->decl)
+				    && !DECL_CXX_DESTRUCTOR_P (item->decl)))
+	    {
+	      filtered.safe_push (m_items[i]);
+	      continue;
+	    }
+	}
+
+      remove_item (item);
+    }
+
+  /* Clean-up of released semantic items.  */
+
+  m_items.release ();
+  for (unsigned int i = 0; i < filtered.length(); i++)
+    m_items.safe_push (filtered[i]);
+}
+
+/* Optimizer entry point.  */
+
+void
+sem_item_optimizer::execute (void)
+{
+  filter_removed_items ();
+  build_hash_based_classes ();
+
+  if (dump_file)
+    fprintf (dump_file, "Dump after hash based groups\n");
+  dump_cong_classes ();
+
+  for (unsigned int i = 0; i < m_items.length(); i++)
+    m_items[i]->init_wpa ();
+
+  build_graph ();
+
+  subdivide_classes_by_equality (true);
+
+  if (dump_file)
+    fprintf (dump_file, "Dump after WPA based types groups\n");
+
+  dump_cong_classes ();
+
+  process_cong_reduction ();
+  verify_classes ();
+
+  if (dump_file)
+    fprintf (dump_file, "Dump after callgraph-based congruence reduction\n");
+
+  dump_cong_classes ();
+
+  parse_nonsingleton_classes ();
+  subdivide_classes_by_equality ();
+
+  if (dump_file)
+    fprintf (dump_file, "Dump after full equality comparison of groups\n");
+
+  dump_cong_classes ();
+
+  unsigned int prev_class_count = m_classes_count;
+
+  process_cong_reduction ();
+  dump_cong_classes ();
+  verify_classes ();
+  merge_classes (prev_class_count);
+
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    symtab_node::dump_table (dump_file);
+}
+
+/* Function responsible for visiting all potential functions and
+   read-only variables that can be merged.  */
+
+void
+sem_item_optimizer::parse_funcs_and_vars (void)
+{
+  cgraph_node *cnode;
+
+  if (flag_ipa_icf_functions)
+    FOR_EACH_DEFINED_FUNCTION (cnode)
+    {
+      sem_function *f = sem_function::parse (cnode, &m_bmstack);
+      if (f)
+	{
+	  m_items.safe_push (f);
+	  m_symtab_node_map.put (cnode, f);
+
+	  if (dump_file)
+	    fprintf (dump_file, "Parsed function:%s\n", f->asm_name ());
+
+	  if (dump_file && (dump_flags & TDF_DETAILS))
+	    f->dump_to_file (dump_file);
+	}
+      else if (dump_file)
+	fprintf (dump_file, "Not parsed function:%s\n", cnode->asm_name ());
+    }
+
+  varpool_node *vnode;
+
+  if (flag_ipa_icf_variables)
+    FOR_EACH_DEFINED_VARIABLE (vnode)
+    {
+      sem_variable *v = sem_variable::parse (vnode, &m_bmstack);
+
+      if (v)
+	{
+	  m_items.safe_push (v);
+	  m_symtab_node_map.put (vnode, v);
+	}
+    }
+}
+
+/* Makes pairing between a congruence class CLS and semantic ITEM.  */
+
+void
+sem_item_optimizer::add_item_to_class (congruence_class *cls, sem_item *item)
+{
+  item->index_in_class = cls->members.length ();
+  cls->members.safe_push (item);
+  item->cls = cls;
+}
+
+/* Congruence classes are built by hash value.  */
+
+void
+sem_item_optimizer::build_hash_based_classes (void)
+{
+  for (unsigned i = 0; i < m_items.length (); i++)
+    {
+      sem_item *item = m_items[i];
+
+      congruence_class_group *group = get_group_by_hash (item->get_hash (),
+				      item->type);
+
+      if (!group->classes.length ())
+	{
+	  m_classes_count++;
+	  group->classes.safe_push (new congruence_class (class_id++));
+	}
+
+      add_item_to_class (group->classes[0], item);
+    }
+}
+
+/* Build references according to call graph.  */
+
+void
+sem_item_optimizer::build_graph (void)
+{
+  for (unsigned i = 0; i < m_items.length (); i++)
+    {
+      sem_item *item = m_items[i];
+      m_symtab_node_map.put (item->node, item);
+    }
+
+  for (unsigned i = 0; i < m_items.length (); i++)
+    {
+      sem_item *item = m_items[i];
+
+      if (item->type == FUNC)
+	{
+	  cgraph_node *cnode = dyn_cast <cgraph_node *> (item->node);
+
+	  cgraph_edge *e = cnode->callees;
+	  while (e)
+	    {
+	      sem_item **slot = m_symtab_node_map.get (e->callee);
+	      if (slot)
+		item->add_reference (*slot);
+
+	      e = e->next_callee;
+	    }
+	}
+
+      ipa_ref *ref = NULL;
+      for (unsigned i = 0; item->node->iterate_reference (i, ref); i++)
+	{
+	  sem_item **slot = m_symtab_node_map.get (ref->referred);
+	  if (slot)
+	    item->add_reference (*slot);
+	}
+    }
+}
+
+/* Semantic items in classes having more than one element and initialized.
+   In case of WPA, we load function body.  */
+
+void
+sem_item_optimizer::parse_nonsingleton_classes (void)
+{
+  unsigned int init_called_count = 0;
+
+  for (unsigned i = 0; i < m_items.length (); i++)
+    if (m_items[i]->cls->members.length () > 1)
+      {
+	m_items[i]->init ();
+	init_called_count++;
+      }
+
+  if (dump_file)
+    fprintf (dump_file, "Init called for %u items (%.2f%%).\n", init_called_count,
+	     100.0f * init_called_count / m_items.length ());
+}
+
+/* Equality function for semantic items is used to subdivide existing
+   classes. If IN_WPA, fast equality function is invoked.  */
+
+void
+sem_item_optimizer::subdivide_classes_by_equality (bool in_wpa)
+{
+  for (hash_table <congruence_class_group_hash>::iterator it = m_classes.begin ();
+       it != m_classes.end (); ++it)
+    {
+      unsigned int class_count = (*it)->classes.length ();
+
+      for (unsigned i = 0; i < class_count; i++)
+	{
+	  congruence_class *c = (*it)->classes [i];
+
+	  if (c->members.length() > 1)
+	    {
+	      auto_vec <sem_item *> new_vector;
+
+	      sem_item *first = c->members[0];
+	      new_vector.safe_push (first);
+
+	      unsigned class_split_first = (*it)->classes.length ();
+
+	      for (unsigned j = 1; j < c->members.length (); j++)
+		{
+		  sem_item *item = c->members[j];
+
+		  bool equals = in_wpa ? first->equals_wpa (item,
+				m_symtab_node_map) : first->equals (item, m_symtab_node_map);
+
+		  if (equals)
+		    new_vector.safe_push (item);
+		  else
+		    {
+		      bool integrated = false;
+
+		      for (unsigned k = class_split_first; k < (*it)->classes.length (); k++)
+			{
+			  sem_item *x = (*it)->classes[k]->members[0];
+			  bool equals = in_wpa ? x->equals_wpa (item,
+								m_symtab_node_map) : x->equals (item, m_symtab_node_map);
+
+			  if (equals)
+			    {
+			      integrated = true;
+			      add_item_to_class ((*it)->classes[k], item);
+
+			      break;
+			    }
+			}
+
+		      if (!integrated)
+			{
+			  congruence_class *c = new congruence_class (class_id++);
+			  m_classes_count++;
+			  add_item_to_class (c, item);
+
+			  (*it)->classes.safe_push (c);
+			}
+		    }
+		}
+
+	      // we replace newly created new_vector for the class we've just splitted
+	      c->members.release ();
+	      c->members.create (new_vector.length ());
+
+	      for (unsigned int j = 0; j < new_vector.length (); j++)
+		add_item_to_class (c, new_vector[j]);
+	    }
+	}
+    }
+
+  verify_classes ();
+}
+
+/* Verify congruence classes if checking is enabled.  */
+
+void
+sem_item_optimizer::verify_classes (void)
+{
+#if ENABLE_CHECKING
+  for (hash_table <congruence_class_group_hash>::iterator it = m_classes.begin ();
+       it != m_classes.end (); ++it)
+    {
+      for (unsigned int i = 0; i < (*it)->classes.length (); i++)
+	{
+	  congruence_class *cls = (*it)->classes[i];
+
+	  gcc_checking_assert (cls);
+	  gcc_checking_assert (cls->members.length () > 0);
+
+	  for (unsigned int j = 0; j < cls->members.length (); j++)
+	    {
+	      sem_item *item = cls->members[j];
+
+	      gcc_checking_assert (item);
+	      gcc_checking_assert (item->cls == cls);
+
+	      for (unsigned k = 0; k < item->usages.length (); k++)
+		{
+		  sem_usage_pair *usage = item->usages[k];
+		  gcc_checking_assert (usage->item->index_in_class <
+				       usage->item->cls->members.length ());
+		}
+	    }
+	}
+    }
+#endif
+}
+
+/* Disposes split map traverse function. CLS_PTR is pointer to congruence
+   class, BSLOT is bitmap slot we want to release. DATA is mandatory,
+   but unused argument.  */
+
+bool
+sem_item_optimizer::release_split_map (congruence_class * const &,
+				       bitmap const &b, traverse_split_pair *)
+{
+  bitmap bmp = b;
+
+  BITMAP_FREE (bmp);
+
+  return true;
+}
+
+/* Process split operation for a class given as pointer CLS_PTR,
+   where bitmap B splits congruence class members. DATA is used
+   as argument of split pair.  */
+
+bool
+sem_item_optimizer::traverse_congruence_split (congruence_class * const &cls,
+    bitmap const &b, traverse_split_pair *pair)
+{
+  sem_item_optimizer *optimizer = pair->optimizer;
+  const congruence_class *splitter_cls = pair->cls;
+
+  /* If counted bits are greater than zero and less than the number of members
+     a group will be splitted.  */
+  unsigned popcount = bitmap_count_bits (b);
+
+  if (popcount > 0 && popcount < cls->members.length ())
+    {
+      congruence_class* newclasses[2] = { new congruence_class (class_id++), new congruence_class (class_id++) };
+
+      for (unsigned int i = 0; i < cls->members.length (); i++)
+	{
+	  int target = bitmap_bit_p (b, i);
+	  congruence_class *tc = newclasses[target];
+
+	  add_item_to_class (tc, cls->members[i]);
+	}
+
+#ifdef ENABLE_CHECKING
+      for (unsigned int i = 0; i < 2; i++)
+	gcc_checking_assert (newclasses[i]->members.length ());
+#endif
+
+      if (splitter_cls == cls)
+	optimizer->splitter_class_removed = true;
+
+      /* Remove old class from worklist if presented.  */
+      bool in_worklist = cls->in_worklist;
+
+      if (in_worklist)
+	cls->in_worklist = false;
+
+      congruence_class_group g;
+      g.hash = cls->members[0]->get_hash ();
+      g.type = cls->members[0]->type;
+
+      congruence_class_group *slot = optimizer->m_classes.find(&g);
+
+      for (unsigned int i = 0; i < slot->classes.length (); i++)
+	if (slot->classes[i] == cls)
+	  {
+	    slot->classes.ordered_remove (i);
+	    break;
+	  }
+
+      /* New class will be inserted and integrated to work list.  */
+      for (unsigned int i = 0; i < 2; i++)
+	optimizer->add_class (newclasses[i]);
+
+      /* Two classes replace one, so that increment just by one.  */
+      optimizer->m_classes_count++;
+
+      /* If OLD class was presented in the worklist, we remove the class
+         and replace it will both newly created classes.  */
+      if (in_worklist)
+	for (unsigned int i = 0; i < 2; i++)
+	  optimizer->worklist_push (newclasses[i]);
+      else /* Just smaller class is inserted.  */
+	{
+	  unsigned int smaller_index = newclasses[0]->members.length () <
+				       newclasses[1]->members.length () ?
+				       0 : 1;
+	  optimizer->worklist_push (newclasses[smaller_index]);
+	}
+
+      if (dump_file && (dump_flags & TDF_DETAILS))
+	{
+	  fprintf (dump_file, "  congruence class splitted:\n");
+	  cls->dump (dump_file, 4);
+
+	  fprintf (dump_file, "  newly created groups:\n");
+	  for (unsigned int i = 0; i < 2; i++)
+	    newclasses[i]->dump (dump_file, 4);
+	}
+
+      /* Release class if not presented in work list.  */
+      if (!in_worklist)
+	delete cls;
+    }
+
+
+  return true;
+}
+
+/* Tests if a class CLS used as INDEXth splits any congruence classes.
+   Bitmap stack BMSTACK is used for bitmap allocation.  */
+
+void
+sem_item_optimizer::do_congruence_step_for_index (congruence_class *cls,
+    unsigned int index)
+{
+  hash_map <congruence_class *, bitmap> split_map;
+
+  for (unsigned int i = 0; i < cls->members.length (); i++)
+    {
+      sem_item *item = cls->members[i];
+
+      /* Iterate all usages that have INDEX as usage of the item.  */
+      for (unsigned int j = 0; j < item->usages.length (); j++)
+	{
+	  sem_usage_pair *usage = item->usages[j];
+
+	  if (usage->index != index)
+	    continue;
+
+	  bitmap *slot = split_map.get (usage->item->cls);
+	  bitmap b;
+
+	  if(!slot)
+	    {
+	      b = BITMAP_ALLOC (&m_bmstack);
+	      split_map.put (usage->item->cls, b);
+	    }
+	  else
+	    b = *slot;
+
+#if ENABLE_CHECKING
+	  gcc_checking_assert (usage->item->cls);
+	  gcc_checking_assert (usage->item->index_in_class <
+			       usage->item->cls->members.length ());
+#endif
+
+	  bitmap_set_bit (b, usage->item->index_in_class);
+	}
+    }
+
+  traverse_split_pair pair;
+  pair.optimizer = this;
+  pair.cls = cls;
+
+  splitter_class_removed = false;
+  split_map.traverse
+  <traverse_split_pair *, sem_item_optimizer::traverse_congruence_split> (&pair);
+
+  /* Bitmap clean-up.  */
+  split_map.traverse
+  <traverse_split_pair *, sem_item_optimizer::release_split_map> (NULL);
+}
+
+/* Every usage of a congruence class CLS is a candidate that can split the
+   collection of classes. Bitmap stack BMSTACK is used for bitmap
+   allocation.  */
+
+void
+sem_item_optimizer::do_congruence_step (congruence_class *cls)
+{
+  bitmap_iterator bi;
+  unsigned int i;
+
+  bitmap usage = BITMAP_ALLOC (&m_bmstack);
+
+  for (unsigned int i = 0; i < cls->members.length (); i++)
+    bitmap_ior_into (usage, cls->members[i]->usage_index_bitmap);
+
+  EXECUTE_IF_SET_IN_BITMAP (usage, 0, i, bi)
+  {
+    if (dump_file && (dump_flags & TDF_DETAILS))
+      fprintf (dump_file, "  processing congruece step for class: %u, index: %u\n",
+	       cls->id, i);
+
+    do_congruence_step_for_index (cls, i);
+
+    if (splitter_class_removed)
+      break;
+  }
+
+  BITMAP_FREE (usage);
+}
+
+/* Adds a newly created congruence class CLS to worklist.  */
+
+void
+sem_item_optimizer::worklist_push (congruence_class *cls)
+{
+  /* Return if the class CLS is already presented in work list.  */
+  if (cls->in_worklist)
+    return;
+
+  cls->in_worklist = true;
+  worklist.push_back (cls);
+}
+
+/* Pops a class from worklist. */
+
+congruence_class *
+sem_item_optimizer::worklist_pop (void)
+{
+  congruence_class *cls;
+
+  while (!worklist.empty ())
+    {
+      cls = worklist.front ();
+      worklist.pop_front ();
+      if (cls->in_worklist)
+	{
+	  cls->in_worklist = false;
+
+	  return cls;
+	}
+      else
+	{
+	  /* Work list item was already intended to be removed.
+	     The only reason for doing it is to split a class.
+	     Thus, the class CLS is deleted.  */
+	  delete cls;
+	}
+    }
+
+  return NULL;
+}
+
+/* Iterative congruence reduction function.  */
+
+void
+sem_item_optimizer::process_cong_reduction (void)
+{
+  for (hash_table<congruence_class_group_hash>::iterator it = m_classes.begin ();
+       it != m_classes.end (); ++it)
+    for (unsigned i = 0; i < (*it)->classes.length (); i++)
+      if ((*it)->classes[i]->is_class_used ())
+	worklist_push ((*it)->classes[i]);
+
+  if (dump_file)
+    fprintf (dump_file, "Worklist has been filled with: %lu\n",
+	     worklist.size ());
+
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    fprintf (dump_file, "Congruence class reduction\n");
+
+  congruence_class *cls;
+  while ((cls = worklist_pop ()) != NULL)
+    do_congruence_step (cls);
+}
+
+/* Debug function prints all informations about congruence classes.  */
+
+void
+sem_item_optimizer::dump_cong_classes (void)
+{
+  if (!dump_file)
+    return;
+
+  fprintf (dump_file,
+	   "Congruence classes: %u (unique hash values: %lu), with total: %u items\n",
+	   m_classes_count, m_classes.elements(), m_items.length ());
+
+  /* Histogram calculation.  */
+  unsigned int max_index = 0;
+  unsigned int* histogram = XCNEWVEC (unsigned int, m_items.length () + 1);
+
+  for (hash_table<congruence_class_group_hash>::iterator it = m_classes.begin ();
+       it != m_classes.end (); ++it)
+
+    for (unsigned i = 0; i < (*it)->classes.length (); i++)
+      {
+	unsigned int c = (*it)->classes[i]->members.length ();
+	histogram[c]++;
+
+	if (c > max_index)
+	  max_index = c;
+      }
+
+  fprintf (dump_file,
+	   "Class size histogram [num of members]: number of classe number of classess\n");
+
+  for (unsigned int i = 0; i <= max_index; i++)
+    if (histogram[i])
+      fprintf (dump_file, "[%u]: %u classes\n", i, histogram[i]);
+
+  fprintf (dump_file, "\n\n");
+
+
+  if (dump_flags & TDF_DETAILS)
+    for (hash_table<congruence_class_group_hash>::iterator it = m_classes.begin ();
+	 it != m_classes.end (); ++it)
+      {
+	fprintf (dump_file, "  group: with %u classes:\n", (*it)->classes.length ());
+
+	for (unsigned i = 0; i < (*it)->classes.length (); i++)
+	  {
+	    (*it)->classes[i]->dump (dump_file, 4);
+
+	    if(i < (*it)->classes.length () - 1)
+	      fprintf (dump_file, " ");
+	  }
+      }
+
+  free (histogram);
+}
+
+/* After reduction is done, we can declare all items in a group
+   to be equal. PREV_CLASS_COUNT is start number of classes
+   before reduction.  */
+
+void
+sem_item_optimizer::merge_classes (unsigned int prev_class_count)
+{
+  unsigned int item_count = m_items.length ();
+  unsigned int class_count = m_classes_count;
+  unsigned int equal_items = item_count - class_count;
+
+  if (dump_file)
+    {
+      fprintf (dump_file, "\nItem count: %u\n", item_count);
+      fprintf (dump_file, "Congruent classes before: %u, after: %u\n",
+	       prev_class_count, class_count);
+      fprintf (dump_file, "Average class size before: %.2f, after: %.2f\n",
+	       1.0f * item_count / prev_class_count,
+	       1.0f * item_count / class_count);
+      fprintf (dump_file, "Equal symbols: %u\n", equal_items);
+      fprintf (dump_file, "Fraction of visited symbols: %.2f%%\n\n",
+	       100.0f * equal_items / item_count);
+    }
+
+  for (hash_table<congruence_class_group_hash>::iterator it = m_classes.begin ();
+       it != m_classes.end (); ++it)
+    for (unsigned int i = 0; i < (*it)->classes.length (); i++)
+      {
+	congruence_class *c = (*it)->classes[i];
+
+	if (c->members.length () == 1)
+	  continue;
+
+	gcc_assert (c->members.length ());
+
+	sem_item *source = c->members[0];
+
+	for (unsigned int j = 1; j < c->members.length (); j++)
+	  {
+	    sem_item *alias = c->members[j];
+	    source->equals (alias, m_symtab_node_map);
+
+	    if (dump_file)
+	      {
+		fprintf (dump_file, "Semantic equality hit:%s->%s\n",
+			 source->name (), alias->name ());
+		fprintf (dump_file, "Assembler symbol names:%s->%s\n",
+			 source->asm_name (), alias->asm_name ());
+	      }
+
+	    if (dump_file && (dump_flags & TDF_DETAILS))
+	      {
+		source->dump_to_file (dump_file);
+		alias->dump_to_file (dump_file);
+	      }
+
+	    source->merge (alias);
+	  }
+      }
+}
+
+/* Dump function prints all class members to a FILE with an INDENT.  */
+
+void
+congruence_class::dump (FILE *file, unsigned int indent) const
+{
+  FPRINTF_SPACES (file, indent, "class with id: %u, hash: %u, items: %u\n",
+		  id, members[0]->get_hash (), members.length ());
+
+  FPUTS_SPACES (file, indent + 2, "");
+  for (unsigned i = 0; i < members.length (); i++)
+    fprintf (file, "%s(%p/%u) ", members[i]->asm_name (), (void *) members[i]->decl,
+	     members[i]->node->order);
+
+  fprintf (file, "\n");
+}
+
+/* Returns true if there's a member that is used from another group.  */
+
+bool
+congruence_class::is_class_used (void)
+{
+  for (unsigned int i = 0; i < members.length (); i++)
+    if (members[i]->usages.length ())
+      return true;
+
+  return false;
+}
+
+/* Initialization and computation of symtab node hash, there data
+   are propagated later on.  */
+
+static sem_item_optimizer *optimizer = NULL;
+
+/* Generate pass summary for IPA ICF pass.  */
+
+static void
+ipa_icf_generate_summary (void)
+{
+  if (!optimizer)
+    optimizer = new sem_item_optimizer ();
+
+  optimizer->parse_funcs_and_vars ();
+}
+
+/* Write pass summary for IPA ICF pass.  */
+
+static void
+ipa_icf_write_summary (void)
+{
+  gcc_assert (optimizer);
+
+  optimizer->write_summary ();
+}
+
+/* Read pass summary for IPA ICF pass.  */
+
+static void
+ipa_icf_read_summary (void)
+{
+  if (!optimizer)
+    optimizer = new sem_item_optimizer ();
+
+  optimizer->read_summary ();
+  optimizer->register_hooks ();
+}
+
+/* Semantic equality exection function.  */
+
+static unsigned int
+ipa_icf_driver (void)
+{
+  gcc_assert (optimizer);
+
+  optimizer->execute ();
+  optimizer->unregister_hooks ();
+
+  delete optimizer;
+
+  return 0;
+}
+
+const pass_data pass_data_ipa_icf =
+{
+  IPA_PASS,		    /* type */
+  "icf",		    /* name */
+  OPTGROUP_IPA,             /* optinfo_flags */
+  TV_IPA_ICF,		    /* tv_id */
+  0,                        /* properties_required */
+  0,                        /* properties_provided */
+  0,                        /* properties_destroyed */
+  0,                        /* todo_flags_start */
+  0,                        /* todo_flags_finish */
+};
+
+class pass_ipa_icf : public ipa_opt_pass_d
+{
+public:
+  pass_ipa_icf (gcc::context *ctxt)
+    : ipa_opt_pass_d (pass_data_ipa_icf, ctxt,
+		      ipa_icf_generate_summary, /* generate_summary */
+		      ipa_icf_write_summary, /* write_summary */
+		      ipa_icf_read_summary, /* read_summary */
+		      NULL, /*
+		      write_optimization_summary */
+		      NULL, /*
+		      read_optimization_summary */
+		      NULL, /* stmt_fixup */
+		      0, /* function_transform_todo_flags_start */
+		      NULL, /* function_transform */
+		      NULL) /* variable_transform */
+  {}
+
+  /* opt_pass methods: */
+  virtual bool gate (function *)
+  {
+    return flag_ipa_icf_variables || flag_ipa_icf_functions;
+  }
+
+  virtual unsigned int execute (function *)
+  {
+    return ipa_icf_driver();
+  }
+}; // class pass_ipa_icf
+
+} // ipa_icf namespace
+
+ipa_opt_pass_d *
+make_pass_ipa_icf (gcc::context *ctxt)
+{
+  return new ipa_icf::pass_ipa_icf (ctxt);
+}
diff --git a/gcc/ipa-icf.h b/gcc/ipa-icf.h
new file mode 100644
index 0000000..d8e7b16
--- /dev/null
+++ b/gcc/ipa-icf.h
@@ -0,0 +1,553 @@
+/* Interprocedural semantic function equality pass
+   Copyright (C) 2014 Free Software Foundation, Inc.
+
+   Contributed by Jan Hubicka <hubicka@ucw.cz> and Martin Liska <mliska@suse.cz>
+
+This file is part of GCC.
+
+GCC 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, or (at your option) any later
+version.
+
+GCC 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 GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+namespace ipa_icf {
+class sem_item;
+
+/* Congruence class encompasses a collection of either functions or
+   read-only variables. These items are considered to be equivalent
+   if not proved the oposite.  */
+class congruence_class
+{
+public:
+  /* Congruence class constructor for a new class with _ID.  */
+  congruence_class (unsigned int _id): in_worklist (false), id(_id)
+  {
+  }
+
+  /* Destructor.  */
+  ~congruence_class ()
+  {
+  }
+
+  /* Dump function prints all class members to a FILE with an INDENT.  */
+  void dump (FILE *file, unsigned int indent = 0) const;
+
+  /* Returns true if there's a member that is used from another group.  */
+  bool is_class_used (void);
+
+  /* Flag is used in case we want to remove a class from worklist and
+     delete operation is quite expensive for
+     the data structure (linked list).  */
+  bool in_worklist;
+
+  /* Vector of all group members.  */
+  auto_vec <sem_item *> members;
+
+  /* Global unique class identifier.  */
+  unsigned int id;
+};
+
+/* Semantic item type enum.  */
+enum sem_item_type
+{
+  FUNC,
+  VAR
+};
+
+/* Semantic item usage pair.  */
+class sem_usage_pair
+{
+public:
+  /* Constructor for key value pair, where _ITEM is key and _INDEX is a target.  */
+  sem_usage_pair (sem_item *_item, unsigned int _index);
+
+  /* Target semantic item where an item is used.  */
+  sem_item *item;
+
+  /* Index of usage of such an item.  */
+  unsigned int index;
+};
+
+/* Semantic item is a base class that encapsulates all shared functionality
+   for both semantic function and variable items.  */
+class sem_item
+{
+public:
+  /* Semantic item constructor for a node of _TYPE, where STACK is used
+     for bitmap memory allocation.  */
+  sem_item (sem_item_type _type, bitmap_obstack *stack);
+
+  /* Semantic item constructor for a node of _TYPE, where STACK is used
+     for bitmap memory allocation. The item is based on symtab node _NODE
+     with computed _HASH.  */
+  sem_item (sem_item_type _type, symtab_node *_node, hashval_t _hash,
+	    bitmap_obstack *stack);
+
+  virtual ~sem_item ();
+
+  /* Dump function for debugging purpose.  */
+  DEBUG_FUNCTION void dump (void);
+
+  /* Initialize semantic item by info reachable during LTO WPA phase.  */
+  virtual void init_wpa (void) = 0;
+
+  /* Semantic item initialization function.  */
+  virtual void init (void) = 0;
+
+  /* Add reference to a semantic TARGET.  */
+  void add_reference (sem_item *target);
+
+  /* Gets symbol name of the item.  */
+  const char *name (void)
+  {
+    return node->name ();
+  }
+
+  /* Gets assembler name of the item.  */
+  const char *asm_name (void)
+  {
+    return node->asm_name ();
+  }
+
+  /* Fast equality function based on knowledge known in WPA.  */
+  virtual bool equals_wpa (sem_item *item,
+			   hash_map <symtab_node *, sem_item *> &ignored_nodes) = 0;
+
+  /* Returns true if the item equals to ITEM given as arguemnt.  */
+  virtual bool equals (sem_item *item,
+		       hash_map <symtab_node *, sem_item *> &ignored_nodes) = 0;
+
+  /* References independent hash function.  */
+  virtual hashval_t get_hash (void) = 0;
+
+  /* Merges instance with an ALIAS_ITEM, where alias, thunk or redirection can
+     be applied.  */
+  virtual bool merge (sem_item *alias_item) = 0;
+
+  /* Dump symbol to FILE.  */
+  virtual void dump_to_file (FILE *file) = 0;
+
+  /* Return base tree that can be used for compatible_types_p and
+     contains_polymorphic_type_p comparison.  */
+
+  static bool get_base_types (tree *t1, tree *t2);
+
+  /* Item type.  */
+  sem_item_type type;
+
+  /* Symtab node.  */
+  symtab_node *node;
+
+  /* Declaration tree node.  */
+  tree decl;
+
+  /* Semantic references used that generate congruence groups.  */
+  vec <sem_item *> refs;
+
+  /* Pointer to a congruence class the item belongs to.  */
+  congruence_class *cls;
+
+  /* Index of the item in a class belonging to.  */
+  unsigned int index_in_class;
+
+  /* List of semantic items where the instance is used.  */
+  vec <sem_usage_pair *> usages;
+
+  /* A bitmap with indices of all classes referencing this item.  */
+  bitmap usage_index_bitmap;
+
+  /* List of tree references (either FUNC_DECL or VAR_DECL).  */
+  vec <tree> tree_refs;
+
+  /* A set with symbol table references.  */
+  hash_set <symtab_node *> refs_set;
+
+protected:
+  /* Cached, once calculated hash for the item.  */
+  hashval_t hash;
+
+private:
+  /* Initialize internal data structures. Bitmap STACK is used for
+     bitmap memory allocation process.  */
+  void setup (bitmap_obstack *stack);
+}; // class sem_item
+
+class sem_function: public sem_item
+{
+public:
+  /* Semantic function constructor that uses STACK as bitmap memory stack.  */
+  sem_function (bitmap_obstack *stack);
+
+  /*  Constructor based on callgraph node _NODE with computed hash _HASH.
+      Bitmap STACK is used for memory allocation.  */
+  sem_function (cgraph_node *_node, hashval_t _hash, bitmap_obstack *stack);
+
+  ~sem_function ();
+
+  inline virtual void init_wpa (void)
+  {
+    parse_tree_args ();
+  }
+
+  virtual void init (void);
+  virtual bool equals_wpa (sem_item *item,
+			   hash_map <symtab_node *, sem_item *> &ignored_nodes);
+  virtual hashval_t get_hash (void);
+  virtual bool equals (sem_item *item,
+		       hash_map <symtab_node *, sem_item *> &ignored_nodes);
+  virtual bool merge (sem_item *alias_item);
+
+  /* Dump symbol to FILE.  */
+  virtual void dump_to_file (FILE *file)
+  {
+    gcc_assert (file);
+    dump_function_to_file (decl, file, TDF_DETAILS);
+  }
+
+  /* Parses function arguments and result type.  */
+  void parse_tree_args (void);
+
+  /* Returns cgraph_node.  */
+  inline cgraph_node *get_node (void)
+  {
+    return dyn_cast <cgraph_node *> (node);
+  }
+
+  /* Improve accumulated hash for HSTATE based on a gimple statement STMT.  */
+  void hash_stmt (inchash::hash *inchash, gimple stmt);
+
+  /* Return true if polymorphic comparison must be processed.  */
+  bool compare_polymorphic_p (void);
+
+  /* For a given call graph NODE, the function constructs new
+     semantic function item.  */
+  static sem_function *parse (cgraph_node *node, bitmap_obstack *stack);
+
+  /* Exception handling region tree.  */
+  eh_region region_tree;
+
+  /* Result type tree node.  */
+  tree result_type;
+
+  /* Array of argument tree types.  */
+  vec <tree> arg_types;
+
+  /* Number of function arguments.  */
+  unsigned int arg_count;
+
+  /* Total amount of edges in the function.  */
+  unsigned int edge_count;
+
+  /* Vector of sizes of all basic blocks.  */
+  vec <unsigned int> bb_sizes;
+
+  /* Control flow graph checksum.  */
+  hashval_t cfg_checksum;
+
+  /* GIMPLE codes hash value.  */
+  hashval_t gcode_hash;
+
+  /* Total number of SSA names used in the function.  */
+  unsigned ssa_names_size;
+
+  /* Array of structures for all basic blocks.  */
+  vec <ipa_icf_gimple::sem_bb *> bb_sorted;
+
+private:
+  /* Calculates hash value based on a BASIC_BLOCK.  */
+  hashval_t get_bb_hash (const ipa_icf_gimple::sem_bb *basic_block);
+
+  /* For given basic blocks BB1 and BB2 (from functions FUNC1 and FUNC),
+     true value is returned if phi nodes are semantically
+     equivalent in these blocks .  */
+  bool compare_phi_node (basic_block bb1, basic_block bb2);
+
+  /* Basic blocks dictionary BB_DICT returns true if SOURCE index BB
+     corresponds to TARGET.  */
+  bool bb_dict_test (int* bb_dict, int source, int target);
+
+  /* Iterates all tree types in T1 and T2 and returns true if all types
+     are compatible. If COMPARE_POLYMORPHIC is set to true,
+     more strict comparison is executed.  */
+  bool compare_type_list (tree t1, tree t2, bool compare_polymorphic);
+
+  /* If cgraph edges E1 and E2 are indirect calls, verify that
+     ICF flags are the same.  */
+  bool compare_edge_flags (cgraph_edge *e1, cgraph_edge *e2);
+
+  /* For a given symbol table nodes N1 and N2, we check that FUNCTION_DECLs
+     point to a same function. Comparison can be skipped if IGNORED_NODES
+     contains these nodes.  */
+  bool compare_cgraph_references (hash_map <symtab_node *, sem_item *>
+				  &ignored_nodes,
+				  symtab_node *n1, symtab_node *n2);
+
+  /* Processes function equality comparison.  */
+  bool equals_private (sem_item *item,
+		       hash_map <symtab_node *, sem_item *> &ignored_nodes);
+
+  /* Returns true if tree T can be compared as a handled component.  */
+  static bool icf_handled_component_p (tree t);
+
+  /* Function checker stores binding between functions.   */
+  ipa_icf_gimple::func_checker *m_checker;
+
+  /* COMPARED_FUNC is a function that we compare to.  */
+  sem_function *m_compared_func;
+}; // class sem_function
+
+class sem_variable: public sem_item
+{
+public:
+  /* Semantic variable constructor that uses STACK as bitmap memory stack.  */
+  sem_variable (bitmap_obstack *stack);
+
+  /*  Constructor based on callgraph node _NODE with computed hash _HASH.
+      Bitmap STACK is used for memory allocation.  */
+
+  sem_variable (varpool_node *_node, hashval_t _hash, bitmap_obstack *stack);
+
+  inline virtual void init_wpa (void) {}
+
+  /* Semantic variable initialization function.  */
+  inline virtual void init (void)
+  {
+    decl = get_node ()->decl;
+    ctor = ctor_for_folding (decl);
+  }
+
+  virtual hashval_t get_hash (void);
+  virtual bool merge (sem_item *alias_item);
+  virtual void dump_to_file (FILE *file);
+  virtual bool equals (sem_item *item,
+		       hash_map <symtab_node *, sem_item *> &ignored_nodes);
+
+  /* Fast equality variable based on knowledge known in WPA.  */
+  inline virtual bool equals_wpa (sem_item *item,
+				  hash_map <symtab_node *, sem_item *> & ARG_UNUSED(ignored_nodes))
+  {
+    gcc_assert (item->type == VAR);
+    return true;
+  }
+
+  /* Returns varpool_node.  */
+  inline varpool_node *get_node (void)
+  {
+    return dyn_cast <varpool_node *> (node);
+  }
+
+  /* Parser function that visits a varpool NODE.  */
+  static sem_variable *parse (varpool_node *node, bitmap_obstack *stack);
+
+  /* Variable constructor.  */
+  tree ctor;
+
+private:
+  /* Iterates though a constructor and identifies tree references
+     we are interested in semantic function equality.  */
+  void parse_tree_refs (tree t);
+
+  /* Compares trees T1 and T2 for semantic equality.  */
+  static bool equals (tree t1, tree t2);
+
+  /* Compare that symbol sections are either NULL or have same name.  */
+  bool compare_sections (sem_variable *alias);
+
+}; // class sem_variable
+
+class sem_item_optimizer;
+
+struct congruence_class_group
+{
+  hashval_t hash;
+  sem_item_type type;
+  vec <congruence_class *> classes;
+};
+
+/* Congruence class set structure.  */
+struct congruence_class_group_hash: typed_noop_remove <congruence_class_group>
+{
+  typedef congruence_class_group value_type;
+  typedef congruence_class_group compare_type;
+
+  static inline hashval_t hash (const value_type *item)
+  {
+    return item->hash;
+  }
+
+  static inline int equal (const value_type *item1, const compare_type *item2)
+  {
+    return item1->hash == item2->hash && item1->type == item2->type;
+  }
+};
+
+struct traverse_split_pair
+{
+  sem_item_optimizer *optimizer;
+  class congruence_class *cls;
+};
+
+/* Semantic item optimizer includes all top-level logic
+   related to semantic equality comparison.  */
+class sem_item_optimizer
+{
+public:
+  sem_item_optimizer ();
+  ~sem_item_optimizer ();
+
+  /* Function responsible for visiting all potential functions and
+     read-only variables that can be merged.  */
+  void parse_funcs_and_vars (void);
+
+  /* Optimizer entry point.  */
+  void execute (void);
+
+  /* Dump function. */
+  void dump (void);
+
+  /* Verify congruence classes if checking is enabled.  */
+  void verify_classes (void);
+
+  /* Write IPA ICF summary for symbols.  */
+  void write_summary (void);
+
+  /* Read IPA IPA ICF summary for symbols.  */
+  void read_summary (void);
+
+  /* Callgraph removal hook called for a NODE with a custom DATA.  */
+  static void cgraph_removal_hook (cgraph_node *node, void *data);
+
+  /* Varpool removal hook called for a NODE with a custom DATA.  */
+  static void varpool_removal_hook (varpool_node *node, void *data);
+
+  /* Worklist of congruence classes that can potentially
+     refine classes of congruence.  */
+  std::list<congruence_class *> worklist;
+
+  /* Remove semantic ITEM and release memory.  */
+  void remove_item (sem_item *item);
+
+  /* Remove symtab NODE triggered by symtab removal hooks.  */
+  void remove_symtab_node (symtab_node *node);
+
+  /* Register callgraph and varpool hooks.  */
+  void register_hooks (void);
+
+  /* Unregister callgraph and varpool hooks.  */
+  void unregister_hooks (void);
+
+  /* Adds a CLS to hashtable associated by hash value.  */
+  void add_class (congruence_class *cls);
+
+  /* Gets a congruence class group based on given HASH value and TYPE.  */
+  congruence_class_group *get_group_by_hash (hashval_t hash,
+      sem_item_type type);
+
+private:
+
+  /* Congruence classes are built by hash value.  */
+  void build_hash_based_classes (void);
+
+  /* Semantic items in classes having more than one element and initialized.
+     In case of WPA, we load function body.  */
+  void parse_nonsingleton_classes (void);
+
+  /* Equality function for semantic items is used to subdivide existing
+     classes. If IN_WPA, fast equality function is invoked.  */
+  void subdivide_classes_by_equality (bool in_wpa = false);
+
+  /* Debug function prints all informations about congruence classes.  */
+  void dump_cong_classes (void);
+
+  /* Build references according to call graph.  */
+  void build_graph (void);
+
+  /* Iterative congruence reduction function.  */
+  void process_cong_reduction (void);
+
+  /* After reduction is done, we can declare all items in a group
+     to be equal. PREV_CLASS_COUNT is start number of classes
+     before reduction.  */
+  void merge_classes (unsigned int prev_class_count);
+
+  /* Adds a newly created congruence class CLS to worklist.  */
+  void worklist_push (congruence_class *cls);
+
+  /* Pops a class from worklist. */
+  congruence_class *worklist_pop ();
+
+  /* Every usage of a congruence class CLS is a candidate that can split the
+     collection of classes. Bitmap stack BMSTACK is used for bitmap
+     allocation.  */
+  void do_congruence_step (congruence_class *cls);
+
+  /* Tests if a class CLS used as INDEXth splits any congruence classes.
+     Bitmap stack BMSTACK is used for bitmap allocation.  */
+  void do_congruence_step_for_index (congruence_class *cls, unsigned int index);
+
+  /* Makes pairing between a congruence class CLS and semantic ITEM.  */
+  static void add_item_to_class (congruence_class *cls, sem_item *item);
+
+  /* Disposes split map traverse function. CLS is congruence
+     class, BSLOT is bitmap slot we want to release. DATA is mandatory,
+     but unused argument.  */
+  static bool release_split_map (congruence_class * const &cls, bitmap const &b,
+				 traverse_split_pair *pair);
+
+  /* Process split operation for a cognruence class CLS,
+     where bitmap B splits congruence class members. DATA is used
+     as argument of split pair.  */
+  static bool traverse_congruence_split (congruence_class * const &cls,
+					 bitmap const &b,
+					 traverse_split_pair *pair);
+
+  /* Reads a section from LTO stream file FILE_DATA. Input block for DATA
+     contains LEN bytes.  */
+  void read_section (lto_file_decl_data *file_data, const char *data,
+		     size_t len);
+
+  /* Removes all callgraph and varpool nodes that are marked by symtab
+     as deleted.  */
+  void filter_removed_items (void);
+
+  /* Vector of semantic items.  */
+  vec <sem_item *> m_items;
+
+  /* A set containing all items removed by hooks.  */
+  hash_set <symtab_node *> m_removed_items_set;
+
+  /* Hashtable of congruence classes */
+  hash_table <congruence_class_group_hash> m_classes;
+
+  /* Count of congruence classes.  */
+  unsigned int m_classes_count;
+
+  /* Map data structure maps symtab nodes to semantic items.  */
+  hash_map <symtab_node *, sem_item *> m_symtab_node_map;
+
+  /* Set to true if a splitter class is removed.  */
+  bool splitter_class_removed;
+
+  /* Global unique class id counter.  */
+  static unsigned int class_id;
+
+  /* Callgraph node removal hook holder.  */
+  cgraph_node_hook_list *m_cgraph_node_hooks;
+
+  /* Varpool node removal hook holder.  */
+  varpool_node_hook_list *m_varpool_node_hooks;
+
+  /* Bitmap stack.  */
+  bitmap_obstack m_bmstack;
+}; // class sem_item_optimizer
+
+} // ipa_icf namespace
diff --git a/gcc/lto-cgraph.c b/gcc/lto-cgraph.c
index 0584946..c5aa797 100644
--- a/gcc/lto-cgraph.c
+++ b/gcc/lto-cgraph.c
@@ -539,6 +539,7 @@ lto_output_node (struct lto_simple_output_block *ob, struct cgraph_node *node,
   bp_pack_value (&bp, node->only_called_at_exit, 1);
   bp_pack_value (&bp, node->tm_clone, 1);
   bp_pack_value (&bp, node->calls_comdat_local, 1);
+  bp_pack_value (&bp, node->icf_merged, 1);
   bp_pack_value (&bp, node->thunk.thunk_p && !boundary_p, 1);
   bp_pack_enum (&bp, ld_plugin_symbol_resolution,
 	        LDPR_NUM_KNOWN, node->resolution);
@@ -1079,6 +1080,7 @@ input_overwrite_node (struct lto_file_decl_data *file_data,
   node->only_called_at_exit = bp_unpack_value (bp, 1);
   node->tm_clone = bp_unpack_value (bp, 1);
   node->calls_comdat_local = bp_unpack_value (bp, 1);
+  node->icf_merged = bp_unpack_value (bp, 1);
   node->thunk.thunk_p = bp_unpack_value (bp, 1);
   node->resolution = bp_unpack_enum (bp, ld_plugin_symbol_resolution,
 				     LDPR_NUM_KNOWN);
diff --git a/gcc/lto-section-in.c b/gcc/lto-section-in.c
index 5623706..c053545 100644
--- a/gcc/lto-section-in.c
+++ b/gcc/lto-section-in.c
@@ -60,7 +60,8 @@ const char *lto_section_name[LTO_N_SECTION_TYPES] =
   "opts",
   "cgraphopt",
   "inline",
-  "ipcp_trans"
+  "ipcp_trans",
+  "icf"
 };
 
 
diff --git a/gcc/lto-streamer.h b/gcc/lto-streamer.h
index 4bec969..63e4b32 100644
--- a/gcc/lto-streamer.h
+++ b/gcc/lto-streamer.h
@@ -247,6 +247,7 @@ enum lto_section_type
   LTO_section_cgraph_opt_sum,
   LTO_section_inline_summary,
   LTO_section_ipcp_transform,
+  LTO_section_ipa_icf,
   LTO_N_SECTION_TYPES		/* Must be last.  */
 };
 
diff --git a/gcc/opts.c b/gcc/opts.c
index 5cb5a39..7e9374a 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -497,6 +497,7 @@ static const struct default_options default_options_table[] =
     { OPT_LEVELS_2_PLUS, OPT_fvect_cost_model_, NULL, VECT_COST_MODEL_CHEAP },
     { OPT_LEVELS_2_PLUS_SPEED_ONLY, OPT_foptimize_strlen, NULL, 1 },
     { OPT_LEVELS_2_PLUS, OPT_fhoist_adjacent_loads, NULL, 1 },
+    { OPT_LEVELS_2_PLUS, OPT_fipa_icf, NULL, 1 },
     { OPT_LEVELS_2_PLUS, OPT_fisolate_erroneous_paths_dereference, NULL, 1 },
     { OPT_LEVELS_2_PLUS, OPT_fuse_caller_save, NULL, 1 },
 
@@ -1978,6 +1979,11 @@ common_handle_option (struct gcc_options *opts,
 	opts->x_flag_wrapv = 0;
       break;
 
+    case OPT_fipa_icf:
+	opts->x_flag_ipa_icf_functions = value;
+	opts->x_flag_ipa_icf_variables = value;
+      break;
+
     default:
       /* If the flag was handled in a standard way, assume the lack of
 	 processing here is intentional.  */
diff --git a/gcc/passes.def b/gcc/passes.def
index 801998f..b8efc9c 100644
--- a/gcc/passes.def
+++ b/gcc/passes.def
@@ -103,6 +103,7 @@ along with GCC; see the file COPYING3.  If not see
   INSERT_PASSES_AFTER (all_regular_ipa_passes)
   NEXT_PASS (pass_ipa_whole_program_visibility);
   NEXT_PASS (pass_ipa_profile);
+  NEXT_PASS (pass_ipa_icf);
   NEXT_PASS (pass_ipa_devirt);
   NEXT_PASS (pass_ipa_cp);
   NEXT_PASS (pass_ipa_cdtor_merge);
diff --git a/gcc/timevar.def b/gcc/timevar.def
index a04d05c..55a230b 100644
--- a/gcc/timevar.def
+++ b/gcc/timevar.def
@@ -90,6 +90,7 @@ DEFTIMEVAR (TV_WHOPR_LTRANS          , "whopr ltrans")
 DEFTIMEVAR (TV_IPA_REFERENCE         , "ipa reference")
 DEFTIMEVAR (TV_IPA_PROFILE           , "ipa profile")
 DEFTIMEVAR (TV_IPA_PURE_CONST        , "ipa pure const")
+DEFTIMEVAR (TV_IPA_ICF		     , "ipa icf")
 DEFTIMEVAR (TV_IPA_PTA               , "ipa points-to")
 DEFTIMEVAR (TV_IPA_SRA               , "ipa SRA")
 DEFTIMEVAR (TV_IPA_FREE_LANG_DATA    , "ipa free lang data")
diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h
index ed109c3..aac6703 100644
--- a/gcc/tree-pass.h
+++ b/gcc/tree-pass.h
@@ -461,6 +461,7 @@ extern simple_ipa_opt_pass *make_pass_ipa_free_lang_data (gcc::context *ctxt);
 extern simple_ipa_opt_pass *make_pass_ipa_free_inline_summary (gcc::context
 							       *ctxt);
 extern ipa_opt_pass_d *make_pass_ipa_cp (gcc::context *ctxt);
+extern ipa_opt_pass_d *make_pass_ipa_icf (gcc::context *ctxt);
 extern ipa_opt_pass_d *make_pass_ipa_devirt (gcc::context *ctxt);
 extern ipa_opt_pass_d *make_pass_ipa_reference (gcc::context *ctxt);
 extern ipa_opt_pass_d *make_pass_ipa_pure_const (gcc::context *ctxt);

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 3/5] IPA ICF pass
  2014-09-28  2:21                     ` Jan Hubicka
  2014-10-10 23:54                       ` Fakturace
@ 2014-10-11  0:02                       ` Martin Liška
  2014-10-11  8:23                         ` Jan Hubicka
  1 sibling, 1 reply; 70+ messages in thread
From: Martin Liška @ 2014-10-11  0:02 UTC (permalink / raw)
  To: gcc-patches; +Cc: hubicka >> Jan Hubicka

[-- Attachment #1: Type: text/plain, Size: 5516 bytes --]

On 09/28/2014 03:20 AM, Jan Hubicka wrote:
>>
>> Hi.
>>
>> Thank you Markus for presenting numbers, it corresponds with I measured. If I see correctly, IPA ICF pass takes about 7 seconds,
>> the rest is distributed in verifier (not interesting for release version of the compiler) and 'phase opt and generate'. No idea
>> what can make the difference?
> 
> phase opt and generate just combine all the optimization times together, so it
> is same 7 seconds as in the ICF pass :)
> 1GB of function bodies just to elimnate 2-3% of code seems quite alot. Do you
> have any idea how many of those turns out to be different?
> It would be nice to be able to release the duplicate bodies from memory after
> the equivalency was stablished....
> 
> Honza
> 
>>
>> Martin

(I resend the message, my mail client was a bit confused, please do _not_ reply to fakturace@foxlink.cz)

Hello.

After few days of measurement and tuning, I was able to get numbers to the following shape:
Execution times (seconds)
 phase setup             :   0.00 ( 0%) usr   0.00 ( 0%) sys   0.00 ( 0%) wall    1412 kB ( 0%) ggc
 phase opt and generate  :  27.83 (59%) usr   0.66 (19%) sys  28.52 (37%) wall 1028813 kB (24%) ggc
 phase stream in         :  16.90 (36%) usr   0.63 (18%) sys  17.60 (23%) wall 3246453 kB (76%) ggc
 phase stream out        :   2.76 ( 6%) usr   2.19 (63%) sys  31.34 (40%) wall       2 kB ( 0%) ggc
 callgraph optimization  :   0.36 ( 1%) usr   0.00 ( 0%) sys   0.35 ( 0%) wall      40 kB ( 0%) ggc
 ipa dead code removal   :   3.31 ( 7%) usr   0.01 ( 0%) sys   3.25 ( 4%) wall       0 kB ( 0%) ggc
 ipa virtual call target :   3.69 ( 8%) usr   0.03 ( 1%) sys   3.80 ( 5%) wall      21 kB ( 0%) ggc
 ipa devirtualization    :   0.12 ( 0%) usr   0.00 ( 0%) sys   0.15 ( 0%) wall   13704 kB ( 0%) ggc
 ipa cp                  :   1.11 ( 2%) usr   0.07 ( 2%) sys   1.17 ( 2%) wall  188558 kB ( 4%) ggc
 ipa inlining heuristics :   8.17 (17%) usr   0.14 ( 4%) sys   8.27 (11%) wall  494738 kB (12%) ggc
 ipa comdats             :   0.12 ( 0%) usr   0.00 ( 0%) sys   0.12 ( 0%) wall       0 kB ( 0%) ggc
 ipa lto gimple in       :   1.86 ( 4%) usr   0.40 (11%) sys   2.20 ( 3%) wall  537970 kB (13%) ggc
 ipa lto gimple out      :   0.19 ( 0%) usr   0.08 ( 2%) sys   0.27 ( 0%) wall       2 kB ( 0%) ggc
 ipa lto decl in         :  12.20 (26%) usr   0.37 (11%) sys  12.64 (16%) wall 2441687 kB (57%) ggc
 ipa lto decl out        :   2.51 ( 5%) usr   0.21 ( 6%) sys   2.71 ( 3%) wall       0 kB ( 0%) ggc
 ipa lto constructors in :   0.13 ( 0%) usr   0.02 ( 1%) sys   0.17 ( 0%) wall   15692 kB ( 0%) ggc
 ipa lto constructors out:   0.03 ( 0%) usr   0.00 ( 0%) sys   0.03 ( 0%) wall       0 kB ( 0%) ggc
 ipa lto cgraph I/O      :   0.54 ( 1%) usr   0.09 ( 3%) sys   0.63 ( 1%) wall  407182 kB (10%) ggc
 ipa lto decl merge      :   1.34 ( 3%) usr   0.00 ( 0%) sys   1.34 ( 2%) wall    8220 kB ( 0%) ggc
 ipa lto cgraph merge    :   1.00 ( 2%) usr   0.00 ( 0%) sys   1.00 ( 1%) wall   14605 kB ( 0%) ggc
 whopr wpa               :   0.92 ( 2%) usr   0.00 ( 0%) sys   0.89 ( 1%) wall       1 kB ( 0%) ggc
 whopr wpa I/O           :   0.01 ( 0%) usr   1.90 (55%) sys  28.31 (37%) wall       0 kB ( 0%) ggc
 whopr partitioning      :   2.81 ( 6%) usr   0.01 ( 0%) sys   2.83 ( 4%) wall    4943 kB ( 0%) ggc
 ipa reference           :   1.34 ( 3%) usr   0.00 ( 0%) sys   1.35 ( 2%) wall       0 kB ( 0%) ggc
 ipa profile             :   0.20 ( 0%) usr   0.01 ( 0%) sys   0.21 ( 0%) wall       0 kB ( 0%) ggc
 ipa pure const          :   1.62 ( 3%) usr   0.00 ( 0%) sys   1.63 ( 2%) wall       0 kB ( 0%) ggc
 ipa icf                 :   2.65 ( 6%) usr   0.02 ( 1%) sys   2.68 ( 3%) wall    1352 kB ( 0%) ggc
 inline parameters       :   0.00 ( 0%) usr   0.01 ( 0%) sys   0.00 ( 0%) wall       0 kB ( 0%) ggc
 tree SSA rewrite        :   0.11 ( 0%) usr   0.01 ( 0%) sys   0.08 ( 0%) wall   18919 kB ( 0%) ggc
 tree SSA other          :   0.01 ( 0%) usr   0.00 ( 0%) sys   0.01 ( 0%) wall       0 kB ( 0%) ggc
 tree SSA incremental    :   0.24 ( 1%) usr   0.01 ( 0%) sys   0.32 ( 0%) wall   11325 kB ( 0%) ggc
 tree operand scan       :   0.15 ( 0%) usr   0.02 ( 1%) sys   0.18 ( 0%) wall  116283 kB ( 3%) ggc
 dominance frontiers     :   0.01 ( 0%) usr   0.00 ( 0%) sys   0.02 ( 0%) wall       0 kB ( 0%) ggc
 dominance computation   :   0.13 ( 0%) usr   0.01 ( 0%) sys   0.16 ( 0%) wall       0 kB ( 0%) ggc
 varconst                :   0.01 ( 0%) usr   0.02 ( 1%) sys   0.01 ( 0%) wall       0 kB ( 0%) ggc
 loop fini               :   0.02 ( 0%) usr   0.00 ( 0%) sys   0.04 ( 0%) wall       0 kB ( 0%) ggc
 unaccounted todo        :   0.55 ( 1%) usr   0.00 ( 0%) sys   0.56 ( 1%) wall       0 kB ( 0%) ggc
 TOTAL                 :  47.49             3.48            77.46            4276682 kB

and I was able to reduce function bodies loaded in WPA to 35% (from previous 55%). The main problem
with speed was hidden in work list for congruence classes, where hash_set was used. I chose the data
structure to support delete operation, but it was really slow. Thus, hash_set was replaced with linked list
and a flag is used to identify if a set is removed or not.

I have no clue who complicated can it be to implement release_body function to an operation that
really releases the memory?

Markus' problem with -fprofile-use has been removed, IPA-ICF is preceding devirtualization pass. I hope it is fine?

There's new version of the patch and I plan to comment both Honza's emails where he pointed some nits.

Thanks,
Martin

[-- Attachment #2: ipa-icf-3.patch --]
[-- Type: text/x-patch, Size: 122464 bytes --]

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index b38f8ef..f2580ba 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1265,6 +1265,8 @@ OBJS = \
 	ipa-profile.o \
 	ipa-prop.o \
 	ipa-pure-const.o \
+	ipa-icf.o \
+	ipa-icf-gimple.o \
 	ipa-reference.o \
 	ipa-ref.o \
 	ipa-utils.o \
diff --git a/gcc/cgraph.c b/gcc/cgraph.c
index fdcaf79..07e7ecb 100644
--- a/gcc/cgraph.c
+++ b/gcc/cgraph.c
@@ -1913,6 +1913,8 @@ cgraph_node::dump (FILE *f)
     fprintf (f, " only_called_at_exit");
   if (tm_clone)
     fprintf (f, " tm_clone");
+  if (icf_merged)
+    fprintf (f, " icf_merged");
   if (DECL_STATIC_CONSTRUCTOR (decl))
     fprintf (f," static_constructor (priority:%i)", get_init_priority ());
   if (DECL_STATIC_DESTRUCTOR (decl))
@@ -2561,6 +2563,7 @@ verify_edge_corresponds_to_fndecl (cgraph_edge *e, tree decl)
   if (!node
       || node->body_removed
       || node->in_other_partition
+      || node->icf_merged
       || e->callee->in_other_partition)
     return false;
 
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index fb41b01..2de98b4 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -172,6 +172,12 @@ public:
   /* Dump referring in list to FILE.  */
   void dump_referring (FILE *);
 
+  /* Get number of references for this node.  */
+  inline unsigned get_references_count (void)
+  {
+    return ref_list.references ? ref_list.references->length () : 0;
+  }
+
   /* Iterates I-th reference in the list, REF is also set.  */
   ipa_ref *iterate_reference (unsigned i, ipa_ref *&ref);
 
@@ -1230,6 +1236,8 @@ public:
   /* True if this decl calls a COMDAT-local function.  This is set up in
      compute_inline_parameters and inline_call.  */
   unsigned calls_comdat_local : 1;
+  /* True if node has been created by merge operation in IPA-ICF.  */
+  unsigned icf_merged: 1;
 };
 
 /* A cgraph node set is a collection of cgraph nodes.  A cgraph node
diff --git a/gcc/common.opt b/gcc/common.opt
index b4f0ed4..5db5e1e 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -1443,6 +1443,18 @@ fipa-pure-const
 Common Report Var(flag_ipa_pure_const) Init(0) Optimization
 Discover pure and const functions
 
+fipa-icf
+Common Report Var(flag_ipa_icf) Optimization
+Perform Identical Code Folding for functions and read-only variables
+
+fipa-icf-functions
+Common Report Var(flag_ipa_icf_functions) Optimization
+Perform Identical Code Folding for functions
+
+fipa-icf-variables
+Common Report Var(flag_ipa_icf_variables) Optimization
+Perform Identical Code Folding for variables
+
 fipa-reference
 Common Report Var(flag_ipa_reference) Init(0) Optimization
 Discover readonly and non addressable static variables
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 5fe7e15..38356e4 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -382,7 +382,7 @@ Objective-C and Objective-C++ Dialects}.
 -fif-conversion2 -findirect-inlining @gol
 -finline-functions -finline-functions-called-once -finline-limit=@var{n} @gol
 -finline-small-functions -fipa-cp -fipa-cp-clone @gol
--fipa-pta -fipa-profile -fipa-pure-const -fipa-reference @gol
+-fipa-pta -fipa-profile -fipa-pure-const -fipa-reference -fipa-icf @gol
 -fira-algorithm=@var{algorithm} @gol
 -fira-region=@var{region} -fira-hoist-pressure @gol
 -fira-loop-pressure -fno-ira-share-save-slots @gol
@@ -7123,6 +7123,7 @@ also turns on the following optimization flags:
 -findirect-inlining @gol
 -fipa-cp @gol
 -fipa-sra @gol
+-fipa-icf @gol
 -fisolate-erroneous-paths-dereference @gol
 -foptimize-sibling-calls @gol
 -foptimize-strlen @gol
@@ -8068,6 +8069,19 @@ it may significantly increase code size
 (see @option{--param ipcp-unit-growth=@var{value}}).
 This flag is enabled by default at @option{-O3}.
 
+@item -fipa-icf
+@opindex fipa-icf
+Perform Identical Code Folding for functions and read-only variables.
+The optimization reduces code size and may disturb unwind stacks by replacing
+a function by equivalent one with a different name. The optimization works
+more effectively with link time optimization enabled.
+
+Nevertheless the behavior is similar to Gold Linker ICF optimization, GCC ICF
+works on different levels and thus the optimizations are not same - there are
+equivalences that are found only by GCC and equivalences found only by Gold.
+
+This flag is enabled by default at @option{-O2}.
+
 @item -fisolate-erroneous-paths-dereference
 Detect paths which trigger erroneous or undefined behaviour due to
 dereferencing a NULL pointer.  Isolate those paths from the main control
diff --git a/gcc/ipa-icf-gimple.c b/gcc/ipa-icf-gimple.c
new file mode 100644
index 0000000..2f036fe
--- /dev/null
+++ b/gcc/ipa-icf-gimple.c
@@ -0,0 +1,889 @@
+/* Interprocedural Identical Code Folding pass
+   Copyright (C) 2014 Free Software Foundation, Inc.
+
+   Contributed by Jan Hubicka <hubicka@ucw.cz> and Martin Liska <mliska@suse.cz>
+
+This file is part of GCC.
+
+GCC 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, or (at your option) any later
+version.
+
+GCC 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 GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tree.h"
+#include "basic-block.h"
+#include "tree-ssa-alias.h"
+#include "internal-fn.h"
+#include "gimple-expr.h"
+#include "is-a.h"
+#include "gimple.h"
+#include "expr.h"
+#include "gimple-iterator.h"
+#include "gimple-ssa.h"
+#include "tree-cfg.h"
+#include "stringpool.h"
+#include "tree-dfa.h"
+#include "tree-pass.h"
+#include "gimple-pretty-print.h"
+#include "cfgloop.h"
+#include "except.h"
+#include "data-streamer.h"
+#include "ipa-utils.h"
+#include <list>
+#include "tree-ssanames.h"
+#include "tree-eh.h"
+
+#include "ipa-icf-gimple.h"
+#include "ipa-icf.h"
+
+namespace ipa_icf_gimple {
+
+/* Initialize internal structures for a given SOURCE_FUNC_DECL and
+   TARGET_FUNC_DECL. Strict polymorphic comparison is processed if
+   an option COMPARE_POLYMORPHIC is true. For special cases, one can
+   set IGNORE_LABELS to skip label comparison.
+   Similarly, IGNORE_SOURCE_DECLS and IGNORE_TARGET_DECLS are sets
+   of declarations that can be skipped.  */
+
+func_checker::func_checker (tree source_func_decl, tree target_func_decl,
+			    bool compare_polymorphic,
+			    bool ignore_labels,
+			    hash_set<symtab_node *> *ignored_source_nodes,
+			    hash_set<symtab_node *> *ignored_target_nodes)
+  : m_source_func_decl (source_func_decl), m_target_func_decl (target_func_decl),
+    m_ignored_source_nodes (ignored_source_nodes),
+    m_ignored_target_nodes (ignored_target_nodes),
+    m_compare_polymorphic (compare_polymorphic),
+    m_ignore_labels (ignore_labels)
+{
+  function *source_func = DECL_STRUCT_FUNCTION (source_func_decl);
+  function *target_func = DECL_STRUCT_FUNCTION (target_func_decl);
+
+  unsigned ssa_source = SSANAMES (source_func)->length ();
+  unsigned ssa_target = SSANAMES (target_func)->length ();
+
+  m_source_ssa_names.create (ssa_source);
+  m_target_ssa_names.create (ssa_target);
+
+  for (unsigned i = 0; i < ssa_source; i++)
+    m_source_ssa_names.safe_push (-1);
+
+  for (unsigned i = 0; i < ssa_target; i++)
+    m_target_ssa_names.safe_push (-1);
+}
+
+/* Memory release routine.  */
+
+func_checker::~func_checker ()
+{
+  m_source_ssa_names.release();
+  m_target_ssa_names.release();
+}
+
+/* Verifies that trees T1 and T2 are equivalent from perspective of ICF.  */
+
+bool
+func_checker::compare_ssa_name (tree t1, tree t2)
+{
+  unsigned i1 = SSA_NAME_VERSION (t1);
+  unsigned i2 = SSA_NAME_VERSION (t2);
+
+  if (m_source_ssa_names[i1] == -1)
+    m_source_ssa_names[i1] = i2;
+  else if (m_source_ssa_names[i1] != (int) i2)
+    return false;
+
+  if(m_target_ssa_names[i2] == -1)
+    m_target_ssa_names[i2] = i1;
+  else if (m_target_ssa_names[i2] != (int) i1)
+    return false;
+
+  return true;
+}
+
+/* Verification function for edges E1 and E2.  */
+
+bool
+func_checker::compare_edge (edge e1, edge e2)
+{
+  if (e1->flags != e2->flags)
+    return false;
+
+  bool existed_p;
+
+  edge &slot = m_edge_map.get_or_insert (e1, &existed_p);
+  if (existed_p)
+    return return_with_debug (slot == e2);
+  else
+    slot = e2;
+
+  /* TODO: filter edge probabilities for profile feedback match.  */
+
+  return true;
+}
+
+/* Verification function for declaration trees T1 and T2 that
+   come from functions FUNC1 and FUNC2.  */
+
+bool
+func_checker::compare_decl (tree t1, tree t2)
+{
+  if (!auto_var_in_fn_p (t1, m_source_func_decl)
+      || !auto_var_in_fn_p (t2, m_target_func_decl))
+    return return_with_debug (t1 == t2);
+
+  tree_code t = TREE_CODE (t1);
+  if ((t == VAR_DECL || t == PARM_DECL || t == RESULT_DECL)
+      && DECL_BY_REFERENCE (t1) != DECL_BY_REFERENCE (t2))
+    return return_false_with_msg ("DECL_BY_REFERENCE flags are different");
+
+  if (!compatible_types_p (TREE_TYPE (t1), TREE_TYPE (t2),
+			   m_compare_polymorphic))
+    return return_false ();
+
+  bool existed_p;
+
+  tree &slot = m_decl_map.get_or_insert (t1, &existed_p);
+  if (existed_p)
+    return return_with_debug (slot == t2);
+  else
+    slot = t2;
+
+  return true;
+}
+
+/* Return true if types are compatible from perspective of ICF.  */
+bool func_checker::compatible_types_p (tree t1, tree t2,
+				       bool compare_polymorphic,
+				       bool first_argument)
+{
+  if (TREE_CODE (t1) != TREE_CODE (t2))
+    return return_false_with_msg ("different tree types");
+
+  if (!types_compatible_p (t1, t2))
+    return return_false_with_msg ("types are not compatible");
+
+  if (get_alias_set (t1) != get_alias_set (t2))
+    return return_false_with_msg ("alias sets are different");
+
+  /* We call contains_polymorphic_type_p with this pointer type.  */
+  if (first_argument && TREE_CODE (t1) == POINTER_TYPE)
+    {
+      t1 = TREE_TYPE (t1);
+      t2 = TREE_TYPE (t2);
+    }
+
+  if (compare_polymorphic)
+    if (contains_polymorphic_type_p (t1) || contains_polymorphic_type_p (t2))
+      {
+	if (!contains_polymorphic_type_p (t1) || !contains_polymorphic_type_p (t2))
+	  return return_false_with_msg ("one type is not polymorphic");
+
+	if (!types_must_be_same_for_odr (t1, t2))
+	  return return_false_with_msg ("types are not same for ODR");
+      }
+
+  return true;
+}
+
+
+
+/* Function responsible for comparison of handled components T1 and T2.
+   If these components, from functions FUNC1 and FUNC2, are equal, true
+   is returned.  */
+
+bool
+func_checker::compare_operand (tree t1, tree t2)
+{
+  tree base1, base2, x1, x2, y1, y2, z1, z2;
+  HOST_WIDE_INT offset1 = 0, offset2 = 0;
+  bool ret;
+
+  if (!t1 && !t2)
+    return true;
+  else if (!t1 || !t2)
+    return false;
+
+  tree tt1 = TREE_TYPE (t1);
+  tree tt2 = TREE_TYPE (t2);
+
+  if (!func_checker::compatible_types_p (tt1, tt2))
+    return false;
+
+  base1 = get_addr_base_and_unit_offset (t1, &offset1);
+  base2 = get_addr_base_and_unit_offset (t2, &offset2);
+
+  if (base1 && base2)
+    {
+      if (offset1 != offset2)
+	return return_false_with_msg ("base offsets are different");
+
+      t1 = base1;
+      t2 = base2;
+    }
+
+  if (TREE_CODE (t1) != TREE_CODE (t2))
+    return return_false ();
+
+  switch (TREE_CODE (t1))
+    {
+    case CONSTRUCTOR:
+      {
+	unsigned length1 = vec_safe_length (CONSTRUCTOR_ELTS (t1));
+	unsigned length2 = vec_safe_length (CONSTRUCTOR_ELTS (t2));
+
+	if (length1 != length2)
+	  return return_false ();
+
+	for (unsigned i = 0; i < length1; i++)
+	  if (!compare_operand (CONSTRUCTOR_ELT (t1, i)->value,
+				CONSTRUCTOR_ELT (t2, i)->value))
+	    return return_false();
+
+	return true;
+      }
+    case ARRAY_REF:
+    case ARRAY_RANGE_REF:
+      {
+	x1 = TREE_OPERAND (t1, 0);
+	x2 = TREE_OPERAND (t2, 0);
+	y1 = TREE_OPERAND (t1, 1);
+	y2 = TREE_OPERAND (t2, 1);
+
+	if (!compare_operand (array_ref_low_bound (t1),
+			      array_ref_low_bound (t2)))
+	  return return_false_with_msg ("");
+	if (!compare_operand (array_ref_element_size (t1),
+			      array_ref_element_size (t2)))
+	  return return_false_with_msg ("");
+	if (!compare_operand (x1, x2))
+	  return return_false_with_msg ("");
+	return compare_operand (y1, y2);
+      }
+
+    case MEM_REF:
+      {
+	x1 = TREE_OPERAND (t1, 0);
+	x2 = TREE_OPERAND (t2, 0);
+	y1 = TREE_OPERAND (t1, 1);
+	y2 = TREE_OPERAND (t2, 1);
+
+	/* See if operand is an memory access (the test originate from
+	 gimple_load_p).
+
+	In this case the alias set of the function being replaced must
+	be subset of the alias set of the other function.  At the moment
+	we seek for equivalency classes, so simply require inclussion in
+	both directions.  */
+
+	if (!func_checker::compatible_types_p (TREE_TYPE (x1), TREE_TYPE (x2)))
+	  return return_false ();
+
+	if (!compare_operand (x1, x2))
+	  return return_false_with_msg ("");
+
+	if (get_alias_set (TREE_TYPE (y1)) != get_alias_set (TREE_TYPE (y2)))
+	  return return_false_with_msg ("alias set for MEM_REF offsets are different");
+
+	ao_ref r1, r2;
+	ao_ref_init (&r1, t1);
+	ao_ref_init (&r2, t2);
+	if (ao_ref_alias_set (&r1) != ao_ref_alias_set (&r2)
+	    || ao_ref_base_alias_set (&r1) != ao_ref_base_alias_set (&r2))
+	  return return_false_with_msg ("ao alias sets are different");
+
+	/* Type of the offset on MEM_REF does not matter.  */
+	return wi::to_offset  (y1) == wi::to_offset  (y2);
+      }
+    case COMPONENT_REF:
+      {
+	x1 = TREE_OPERAND (t1, 0);
+	x2 = TREE_OPERAND (t2, 0);
+	y1 = TREE_OPERAND (t1, 1);
+	y2 = TREE_OPERAND (t2, 1);
+
+	ret = compare_operand (x1, x2)
+	      && compare_operand (y1, y2);
+
+	return return_with_debug (ret);
+      }
+    /* Virtual table call.  */
+    case OBJ_TYPE_REF:
+      {
+	x1 = TREE_OPERAND (t1, 0);
+	x2 = TREE_OPERAND (t2, 0);
+	y1 = TREE_OPERAND (t1, 1);
+	y2 = TREE_OPERAND (t2, 1);
+	z1 = TREE_OPERAND (t1, 2);
+	z2 = TREE_OPERAND (t2, 2);
+
+	ret = compare_operand (x1, x2)
+	      && compare_operand (y1, y2)
+	      && compare_operand (z1, z2);
+
+	return return_with_debug (ret);
+      }
+    case ADDR_EXPR:
+      {
+	x1 = TREE_OPERAND (t1, 0);
+	x2 = TREE_OPERAND (t2, 0);
+
+	ret = compare_operand (x1, x2);
+	return return_with_debug (ret);
+      }
+    case SSA_NAME:
+      {
+	ret = compare_ssa_name (t1, t2);
+
+	if (!ret)
+	  return return_with_debug (ret);
+
+	if (SSA_NAME_IS_DEFAULT_DEF (t1))
+	  {
+	    tree b1 = SSA_NAME_VAR (t1);
+	    tree b2 = SSA_NAME_VAR (t2);
+
+	    if (b1 == NULL && b2 == NULL)
+	      return true;
+
+	    if (b1 == NULL || b2 == NULL || TREE_CODE (b1) != TREE_CODE (b2))
+	      return return_false ();
+
+	    switch (TREE_CODE (b1))
+	      {
+	      case VAR_DECL:
+		return return_with_debug (compare_variable_decl (t1, t2));
+	      case PARM_DECL:
+	      case RESULT_DECL:
+		ret = compare_decl (b1, b2);
+		return return_with_debug (ret);
+	      default:
+		return return_false_with_msg ("Unknown TREE code reached");
+	      }
+	  }
+	else
+	  return true;
+      }
+    case INTEGER_CST:
+      {
+	ret = compatible_types_p (TREE_TYPE (t1), TREE_TYPE (t2))
+	      && wi::to_offset  (t1) == wi::to_offset  (t2);
+
+	return return_with_debug (ret);
+      }
+    case COMPLEX_CST:
+    case VECTOR_CST:
+    case STRING_CST:
+    case REAL_CST:
+      {
+	ret = operand_equal_p (t1, t2, OEP_ONLY_CONST);
+	return return_with_debug (ret);
+      }
+    case FUNCTION_DECL:
+      {
+	ret = compare_function_decl (t1, t2);
+	return return_with_debug (ret);
+      }
+    case VAR_DECL:
+      return return_with_debug (compare_variable_decl (t1, t2));
+    case FIELD_DECL:
+      {
+	tree offset1 = DECL_FIELD_OFFSET (t1);
+	tree offset2 = DECL_FIELD_OFFSET (t2);
+
+	tree bit_offset1 = DECL_FIELD_BIT_OFFSET (t1);
+	tree bit_offset2 = DECL_FIELD_BIT_OFFSET (t2);
+
+	ret = compare_operand (offset1, offset2)
+	      && compare_operand (bit_offset1, bit_offset2);
+
+	return return_with_debug (ret);
+      }
+    case LABEL_DECL:
+      {
+	int *bb1 = m_label_bb_map.get (t1);
+	int *bb2 = m_label_bb_map.get (t2);
+
+	return return_with_debug (*bb1 == *bb2);
+      }
+    case PARM_DECL:
+    case RESULT_DECL:
+    case CONST_DECL:
+    case BIT_FIELD_REF:
+      {
+	ret = compare_decl (t1, t2);
+	return return_with_debug (ret);
+      }
+    default:
+      return return_false_with_msg ("Unknown TREE code reached");
+    }
+}
+
+
+/* Verifies that trees T1 and T2, representing function declarations
+   are equivalent from perspective of ICF.  */
+
+bool
+func_checker::compare_function_decl (tree t1, tree t2)
+{
+  bool ret = false;
+
+  if (t1 == t2)
+    return true;
+
+  symtab_node *n1 = symtab_node::get (t1);
+  symtab_node *n2 = symtab_node::get (t2);
+
+  if (m_ignored_source_nodes != NULL && m_ignored_target_nodes != NULL)
+    {
+      ret = m_ignored_source_nodes->contains (n1)
+	    && m_ignored_target_nodes->contains (n2);
+
+      if (ret)
+	return true;
+    }
+
+  /* If function decl is WEAKREF, we compare targets.  */
+  cgraph_node *f1 = cgraph_node::get (t1);
+  cgraph_node *f2 = cgraph_node::get (t2);
+
+  if(f1 && f2 && f1->weakref && f2->weakref)
+    ret = f1->alias_target == f2->alias_target;
+
+  return ret;
+}
+
+/* Verifies that trees T1 and T2 do correspond.  */
+
+bool
+func_checker::compare_variable_decl (tree t1, tree t2)
+{
+  bool ret = false;
+
+  if (t1 == t2)
+    return true;
+
+  if (TREE_CODE (t1) == VAR_DECL && (DECL_EXTERNAL (t1) || TREE_STATIC (t1)))
+    {
+      symtab_node *n1 = symtab_node::get (t1);
+      symtab_node *n2 = symtab_node::get (t2);
+
+      if (m_ignored_source_nodes != NULL && m_ignored_target_nodes != NULL)
+	{
+	  ret = m_ignored_source_nodes->contains (n1)
+		&& m_ignored_target_nodes->contains (n2);
+
+	  if (ret)
+	    return true;
+	}
+    }
+  ret = compare_decl (t1, t2);
+
+  return return_with_debug (ret);
+}
+
+void
+func_checker::parse_labels (sem_bb *bb)
+{
+  for (gimple_stmt_iterator gsi = gsi_start_bb (bb->bb); !gsi_end_p (gsi);
+       gsi_next (&gsi))
+    {
+      gimple stmt = gsi_stmt (gsi);
+
+      if (gimple_code (stmt) == GIMPLE_LABEL)
+	{
+	  tree t = gimple_label_label (stmt);
+	  gcc_assert (TREE_CODE (t) == LABEL_DECL);
+
+	  m_label_bb_map.put (t, bb->bb->index);
+	}
+    }
+}
+
+/* Basic block equivalence comparison function that returns true if
+   basic blocks BB1 and BB2 (from functions FUNC1 and FUNC2) correspond.
+
+   In general, a collection of equivalence dictionaries is built for types
+   like SSA names, declarations (VAR_DECL, PARM_DECL, ..). This infrastructure
+   is utilized by every statement-by-stament comparison function.  */
+
+bool
+func_checker::compare_bb (sem_bb *bb1, sem_bb *bb2)
+{
+  unsigned i;
+  gimple_stmt_iterator gsi1, gsi2;
+  gimple s1, s2;
+
+  if (bb1->nondbg_stmt_count != bb2->nondbg_stmt_count
+      || bb1->edge_count != bb2->edge_count)
+    return return_false ();
+
+  gsi1 = gsi_start_bb (bb1->bb);
+  gsi2 = gsi_start_bb (bb2->bb);
+
+  for (i = 0; i < bb1->nondbg_stmt_count; i++)
+    {
+      if (is_gimple_debug (gsi_stmt (gsi1)))
+	gsi_next_nondebug (&gsi1);
+
+      if (is_gimple_debug (gsi_stmt (gsi2)))
+	gsi_next_nondebug (&gsi2);
+
+      s1 = gsi_stmt (gsi1);
+      s2 = gsi_stmt (gsi2);
+
+      int eh1 = lookup_stmt_eh_lp_fn
+		(DECL_STRUCT_FUNCTION (m_source_func_decl), s1);
+      int eh2 = lookup_stmt_eh_lp_fn
+		(DECL_STRUCT_FUNCTION (m_target_func_decl), s2);
+
+      if (eh1 != eh2)
+	return return_false_with_msg ("EH regions are different");
+
+      if (gimple_code (s1) != gimple_code (s2))
+	return return_false_with_msg ("gimple codes are different");
+
+      switch (gimple_code (s1))
+	{
+	case GIMPLE_CALL:
+	  if (!compare_gimple_call (s1, s2))
+	    return return_different_stmts (s1, s2, "GIMPLE_CALL");
+	  break;
+	case GIMPLE_ASSIGN:
+	  if (!compare_gimple_assign (s1, s2))
+	    return return_different_stmts (s1, s2, "GIMPLE_ASSIGN");
+	  break;
+	case GIMPLE_COND:
+	  if (!compare_gimple_cond (s1, s2))
+	    return return_different_stmts (s1, s2, "GIMPLE_COND");
+	  break;
+	case GIMPLE_SWITCH:
+	  if (!compare_gimple_switch (s1, s2))
+	    return return_different_stmts (s1, s2, "GIMPLE_SWITCH");
+	  break;
+	case GIMPLE_DEBUG:
+	case GIMPLE_EH_DISPATCH:
+	  break;
+	case GIMPLE_RESX:
+	  if (!compare_gimple_resx (s1, s2))
+	    return return_different_stmts (s1, s2, "GIMPLE_RESX");
+	  break;
+	case GIMPLE_LABEL:
+	  if (!compare_gimple_label (s1, s2))
+	    return return_different_stmts (s1, s2, "GIMPLE_LABEL");
+	  break;
+	case GIMPLE_RETURN:
+	  if (!compare_gimple_return (s1, s2))
+	    return return_different_stmts (s1, s2, "GIMPLE_RETURN");
+	  break;
+	case GIMPLE_GOTO:
+	  if (!compare_gimple_goto (s1, s2))
+	    return return_different_stmts (s1, s2, "GIMPLE_GOTO");
+	  break;
+	case GIMPLE_ASM:
+	  if (!compare_gimple_asm (s1, s2))
+	    return return_different_stmts (s1, s2, "GIMPLE_ASM");
+	  break;
+	case GIMPLE_PREDICT:
+	case GIMPLE_NOP:
+	  return true;
+	default:
+	  return return_false_with_msg ("Unknown GIMPLE code reached");
+	}
+
+      gsi_next (&gsi1);
+      gsi_next (&gsi2);
+    }
+
+  return true;
+}
+
+/* Verifies for given GIMPLEs S1 and S2 that
+   call statements are semantically equivalent.  */
+
+bool
+func_checker::compare_gimple_call (gimple s1, gimple s2)
+{
+  unsigned i;
+  tree t1, t2;
+
+  if (gimple_call_num_args (s1) != gimple_call_num_args (s2))
+    return false;
+
+  t1 = gimple_call_fndecl (s1);
+  t2 = gimple_call_fndecl (s2);
+
+  /* Function pointer variables are not supported yet.  */
+  if (t1 == NULL || t2 == NULL)
+    {
+      if (!compare_operand (t1, t2))
+	return return_false();
+    }
+  else if (!compare_function_decl (t1, t2))
+    return false;
+
+  /* Checking of argument.  */
+  for (i = 0; i < gimple_call_num_args (s1); ++i)
+    {
+      t1 = gimple_call_arg (s1, i);
+      t2 = gimple_call_arg (s2, i);
+
+      if (!compare_operand (t1, t2))
+	return false;
+    }
+
+  /* Return value checking.  */
+  t1 = gimple_get_lhs (s1);
+  t2 = gimple_get_lhs (s2);
+
+  return compare_operand (t1, t2);
+}
+
+
+/* Verifies for given GIMPLEs S1 and S2 that
+   assignment statements are semantically equivalent.  */
+
+bool
+func_checker::compare_gimple_assign (gimple s1, gimple s2)
+{
+  tree arg1, arg2;
+  tree_code code1, code2;
+  unsigned i;
+
+  code1 = gimple_expr_code (s1);
+  code2 = gimple_expr_code (s2);
+
+  if (code1 != code2)
+    return false;
+
+  code1 = gimple_assign_rhs_code (s1);
+  code2 = gimple_assign_rhs_code (s2);
+
+  if (code1 != code2)
+    return false;
+
+  for (i = 0; i < gimple_num_ops (s1); i++)
+    {
+      arg1 = gimple_op (s1, i);
+      arg2 = gimple_op (s2, i);
+
+      if (!compare_operand (arg1, arg2))
+	return false;
+    }
+
+
+  return true;
+}
+
+/* Verifies for given GIMPLEs S1 and S2 that
+   condition statements are semantically equivalent.  */
+
+bool
+func_checker::compare_gimple_cond (gimple s1, gimple s2)
+{
+  tree t1, t2;
+  tree_code code1, code2;
+
+  code1 = gimple_expr_code (s1);
+  code2 = gimple_expr_code (s2);
+
+  if (code1 != code2)
+    return false;
+
+  t1 = gimple_cond_lhs (s1);
+  t2 = gimple_cond_lhs (s2);
+
+  if (!compare_operand (t1, t2))
+    return false;
+
+  t1 = gimple_cond_rhs (s1);
+  t2 = gimple_cond_rhs (s2);
+
+  return compare_operand (t1, t2);
+}
+
+/* Verifies that tree labels T1 and T2 correspond in FUNC1 and FUNC2.  */
+
+bool
+func_checker::compare_tree_ssa_label (tree t1, tree t2)
+{
+  return compare_operand (t1, t2);
+}
+
+/* Verifies for given GIMPLEs S1 and S2 that
+   label statements are semantically equivalent.  */
+
+bool
+func_checker::compare_gimple_label (gimple g1, gimple g2)
+{
+  if (m_ignore_labels)
+    return true;
+
+  tree t1 = gimple_label_label (g1);
+  tree t2 = gimple_label_label (g2);
+
+  return compare_tree_ssa_label (t1, t2);
+}
+
+/* Verifies for given GIMPLEs S1 and S2 that
+   switch statements are semantically equivalent.  */
+
+bool
+func_checker::compare_gimple_switch (gimple g1, gimple g2)
+{
+  unsigned lsize1, lsize2, i;
+
+  lsize1 = gimple_switch_num_labels (g1);
+  lsize2 = gimple_switch_num_labels (g2);
+
+  if (lsize1 != lsize2)
+    return false;
+
+  tree t1 = gimple_switch_index (g1);
+  tree t2 = gimple_switch_index (g2);
+
+  if (TREE_CODE (t1) != SSA_NAME || TREE_CODE(t2) != SSA_NAME)
+    gcc_unreachable ();
+
+  if (!compare_operand (t1, t2))
+    return false;
+
+  for (i = 0; i < lsize1; i++)
+    {
+      tree label1 = gimple_switch_label (g1, i);
+      tree label2 = gimple_switch_label (g2, i);
+
+      if (TREE_CODE (label1) == CASE_LABEL_EXPR
+	  && TREE_CODE (label2) == CASE_LABEL_EXPR)
+	{
+	  label1 = CASE_LABEL (label1);
+	  label2 = CASE_LABEL (label2);
+
+	  if (!compare_operand (label1, label2))
+	    return return_false_with_msg ("switch label_exprs are different");
+	}
+      else if (!tree_int_cst_equal (label1, label2))
+	return return_false_with_msg ("switch labels are different");
+    }
+
+  return true;
+}
+
+/* Verifies for given GIMPLEs S1 and S2 that
+   return statements are semantically equivalent.  */
+
+bool
+func_checker::compare_gimple_return (gimple g1, gimple g2)
+{
+  tree t1, t2;
+
+  t1 = gimple_return_retval (g1);
+  t2 = gimple_return_retval (g2);
+
+  /* Void return type.  */
+  if (t1 == NULL && t2 == NULL)
+    return true;
+  else
+    return compare_operand (t1, t2);
+}
+
+/* Verifies for given GIMPLEs S1 and S2 that
+   goto statements are semantically equivalent.  */
+
+bool
+func_checker::compare_gimple_goto (gimple g1, gimple g2)
+{
+  tree dest1, dest2;
+
+  dest1 = gimple_goto_dest (g1);
+  dest2 = gimple_goto_dest (g2);
+
+  if (TREE_CODE (dest1) != TREE_CODE (dest2) || TREE_CODE (dest1) != SSA_NAME)
+    return false;
+
+  return compare_operand (dest1, dest2);
+}
+
+/* Verifies for given GIMPLEs S1 and S2 that
+   resx statements are semantically equivalent.  */
+
+bool
+func_checker::compare_gimple_resx (gimple g1, gimple g2)
+{
+  return gimple_resx_region (g1) == gimple_resx_region (g2);
+}
+
+/* Verifies for given GIMPLEs S1 and S2 that ASM statements are equivalent.
+   For the beginning, the pass only supports equality for
+   '__asm__ __volatile__ ("", "", "", "memory")'.  */
+
+bool
+func_checker::compare_gimple_asm (gimple g1, gimple g2)
+{
+  if (gimple_asm_volatile_p (g1) != gimple_asm_volatile_p (g2))
+    return false;
+
+  if (gimple_asm_ninputs (g1) != gimple_asm_ninputs (g2))
+    return false;
+
+  if (gimple_asm_noutputs (g1) != gimple_asm_noutputs (g2))
+    return false;
+
+  if (gimple_asm_nlabels (g1) != gimple_asm_nlabels (g2))
+    return false;
+
+  if (gimple_asm_nclobbers (g1) != gimple_asm_nclobbers (g2))
+    return false;
+
+  for (unsigned i = 0; i < gimple_asm_ninputs (g1); i++)
+    {
+      tree input1 = TREE_VALUE (gimple_asm_input_op (g1, i));
+      tree input2 = TREE_VALUE (gimple_asm_input_op (g2, i));
+
+      if (!compare_operand (input1, input2))
+	return return_false_with_msg ("ASM input is different");
+    }
+
+  for (unsigned i = 0; i < gimple_asm_noutputs (g1); i++)
+    {
+      tree output1 = TREE_VALUE (gimple_asm_output_op (g1, i));
+      tree output2 = TREE_VALUE (gimple_asm_output_op (g2, i));
+
+      if (!compare_operand (output1, output2))
+	return return_false_with_msg ("ASM output is different");
+    }
+
+  for (unsigned i = 0; i < gimple_asm_nlabels (g1); i++)
+    {
+      tree label1 = TREE_VALUE (gimple_asm_label_op (g1, i));
+      tree label2 = TREE_VALUE (gimple_asm_label_op (g2, i));
+
+      if (!compare_operand (label1, label2))
+	return return_false_with_msg ("ASM label is different");
+    }
+
+  for (unsigned i = 0; i < gimple_asm_nclobbers (g1); i++)
+    {
+      tree clobber1 = TREE_VALUE (gimple_asm_clobber_op (g1, i));
+      tree clobber2 = TREE_VALUE (gimple_asm_clobber_op (g2, i));
+
+      if (!operand_equal_p (clobber1, clobber2, OEP_ONLY_CONST))
+	return return_false_with_msg ("ASM clobber is different");
+    }
+
+  return true;
+}
+
+} // ipa_icf_gimple namespace
diff --git a/gcc/ipa-icf-gimple.h b/gcc/ipa-icf-gimple.h
new file mode 100644
index 0000000..456edb6
--- /dev/null
+++ b/gcc/ipa-icf-gimple.h
@@ -0,0 +1,251 @@
+/* Interprocedural semantic function equality pass
+   Copyright (C) 2014 Free Software Foundation, Inc.
+
+   Contributed by Jan Hubicka <hubicka@ucw.cz> and Martin Liska <mliska@suse.cz>
+
+This file is part of GCC.
+
+GCC 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, or (at your option) any later
+version.
+
+GCC 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 GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+/* Prints string STRING to a FILE with a given number of SPACE_COUNT.  */
+#define FPUTS_SPACES(file, space_count, string) \
+  fprintf (file, "%*s" string, space_count, " ");
+
+/* fprintf function wrapper that transforms given FORMAT to follow given
+   number for SPACE_COUNT and call fprintf for a FILE.  */
+#define FPRINTF_SPACES(file, space_count, format, ...) \
+  fprintf (file, "%*s" format, space_count, " ", ##__VA_ARGS__);
+
+/* Prints a MESSAGE to dump_file if exists. FUNC is name of function and
+   LINE is location in the source file.  */
+
+static inline void
+dump_message_1 (const char *message, const char *func, unsigned int line)
+{
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    fprintf (dump_file, "  debug message: %s (%s:%u)\n", message, func, line);
+}
+
+/* Prints a MESSAGE to dump_file if exists.  */
+#define dump_message(message) dump_message_1 (message, __func__, __LINE__)
+
+/* Logs a MESSAGE to dump_file if exists and returns false. FUNC is name
+   of function and LINE is location in the source file.  */
+
+static inline bool
+return_false_with_message_1 (const char *message, const char *func,
+			     unsigned int line)
+{
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    fprintf (dump_file, "  false returned: '%s' (%s:%u)\n", message, func, line);
+  return false;
+}
+
+/* Logs a MESSAGE to dump_file if exists and returns false.  */
+#define return_false_with_msg(message) \
+  return_false_with_message_1 (message, __func__, __LINE__)
+
+/* Return false and log that false value is returned.  */
+#define return_false() return_false_with_msg ("")
+
+/* Logs return value if RESULT is false. FUNC is name of function and LINE
+   is location in the source file.  */
+
+static inline bool
+return_with_result (bool result, const char *func, unsigned int line)
+{
+  if (!result && dump_file && (dump_flags & TDF_DETAILS))
+    fprintf (dump_file, "  false returned (%s:%u)\n", func, line);
+
+  return result;
+}
+
+/* Logs return value if RESULT is false.  */
+#define return_with_debug(result) return_with_result (result, __func__, __LINE__)
+
+/* Verbose logging function logging statements S1 and S2 of a CODE.
+   FUNC is name of function and LINE is location in the source file.  */
+
+static inline bool
+return_different_stmts_1 (gimple s1, gimple s2, const char *code,
+			  const char *func, unsigned int line)
+{
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    {
+      fprintf (dump_file, "  different statement for code: %s (%s:%u):\n",
+	       code, func, line);
+
+      print_gimple_stmt (dump_file, s1, 3, TDF_DETAILS);
+      print_gimple_stmt (dump_file, s2, 3, TDF_DETAILS);
+    }
+
+  return false;
+}
+
+/* Verbose logging function logging statements S1 and S2 of a CODE.  */
+#define return_different_stmts(s1, s2, code) \
+  return_different_stmts_1 (s1, s2, code, __func__, __LINE__)
+
+namespace ipa_icf_gimple {
+
+/* Basic block struct for semantic equality pass.  */
+class sem_bb
+{
+public:
+  sem_bb (basic_block bb_, unsigned nondbg_stmt_count_, unsigned edge_count_):
+    bb (bb_), nondbg_stmt_count (nondbg_stmt_count_), edge_count (edge_count_) {}
+
+  /* Basic block the structure belongs to.  */
+  basic_block bb;
+
+  /* Number of non-debug statements in the basic block.  */
+  unsigned nondbg_stmt_count;
+
+  /* Number of edges connected to the block.  */
+  unsigned edge_count;
+};
+
+/* A class aggregating all connections and semantic equivalents
+   for a given pair of semantic function candidates.  */
+class func_checker
+{
+public:
+  /* Initialize internal structures for a given SOURCE_FUNC_DECL and
+     TARGET_FUNC_DECL. Strict polymorphic comparison is processed if
+     an option COMPARE_POLYMORPHIC is true. For special cases, one can
+     set IGNORE_LABELS to skip label comparison.
+     Similarly, IGNORE_SOURCE_DECLS and IGNORE_TARGET_DECLS are sets
+     of declarations that can be skipped.  */
+  func_checker (tree source_func_decl, tree target_func_decl,
+		bool compare_polymorphic,
+		bool ignore_labels = false,
+		hash_set<symtab_node *> *ignored_source_nodes = NULL,
+		hash_set<symtab_node *> *ignored_target_nodes = NULL);
+
+  /* Memory release routine.  */
+  ~func_checker();
+
+  void parse_labels (sem_bb *bb);
+
+  /* Basic block equivalence comparison function that returns true if
+     basic blocks BB1 and BB2 correspond.  */
+  bool compare_bb (sem_bb *bb1, sem_bb *bb2);
+
+  /* Verifies that trees T1 and T2 are equivalent from perspective of ICF.  */
+  bool compare_ssa_name (tree t1, tree t2);
+
+  /* Verification function for edges E1 and E2.  */
+  bool compare_edge (edge e1, edge e2);
+
+  /* Verifies for given GIMPLEs S1 and S2 that
+     call statements are semantically equivalent.  */
+  bool compare_gimple_call (gimple s1, gimple s2);
+
+  /* Verifies for given GIMPLEs S1 and S2 that
+     assignment statements are semantically equivalent.  */
+  bool compare_gimple_assign (gimple s1, gimple s2);
+
+  /* Verifies for given GIMPLEs S1 and S2 that
+     condition statements are semantically equivalent.  */
+  bool compare_gimple_cond (gimple s1, gimple s2);
+
+  /* Verifies for given GIMPLEs S1 and S2 that
+     label statements are semantically equivalent.  */
+  bool compare_gimple_label (gimple s1, gimple s2);
+
+  /* Verifies for given GIMPLEs S1 and S2 that
+     switch statements are semantically equivalent.  */
+  bool compare_gimple_switch (gimple s1, gimple s2);
+
+  /* Verifies for given GIMPLEs S1 and S2 that
+     return statements are semantically equivalent.  */
+  bool compare_gimple_return (gimple s1, gimple s2);
+
+  /* Verifies for given GIMPLEs S1 and S2 that
+     goto statements are semantically equivalent.  */
+  bool compare_gimple_goto (gimple s1, gimple s2);
+
+  /* Verifies for given GIMPLEs S1 and S2 that
+     resx statements are semantically equivalent.  */
+  bool compare_gimple_resx (gimple s1, gimple s2);
+
+  /* Verifies for given GIMPLEs S1 and S2 that ASM statements are equivalent.
+     For the beginning, the pass only supports equality for
+     '__asm__ __volatile__ ("", "", "", "memory")'.  */
+  bool compare_gimple_asm (gimple s1, gimple s2);
+
+  /* Verification function for declaration trees T1 and T2.  */
+  bool compare_decl (tree t1, tree t2);
+
+  /* Verifies that tree labels T1 and T2 correspond.  */
+  bool compare_tree_ssa_label (tree t1, tree t2);
+
+  /* Function compares two operands T1 and T2 and returns true if these
+     two trees are semantically equivalent.  */
+  bool compare_operand (tree t1, tree t2);
+
+  /* Verifies that trees T1 and T2, representing function declarations
+     are equivalent from perspective of ICF.  */
+  bool compare_function_decl (tree t1, tree t2);
+
+  /* Verifies that trees T1 and T2 do correspond.  */
+  bool compare_variable_decl (tree t1, tree t2);
+
+  /* Return true if types are compatible from perspective of ICF.
+     FIRST_ARGUMENT indicates if the comparison is called for
+     first parameter of a function.  */
+  static bool compatible_types_p (tree t1, tree t2,
+				  bool compare_polymorphic = true,
+				  bool first_argument = false);
+
+
+private:
+  /* Vector mapping source SSA names to target ones.  */
+  vec <int> m_source_ssa_names;
+
+  /* Vector mapping target SSA names to source ones.  */
+  vec <int> m_target_ssa_names;
+
+  /* Source TREE function declaration.  */
+  tree m_source_func_decl;
+
+  /* Target TREE function declaration.  */
+  tree m_target_func_decl;
+
+  /* Source symbol nodes that should be skipped by
+     declaration comparison.  */
+  hash_set<symtab_node *> *m_ignored_source_nodes;
+
+  /* Target symbol nodes that should be skipped by
+     declaration comparison.  */
+  hash_set<symtab_node *> *m_ignored_target_nodes;
+
+  /* Source to target edge map.  */
+  hash_map <edge, edge> m_edge_map;
+
+  /* Source to target declaration map.  */
+  hash_map <tree, tree> m_decl_map;
+
+  /* Label to basic block index mapping.  */
+  hash_map <tree, int> m_label_bb_map;
+
+  /* Flag if polymorphic comparison should be executed.  */
+  bool m_compare_polymorphic;
+
+  /* Flag if ignore labels in comparison.  */
+  bool m_ignore_labels;
+};
+
+} // ipa_icf_gimple namespace
diff --git a/gcc/ipa-icf.c b/gcc/ipa-icf.c
new file mode 100644
index 0000000..5184210
--- /dev/null
+++ b/gcc/ipa-icf.c
@@ -0,0 +1,2353 @@
+/* Interprocedural Identical Code Folding pass
+   Copyright (C) 2014 Free Software Foundation, Inc.
+
+   Contributed by Jan Hubicka <hubicka@ucw.cz> and Martin Liska <mliska@suse.cz>
+
+This file is part of GCC.
+
+GCC 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, or (at your option) any later
+version.
+
+GCC 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 GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+/* Interprocedural Identical Code Folding for functions and
+   read-only variables.
+
+   The goal of this transformation is to discover functions and read-only
+   variables which do have exactly the same semantics.
+
+   In case of functions,
+   we could either create a virtual clone or do a simple function wrapper
+   that will call equivalent function. If the function is just locally visible,
+   all function calls can be redirected. For read-only variables, we create
+   aliases if possible.
+
+   Optimization pass arranges as follows:
+   1) All functions and read-only variables are visited and internal
+      data structure, either sem_function or sem_variables is created.
+   2) For every symbol from the previous step, VAR_DECL and FUNCTION_DECL are
+      saved and matched to corresponding sem_items.
+   3) These declaration are ignored for equality check and are solved
+      by Value Numbering algorithm published by Alpert, Zadeck in 1992.
+   4) We compute hash value for each symbol.
+   5) Congruence classes are created based on hash value. If hash value are
+      equal, equals function is called and symbols are deeply compared.
+      We must prove that all SSA names, declarations and other items
+      correspond.
+   6) Value Numbering is executed for these classes. At the end of the process
+      all symbol members in remaining classes can be merged.
+   7) Merge operation creates alias in case of read-only variables. For
+      callgraph node, we must decide if we can redirect local calls,
+      create an alias or a thunk.
+
+*/
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tree.h"
+#include "basic-block.h"
+#include "tree-ssa-alias.h"
+#include "internal-fn.h"
+#include "gimple-expr.h"
+#include "is-a.h"
+#include "gimple.h"
+#include "expr.h"
+#include "gimple-iterator.h"
+#include "gimple-ssa.h"
+#include "tree-cfg.h"
+#include "tree-phinodes.h"
+#include "stringpool.h"
+#include "tree-ssanames.h"
+#include "tree-dfa.h"
+#include "tree-pass.h"
+#include "gimple-pretty-print.h"
+#include "ipa-inline.h"
+#include "cfgloop.h"
+#include "except.h"
+#include "hash-table.h"
+#include "coverage.h"
+#include "attribs.h"
+#include "print-tree.h"
+#include "lto-streamer.h"
+#include "data-streamer.h"
+#include "ipa-utils.h"
+#include <list>
+#include "ipa-icf-gimple.h"
+#include "ipa-icf.h"
+
+using namespace ipa_icf_gimple;
+
+namespace ipa_icf {
+/* Constructor for key value pair, where _ITEM is key and _INDEX is a target.  */
+
+sem_usage_pair::sem_usage_pair (sem_item *_item, unsigned int _index):
+  item (_item), index (_index)
+{
+}
+
+/* Semantic item constructor for a node of _TYPE, where STACK is used
+   for bitmap memory allocation.  */
+
+sem_item::sem_item (sem_item_type _type,
+		    bitmap_obstack *stack): type(_type), hash(0)
+{
+  setup (stack);
+}
+
+/* Semantic item constructor for a node of _TYPE, where STACK is used
+   for bitmap memory allocation. The item is based on symtab node _NODE
+   with computed _HASH.  */
+
+sem_item::sem_item (sem_item_type _type, symtab_node *_node,
+		    hashval_t _hash, bitmap_obstack *stack): type(_type),
+  node (_node), hash (_hash)
+{
+  decl = node->decl;
+  setup (stack);
+}
+
+/* Add reference to a semantic TARGET.  */
+
+void
+sem_item::add_reference (sem_item *target)
+{
+  refs.safe_push (target);
+  unsigned index = refs.length ();
+  target->usages.safe_push (new sem_usage_pair(this, index));
+  bitmap_set_bit (target->usage_index_bitmap, index);
+  refs_set.add (target->node);
+}
+
+/* Initialize internal data structures. Bitmap STACK is used for
+   bitmap memory allocation process.  */
+
+void
+sem_item::setup (bitmap_obstack *stack)
+{
+  gcc_checking_assert (node);
+
+  refs.create (0);
+  tree_refs.create (0);
+  usages.create (0);
+  usage_index_bitmap = BITMAP_ALLOC (stack);
+}
+
+sem_item::~sem_item ()
+{
+  for (unsigned i = 0; i < usages.length (); i++)
+    delete usages[i];
+
+  refs.release ();
+  tree_refs.release ();
+  usages.release ();
+
+  BITMAP_FREE (usage_index_bitmap);
+}
+
+/* Dump function for debugging purpose.  */
+
+DEBUG_FUNCTION void
+sem_item::dump (void)
+{
+  if (dump_file)
+    {
+      fprintf (dump_file, "[%s] %s (%u) (tree:%p)\n", type == FUNC ? "func" : "var",
+	       name(), node->order, (void *) node->decl);
+      fprintf (dump_file, "  hash: %u\n", get_hash ());
+      fprintf (dump_file, "  references: ");
+
+      for (unsigned i = 0; i < refs.length (); i++)
+	fprintf (dump_file, "%s%s ", refs[i]->name (),
+		 i < refs.length() - 1 ? "," : "");
+
+      fprintf (dump_file, "\n");
+    }
+}
+
+/* Semantic function constructor that uses STACK as bitmap memory stack.  */
+
+sem_function::sem_function (bitmap_obstack *stack): sem_item (FUNC, stack),
+  m_checker (NULL), m_compared_func (NULL)
+{
+  arg_types.create (0);
+  bb_sizes.create (0);
+  bb_sorted.create (0);
+}
+
+/*  Constructor based on callgraph node _NODE with computed hash _HASH.
+    Bitmap STACK is used for memory allocation.  */
+sem_function::sem_function (cgraph_node *node, hashval_t hash,
+			    bitmap_obstack *stack):
+  sem_item (FUNC, node, hash, stack),
+  m_checker (NULL), m_compared_func (NULL)
+{
+  arg_types.create (0);
+  bb_sizes.create (0);
+  bb_sorted.create (0);
+}
+
+sem_function::~sem_function ()
+{
+  for (unsigned i = 0; i < bb_sorted.length (); i++)
+    free (bb_sorted[i]);
+
+  arg_types.release ();
+  bb_sizes.release ();
+  bb_sorted.release ();
+}
+
+/* Calculates hash value based on a BASIC_BLOCK.  */
+
+hashval_t
+sem_function::get_bb_hash (const sem_bb *basic_block)
+{
+  inchash::hash hstate;
+
+  hstate.add_int (basic_block->nondbg_stmt_count);
+  hstate.add_int (basic_block->edge_count);
+
+  return hstate.end ();
+}
+
+/* References independent hash function.  */
+
+hashval_t
+sem_function::get_hash (void)
+{
+  if(!hash)
+    {
+      inchash::hash hstate;
+      hstate.add_int (177454); /* Random number for function type.  */
+
+      hstate.add_int (arg_count);
+      hstate.add_int (cfg_checksum);
+      hstate.add_int (gcode_hash);
+
+      for (unsigned i = 0; i < bb_sorted.length (); i++)
+	hstate.merge_hash (get_bb_hash (bb_sorted[i]));
+
+      for (unsigned i = 0; i < bb_sizes.length (); i++)
+	hstate.add_int (bb_sizes[i]);
+
+      hash = hstate.end ();
+    }
+
+  return hash;
+}
+
+/* For a given symbol table nodes N1 and N2, we check that FUNCTION_DECLs
+   point to a same function. Comparison can be skipped if IGNORED_NODES
+   contains these nodes.  */
+
+bool
+sem_function::compare_cgraph_references (hash_map <symtab_node *, sem_item *>
+    &ignored_nodes,
+    symtab_node *n1, symtab_node *n2)
+{
+  if (n1 == n2 || (ignored_nodes.get (n1) && ignored_nodes.get (n2)))
+    return true;
+
+  cgraph_node *cn1 = dyn_cast <cgraph_node *> (n1);
+  cgraph_node *cn2 = dyn_cast <cgraph_node *> (n2);
+
+  if (cn1 && cn2 && cn1->weakref && cn2->weakref
+      && cn1->alias_target == cn2->alias_target)
+    return true;
+
+  return return_false_with_msg ("different references");
+}
+
+/* If cgraph edges E1 and E2 are indirect calls, verify that
+   ECF flags are the same.  */
+
+bool sem_function::compare_edge_flags (cgraph_edge *e1, cgraph_edge *e2)
+{
+  if (e1->indirect_info && e2->indirect_info)
+    {
+      int e1_flags = e1->indirect_info->ecf_flags;
+      int e2_flags = e2->indirect_info->ecf_flags;
+
+      if (e1_flags != e2_flags)
+	return return_false_with_msg ("ICF flags are different");
+    }
+  else if (e1->indirect_info || e2->indirect_info)
+    return false;
+
+  return true;
+}
+
+/* Fast equality function based on knowledge known in WPA.  */
+
+bool
+sem_function::equals_wpa (sem_item *item,
+			  hash_map <symtab_node *, sem_item *> &ignored_nodes)
+{
+  gcc_assert (item->type == FUNC);
+
+  m_compared_func = static_cast<sem_function *> (item);
+
+  if (arg_types.length () != m_compared_func->arg_types.length ())
+    return return_false_with_msg ("different number of arguments");
+
+  /* Checking types of arguments.  */
+  for (unsigned i = 0; i < arg_types.length (); i++)
+    {
+      /* This guard is here for function pointer with attributes (pr59927.c).  */
+      if (!arg_types[i] || !m_compared_func->arg_types[i])
+	return return_false_with_msg ("NULL argument type");
+
+      /* Polymorphic comparison is executed just for non-leaf functions.  */
+      bool is_not_leaf = get_node ()->callees != NULL;
+
+      if (!func_checker::compatible_types_p (arg_types[i],
+					     m_compared_func->arg_types[i],
+					     is_not_leaf, i == 0))
+	return return_false_with_msg ("argument type is different");
+    }
+
+  /* Result type checking.  */
+  if (!func_checker::compatible_types_p (result_type,
+					 m_compared_func->result_type))
+    return return_false_with_msg ("result types are different");
+
+  if (node->get_references_count () != item->node->get_references_count ())
+    return return_false_with_msg ("different number of references");
+
+  ipa_ref *ref = NULL, *ref2 = NULL;
+  for (unsigned i = 0; node->iterate_reference (i, ref); i++)
+    {
+      item->node->iterate_reference (i, ref2);
+
+      if (!compare_cgraph_references (ignored_nodes, ref->referred, ref2->referred))
+	return false;
+    }
+
+  cgraph_edge *e1 = dyn_cast <cgraph_node *> (node)->callees;
+  cgraph_edge *e2 = dyn_cast <cgraph_node *> (item->node)->callees;
+
+  while (e1 && e2)
+    {
+      if (!compare_cgraph_references (ignored_nodes, e1->callee, e2->callee))
+	return false;
+
+      e1 = e1->next_callee;
+      e2 = e2->next_callee;
+    }
+
+  if (e1 || e2)
+    return return_false_with_msg ("different number of edges");
+
+  return true;
+}
+
+/* Returns true if the item equals to ITEM given as argument.  */
+
+bool
+sem_function::equals (sem_item *item,
+		      hash_map <symtab_node *, sem_item *> &ignored_nodes)
+{
+  gcc_assert (item->type == FUNC);
+  bool eq = equals_private (item, ignored_nodes);
+
+  if (m_checker != NULL)
+    {
+      delete m_checker;
+      m_checker = NULL;
+    }
+
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    fprintf (dump_file,
+	     "Equals called for:%s:%s (%u:%u) (%s:%s) with result: %s\n\n",
+	     name(), item->name (), node->order, item->node->order, asm_name (),
+	     item->asm_name (), eq ? "true" : "false");
+
+  return eq;
+}
+
+/* Processes function equality comparison.  */
+
+bool
+sem_function::equals_private (sem_item *item,
+			      hash_map <symtab_node *, sem_item *> &ignored_nodes)
+{
+  if (item->type != FUNC)
+    return false;
+
+  basic_block bb1, bb2;
+  edge e1, e2;
+  edge_iterator ei1, ei2;
+  int *bb_dict = NULL;
+  bool result = true;
+  tree arg1, arg2;
+
+  m_compared_func = static_cast<sem_function *> (item);
+
+  gcc_assert (decl != item->decl);
+
+  if (bb_sorted.length () != m_compared_func->bb_sorted.length ()
+      || edge_count != m_compared_func->edge_count
+      || cfg_checksum != m_compared_func->cfg_checksum)
+    return return_false ();
+
+  if (!equals_wpa (item, ignored_nodes))
+    return false;
+
+  /* Checking function arguments.  */
+  tree decl1 = DECL_ATTRIBUTES (decl);
+  tree decl2 = DECL_ATTRIBUTES (m_compared_func->decl);
+
+  m_checker = new func_checker (decl, m_compared_func->decl,
+				compare_polymorphic_p (),
+				false,
+				&refs_set,
+				&m_compared_func->refs_set);
+  while (decl1)
+    {
+      if (decl2 == NULL)
+	return return_false ();
+
+      if (get_attribute_name (decl1) != get_attribute_name (decl2))
+	return return_false ();
+
+      tree attr_value1 = TREE_VALUE (decl1);
+      tree attr_value2 = TREE_VALUE (decl2);
+
+      if (attr_value1 && attr_value2)
+	{
+	  bool ret = m_checker->compare_operand (TREE_VALUE (attr_value1),
+						 TREE_VALUE (attr_value2));
+	  if (!ret)
+	    return return_false_with_msg ("attribute values are different");
+	}
+      else if (!attr_value1 && !attr_value2)
+	{}
+      else
+	return return_false ();
+
+      decl1 = TREE_CHAIN (decl1);
+      decl2 = TREE_CHAIN (decl2);
+    }
+
+  if (decl1 != decl2)
+    return return_false();
+
+
+  for (arg1 = DECL_ARGUMENTS (decl),
+       arg2 = DECL_ARGUMENTS (m_compared_func->decl);
+       arg1; arg1 = DECL_CHAIN (arg1), arg2 = DECL_CHAIN (arg2))
+    if (!m_checker->compare_decl (arg1, arg2))
+      return return_false ();
+
+  /* Fill-up label dictionary.  */
+  for (unsigned i = 0; i < bb_sorted.length (); ++i)
+    {
+      m_checker->parse_labels (bb_sorted[i]);
+      m_checker->parse_labels (m_compared_func->bb_sorted[i]);
+    }
+
+  /* Checking all basic blocks.  */
+  for (unsigned i = 0; i < bb_sorted.length (); ++i)
+    if(!m_checker->compare_bb (bb_sorted[i], m_compared_func->bb_sorted[i]))
+      return return_false();
+
+  dump_message ("All BBs are equal\n");
+
+  /* Basic block edges check.  */
+  for (unsigned i = 0; i < bb_sorted.length (); ++i)
+    {
+      bb_dict = XNEWVEC (int, bb_sorted.length () + 2);
+      memset (bb_dict, -1, (bb_sorted.length () + 2) * sizeof (int));
+
+      bb1 = bb_sorted[i]->bb;
+      bb2 = m_compared_func->bb_sorted[i]->bb;
+
+      ei2 = ei_start (bb2->preds);
+
+      for (ei1 = ei_start (bb1->preds); ei_cond (ei1, &e1); ei_next (&ei1))
+	{
+	  ei_cond (ei2, &e2);
+
+	  if (e1->flags != e2->flags)
+	    return return_false_with_msg ("flags comparison returns false");
+
+	  if (!bb_dict_test (bb_dict, e1->src->index, e2->src->index))
+	    return return_false_with_msg ("edge comparison returns false");
+
+	  if (!bb_dict_test (bb_dict, e1->dest->index, e2->dest->index))
+	    return return_false_with_msg ("BB comparison returns false");
+
+	  if (!m_checker->compare_edge (e1, e2))
+	    return return_false_with_msg ("edge comparison returns false");
+
+	  ei_next (&ei2);
+	}
+    }
+
+  /* Basic block PHI nodes comparison.  */
+  for (unsigned i = 0; i < bb_sorted.length (); i++)
+    if (!compare_phi_node (bb_sorted[i]->bb, m_compared_func->bb_sorted[i]->bb))
+      return return_false_with_msg ("PHI node comparison returns false");
+
+  return result;
+}
+
+/* Merges instance with an ALIAS_ITEM, where alias, thunk or redirection can
+   be applied.  */
+bool
+sem_function::merge (sem_item *alias_item)
+{
+  gcc_assert (alias_item->type == FUNC);
+
+  sem_function *alias_func = static_cast<sem_function *> (alias_item);
+
+  cgraph_node *original = get_node ();
+  cgraph_node *local_original = original;
+  cgraph_node *alias = alias_func->get_node ();
+  bool original_address_matters;
+  bool alias_address_matters;
+
+  bool create_thunk = false;
+  bool create_alias = false;
+  bool redirect_callers = false;
+  bool original_discardable = false;
+
+  /* Do not attempt to mix functions from different user sections;
+     we do not know what user intends with those.  */
+  if (((DECL_SECTION_NAME (original->decl) && !original->implicit_section)
+       || (DECL_SECTION_NAME (alias->decl) && !alias->implicit_section))
+      && DECL_SECTION_NAME (original->decl) != DECL_SECTION_NAME (alias->decl))
+    {
+      if (dump_file)
+	fprintf (dump_file,
+		 "Not unifying; original and alias are in different sections.\n\n");
+      return false;
+    }
+
+  /* See if original is in a section that can be discarded if the main
+     symbol is not used.  */
+  if (DECL_EXTERNAL (original->decl))
+    original_discardable = true;
+  if (original->resolution == LDPR_PREEMPTED_REG
+      || original->resolution == LDPR_PREEMPTED_IR)
+    original_discardable = true;
+  if (original->can_be_discarded_p ())
+    original_discardable = true;
+
+  /* See if original and/or alias address can be compared for equality.  */
+  original_address_matters
+    = (!DECL_VIRTUAL_P (original->decl)
+       && (original->externally_visible
+	   || original->address_taken_from_non_vtable_p ()));
+  alias_address_matters
+    = (!DECL_VIRTUAL_P (alias->decl)
+       && (alias->externally_visible
+	   || alias->address_taken_from_non_vtable_p ()));
+
+  /* If alias and original can be compared for address equality, we need
+     to create a thunk.  Also we can not create extra aliases into discardable
+     section (or we risk link failures when section is discarded).  */
+  if ((original_address_matters
+       && alias_address_matters)
+      || original_discardable)
+    {
+      create_thunk = !stdarg_p (TREE_TYPE (alias->decl));
+      create_alias = false;
+      /* When both alias and original are not overwritable, we can save
+         the extra thunk wrapper for direct calls.  */
+      redirect_callers
+	= (!original_discardable
+	   && alias->get_availability () > AVAIL_INTERPOSABLE
+	   && original->get_availability () > AVAIL_INTERPOSABLE);
+    }
+  else
+    {
+      create_alias = true;
+      create_thunk = false;
+      redirect_callers = false;
+    }
+
+  if (create_alias && DECL_COMDAT_GROUP (alias->decl))
+    {
+      create_alias = false;
+      create_thunk = true;
+    }
+
+  /* We want thunk to always jump to the local function body
+     unless the body is comdat and may be optimized out.  */
+  if ((create_thunk || redirect_callers)
+      && (!original_discardable
+	  || (DECL_COMDAT_GROUP (original->decl)
+	      && (DECL_COMDAT_GROUP (original->decl)
+		  == DECL_COMDAT_GROUP (alias->decl)))))
+    local_original
+      = dyn_cast <cgraph_node *> (original->noninterposable_alias ());
+
+  if (redirect_callers)
+    {
+      /* If alias is non-overwritable then
+         all direct calls are safe to be redirected to the original.  */
+      bool redirected = false;
+      while (alias->callers)
+	{
+	  cgraph_edge *e = alias->callers;
+	  e->redirect_callee (local_original);
+	  push_cfun (DECL_STRUCT_FUNCTION (e->caller->decl));
+
+	  if (e->call_stmt)
+	    e->redirect_call_stmt_to_callee ();
+
+	  pop_cfun ();
+	  redirected = true;
+	}
+
+      alias->icf_merged = true;
+
+      /* The alias function is removed if symbol address
+         does not matter.  */
+      if (!alias_address_matters)
+	alias->remove ();
+
+      if (dump_file && redirected)
+	fprintf (dump_file, "Callgraph local calls have been redirected.\n\n");
+    }
+  /* If the condtion above is not met, we are lucky and can turn the
+     function into real alias.  */
+  else if (create_alias)
+    {
+      alias->icf_merged = true;
+
+      /* Remove the function's body.  */
+      ipa_merge_profiles (original, alias);
+      alias->release_body (true);
+      alias->reset ();
+
+      /* Create the alias.  */
+      cgraph_node::create_alias (alias_func->decl, decl);
+      alias->resolve_alias (original);
+
+      if (dump_file)
+	fprintf (dump_file, "Callgraph alias has been created.\n\n");
+    }
+  else if (create_thunk)
+    {
+      if (DECL_COMDAT_GROUP (alias->decl))
+	{
+	  if (dump_file)
+	    fprintf (dump_file, "Callgraph thunk cannot be created because of COMDAT\n");
+
+	  return 0;
+	}
+
+      alias->icf_merged = true;
+      ipa_merge_profiles (local_original, alias);
+      alias->create_wrapper (local_original);
+
+      if (dump_file)
+	fprintf (dump_file, "Callgraph thunk has been created.\n\n");
+    }
+  else if (dump_file)
+    fprintf (dump_file, "Callgraph merge operation cannot be performed.\n\n");
+
+  return true;
+}
+
+/* Semantic item initialization function.  */
+
+void
+sem_function::init (void)
+{
+  if (in_lto_p)
+    get_node ()->get_body ();
+
+  tree fndecl = node->decl;
+  function *func = DECL_STRUCT_FUNCTION (fndecl);
+
+  gcc_assert (func);
+  gcc_assert (SSANAMES (func));
+
+  ssa_names_size = SSANAMES (func)->length ();
+  node = node;
+
+  decl = fndecl;
+  region_tree = func->eh->region_tree;
+
+  /* iterating all function arguments.  */
+  arg_count = count_formal_params (fndecl);
+
+  edge_count = n_edges_for_fn (func);
+  cfg_checksum = coverage_compute_cfg_checksum (func);
+
+  inchash::hash hstate;
+
+  basic_block bb;
+  FOR_EACH_BB_FN (bb, func)
+  {
+    unsigned nondbg_stmt_count = 0;
+
+    edge e;
+    for (edge_iterator ei = ei_start (bb->preds); ei_cond (ei, &e); ei_next (&ei))
+      cfg_checksum = iterative_hash_host_wide_int (e->flags,
+		     cfg_checksum);
+
+    for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi);
+	 gsi_next (&gsi))
+      {
+	gimple stmt = gsi_stmt (gsi);
+
+	if (gimple_code (stmt) != GIMPLE_DEBUG)
+	  {
+	    hash_stmt (&hstate, stmt);
+	    nondbg_stmt_count++;
+	  }
+      }
+
+    gcode_hash = hstate.end ();
+    bb_sizes.safe_push (nondbg_stmt_count);
+
+    /* Inserting basic block to hash table.  */
+    sem_bb *semantic_bb = new sem_bb (bb, nondbg_stmt_count,
+				      EDGE_COUNT (bb->preds) + EDGE_COUNT (bb->succs));
+
+    bb_sorted.safe_push (semantic_bb);
+  }
+
+  parse_tree_args ();
+}
+
+/* Improve accumulated hash for HSTATE based on a gimple statement STMT.  */
+
+void
+sem_function::hash_stmt (inchash::hash *hstate, gimple stmt)
+{
+  enum gimple_code code = gimple_code (stmt);
+
+  hstate->add_int (code);
+
+  if (code == GIMPLE_CALL)
+    {
+      /* Checking of argument.  */
+      for (unsigned i = 0; i < gimple_call_num_args (stmt); ++i)
+	{
+	  tree argument = gimple_call_arg (stmt, i);
+
+	  switch (TREE_CODE (argument))
+	    {
+	    case INTEGER_CST:
+	      if (tree_fits_shwi_p (argument))
+		hstate->add_wide_int (tree_to_shwi (argument));
+	      else if (tree_fits_uhwi_p (argument))
+		hstate->add_wide_int (tree_to_uhwi (argument));
+	      break;
+	    case REAL_CST:
+	      REAL_VALUE_TYPE c;
+	      HOST_WIDE_INT n;
+
+	      c = TREE_REAL_CST (argument);
+	      n = real_to_integer (&c);
+
+	      hstate->add_wide_int (n);
+	      break;
+	    case ADDR_EXPR:
+	      {
+		tree addr_operand = TREE_OPERAND (argument, 0);
+
+		if (TREE_CODE (addr_operand) == STRING_CST)
+		  hstate->add (TREE_STRING_POINTER (addr_operand),
+			       TREE_STRING_LENGTH (addr_operand));
+		break;
+	      }
+	    default:
+	      break;
+	    }
+	}
+    }
+}
+
+
+/* Return true if polymorphic comparison must be processed.  */
+
+bool
+sem_function::compare_polymorphic_p (void)
+{
+  return get_node ()->callees != NULL
+	 || m_compared_func->get_node ()->callees != NULL;
+}
+
+/* For a given call graph NODE, the function constructs new
+   semantic function item.  */
+
+sem_function *
+sem_function::parse (cgraph_node *node, bitmap_obstack *stack)
+{
+  tree fndecl = node->decl;
+  function *func = DECL_STRUCT_FUNCTION (fndecl);
+
+  /* TODO: add support for thunks and aliases.  */
+
+  if (!func || !node->has_gimple_body_p ())
+    return NULL;
+
+  if (lookup_attribute_by_prefix ("omp ", DECL_ATTRIBUTES (node->decl)) != NULL)
+    return NULL;
+
+  sem_function *f = new sem_function (node, 0, stack);
+
+  f->init ();
+
+  return f;
+}
+
+/* Parses function arguments and result type.  */
+
+void
+sem_function::parse_tree_args (void)
+{
+  tree result;
+
+  if (arg_types.exists ())
+    arg_types.release ();
+
+  arg_types.create (4);
+  tree fnargs = DECL_ARGUMENTS (decl);
+
+  for (tree parm = fnargs; parm; parm = DECL_CHAIN (parm))
+    arg_types.safe_push (DECL_ARG_TYPE (parm));
+
+  /* Function result type.  */
+  result = DECL_RESULT (decl);
+  result_type = result ? TREE_TYPE (result) : NULL;
+
+  /* During WPA, we can get arguments by following method.  */
+  if (!fnargs)
+    {
+      tree type = TYPE_ARG_TYPES (TREE_TYPE (decl));
+      for (tree parm = type; parm; parm = TREE_CHAIN (parm))
+	arg_types.safe_push (TYPE_CANONICAL (TREE_VALUE (parm)));
+
+      result_type = TREE_TYPE (TREE_TYPE (decl));
+    }
+}
+
+/* For given basic blocks BB1 and BB2 (from functions FUNC1 and FUNC),
+   return true if phi nodes are semantically equivalent in these blocks .  */
+
+bool
+sem_function::compare_phi_node (basic_block bb1, basic_block bb2)
+{
+  gimple_stmt_iterator si1, si2;
+  gimple phi1, phi2;
+  unsigned size1, size2, i;
+  tree t1, t2;
+  edge e1, e2;
+
+  gcc_assert (bb1 != NULL);
+  gcc_assert (bb2 != NULL);
+
+  si2 = gsi_start_phis (bb2);
+  for (si1 = gsi_start_phis (bb1); !gsi_end_p (si1);
+       gsi_next (&si1))
+    {
+      gsi_next_nonvirtual_phi (&si1);
+      gsi_next_nonvirtual_phi (&si2);
+
+      if (gsi_end_p (si1) && gsi_end_p (si2))
+	break;
+
+      if (gsi_end_p (si1) || gsi_end_p (si2))
+	return return_false();
+
+      phi1 = gsi_stmt (si1);
+      phi2 = gsi_stmt (si2);
+
+      size1 = gimple_phi_num_args (phi1);
+      size2 = gimple_phi_num_args (phi2);
+
+      if (size1 != size2)
+	return return_false ();
+
+      for (i = 0; i < size1; ++i)
+	{
+	  t1 = gimple_phi_arg (phi1, i)->def;
+	  t2 = gimple_phi_arg (phi2, i)->def;
+
+	  if (!m_checker->compare_operand (t1, t2))
+	    return return_false ();
+
+	  e1 = gimple_phi_arg_edge (phi1, i);
+	  e2 = gimple_phi_arg_edge (phi2, i);
+
+	  if (!m_checker->compare_edge (e1, e2))
+	    return return_false ();
+	}
+
+      gsi_next (&si2);
+    }
+
+  return true;
+}
+
+/* Returns true if tree T can be compared as a handled component.  */
+
+bool
+sem_function::icf_handled_component_p (tree t)
+{
+  tree_code tc = TREE_CODE (t);
+
+  return ((handled_component_p (t))
+	  || tc == ADDR_EXPR || tc == MEM_REF || tc == REALPART_EXPR
+	  || tc == IMAGPART_EXPR || tc == OBJ_TYPE_REF);
+}
+
+/* Basic blocks dictionary BB_DICT returns true if SOURCE index BB
+   corresponds to TARGET.  */
+
+bool
+sem_function::bb_dict_test (int* bb_dict, int source, int target)
+{
+  if (bb_dict[source] == -1)
+    {
+      bb_dict[source] = target;
+      return true;
+    }
+  else
+    return bb_dict[source] == target;
+}
+
+/* Iterates all tree types in T1 and T2 and returns true if all types
+   are compatible. If COMPARE_POLYMORPHIC is set to true,
+   more strict comparison is executed.  */
+
+bool
+sem_function::compare_type_list (tree t1, tree t2, bool compare_polymorphic)
+{
+  tree tv1, tv2;
+  tree_code tc1, tc2;
+
+  if (!t1 && !t2)
+    return true;
+
+  while (t1 != NULL && t2 != NULL)
+    {
+      tv1 = TREE_VALUE (t1);
+      tv2 = TREE_VALUE (t2);
+
+      tc1 = TREE_CODE (tv1);
+      tc2 = TREE_CODE (tv2);
+
+      if (tc1 == NOP_EXPR && tc2 == NOP_EXPR)
+	{}
+      else if (tc1 == NOP_EXPR || tc2 == NOP_EXPR)
+	return false;
+      else if (!func_checker::compatible_types_p (tv1, tv2, compare_polymorphic))
+	return false;
+
+      t1 = TREE_CHAIN (t1);
+      t2 = TREE_CHAIN (t2);
+    }
+
+  return !(t1 || t2);
+}
+
+
+/* Semantic variable constructor that uses STACK as bitmap memory stack.  */
+
+sem_variable::sem_variable (bitmap_obstack *stack): sem_item (VAR, stack)
+{
+}
+
+/*  Constructor based on varpool node _NODE with computed hash _HASH.
+    Bitmap STACK is used for memory allocation.  */
+
+sem_variable::sem_variable (varpool_node *node, hashval_t _hash,
+			    bitmap_obstack *stack): sem_item(VAR,
+				  node, _hash, stack)
+{
+  gcc_checking_assert (node);
+  gcc_checking_assert (get_node ());
+}
+
+/* Returns true if the item equals to ITEM given as argument.  */
+
+bool
+sem_variable::equals (sem_item *item,
+		      hash_map <symtab_node *, sem_item *> & ARG_UNUSED (ignored_nodes))
+{
+  gcc_assert (item->type == VAR);
+
+  return sem_variable::equals (ctor, static_cast<sem_variable *>(item)->ctor);
+}
+
+/* Compares trees T1 and T2 for semantic equality.  */
+
+bool
+sem_variable::equals (tree t1, tree t2)
+{
+  tree_code tc1 = TREE_CODE (t1);
+  tree_code tc2 = TREE_CODE (t2);
+
+  if (tc1 != tc2)
+    return false;
+
+  switch (tc1)
+    {
+    case CONSTRUCTOR:
+      {
+	unsigned len1 = vec_safe_length (CONSTRUCTOR_ELTS (t1));
+	unsigned len2 = vec_safe_length (CONSTRUCTOR_ELTS (t2));
+
+	if (len1 != len2)
+	  return false;
+
+	for (unsigned i = 0; i < len1; i++)
+	  if (!sem_variable::equals (CONSTRUCTOR_ELT (t1, i)->value,
+				     CONSTRUCTOR_ELT (t2, i)->value)
+	      || CONSTRUCTOR_ELT (t1, i)->index != CONSTRUCTOR_ELT (t2, i)->index)
+	    return false;
+
+	return true;
+      }
+    case MEM_REF:
+      {
+	tree x1 = TREE_OPERAND (t1, 0);
+	tree x2 = TREE_OPERAND (t2, 0);
+	tree y1 = TREE_OPERAND (t1, 1);
+	tree y2 = TREE_OPERAND (t2, 1);
+
+	if (!func_checker::compatible_types_p (TREE_TYPE (x1), TREE_TYPE (x2),
+					       true))
+	  return return_false ();
+
+	/* Type of the offset on MEM_REF does not matter.  */
+	return sem_variable::equals (x1, x2)
+	       && wi::to_offset  (y1) == wi::to_offset  (y2);
+      }
+    case NOP_EXPR:
+    case ADDR_EXPR:
+      {
+	tree op1 = TREE_OPERAND (t1, 0);
+	tree op2 = TREE_OPERAND (t2, 0);
+	return sem_variable::equals (op1, op2);
+      }
+    case FUNCTION_DECL:
+    case VAR_DECL:
+    case FIELD_DECL:
+    case LABEL_DECL:
+      return t1 == t2;
+    case INTEGER_CST:
+      return func_checker::compatible_types_p (TREE_TYPE (t1), TREE_TYPE (t2),
+	     true)
+	     && wi::to_offset (t1) == wi::to_offset (t2);
+    case STRING_CST:
+    case REAL_CST:
+    case COMPLEX_CST:
+      return operand_equal_p (t1, t2, OEP_ONLY_CONST);
+    case COMPONENT_REF:
+    case ARRAY_REF:
+    case POINTER_PLUS_EXPR:
+      {
+	tree x1 = TREE_OPERAND (t1, 0);
+	tree x2 = TREE_OPERAND (t2, 0);
+	tree y1 = TREE_OPERAND (t1, 1);
+	tree y2 = TREE_OPERAND (t2, 1);
+
+	return sem_variable::equals (x1, x2) && sem_variable::equals (y1, y2);
+      }
+    case ERROR_MARK:
+      return return_false_with_msg ("ERROR_MARK");
+    default:
+      return return_false_with_msg ("Unknown TREE code reached");
+    }
+}
+
+/* Parser function that visits a varpool NODE.  */
+
+sem_variable *
+sem_variable::parse (varpool_node *node, bitmap_obstack *stack)
+{
+  tree decl = node->decl;
+
+  bool readonly = TYPE_P (decl) ? TYPE_READONLY (decl) : TREE_READONLY (decl);
+  bool can_handle = readonly && (DECL_VIRTUAL_P (decl)
+				 || !TREE_ADDRESSABLE (decl));
+
+  if (!can_handle)
+    return NULL;
+
+  tree ctor = ctor_for_folding (decl);
+  if (!ctor)
+    return NULL;
+
+  sem_variable *v = new sem_variable (node, 0, stack);
+
+  v->init ();
+
+  return v;
+}
+
+/* References independent hash function.  */
+
+hashval_t
+sem_variable::get_hash (void)
+{
+  if (hash)
+    return hash;
+
+  inchash::hash hstate;
+
+  hstate.add_int (456346417);
+  hstate.add_int (TREE_CODE (ctor));
+
+  if (TREE_CODE (ctor) == CONSTRUCTOR)
+    {
+      unsigned length = vec_safe_length (CONSTRUCTOR_ELTS (ctor));
+      hstate.add_int (length);
+    }
+
+  hash = hstate.end ();
+
+  return hash;
+}
+
+/* Merges instance with an ALIAS_ITEM, where alias, thunk or redirection can
+   be applied.  */
+
+bool
+sem_variable::merge (sem_item *alias_item)
+{
+  gcc_assert (alias_item->type == VAR);
+
+  sem_variable *alias_var = static_cast<sem_variable *> (alias_item);
+
+  varpool_node *original = get_node ();
+  varpool_node *alias = alias_var->get_node ();
+  bool original_discardable = false;
+
+  /* See if original is in a section that can be discarded if the main
+     symbol is not used.  */
+  if (DECL_EXTERNAL (original->decl))
+    original_discardable = true;
+  if (original->resolution == LDPR_PREEMPTED_REG
+      || original->resolution == LDPR_PREEMPTED_IR)
+    original_discardable = true;
+  if (original->can_be_discarded_p ())
+    original_discardable = true;
+
+  gcc_assert (!TREE_ASM_WRITTEN (alias->decl));
+
+  if (original_discardable || DECL_EXTERNAL (alias_var->decl) ||
+      !compare_sections (alias_var))
+    {
+      if (dump_file)
+	fprintf (dump_file, "Varpool alias cannot be created\n\n");
+
+      return false;
+    }
+  else
+    {
+      // alias cycle creation check
+      varpool_node *n = original;
+
+      while (n->alias)
+	{
+	  n = n->get_alias_target ();
+	  if (n == alias)
+	    {
+	      if (dump_file)
+		fprintf (dump_file, "Varpool alias cannot be created (alias cycle).\n\n");
+
+	      return false;
+	    }
+	}
+
+      alias->analyzed = false;
+
+      DECL_INITIAL (alias->decl) = NULL;
+      alias->remove_all_references ();
+
+      varpool_node::create_alias (alias_var->decl, decl);
+      alias->resolve_alias (original);
+
+      if (dump_file)
+	fprintf (dump_file, "Varpool alias has been created.\n\n");
+
+      return true;
+    }
+}
+
+bool
+sem_variable::compare_sections (sem_variable *alias)
+{
+  const char *source = node->get_section ();
+  const char *target = alias->node->get_section();
+
+  if (source == NULL && target == NULL)
+    return true;
+  else if(!source || !target)
+    return false;
+  else
+    return strcmp (source, target) == 0;
+}
+
+/* Dump symbol to FILE.  */
+
+void
+sem_variable::dump_to_file (FILE *file)
+{
+  gcc_assert (file);
+
+  print_node (file, "", decl, 0);
+  fprintf (file, "\n\n");
+}
+
+/* Iterates though a constructor and identifies tree references
+   we are interested in semantic function equality.  */
+
+void
+sem_variable::parse_tree_refs (tree t)
+{
+  switch (TREE_CODE (t))
+    {
+    case CONSTRUCTOR:
+      {
+	unsigned length = vec_safe_length (CONSTRUCTOR_ELTS (t));
+
+	for (unsigned i = 0; i < length; i++)
+	  parse_tree_refs(CONSTRUCTOR_ELT (t, i)->value);
+
+	break;
+      }
+    case NOP_EXPR:
+    case ADDR_EXPR:
+      {
+	tree op = TREE_OPERAND (t, 0);
+	parse_tree_refs (op);
+	break;
+      }
+    case FUNCTION_DECL:
+      {
+	tree_refs.safe_push (t);
+	break;
+      }
+    default:
+      break;
+    }
+}
+
+unsigned int sem_item_optimizer::class_id = 0;
+
+sem_item_optimizer::sem_item_optimizer (): worklist (0), m_classes (0),
+  m_classes_count (0), m_cgraph_node_hooks (NULL), m_varpool_node_hooks (NULL)
+{
+  m_items.create (0);
+  bitmap_obstack_initialize (&m_bmstack);
+}
+
+sem_item_optimizer::~sem_item_optimizer ()
+{
+  for (unsigned int i = 0; i < m_items.length (); i++)
+    delete m_items[i];
+
+  for (hash_table<congruence_class_group_hash>::iterator it = m_classes.begin ();
+       it != m_classes.end (); ++it)
+    {
+      for (unsigned int i = 0; i < (*it)->classes.length (); i++)
+	delete (*it)->classes[i];
+
+      (*it)->classes.release ();
+    }
+
+  m_items.release ();
+
+  bitmap_obstack_release (&m_bmstack);
+}
+
+/* Write IPA ICF summary for symbols.  */
+
+void
+sem_item_optimizer::write_summary (void)
+{
+  unsigned int count = 0;
+
+  output_block *ob = create_output_block (LTO_section_ipa_icf);
+  lto_symtab_encoder_t encoder = ob->decl_state->symtab_node_encoder;
+  ob->symbol = NULL;
+
+  /* Calculate number of symbols to be serialized.  */
+  for (lto_symtab_encoder_iterator lsei = lsei_start_in_partition (encoder);
+       !lsei_end_p (lsei);
+       lsei_next_in_partition (&lsei))
+    {
+      symtab_node *node = lsei_node (lsei);
+
+      if (m_symtab_node_map.get (node))
+	count++;
+    }
+
+  streamer_write_uhwi (ob, count);
+
+  /* Process all of the symbols.  */
+  for (lto_symtab_encoder_iterator lsei = lsei_start_in_partition (encoder);
+       !lsei_end_p (lsei);
+       lsei_next_in_partition (&lsei))
+    {
+      symtab_node *node = lsei_node (lsei);
+
+      sem_item **item = m_symtab_node_map.get (node);
+
+      if (item && *item)
+	{
+	  int node_ref = lto_symtab_encoder_encode (encoder, node);
+	  streamer_write_uhwi_stream (ob->main_stream, node_ref);
+
+	  streamer_write_uhwi (ob, (*item)->get_hash ());
+	}
+    }
+
+  streamer_write_char_stream (ob->main_stream, 0);
+  produce_asm (ob, NULL);
+  destroy_output_block (ob);
+}
+
+/* Reads a section from LTO stream file FILE_DATA. Input block for DATA
+   contains LEN bytes.  */
+
+void
+sem_item_optimizer::read_section (lto_file_decl_data *file_data,
+				  const char *data, size_t len)
+{
+  const lto_function_header *header =
+    (const lto_function_header *) data;
+  const int cfg_offset = sizeof (lto_function_header);
+  const int main_offset = cfg_offset + header->cfg_size;
+  const int string_offset = main_offset + header->main_size;
+  data_in *data_in;
+  unsigned int i;
+  unsigned int count;
+
+  lto_input_block ib_main ((const char *) data + main_offset, 0,
+			   header->main_size);
+
+  data_in =
+    lto_data_in_create (file_data, (const char *) data + string_offset,
+			header->string_size, vNULL);
+
+  count = streamer_read_uhwi (&ib_main);
+
+  for (i = 0; i < count; i++)
+    {
+      unsigned int index;
+      symtab_node *node;
+      lto_symtab_encoder_t encoder;
+
+      index = streamer_read_uhwi (&ib_main);
+      encoder = file_data->symtab_node_encoder;
+      node = lto_symtab_encoder_deref (encoder, index);
+
+      hashval_t hash = streamer_read_uhwi (&ib_main);
+
+      gcc_assert (node->definition);
+
+      if (dump_file)
+	fprintf (dump_file, "Symbol added:%s (tree: %p, uid:%u)\n", node->asm_name (),
+		 (void *) node->decl, node->order);
+
+      if (is_a<cgraph_node *> (node))
+	{
+	  cgraph_node *cnode = dyn_cast <cgraph_node *> (node);
+
+	  m_items.safe_push (new sem_function (cnode, hash, &m_bmstack));
+	}
+      else
+	{
+	  varpool_node *vnode = dyn_cast <varpool_node *> (node);
+
+	  m_items.safe_push (new sem_variable (vnode, hash, &m_bmstack));
+	}
+    }
+
+  lto_free_section_data (file_data, LTO_section_ipa_icf, NULL, data,
+			 len);
+  lto_data_in_delete (data_in);
+}
+
+/* Read IPA IPA ICF summary for symbols.  */
+
+void
+sem_item_optimizer::read_summary (void)
+{
+  lto_file_decl_data **file_data_vec = lto_get_file_decl_data ();
+  lto_file_decl_data *file_data;
+  unsigned int j = 0;
+
+  while ((file_data = file_data_vec[j++]))
+    {
+      size_t len;
+      const char *data = lto_get_section_data (file_data,
+			 LTO_section_ipa_icf, NULL, &len);
+
+      if (data)
+	read_section (file_data, data, len);
+    }
+}
+
+/* Register callgraph and varpool hooks.  */
+
+void
+sem_item_optimizer::register_hooks (void)
+{
+  m_cgraph_node_hooks = symtab->add_cgraph_removal_hook
+			(&sem_item_optimizer::cgraph_removal_hook, this);
+
+  m_varpool_node_hooks = symtab->add_varpool_removal_hook
+			 (&sem_item_optimizer::varpool_removal_hook, this);
+}
+
+/* Unregister callgraph and varpool hooks.  */
+
+void
+sem_item_optimizer::unregister_hooks (void)
+{
+  if (m_cgraph_node_hooks)
+    symtab->remove_cgraph_removal_hook (m_cgraph_node_hooks);
+
+  if (m_varpool_node_hooks)
+    symtab->remove_varpool_removal_hook (m_varpool_node_hooks);
+}
+
+/* Adds a CLS to hashtable associated by hash value.  */
+
+void
+sem_item_optimizer::add_class (congruence_class *cls)
+{
+  gcc_assert (cls->members.length ());
+
+  congruence_class_group *group = get_group_by_hash (
+				    cls->members[0]->get_hash (),
+				    cls->members[0]->type);
+  group->classes.safe_push (cls);
+}
+
+/* Gets a congruence class group based on given HASH value and TYPE.  */
+
+congruence_class_group *
+sem_item_optimizer::get_group_by_hash (hashval_t hash, sem_item_type type)
+{
+  congruence_class_group *item = XNEW (congruence_class_group);
+  item->hash = hash;
+  item->type = type;
+
+  congruence_class_group **slot = m_classes.find_slot (item, INSERT);
+
+  if (*slot)
+    free (item);
+  else
+    {
+      item->classes.create (1);
+      *slot = item;
+    }
+
+  return *slot;
+}
+
+/* Callgraph removal hook called for a NODE with a custom DATA.  */
+
+void
+sem_item_optimizer::cgraph_removal_hook (cgraph_node *node, void *data)
+{
+  sem_item_optimizer *optimizer = (sem_item_optimizer *) data;
+  optimizer->remove_symtab_node (node);
+}
+
+/* Varpool removal hook called for a NODE with a custom DATA.  */
+
+void
+sem_item_optimizer::varpool_removal_hook (varpool_node *node, void *data)
+{
+  sem_item_optimizer *optimizer = (sem_item_optimizer *) data;
+  optimizer->remove_symtab_node (node);
+}
+
+/* Remove symtab NODE triggered by symtab removal hooks.  */
+
+void
+sem_item_optimizer::remove_symtab_node (symtab_node *node)
+{
+  gcc_assert (!m_classes.elements());
+
+  m_removed_items_set.add (node);
+}
+
+void
+sem_item_optimizer::remove_item (sem_item *item)
+{
+  if (m_symtab_node_map.get (item->node))
+    m_symtab_node_map.remove (item->node);
+  delete item;
+}
+
+/* Removes all callgraph and varpool nodes that are marked by symtab
+   as deleted.  */
+
+void
+sem_item_optimizer::filter_removed_items (void)
+{
+  auto_vec <sem_item *> filtered;
+
+  for (unsigned int i = 0; i < m_items.length(); i++)
+    {
+      sem_item *item = m_items[i];
+
+      if (!flag_ipa_icf_functions && item->type == FUNC)
+	{
+	  remove_item (item);
+	  continue;
+	}
+
+      if (!flag_ipa_icf_variables && item->type == VAR)
+	{
+	  remove_item (item);
+	  continue;
+	}
+
+      bool no_body_function = false;
+
+      if (item->type == FUNC)
+	{
+	  cgraph_node *cnode = static_cast <sem_function *>(item)->get_node ();
+
+	  no_body_function = in_lto_p && (cnode->alias || cnode->body_removed);
+	}
+
+      if(!m_removed_items_set.contains (m_items[i]->node)
+	  && !no_body_function)
+	{
+	  if (item->type == VAR || (!DECL_CXX_CONSTRUCTOR_P (item->decl)
+				    && !DECL_CXX_DESTRUCTOR_P (item->decl)))
+	    {
+	      filtered.safe_push (m_items[i]);
+	      continue;
+	    }
+	}
+
+      remove_item (item);
+    }
+
+  /* Clean-up of released semantic items.  */
+
+  m_items.release ();
+  for (unsigned int i = 0; i < filtered.length(); i++)
+    m_items.safe_push (filtered[i]);
+}
+
+/* Optimizer entry point.  */
+
+void
+sem_item_optimizer::execute (void)
+{
+  filter_removed_items ();
+  build_hash_based_classes ();
+
+  if (dump_file)
+    fprintf (dump_file, "Dump after hash based groups\n");
+  dump_cong_classes ();
+
+  for (unsigned int i = 0; i < m_items.length(); i++)
+    m_items[i]->init_wpa ();
+
+  build_graph ();
+
+  subdivide_classes_by_equality (true);
+
+  if (dump_file)
+    fprintf (dump_file, "Dump after WPA based types groups\n");
+
+  dump_cong_classes ();
+
+  process_cong_reduction ();
+  verify_classes ();
+
+  if (dump_file)
+    fprintf (dump_file, "Dump after callgraph-based congruence reduction\n");
+
+  dump_cong_classes ();
+
+  parse_nonsingleton_classes ();
+  subdivide_classes_by_equality ();
+
+  if (dump_file)
+    fprintf (dump_file, "Dump after full equality comparison of groups\n");
+
+  dump_cong_classes ();
+
+  unsigned int prev_class_count = m_classes_count;
+
+  process_cong_reduction ();
+  dump_cong_classes ();
+  verify_classes ();
+  merge_classes (prev_class_count);
+
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    symtab_node::dump_table (dump_file);
+}
+
+/* Function responsible for visiting all potential functions and
+   read-only variables that can be merged.  */
+
+void
+sem_item_optimizer::parse_funcs_and_vars (void)
+{
+  cgraph_node *cnode;
+
+  if (flag_ipa_icf_functions)
+    FOR_EACH_DEFINED_FUNCTION (cnode)
+    {
+      sem_function *f = sem_function::parse (cnode, &m_bmstack);
+      if (f)
+	{
+	  m_items.safe_push (f);
+	  m_symtab_node_map.put (cnode, f);
+
+	  if (dump_file)
+	    fprintf (dump_file, "Parsed function:%s\n", f->asm_name ());
+
+	  if (dump_file && (dump_flags & TDF_DETAILS))
+	    f->dump_to_file (dump_file);
+	}
+      else if (dump_file)
+	fprintf (dump_file, "Not parsed function:%s\n", cnode->asm_name ());
+    }
+
+  varpool_node *vnode;
+
+  if (flag_ipa_icf_variables)
+    FOR_EACH_DEFINED_VARIABLE (vnode)
+    {
+      sem_variable *v = sem_variable::parse (vnode, &m_bmstack);
+
+      if (v)
+	{
+	  m_items.safe_push (v);
+	  m_symtab_node_map.put (vnode, v);
+	}
+    }
+}
+
+/* Makes pairing between a congruence class CLS and semantic ITEM.  */
+
+void
+sem_item_optimizer::add_item_to_class (congruence_class *cls, sem_item *item)
+{
+  item->index_in_class = cls->members.length ();
+  cls->members.safe_push (item);
+  item->cls = cls;
+}
+
+/* Congruence classes are built by hash value.  */
+
+void
+sem_item_optimizer::build_hash_based_classes (void)
+{
+  for (unsigned i = 0; i < m_items.length (); i++)
+    {
+      sem_item *item = m_items[i];
+
+      congruence_class_group *group = get_group_by_hash (item->get_hash (),
+				      item->type);
+
+      if (!group->classes.length ())
+	{
+	  m_classes_count++;
+	  group->classes.safe_push (new congruence_class (class_id++));
+	}
+
+      add_item_to_class (group->classes[0], item);
+    }
+}
+
+/* Build references according to call graph.  */
+
+void
+sem_item_optimizer::build_graph (void)
+{
+  for (unsigned i = 0; i < m_items.length (); i++)
+    {
+      sem_item *item = m_items[i];
+      m_symtab_node_map.put (item->node, item);
+    }
+
+  for (unsigned i = 0; i < m_items.length (); i++)
+    {
+      sem_item *item = m_items[i];
+
+      if (item->type == FUNC)
+	{
+	  cgraph_node *cnode = dyn_cast <cgraph_node *> (item->node);
+
+	  cgraph_edge *e = cnode->callees;
+	  while (e)
+	    {
+	      sem_item **slot = m_symtab_node_map.get (e->callee);
+	      if (slot)
+		item->add_reference (*slot);
+
+	      e = e->next_callee;
+	    }
+	}
+
+      ipa_ref *ref = NULL;
+      for (unsigned i = 0; item->node->iterate_reference (i, ref); i++)
+	{
+	  sem_item **slot = m_symtab_node_map.get (ref->referred);
+	  if (slot)
+	    item->add_reference (*slot);
+	}
+    }
+}
+
+/* Semantic items in classes having more than one element and initialized.
+   In case of WPA, we load function body.  */
+
+void
+sem_item_optimizer::parse_nonsingleton_classes (void)
+{
+  unsigned int init_called_count = 0;
+
+  for (unsigned i = 0; i < m_items.length (); i++)
+    if (m_items[i]->cls->members.length () > 1)
+      {
+	m_items[i]->init ();
+	init_called_count++;
+      }
+
+  if (dump_file)
+    fprintf (dump_file, "Init called for %u items (%.2f%%).\n", init_called_count,
+	     100.0f * init_called_count / m_items.length ());
+}
+
+/* Equality function for semantic items is used to subdivide existing
+   classes. If IN_WPA, fast equality function is invoked.  */
+
+void
+sem_item_optimizer::subdivide_classes_by_equality (bool in_wpa)
+{
+  for (hash_table <congruence_class_group_hash>::iterator it = m_classes.begin ();
+       it != m_classes.end (); ++it)
+    {
+      unsigned int class_count = (*it)->classes.length ();
+
+      for (unsigned i = 0; i < class_count; i++)
+	{
+	  congruence_class *c = (*it)->classes [i];
+
+	  if (c->members.length() > 1)
+	    {
+	      auto_vec <sem_item *> new_vector;
+
+	      sem_item *first = c->members[0];
+	      new_vector.safe_push (first);
+
+	      unsigned class_split_first = (*it)->classes.length ();
+
+	      for (unsigned j = 1; j < c->members.length (); j++)
+		{
+		  sem_item *item = c->members[j];
+
+		  bool equals = in_wpa ? first->equals_wpa (item,
+				m_symtab_node_map) : first->equals (item, m_symtab_node_map);
+
+		  if (equals)
+		    new_vector.safe_push (item);
+		  else
+		    {
+		      bool integrated = false;
+
+		      for (unsigned k = class_split_first; k < (*it)->classes.length (); k++)
+			{
+			  sem_item *x = (*it)->classes[k]->members[0];
+			  bool equals = in_wpa ? x->equals_wpa (item,
+								m_symtab_node_map) : x->equals (item, m_symtab_node_map);
+
+			  if (equals)
+			    {
+			      integrated = true;
+			      add_item_to_class ((*it)->classes[k], item);
+
+			      break;
+			    }
+			}
+
+		      if (!integrated)
+			{
+			  congruence_class *c = new congruence_class (class_id++);
+			  m_classes_count++;
+			  add_item_to_class (c, item);
+
+			  (*it)->classes.safe_push (c);
+			}
+		    }
+		}
+
+	      // we replace newly created new_vector for the class we've just splitted
+	      c->members.release ();
+	      c->members.create (new_vector.length ());
+
+	      for (unsigned int j = 0; j < new_vector.length (); j++)
+		add_item_to_class (c, new_vector[j]);
+	    }
+	}
+    }
+
+  verify_classes ();
+}
+
+/* Verify congruence classes if checking is enabled.  */
+
+void
+sem_item_optimizer::verify_classes (void)
+{
+#if ENABLE_CHECKING
+  for (hash_table <congruence_class_group_hash>::iterator it = m_classes.begin ();
+       it != m_classes.end (); ++it)
+    {
+      for (unsigned int i = 0; i < (*it)->classes.length (); i++)
+	{
+	  congruence_class *cls = (*it)->classes[i];
+
+	  gcc_checking_assert (cls);
+	  gcc_checking_assert (cls->members.length () > 0);
+
+	  for (unsigned int j = 0; j < cls->members.length (); j++)
+	    {
+	      sem_item *item = cls->members[j];
+
+	      gcc_checking_assert (item);
+	      gcc_checking_assert (item->cls == cls);
+
+	      for (unsigned k = 0; k < item->usages.length (); k++)
+		{
+		  sem_usage_pair *usage = item->usages[k];
+		  gcc_checking_assert (usage->item->index_in_class <
+				       usage->item->cls->members.length ());
+		}
+	    }
+	}
+    }
+#endif
+}
+
+/* Disposes split map traverse function. CLS_PTR is pointer to congruence
+   class, BSLOT is bitmap slot we want to release. DATA is mandatory,
+   but unused argument.  */
+
+bool
+sem_item_optimizer::release_split_map (congruence_class * const &,
+				       bitmap const &b, traverse_split_pair *)
+{
+  bitmap bmp = b;
+
+  BITMAP_FREE (bmp);
+
+  return true;
+}
+
+/* Process split operation for a class given as pointer CLS_PTR,
+   where bitmap B splits congruence class members. DATA is used
+   as argument of split pair.  */
+
+bool
+sem_item_optimizer::traverse_congruence_split (congruence_class * const &cls,
+    bitmap const &b, traverse_split_pair *pair)
+{
+  sem_item_optimizer *optimizer = pair->optimizer;
+  const congruence_class *splitter_cls = pair->cls;
+
+  /* If counted bits are greater than zero and less than the number of members
+     a group will be splitted.  */
+  unsigned popcount = bitmap_count_bits (b);
+
+  if (popcount > 0 && popcount < cls->members.length ())
+    {
+      congruence_class* newclasses[2] = { new congruence_class (class_id++), new congruence_class (class_id++) };
+
+      for (unsigned int i = 0; i < cls->members.length (); i++)
+	{
+	  int target = bitmap_bit_p (b, i);
+	  congruence_class *tc = newclasses[target];
+
+	  add_item_to_class (tc, cls->members[i]);
+	}
+
+#ifdef ENABLE_CHECKING
+      for (unsigned int i = 0; i < 2; i++)
+	gcc_checking_assert (newclasses[i]->members.length ());
+#endif
+
+      if (splitter_cls == cls)
+	optimizer->splitter_class_removed = true;
+
+      /* Remove old class from worklist if presented.  */
+      bool in_worklist = cls->in_worklist;
+
+      if (in_worklist)
+	cls->in_worklist = false;
+
+      congruence_class_group g;
+      g.hash = cls->members[0]->get_hash ();
+      g.type = cls->members[0]->type;
+
+      congruence_class_group *slot = optimizer->m_classes.find(&g);
+
+      for (unsigned int i = 0; i < slot->classes.length (); i++)
+	if (slot->classes[i] == cls)
+	  {
+	    slot->classes.ordered_remove (i);
+	    break;
+	  }
+
+      /* New class will be inserted and integrated to work list.  */
+      for (unsigned int i = 0; i < 2; i++)
+	optimizer->add_class (newclasses[i]);
+
+      /* Two classes replace one, so that increment just by one.  */
+      optimizer->m_classes_count++;
+
+      /* If OLD class was presented in the worklist, we remove the class
+         and replace it will both newly created classes.  */
+      if (in_worklist)
+	for (unsigned int i = 0; i < 2; i++)
+	  optimizer->worklist_push (newclasses[i]);
+      else /* Just smaller class is inserted.  */
+	{
+	  unsigned int smaller_index = newclasses[0]->members.length () <
+				       newclasses[1]->members.length () ?
+				       0 : 1;
+	  optimizer->worklist_push (newclasses[smaller_index]);
+	}
+
+      if (dump_file && (dump_flags & TDF_DETAILS))
+	{
+	  fprintf (dump_file, "  congruence class splitted:\n");
+	  cls->dump (dump_file, 4);
+
+	  fprintf (dump_file, "  newly created groups:\n");
+	  for (unsigned int i = 0; i < 2; i++)
+	    newclasses[i]->dump (dump_file, 4);
+	}
+
+      /* Release class if not presented in work list.  */
+      if (!in_worklist)
+	delete cls;
+    }
+
+
+  return true;
+}
+
+/* Tests if a class CLS used as INDEXth splits any congruence classes.
+   Bitmap stack BMSTACK is used for bitmap allocation.  */
+
+void
+sem_item_optimizer::do_congruence_step_for_index (congruence_class *cls,
+    unsigned int index)
+{
+  hash_map <congruence_class *, bitmap> split_map;
+
+  for (unsigned int i = 0; i < cls->members.length (); i++)
+    {
+      sem_item *item = cls->members[i];
+
+      /* Iterate all usages that have INDEX as usage of the item.  */
+      for (unsigned int j = 0; j < item->usages.length (); j++)
+	{
+	  sem_usage_pair *usage = item->usages[j];
+
+	  if (usage->index != index)
+	    continue;
+
+	  bitmap *slot = split_map.get (usage->item->cls);
+	  bitmap b;
+
+	  if(!slot)
+	    {
+	      b = BITMAP_ALLOC (&m_bmstack);
+	      split_map.put (usage->item->cls, b);
+	    }
+	  else
+	    b = *slot;
+
+#if ENABLE_CHECKING
+	  gcc_checking_assert (usage->item->cls);
+	  gcc_checking_assert (usage->item->index_in_class <
+			       usage->item->cls->members.length ());
+#endif
+
+	  bitmap_set_bit (b, usage->item->index_in_class);
+	}
+    }
+
+  traverse_split_pair pair;
+  pair.optimizer = this;
+  pair.cls = cls;
+
+  splitter_class_removed = false;
+  split_map.traverse
+  <traverse_split_pair *, sem_item_optimizer::traverse_congruence_split> (&pair);
+
+  /* Bitmap clean-up.  */
+  split_map.traverse
+  <traverse_split_pair *, sem_item_optimizer::release_split_map> (NULL);
+}
+
+/* Every usage of a congruence class CLS is a candidate that can split the
+   collection of classes. Bitmap stack BMSTACK is used for bitmap
+   allocation.  */
+
+void
+sem_item_optimizer::do_congruence_step (congruence_class *cls)
+{
+  bitmap_iterator bi;
+  unsigned int i;
+
+  bitmap usage = BITMAP_ALLOC (&m_bmstack);
+
+  for (unsigned int i = 0; i < cls->members.length (); i++)
+    bitmap_ior_into (usage, cls->members[i]->usage_index_bitmap);
+
+  EXECUTE_IF_SET_IN_BITMAP (usage, 0, i, bi)
+  {
+    if (dump_file && (dump_flags & TDF_DETAILS))
+      fprintf (dump_file, "  processing congruece step for class: %u, index: %u\n",
+	       cls->id, i);
+
+    do_congruence_step_for_index (cls, i);
+
+    if (splitter_class_removed)
+      break;
+  }
+
+  BITMAP_FREE (usage);
+}
+
+/* Adds a newly created congruence class CLS to worklist.  */
+
+void
+sem_item_optimizer::worklist_push (congruence_class *cls)
+{
+  /* Return if the class CLS is already presented in work list.  */
+  if (cls->in_worklist)
+    return;
+
+  cls->in_worklist = true;
+  worklist.push_back (cls);
+}
+
+/* Pops a class from worklist. */
+
+congruence_class *
+sem_item_optimizer::worklist_pop (void)
+{
+  congruence_class *cls;
+
+  while (!worklist.empty ())
+    {
+      cls = worklist.front ();
+      worklist.pop_front ();
+      if (cls->in_worklist)
+	{
+	  cls->in_worklist = false;
+
+	  return cls;
+	}
+      else
+	{
+	  /* Work list item was already intended to be removed.
+	     The only reason for doing it is to split a class.
+	     Thus, the class CLS is deleted.  */
+	  delete cls;
+	}
+    }
+
+  return NULL;
+}
+
+/* Iterative congruence reduction function.  */
+
+void
+sem_item_optimizer::process_cong_reduction (void)
+{
+  for (hash_table<congruence_class_group_hash>::iterator it = m_classes.begin ();
+       it != m_classes.end (); ++it)
+    for (unsigned i = 0; i < (*it)->classes.length (); i++)
+      if ((*it)->classes[i]->is_class_used ())
+	worklist_push ((*it)->classes[i]);
+
+  if (dump_file)
+    fprintf (dump_file, "Worklist has been filled with: %lu\n",
+	     worklist.size ());
+
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    fprintf (dump_file, "Congruence class reduction\n");
+
+  congruence_class *cls;
+  while ((cls = worklist_pop ()) != NULL)
+    do_congruence_step (cls);
+}
+
+/* Debug function prints all informations about congruence classes.  */
+
+void
+sem_item_optimizer::dump_cong_classes (void)
+{
+  if (!dump_file)
+    return;
+
+  fprintf (dump_file,
+	   "Congruence classes: %u (unique hash values: %lu), with total: %u items\n",
+	   m_classes_count, m_classes.elements(), m_items.length ());
+
+  /* Histogram calculation.  */
+  unsigned int max_index = 0;
+  unsigned int* histogram = XCNEWVEC (unsigned int, m_items.length () + 1);
+
+  for (hash_table<congruence_class_group_hash>::iterator it = m_classes.begin ();
+       it != m_classes.end (); ++it)
+
+    for (unsigned i = 0; i < (*it)->classes.length (); i++)
+      {
+	unsigned int c = (*it)->classes[i]->members.length ();
+	histogram[c]++;
+
+	if (c > max_index)
+	  max_index = c;
+      }
+
+  fprintf (dump_file,
+	   "Class size histogram [num of members]: number of classe number of classess\n");
+
+  for (unsigned int i = 0; i <= max_index; i++)
+    if (histogram[i])
+      fprintf (dump_file, "[%u]: %u classes\n", i, histogram[i]);
+
+  fprintf (dump_file, "\n\n");
+
+
+  if (dump_flags & TDF_DETAILS)
+    for (hash_table<congruence_class_group_hash>::iterator it = m_classes.begin ();
+	 it != m_classes.end (); ++it)
+      {
+	fprintf (dump_file, "  group: with %u classes:\n", (*it)->classes.length ());
+
+	for (unsigned i = 0; i < (*it)->classes.length (); i++)
+	  {
+	    (*it)->classes[i]->dump (dump_file, 4);
+
+	    if(i < (*it)->classes.length () - 1)
+	      fprintf (dump_file, " ");
+	  }
+      }
+
+  free (histogram);
+}
+
+/* After reduction is done, we can declare all items in a group
+   to be equal. PREV_CLASS_COUNT is start number of classes
+   before reduction.  */
+
+void
+sem_item_optimizer::merge_classes (unsigned int prev_class_count)
+{
+  unsigned int item_count = m_items.length ();
+  unsigned int class_count = m_classes_count;
+  unsigned int equal_items = item_count - class_count;
+
+  if (dump_file)
+    {
+      fprintf (dump_file, "\nItem count: %u\n", item_count);
+      fprintf (dump_file, "Congruent classes before: %u, after: %u\n",
+	       prev_class_count, class_count);
+      fprintf (dump_file, "Average class size before: %.2f, after: %.2f\n",
+	       1.0f * item_count / prev_class_count,
+	       1.0f * item_count / class_count);
+      fprintf (dump_file, "Equal symbols: %u\n", equal_items);
+      fprintf (dump_file, "Fraction of visited symbols: %.2f%%\n\n",
+	       100.0f * equal_items / item_count);
+    }
+
+  for (hash_table<congruence_class_group_hash>::iterator it = m_classes.begin ();
+       it != m_classes.end (); ++it)
+    for (unsigned int i = 0; i < (*it)->classes.length (); i++)
+      {
+	congruence_class *c = (*it)->classes[i];
+
+	if (c->members.length () == 1)
+	  continue;
+
+	gcc_assert (c->members.length ());
+
+	sem_item *source = c->members[0];
+
+	for (unsigned int j = 1; j < c->members.length (); j++)
+	  {
+	    sem_item *alias = c->members[j];
+	    source->equals (alias, m_symtab_node_map);
+
+	    if (dump_file)
+	      {
+		fprintf (dump_file, "Semantic equality hit:%s->%s\n",
+			 source->name (), alias->name ());
+		fprintf (dump_file, "Assembler symbol names:%s->%s\n",
+			 source->asm_name (), alias->asm_name ());
+	      }
+
+	    if (dump_file && (dump_flags & TDF_DETAILS))
+	      {
+		source->dump_to_file (dump_file);
+		alias->dump_to_file (dump_file);
+	      }
+
+	    source->merge (alias);
+	  }
+      }
+}
+
+/* Dump function prints all class members to a FILE with an INDENT.  */
+
+void
+congruence_class::dump (FILE *file, unsigned int indent) const
+{
+  FPRINTF_SPACES (file, indent, "class with id: %u, hash: %u, items: %u\n",
+		  id, members[0]->get_hash (), members.length ());
+
+  FPUTS_SPACES (file, indent + 2, "");
+  for (unsigned i = 0; i < members.length (); i++)
+    fprintf (file, "%s(%p/%u) ", members[i]->asm_name (), (void *) members[i]->decl,
+	     members[i]->node->order);
+
+  fprintf (file, "\n");
+}
+
+/* Returns true if there's a member that is used from another group.  */
+
+bool
+congruence_class::is_class_used (void)
+{
+  for (unsigned int i = 0; i < members.length (); i++)
+    if (members[i]->usages.length ())
+      return true;
+
+  return false;
+}
+
+/* Initialization and computation of symtab node hash, there data
+   are propagated later on.  */
+
+static sem_item_optimizer *optimizer = NULL;
+
+/* Generate pass summary for IPA ICF pass.  */
+
+static void
+ipa_icf_generate_summary (void)
+{
+  if (!optimizer)
+    optimizer = new sem_item_optimizer ();
+
+  optimizer->parse_funcs_and_vars ();
+}
+
+/* Write pass summary for IPA ICF pass.  */
+
+static void
+ipa_icf_write_summary (void)
+{
+  gcc_assert (optimizer);
+
+  optimizer->write_summary ();
+}
+
+/* Read pass summary for IPA ICF pass.  */
+
+static void
+ipa_icf_read_summary (void)
+{
+  if (!optimizer)
+    optimizer = new sem_item_optimizer ();
+
+  optimizer->read_summary ();
+  optimizer->register_hooks ();
+}
+
+/* Semantic equality exection function.  */
+
+static unsigned int
+ipa_icf_driver (void)
+{
+  gcc_assert (optimizer);
+
+  optimizer->execute ();
+  optimizer->unregister_hooks ();
+
+  delete optimizer;
+
+  return 0;
+}
+
+const pass_data pass_data_ipa_icf =
+{
+  IPA_PASS,		    /* type */
+  "icf",		    /* name */
+  OPTGROUP_IPA,             /* optinfo_flags */
+  TV_IPA_ICF,		    /* tv_id */
+  0,                        /* properties_required */
+  0,                        /* properties_provided */
+  0,                        /* properties_destroyed */
+  0,                        /* todo_flags_start */
+  0,                        /* todo_flags_finish */
+};
+
+class pass_ipa_icf : public ipa_opt_pass_d
+{
+public:
+  pass_ipa_icf (gcc::context *ctxt)
+    : ipa_opt_pass_d (pass_data_ipa_icf, ctxt,
+		      ipa_icf_generate_summary, /* generate_summary */
+		      ipa_icf_write_summary, /* write_summary */
+		      ipa_icf_read_summary, /* read_summary */
+		      NULL, /*
+		      write_optimization_summary */
+		      NULL, /*
+		      read_optimization_summary */
+		      NULL, /* stmt_fixup */
+		      0, /* function_transform_todo_flags_start */
+		      NULL, /* function_transform */
+		      NULL) /* variable_transform */
+  {}
+
+  /* opt_pass methods: */
+  virtual bool gate (function *)
+  {
+    return flag_ipa_icf_variables || flag_ipa_icf_functions;
+  }
+
+  virtual unsigned int execute (function *)
+  {
+    return ipa_icf_driver();
+  }
+}; // class pass_ipa_icf
+
+} // ipa_icf namespace
+
+ipa_opt_pass_d *
+make_pass_ipa_icf (gcc::context *ctxt)
+{
+  return new ipa_icf::pass_ipa_icf (ctxt);
+}
diff --git a/gcc/ipa-icf.h b/gcc/ipa-icf.h
new file mode 100644
index 0000000..d8e7b16
--- /dev/null
+++ b/gcc/ipa-icf.h
@@ -0,0 +1,553 @@
+/* Interprocedural semantic function equality pass
+   Copyright (C) 2014 Free Software Foundation, Inc.
+
+   Contributed by Jan Hubicka <hubicka@ucw.cz> and Martin Liska <mliska@suse.cz>
+
+This file is part of GCC.
+
+GCC 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, or (at your option) any later
+version.
+
+GCC 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 GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+namespace ipa_icf {
+class sem_item;
+
+/* Congruence class encompasses a collection of either functions or
+   read-only variables. These items are considered to be equivalent
+   if not proved the oposite.  */
+class congruence_class
+{
+public:
+  /* Congruence class constructor for a new class with _ID.  */
+  congruence_class (unsigned int _id): in_worklist (false), id(_id)
+  {
+  }
+
+  /* Destructor.  */
+  ~congruence_class ()
+  {
+  }
+
+  /* Dump function prints all class members to a FILE with an INDENT.  */
+  void dump (FILE *file, unsigned int indent = 0) const;
+
+  /* Returns true if there's a member that is used from another group.  */
+  bool is_class_used (void);
+
+  /* Flag is used in case we want to remove a class from worklist and
+     delete operation is quite expensive for
+     the data structure (linked list).  */
+  bool in_worklist;
+
+  /* Vector of all group members.  */
+  auto_vec <sem_item *> members;
+
+  /* Global unique class identifier.  */
+  unsigned int id;
+};
+
+/* Semantic item type enum.  */
+enum sem_item_type
+{
+  FUNC,
+  VAR
+};
+
+/* Semantic item usage pair.  */
+class sem_usage_pair
+{
+public:
+  /* Constructor for key value pair, where _ITEM is key and _INDEX is a target.  */
+  sem_usage_pair (sem_item *_item, unsigned int _index);
+
+  /* Target semantic item where an item is used.  */
+  sem_item *item;
+
+  /* Index of usage of such an item.  */
+  unsigned int index;
+};
+
+/* Semantic item is a base class that encapsulates all shared functionality
+   for both semantic function and variable items.  */
+class sem_item
+{
+public:
+  /* Semantic item constructor for a node of _TYPE, where STACK is used
+     for bitmap memory allocation.  */
+  sem_item (sem_item_type _type, bitmap_obstack *stack);
+
+  /* Semantic item constructor for a node of _TYPE, where STACK is used
+     for bitmap memory allocation. The item is based on symtab node _NODE
+     with computed _HASH.  */
+  sem_item (sem_item_type _type, symtab_node *_node, hashval_t _hash,
+	    bitmap_obstack *stack);
+
+  virtual ~sem_item ();
+
+  /* Dump function for debugging purpose.  */
+  DEBUG_FUNCTION void dump (void);
+
+  /* Initialize semantic item by info reachable during LTO WPA phase.  */
+  virtual void init_wpa (void) = 0;
+
+  /* Semantic item initialization function.  */
+  virtual void init (void) = 0;
+
+  /* Add reference to a semantic TARGET.  */
+  void add_reference (sem_item *target);
+
+  /* Gets symbol name of the item.  */
+  const char *name (void)
+  {
+    return node->name ();
+  }
+
+  /* Gets assembler name of the item.  */
+  const char *asm_name (void)
+  {
+    return node->asm_name ();
+  }
+
+  /* Fast equality function based on knowledge known in WPA.  */
+  virtual bool equals_wpa (sem_item *item,
+			   hash_map <symtab_node *, sem_item *> &ignored_nodes) = 0;
+
+  /* Returns true if the item equals to ITEM given as arguemnt.  */
+  virtual bool equals (sem_item *item,
+		       hash_map <symtab_node *, sem_item *> &ignored_nodes) = 0;
+
+  /* References independent hash function.  */
+  virtual hashval_t get_hash (void) = 0;
+
+  /* Merges instance with an ALIAS_ITEM, where alias, thunk or redirection can
+     be applied.  */
+  virtual bool merge (sem_item *alias_item) = 0;
+
+  /* Dump symbol to FILE.  */
+  virtual void dump_to_file (FILE *file) = 0;
+
+  /* Return base tree that can be used for compatible_types_p and
+     contains_polymorphic_type_p comparison.  */
+
+  static bool get_base_types (tree *t1, tree *t2);
+
+  /* Item type.  */
+  sem_item_type type;
+
+  /* Symtab node.  */
+  symtab_node *node;
+
+  /* Declaration tree node.  */
+  tree decl;
+
+  /* Semantic references used that generate congruence groups.  */
+  vec <sem_item *> refs;
+
+  /* Pointer to a congruence class the item belongs to.  */
+  congruence_class *cls;
+
+  /* Index of the item in a class belonging to.  */
+  unsigned int index_in_class;
+
+  /* List of semantic items where the instance is used.  */
+  vec <sem_usage_pair *> usages;
+
+  /* A bitmap with indices of all classes referencing this item.  */
+  bitmap usage_index_bitmap;
+
+  /* List of tree references (either FUNC_DECL or VAR_DECL).  */
+  vec <tree> tree_refs;
+
+  /* A set with symbol table references.  */
+  hash_set <symtab_node *> refs_set;
+
+protected:
+  /* Cached, once calculated hash for the item.  */
+  hashval_t hash;
+
+private:
+  /* Initialize internal data structures. Bitmap STACK is used for
+     bitmap memory allocation process.  */
+  void setup (bitmap_obstack *stack);
+}; // class sem_item
+
+class sem_function: public sem_item
+{
+public:
+  /* Semantic function constructor that uses STACK as bitmap memory stack.  */
+  sem_function (bitmap_obstack *stack);
+
+  /*  Constructor based on callgraph node _NODE with computed hash _HASH.
+      Bitmap STACK is used for memory allocation.  */
+  sem_function (cgraph_node *_node, hashval_t _hash, bitmap_obstack *stack);
+
+  ~sem_function ();
+
+  inline virtual void init_wpa (void)
+  {
+    parse_tree_args ();
+  }
+
+  virtual void init (void);
+  virtual bool equals_wpa (sem_item *item,
+			   hash_map <symtab_node *, sem_item *> &ignored_nodes);
+  virtual hashval_t get_hash (void);
+  virtual bool equals (sem_item *item,
+		       hash_map <symtab_node *, sem_item *> &ignored_nodes);
+  virtual bool merge (sem_item *alias_item);
+
+  /* Dump symbol to FILE.  */
+  virtual void dump_to_file (FILE *file)
+  {
+    gcc_assert (file);
+    dump_function_to_file (decl, file, TDF_DETAILS);
+  }
+
+  /* Parses function arguments and result type.  */
+  void parse_tree_args (void);
+
+  /* Returns cgraph_node.  */
+  inline cgraph_node *get_node (void)
+  {
+    return dyn_cast <cgraph_node *> (node);
+  }
+
+  /* Improve accumulated hash for HSTATE based on a gimple statement STMT.  */
+  void hash_stmt (inchash::hash *inchash, gimple stmt);
+
+  /* Return true if polymorphic comparison must be processed.  */
+  bool compare_polymorphic_p (void);
+
+  /* For a given call graph NODE, the function constructs new
+     semantic function item.  */
+  static sem_function *parse (cgraph_node *node, bitmap_obstack *stack);
+
+  /* Exception handling region tree.  */
+  eh_region region_tree;
+
+  /* Result type tree node.  */
+  tree result_type;
+
+  /* Array of argument tree types.  */
+  vec <tree> arg_types;
+
+  /* Number of function arguments.  */
+  unsigned int arg_count;
+
+  /* Total amount of edges in the function.  */
+  unsigned int edge_count;
+
+  /* Vector of sizes of all basic blocks.  */
+  vec <unsigned int> bb_sizes;
+
+  /* Control flow graph checksum.  */
+  hashval_t cfg_checksum;
+
+  /* GIMPLE codes hash value.  */
+  hashval_t gcode_hash;
+
+  /* Total number of SSA names used in the function.  */
+  unsigned ssa_names_size;
+
+  /* Array of structures for all basic blocks.  */
+  vec <ipa_icf_gimple::sem_bb *> bb_sorted;
+
+private:
+  /* Calculates hash value based on a BASIC_BLOCK.  */
+  hashval_t get_bb_hash (const ipa_icf_gimple::sem_bb *basic_block);
+
+  /* For given basic blocks BB1 and BB2 (from functions FUNC1 and FUNC),
+     true value is returned if phi nodes are semantically
+     equivalent in these blocks .  */
+  bool compare_phi_node (basic_block bb1, basic_block bb2);
+
+  /* Basic blocks dictionary BB_DICT returns true if SOURCE index BB
+     corresponds to TARGET.  */
+  bool bb_dict_test (int* bb_dict, int source, int target);
+
+  /* Iterates all tree types in T1 and T2 and returns true if all types
+     are compatible. If COMPARE_POLYMORPHIC is set to true,
+     more strict comparison is executed.  */
+  bool compare_type_list (tree t1, tree t2, bool compare_polymorphic);
+
+  /* If cgraph edges E1 and E2 are indirect calls, verify that
+     ICF flags are the same.  */
+  bool compare_edge_flags (cgraph_edge *e1, cgraph_edge *e2);
+
+  /* For a given symbol table nodes N1 and N2, we check that FUNCTION_DECLs
+     point to a same function. Comparison can be skipped if IGNORED_NODES
+     contains these nodes.  */
+  bool compare_cgraph_references (hash_map <symtab_node *, sem_item *>
+				  &ignored_nodes,
+				  symtab_node *n1, symtab_node *n2);
+
+  /* Processes function equality comparison.  */
+  bool equals_private (sem_item *item,
+		       hash_map <symtab_node *, sem_item *> &ignored_nodes);
+
+  /* Returns true if tree T can be compared as a handled component.  */
+  static bool icf_handled_component_p (tree t);
+
+  /* Function checker stores binding between functions.   */
+  ipa_icf_gimple::func_checker *m_checker;
+
+  /* COMPARED_FUNC is a function that we compare to.  */
+  sem_function *m_compared_func;
+}; // class sem_function
+
+class sem_variable: public sem_item
+{
+public:
+  /* Semantic variable constructor that uses STACK as bitmap memory stack.  */
+  sem_variable (bitmap_obstack *stack);
+
+  /*  Constructor based on callgraph node _NODE with computed hash _HASH.
+      Bitmap STACK is used for memory allocation.  */
+
+  sem_variable (varpool_node *_node, hashval_t _hash, bitmap_obstack *stack);
+
+  inline virtual void init_wpa (void) {}
+
+  /* Semantic variable initialization function.  */
+  inline virtual void init (void)
+  {
+    decl = get_node ()->decl;
+    ctor = ctor_for_folding (decl);
+  }
+
+  virtual hashval_t get_hash (void);
+  virtual bool merge (sem_item *alias_item);
+  virtual void dump_to_file (FILE *file);
+  virtual bool equals (sem_item *item,
+		       hash_map <symtab_node *, sem_item *> &ignored_nodes);
+
+  /* Fast equality variable based on knowledge known in WPA.  */
+  inline virtual bool equals_wpa (sem_item *item,
+				  hash_map <symtab_node *, sem_item *> & ARG_UNUSED(ignored_nodes))
+  {
+    gcc_assert (item->type == VAR);
+    return true;
+  }
+
+  /* Returns varpool_node.  */
+  inline varpool_node *get_node (void)
+  {
+    return dyn_cast <varpool_node *> (node);
+  }
+
+  /* Parser function that visits a varpool NODE.  */
+  static sem_variable *parse (varpool_node *node, bitmap_obstack *stack);
+
+  /* Variable constructor.  */
+  tree ctor;
+
+private:
+  /* Iterates though a constructor and identifies tree references
+     we are interested in semantic function equality.  */
+  void parse_tree_refs (tree t);
+
+  /* Compares trees T1 and T2 for semantic equality.  */
+  static bool equals (tree t1, tree t2);
+
+  /* Compare that symbol sections are either NULL or have same name.  */
+  bool compare_sections (sem_variable *alias);
+
+}; // class sem_variable
+
+class sem_item_optimizer;
+
+struct congruence_class_group
+{
+  hashval_t hash;
+  sem_item_type type;
+  vec <congruence_class *> classes;
+};
+
+/* Congruence class set structure.  */
+struct congruence_class_group_hash: typed_noop_remove <congruence_class_group>
+{
+  typedef congruence_class_group value_type;
+  typedef congruence_class_group compare_type;
+
+  static inline hashval_t hash (const value_type *item)
+  {
+    return item->hash;
+  }
+
+  static inline int equal (const value_type *item1, const compare_type *item2)
+  {
+    return item1->hash == item2->hash && item1->type == item2->type;
+  }
+};
+
+struct traverse_split_pair
+{
+  sem_item_optimizer *optimizer;
+  class congruence_class *cls;
+};
+
+/* Semantic item optimizer includes all top-level logic
+   related to semantic equality comparison.  */
+class sem_item_optimizer
+{
+public:
+  sem_item_optimizer ();
+  ~sem_item_optimizer ();
+
+  /* Function responsible for visiting all potential functions and
+     read-only variables that can be merged.  */
+  void parse_funcs_and_vars (void);
+
+  /* Optimizer entry point.  */
+  void execute (void);
+
+  /* Dump function. */
+  void dump (void);
+
+  /* Verify congruence classes if checking is enabled.  */
+  void verify_classes (void);
+
+  /* Write IPA ICF summary for symbols.  */
+  void write_summary (void);
+
+  /* Read IPA IPA ICF summary for symbols.  */
+  void read_summary (void);
+
+  /* Callgraph removal hook called for a NODE with a custom DATA.  */
+  static void cgraph_removal_hook (cgraph_node *node, void *data);
+
+  /* Varpool removal hook called for a NODE with a custom DATA.  */
+  static void varpool_removal_hook (varpool_node *node, void *data);
+
+  /* Worklist of congruence classes that can potentially
+     refine classes of congruence.  */
+  std::list<congruence_class *> worklist;
+
+  /* Remove semantic ITEM and release memory.  */
+  void remove_item (sem_item *item);
+
+  /* Remove symtab NODE triggered by symtab removal hooks.  */
+  void remove_symtab_node (symtab_node *node);
+
+  /* Register callgraph and varpool hooks.  */
+  void register_hooks (void);
+
+  /* Unregister callgraph and varpool hooks.  */
+  void unregister_hooks (void);
+
+  /* Adds a CLS to hashtable associated by hash value.  */
+  void add_class (congruence_class *cls);
+
+  /* Gets a congruence class group based on given HASH value and TYPE.  */
+  congruence_class_group *get_group_by_hash (hashval_t hash,
+      sem_item_type type);
+
+private:
+
+  /* Congruence classes are built by hash value.  */
+  void build_hash_based_classes (void);
+
+  /* Semantic items in classes having more than one element and initialized.
+     In case of WPA, we load function body.  */
+  void parse_nonsingleton_classes (void);
+
+  /* Equality function for semantic items is used to subdivide existing
+     classes. If IN_WPA, fast equality function is invoked.  */
+  void subdivide_classes_by_equality (bool in_wpa = false);
+
+  /* Debug function prints all informations about congruence classes.  */
+  void dump_cong_classes (void);
+
+  /* Build references according to call graph.  */
+  void build_graph (void);
+
+  /* Iterative congruence reduction function.  */
+  void process_cong_reduction (void);
+
+  /* After reduction is done, we can declare all items in a group
+     to be equal. PREV_CLASS_COUNT is start number of classes
+     before reduction.  */
+  void merge_classes (unsigned int prev_class_count);
+
+  /* Adds a newly created congruence class CLS to worklist.  */
+  void worklist_push (congruence_class *cls);
+
+  /* Pops a class from worklist. */
+  congruence_class *worklist_pop ();
+
+  /* Every usage of a congruence class CLS is a candidate that can split the
+     collection of classes. Bitmap stack BMSTACK is used for bitmap
+     allocation.  */
+  void do_congruence_step (congruence_class *cls);
+
+  /* Tests if a class CLS used as INDEXth splits any congruence classes.
+     Bitmap stack BMSTACK is used for bitmap allocation.  */
+  void do_congruence_step_for_index (congruence_class *cls, unsigned int index);
+
+  /* Makes pairing between a congruence class CLS and semantic ITEM.  */
+  static void add_item_to_class (congruence_class *cls, sem_item *item);
+
+  /* Disposes split map traverse function. CLS is congruence
+     class, BSLOT is bitmap slot we want to release. DATA is mandatory,
+     but unused argument.  */
+  static bool release_split_map (congruence_class * const &cls, bitmap const &b,
+				 traverse_split_pair *pair);
+
+  /* Process split operation for a cognruence class CLS,
+     where bitmap B splits congruence class members. DATA is used
+     as argument of split pair.  */
+  static bool traverse_congruence_split (congruence_class * const &cls,
+					 bitmap const &b,
+					 traverse_split_pair *pair);
+
+  /* Reads a section from LTO stream file FILE_DATA. Input block for DATA
+     contains LEN bytes.  */
+  void read_section (lto_file_decl_data *file_data, const char *data,
+		     size_t len);
+
+  /* Removes all callgraph and varpool nodes that are marked by symtab
+     as deleted.  */
+  void filter_removed_items (void);
+
+  /* Vector of semantic items.  */
+  vec <sem_item *> m_items;
+
+  /* A set containing all items removed by hooks.  */
+  hash_set <symtab_node *> m_removed_items_set;
+
+  /* Hashtable of congruence classes */
+  hash_table <congruence_class_group_hash> m_classes;
+
+  /* Count of congruence classes.  */
+  unsigned int m_classes_count;
+
+  /* Map data structure maps symtab nodes to semantic items.  */
+  hash_map <symtab_node *, sem_item *> m_symtab_node_map;
+
+  /* Set to true if a splitter class is removed.  */
+  bool splitter_class_removed;
+
+  /* Global unique class id counter.  */
+  static unsigned int class_id;
+
+  /* Callgraph node removal hook holder.  */
+  cgraph_node_hook_list *m_cgraph_node_hooks;
+
+  /* Varpool node removal hook holder.  */
+  varpool_node_hook_list *m_varpool_node_hooks;
+
+  /* Bitmap stack.  */
+  bitmap_obstack m_bmstack;
+}; // class sem_item_optimizer
+
+} // ipa_icf namespace
diff --git a/gcc/lto-cgraph.c b/gcc/lto-cgraph.c
index 0584946..c5aa797 100644
--- a/gcc/lto-cgraph.c
+++ b/gcc/lto-cgraph.c
@@ -539,6 +539,7 @@ lto_output_node (struct lto_simple_output_block *ob, struct cgraph_node *node,
   bp_pack_value (&bp, node->only_called_at_exit, 1);
   bp_pack_value (&bp, node->tm_clone, 1);
   bp_pack_value (&bp, node->calls_comdat_local, 1);
+  bp_pack_value (&bp, node->icf_merged, 1);
   bp_pack_value (&bp, node->thunk.thunk_p && !boundary_p, 1);
   bp_pack_enum (&bp, ld_plugin_symbol_resolution,
 	        LDPR_NUM_KNOWN, node->resolution);
@@ -1079,6 +1080,7 @@ input_overwrite_node (struct lto_file_decl_data *file_data,
   node->only_called_at_exit = bp_unpack_value (bp, 1);
   node->tm_clone = bp_unpack_value (bp, 1);
   node->calls_comdat_local = bp_unpack_value (bp, 1);
+  node->icf_merged = bp_unpack_value (bp, 1);
   node->thunk.thunk_p = bp_unpack_value (bp, 1);
   node->resolution = bp_unpack_enum (bp, ld_plugin_symbol_resolution,
 				     LDPR_NUM_KNOWN);
diff --git a/gcc/lto-section-in.c b/gcc/lto-section-in.c
index 5623706..c053545 100644
--- a/gcc/lto-section-in.c
+++ b/gcc/lto-section-in.c
@@ -60,7 +60,8 @@ const char *lto_section_name[LTO_N_SECTION_TYPES] =
   "opts",
   "cgraphopt",
   "inline",
-  "ipcp_trans"
+  "ipcp_trans",
+  "icf"
 };
 
 
diff --git a/gcc/lto-streamer.h b/gcc/lto-streamer.h
index 4bec969..63e4b32 100644
--- a/gcc/lto-streamer.h
+++ b/gcc/lto-streamer.h
@@ -247,6 +247,7 @@ enum lto_section_type
   LTO_section_cgraph_opt_sum,
   LTO_section_inline_summary,
   LTO_section_ipcp_transform,
+  LTO_section_ipa_icf,
   LTO_N_SECTION_TYPES		/* Must be last.  */
 };
 
diff --git a/gcc/opts.c b/gcc/opts.c
index 5cb5a39..7e9374a 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -497,6 +497,7 @@ static const struct default_options default_options_table[] =
     { OPT_LEVELS_2_PLUS, OPT_fvect_cost_model_, NULL, VECT_COST_MODEL_CHEAP },
     { OPT_LEVELS_2_PLUS_SPEED_ONLY, OPT_foptimize_strlen, NULL, 1 },
     { OPT_LEVELS_2_PLUS, OPT_fhoist_adjacent_loads, NULL, 1 },
+    { OPT_LEVELS_2_PLUS, OPT_fipa_icf, NULL, 1 },
     { OPT_LEVELS_2_PLUS, OPT_fisolate_erroneous_paths_dereference, NULL, 1 },
     { OPT_LEVELS_2_PLUS, OPT_fuse_caller_save, NULL, 1 },
 
@@ -1978,6 +1979,11 @@ common_handle_option (struct gcc_options *opts,
 	opts->x_flag_wrapv = 0;
       break;
 
+    case OPT_fipa_icf:
+	opts->x_flag_ipa_icf_functions = value;
+	opts->x_flag_ipa_icf_variables = value;
+      break;
+
     default:
       /* If the flag was handled in a standard way, assume the lack of
 	 processing here is intentional.  */
diff --git a/gcc/passes.def b/gcc/passes.def
index 801998f..b8efc9c 100644
--- a/gcc/passes.def
+++ b/gcc/passes.def
@@ -103,6 +103,7 @@ along with GCC; see the file COPYING3.  If not see
   INSERT_PASSES_AFTER (all_regular_ipa_passes)
   NEXT_PASS (pass_ipa_whole_program_visibility);
   NEXT_PASS (pass_ipa_profile);
+  NEXT_PASS (pass_ipa_icf);
   NEXT_PASS (pass_ipa_devirt);
   NEXT_PASS (pass_ipa_cp);
   NEXT_PASS (pass_ipa_cdtor_merge);
diff --git a/gcc/timevar.def b/gcc/timevar.def
index a04d05c..55a230b 100644
--- a/gcc/timevar.def
+++ b/gcc/timevar.def
@@ -90,6 +90,7 @@ DEFTIMEVAR (TV_WHOPR_LTRANS          , "whopr ltrans")
 DEFTIMEVAR (TV_IPA_REFERENCE         , "ipa reference")
 DEFTIMEVAR (TV_IPA_PROFILE           , "ipa profile")
 DEFTIMEVAR (TV_IPA_PURE_CONST        , "ipa pure const")
+DEFTIMEVAR (TV_IPA_ICF		     , "ipa icf")
 DEFTIMEVAR (TV_IPA_PTA               , "ipa points-to")
 DEFTIMEVAR (TV_IPA_SRA               , "ipa SRA")
 DEFTIMEVAR (TV_IPA_FREE_LANG_DATA    , "ipa free lang data")
diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h
index ed109c3..aac6703 100644
--- a/gcc/tree-pass.h
+++ b/gcc/tree-pass.h
@@ -461,6 +461,7 @@ extern simple_ipa_opt_pass *make_pass_ipa_free_lang_data (gcc::context *ctxt);
 extern simple_ipa_opt_pass *make_pass_ipa_free_inline_summary (gcc::context
 							       *ctxt);
 extern ipa_opt_pass_d *make_pass_ipa_cp (gcc::context *ctxt);
+extern ipa_opt_pass_d *make_pass_ipa_icf (gcc::context *ctxt);
 extern ipa_opt_pass_d *make_pass_ipa_devirt (gcc::context *ctxt);
 extern ipa_opt_pass_d *make_pass_ipa_reference (gcc::context *ctxt);
 extern ipa_opt_pass_d *make_pass_ipa_pure_const (gcc::context *ctxt);

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 3/5] IPA ICF pass
  2014-09-26 20:47             ` Jan Hubicka
@ 2014-10-11  0:08               ` Martin Liška
  2014-10-11  8:26                 ` Jan Hubicka
  2014-10-13 15:17                 ` Martin Liška
  0 siblings, 2 replies; 70+ messages in thread
From: Martin Liška @ 2014-10-11  0:08 UTC (permalink / raw)
  To: Jan Hubicka; +Cc: gcc-patches

On 09/26/2014 09:46 PM, Jan Hubicka wrote:
> Hi,
> this is on ipa-icf-gimple.c
> 
> @@ -2827,11 +2829,19 @@ cgraph_node::verify_node (void)
>  			    {
>  			      if (verify_edge_corresponds_to_fndecl (e, decl))
>  				{
> -				  error ("edge points to wrong declaration:");
> -				  debug_tree (e->callee->decl);
> -				  fprintf (stderr," Instead of:");
> -				  debug_tree (decl);
> -				  error_found = true;
> +				  /* The edge can be redirected in WPA by IPA ICF.
> +				     Following check really ensures that it's
> +				     not the case.  */
> +
> +				  cgraph_node *current_node = cgraph_node::get (decl);
> +				  if (!current_node || !current_node->icf_merged)
> 
> I would move this into verify_edge_corresponds_to_fndecl.
> 
> diff --git a/gcc/ipa-icf-gimple.c b/gcc/ipa-icf-gimple.c
> new file mode 100644
> index 0000000..7031eaa
> --- /dev/null
> +++ b/gcc/ipa-icf-gimple.c
> @@ -0,0 +1,384 @@
> +/* Interprocedural Identical Code Folding pass
> +   Copyright (C) 2014 Free Software Foundation, Inc.
> +
> +   Contributed by Jan Hubicka <hubicka@ucw.cz> and Martin Liska <mliska@suse.cz>
> +
> +This file is part of GCC.
> +
> +GCC 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, or (at your option) any later
> +version.
> +
> +GCC 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 GCC; see the file COPYING3.  If not see
> +<http://www.gnu.org/licenses/>.  */
> 
> Please add toplevel comment about what the code does and how to use it.
> 
> +namespace ipa_icf {
> +
> +/* Basic block equivalence comparison function that returns true if
> +   basic blocks BB1 and BB2 (from functions FUNC1 and FUNC2) correspond.  */
> ... to each other?
> I would add short comment that as comparsion goes you build voclabulary
> of equivalences of variables/ssanames etc.
> So people reading the code do not get lost at very beggining.
> 
> +
> +bool
> +func_checker::compare_bb (sem_bb *bb1, sem_bb *bb2)
> +{
> +  unsigned i;
> +  gimple_stmt_iterator gsi1, gsi2;
> +  gimple s1, s2;
> +
> +  if (bb1->nondbg_stmt_count != bb2->nondbg_stmt_count
> +      || bb1->edge_count != bb2->edge_count)
> +    return RETURN_FALSE ();
> 
> The UPPERCASE looks ugly.  I see that RETURN_FALSE is a warpper for return_false_with_msg
> that outputs line and file information.
> 
> I would make it lowercase even if it is macro. You may consider using
> CXX_MEM_STAT_INFO style default argument to avoid function macro completely.
> Probably not big win given that it won't save you from preprocesor mess.
> +
> +  gsi1 = gsi_start_bb (bb1->bb);
> +  gsi2 = gsi_start_bb (bb2->bb);
> +
> +  for (i = 0; i < bb1->nondbg_stmt_count; i++)
> +    {
> +      if (is_gimple_debug (gsi_stmt (gsi1)))
> +	gsi_next_nondebug (&gsi1);
> +
> +      if (is_gimple_debug (gsi_stmt (gsi2)))
> +	gsi_next_nondebug (&gsi2);
> +
> +      s1 = gsi_stmt (gsi1);
> +      s2 = gsi_stmt (gsi2);
> +
> +      if (gimple_code (s1) != gimple_code (s2))
> +	return RETURN_FALSE_WITH_MSG ("gimple codes are different");
> 
> I think you need to compare EH here.  Consider case where one unit
> is compiled with -fno-exception and thus all EH regions are removed,
> while other function has EH regions in it.  Those are not equivalent.
> 
> EH region is obtained by lookup_stmt_eh and then you need to comapre
> them for match as you do with gimple_resx_regoin.
> 
> +  t1 = gimple_call_fndecl (s1);
> +  t2 = gimple_call_fndecl (s2);
> +
> +  /* Function pointer variables are not supported yet.  */
> 
> They seems to be, compare_operand seems just right.
> 
> +
> +/* Verifies for given GIMPLEs S1 and S2 that
> +   label statements are semantically equivalent.  */
> +
> +bool
> +func_checker::compare_gimple_label (gimple g1, gimple g2)
> +{
> +  if (m_ignore_labels)
> +    return true;
> +
> +  tree t1 = gimple_label_label (g1);
> +  tree t2 = gimple_label_label (g2);
> +
> +  return compare_tree_ssa_label (t1, t2);
> +}
> 
> I would expect the main BB loop to record BB in which label belongs to
> and the BB assciatio neing checked here.
> Otherwise I do not see how switch statements are compared to not have
> different permutations of targets. Also note that one BB may have
> multiple labels in them and they are equivalent.
> 
> Also I would punt on occurence of FORCED_LABEL. Those are tricky as they
> may be passed around and compared for address and no one really defines
> what should happen.  Better to avoid those.

Hi.

I will remove this support in the pass.

> 
> +
> +/* Verifies for given GIMPLEs S1 and S2 that
> +   switch statements are semantically equivalent.  */
> +
> +bool
> +func_checker::compare_gimple_switch (gimple g1, gimple g2)
> +{
> +  unsigned lsize1, lsize2, i;
> +  tree t1, t2, low1, low2, high1, high2;
> +
> +  lsize1 = gimple_switch_num_labels (g1);
> +  lsize2 = gimple_switch_num_labels (g2);
> +
> +  if (lsize1 != lsize2)
> +    return false;
> +
> +  t1 = gimple_switch_index (g1);
> +  t2 = gimple_switch_index (g2);
> +
> +  if (TREE_CODE (t1) != SSA_NAME || TREE_CODE(t2) != SSA_NAME)
> +    return false;
> 
> Why non-SSA_NAMES are excluded? I see that constants should be optimized out,
> but I do not see a need for specific test here.
> +
> +  if (!compare_operand (t1, t2))
> +    return false;
> +
> +  for (i = 0; i < lsize1; i++)
> +    {
> +      low1 = CASE_LOW (gimple_switch_label (g1, i));
> +      low2 = CASE_LOW (gimple_switch_label (g2, i));
> +
> +      if ((low1 != NULL) != (low2 != NULL)
> +	  || (low1 && low2 && TREE_INT_CST_LOW (low1) != TREE_INT_CST_LOW (low2)))
> +	return false;
> 
> You want to compare integers for equivality.  Just use tree_int_cst_equal.
> +
> +      high1 = CASE_HIGH (gimple_switch_label (g1, i));
> +      high2 = CASE_HIGH (gimple_switch_label (g2, i));
> +
> +      if ((high1 != NULL) != (high2 != NULL)
> +	  || (high1 && high2
> +	      && TREE_INT_CST_LOW (high1) != TREE_INT_CST_LOW (high2)))
> +	return false;
> 
> Same here.
> +    }
> +
> +  return true;
> +}
> +
> +/* Verifies for given GIMPLEs S1 and S2 that
> +   return statements are semantically equivalent.  */
> +
> +bool
> +func_checker::compare_gimple_return (gimple g1, gimple g2)
> +{
> +  tree t1, t2;
> +
> +  t1 = gimple_return_retval (g1);
> +  t2 = gimple_return_retval (g2);
> +
> +  /* Void return type.  */
> +  if (t1 == NULL && t2 == NULL)
> +    return true;
> +  else
> +    return compare_operand (t1, t2);
> 
> Do you check somewhere DECL_BY_REFERENCE (DECL_RESULT (struct function))? Those needs to match, too.
> Perhaps it is always the case that SSA form differs, but I would test it.
> +}
> +
> +/* Verifies for given GIMPLEs S1 and S2 that
> +   goto statements are semantically equivalent.  */
> +
> +bool
> +func_checker::compare_gimple_goto (gimple g1, gimple g2)
> +{
> +  tree dest1, dest2;
> +
> +  dest1 = gimple_goto_dest (g1);
> +  dest2 = gimple_goto_dest (g2);
> +
> +  if (TREE_CODE (dest1) != TREE_CODE (dest2) || TREE_CODE (dest1) != SSA_NAME)
> +    return false;
> +
> +  return compare_operand (dest1, dest2);
> 
> You probably need to care only about indirect gotos, the direct ones are checked by
> CFG compare.  So is the condtional jump.

It looks that this code is visited quite rare.

> +
> +/* Verifies for given GIMPLEs S1 and S2 that ASM statements are equivalent.
> +   For the beginning, the pass only supports equality for
> +   '__asm__ __volatile__ ("", "", "", "memory")'.  */
> +
> +bool
> +func_checker::compare_gimple_asm (gimple g1, gimple g2)
> +{
> +  if (gimple_asm_volatile_p (g1) != gimple_asm_volatile_p (g2))
> +    return false;
> +
> +  if (gimple_asm_ninputs (g1) || gimple_asm_ninputs (g2))
> +    return false;
> +
> +  if (gimple_asm_noutputs (g1) || gimple_asm_noutputs (g2))
> +    return false;
> +
> +  if (gimple_asm_nlabels (g1) || gimple_asm_nlabels (g2))
> +    return false;
> +
> +  if (gimple_asm_nclobbers (g1) != gimple_asm_nclobbers (g2))
> +    return false;
> +
> +  for (unsigned i = 0; i < gimple_asm_nclobbers (g1); i++)
> +    {
> +      tree clobber1 = TREE_VALUE (gimple_asm_clobber_op (g1, i));
> +      tree clobber2 = TREE_VALUE (gimple_asm_clobber_op (g2, i));
> +
> +      if (!operand_equal_p (clobber1, clobber2, OEP_ONLY_CONST))
> +	return false;
> +    }
> +
> 
> Even asm statements with no inputs or outputs can differ by the actual
> asm statement. Compare it too.
> 
> Comparing inputs/outputs/labels should be very easy to do.
> 
> Compare all gimple_asm_n* for equivalency.

This makes fully sense, but I don't understand what kind of operands do you mean?

> At the end walk operands and watch the case they are TREE_LIST.
> THen compare TREE_VALUE (op) of the list for operand_equal_p
> and TREE_VALUE (TREE_PURPOSE (op)) for equivalency
> (those are the constraints)
> 
> If they are not (clobbers are not, those are just strings), operand_equal_p
> should do.
> 
> +  return true;
> +}
> +
> +} // ipa_icf namespace
> 
> Otherwise I think ipa-gimple-icf is quite fine now.
> Please send updated version and I think it can go to mainline before the actual ipa-icf.

I renamed both files and put them to a newly created namespace ipa_icf_gimple.

Thank you,
Martin

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 3/5] IPA ICF pass
  2014-09-26 22:27             ` Jan Hubicka
@ 2014-10-11  0:23               ` Martin Liška
  2014-10-11  9:05                 ` Jan Hubicka
  0 siblings, 1 reply; 70+ messages in thread
From: Martin Liška @ 2014-10-11  0:23 UTC (permalink / raw)
  To: Jan Hubicka; +Cc: gcc-patches

On 09/26/2014 11:27 PM, Jan Hubicka wrote:
>> diff --git a/gcc/ipa-icf.c b/gcc/ipa-icf.c
>> new file mode 100644
>> index 0000000..f3472fe
>> --- /dev/null
>> +++ b/gcc/ipa-icf.c
>> @@ -0,0 +1,2841 @@
>> +/* Interprocedural Identical Code Folding pass
>> +   Copyright (C) 2014 Free Software Foundation, Inc.
>> +
>> +   Contributed by Jan Hubicka <hubicka@ucw.cz> and Martin Liska <mliska@suse.cz>
>> +
>> +This file is part of GCC.
>> +
>> +GCC 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, or (at your option) any later
>> +version.
>> +
>> +GCC 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 GCC; see the file COPYING3.  If not see
>> +<http://www.gnu.org/licenses/>.  */
>> +
>> +/* Interprocedural Identical Code Folding for functions and
>> +   read-only variables.
>> +
>> +   The goal of this transformation is to discover functions and read-only
>> +   variables which do have exactly the same semantics.
> (or value)
>> +
>> +   In case of functions,
>> +   we could either create a virtual clone or do a simple function wrapper
>> +   that will call equivalent function. If the function is just locally visible,
>> +   all function calls can be redirected. For read-only variables, we create
>> +   aliases if possible.
>> +
>> +   Optimization pass arranges as follows:
> 
> The optimization pass is arranged as follows: (I guess)
> 
> I also wonder if the gimple equality code should be in ipa_icf namespace, it is intended
> to be shared with tail merging pass, so what about just calling it gimple_sem_equality?
> 
>> +/* Verification function for edges E1 and E2.  */
>> +
>> +bool
>> +func_checker::compare_edge (edge e1, edge e2)
>> +{
>> +  if (e1->flags != e2->flags)
>> +    return false;
> 
> In future we may want to experiment with checking that edge probabilities with
> profile feedback match and refuse to merge BBs with different outgoing probabilities
> (i.e. +-5%).
> Just add it as TODO there, please.
>> +
>> +/* Return true if types are compatible from perspective of ICF.  */
>> +bool func_checker::types_are_compatible_p (tree t1, tree t2,
> 
> Perhaps dropping _are_ would make sense, so we do not have two names
> for essentially same thing.
>> +    bool compare_polymorphic,
>> +    bool first_argument)
>> +{
>> +  if (TREE_CODE (t1) != TREE_CODE (t2))
>> +    return RETURN_FALSE_WITH_MSG ("different tree types");
>> +
>> +  if (!types_compatible_p (t1, t2))
>> +    return RETURN_FALSE_WITH_MSG ("types are not compatible");
>> +
>> +  if (get_alias_set (t1) != get_alias_set (t2))
>> +    return RETURN_FALSE_WITH_MSG ("alias sets are different");
> 
> You do not need to compare alias sets except for memory operations IMO.

Hello.

Yeah, you are right. But even Richard advised me to put it to a single place. Maybe we are a bit
more strict than it would be necessary. But I hope that's fine ;)

>> +
>> +  /* We call contains_polymorphic_type_p with this pointer type.  */
>> +  if (first_argument && TREE_CODE (t1) == POINTER_TYPE)
>> +    {
>> +      t1 = TREE_TYPE (t1);
>> +      t2 = TREE_TYPE (t2);
>> +    }
>> +
>> +  if (compare_polymorphic
>> +      && (contains_polymorphic_type_p (t1) || contains_polymorphic_type_p (t2)))
>> +    {
>> +      if (!contains_polymorphic_type_p (t1) || !contains_polymorphic_type_p (t2))
>> +	return RETURN_FALSE_WITH_MSG ("one type is not polymorphic");
>> +
>> +      if (TYPE_MAIN_VARIANT (t1) != TYPE_MAIN_VARIANT (t2))
>> +	return RETURN_FALSE_WITH_MSG ("type variants are different for "
>> +				      "polymorphic type");
> 
> I added types_must_be_same_for_odr (t1,t2) for you here.
>> +/* Fast equality function based on knowledge known in WPA.  */
>> +
>> +bool
>> +sem_function::equals_wpa (sem_item *item)
>> +{
>> +  gcc_assert (item->type == FUNC);
>> +
>> +  m_compared_func = static_cast<sem_function *> (item);
>> +
>> +  if (arg_types.length () != m_compared_func->arg_types.length ())
>> +    return RETURN_FALSE_WITH_MSG ("different number of arguments");
>> +
>> +  /* Checking types of arguments.  */
>> +  for (unsigned i = 0; i < arg_types.length (); i++)
>> +    {
>> +      /* This guard is here for function pointer with attributes (pr59927.c).  */
>> +      if (!arg_types[i] || !m_compared_func->arg_types[i])
>> +	return RETURN_FALSE_WITH_MSG ("NULL argument type");
>> +
>> +      if (!func_checker::types_are_compatible_p (arg_types[i],
>> +	  m_compared_func->arg_types[i],
>> +	  true, i == 0))
>> +	return RETURN_FALSE_WITH_MSG ("argument type is different");
>> +    }
>> +
>> +  /* Result type checking.  */
>> +  if (!func_checker::types_are_compatible_p (result_type,
>> +      m_compared_func->result_type))
>> +    return RETURN_FALSE_WITH_MSG ("result types are different");
> 
> You may want to compare ECF flags, such as nothrow/const/pure.  We do not
> want to merge non-pure function into pure as it may not be pure in the context
> it is used.
> 
> Do you compare attributes? I think optimize attribute needs to be matched.
>> +  /* Checking function arguments.  */
> attributes.
>> +  tree decl1 = DECL_ATTRIBUTES (decl);
>> +  tree decl2 = DECL_ATTRIBUTES (m_compared_func->decl);
>> +
>> +  m_checker = new func_checker (decl, m_compared_func->decl,
>> +				compare_polymorphic_p (),
>> +				false,
>> +				&tree_refs_set,
>> +				&m_compared_func->tree_refs_set);
>> +  while (decl1)
>> +    {
>> +      if (decl2 == NULL)
>> +	return RETURN_FALSE ();
>> +
>> +      if (get_attribute_name (decl1) != get_attribute_name (decl2))
>> +	return RETURN_FALSE ();
>> +
>> +      tree attr_value1 = TREE_VALUE (decl1);
>> +      tree attr_value2 = TREE_VALUE (decl2);
>> +
>> +      if (attr_value1 && attr_value2)
>> +	{
>> +	  bool ret = m_checker->compare_operand (TREE_VALUE (attr_value1),
>> +						 TREE_VALUE (attr_value2));
>> +	  if (!ret)
>> +	    return RETURN_FALSE_WITH_MSG ("attribute values are different");
>> +	}
>> +      else if (!attr_value1 && !attr_value2)
>> +	{}
>> +      else
>> +	return RETURN_FALSE ();
>> +
>> +      decl1 = TREE_CHAIN (decl1);
>> +      decl2 = TREE_CHAIN (decl2);
>> +    }
>> +
>> +  if (decl1 != decl2)
>> +    return RETURN_FALSE();
>> +
>> +
>> +  for (arg1 = DECL_ARGUMENTS (decl),
>> +       arg2 = DECL_ARGUMENTS (m_compared_func->decl);
>> +       arg1; arg1 = DECL_CHAIN (arg1), arg2 = DECL_CHAIN (arg2))
>> +    if (!m_checker->compare_decl (arg1, arg2))
>> +      return RETURN_FALSE ();
> 
> I think you want to compare PARM_DECL flags, such as DECL_BY_REFERENCE
>> +
>> +/* Improve accumulated hash for HSTATE based on a gimple statement STMT.  */
>> +
>> +void
>> +sem_function::improve_hash (inchash::hash *hstate, gimple stmt)
> 
> Hehe, I think simple hash_stmt would be easier to read - it took me some time
> to figure out what you mean by improving.
>> +{
>> +  enum gimple_code code = gimple_code (stmt);
>> +
>> +  hstate->add_int (code);
>> +
>> +  if (code == GIMPLE_CALL)
>> +    {
>> +      /* Checking of argument.  */
>> +      for (unsigned i = 0; i < gimple_call_num_args (stmt); ++i)
>> +	{
>> +	  tree argument = gimple_call_arg (stmt, i);
>> +
>> +	  switch (TREE_CODE (argument))
>> +	    {
>> +	    case INTEGER_CST:
>> +	      if (tree_fits_shwi_p (argument))
>> +		hstate->add_wide_int (tree_to_shwi (argument));
>> +	      else if (tree_fits_uhwi_p (argument))
>> +		hstate->add_wide_int (tree_to_uhwi (argument));
>> +	      break;
>> +	    case ADDR_EXPR:
>> +	      {
>> +		tree addr_operand = TREE_OPERAND (argument, 0);
>> +
>> +		if (TREE_CODE (addr_operand) == STRING_CST)
>> +		  hstate->add (TREE_STRING_POINTER (addr_operand),
>> +			       TREE_STRING_LENGTH (addr_operand));
> 
> It may be nice to reuse some of the hash_tree code, but yep, i guess
> this is good first cut. Perhaps adding also REAL_CST
>> +
>> +/* Return true if polymorphic comparison must be processed.  */
>> +
>> +bool
>> +sem_function::compare_polymorphic_p (void)
>> +{
>> +  return get_node ()->callees != NULL
>> +	 || m_compared_func->get_node ()->callees != NULL;
> This is somewhat kludgy, but probably OK for start.  I do not see how
> local declaration can leak out after inlining.
> You also want to check for no indirect calls.
>> +
>> +  if (!func || !node->has_gimple_body_p ())
>> +    return NULL;
> 
> Do you somewhere handle thunks and aliases?
> (again someting that can be done later, but TODO would be nice.)

Good point. Do you mean cases like, foo (alias_foo) and bar (alias_bar). If we prove that foo equals to bar, can we also merge aliases?
I am curious if such comparison can really save something? Are there any interesting cases?

Martin


>> +    case INTEGER_CST:
>> +      {
>> +	ret = types_are_compatible_p (TREE_TYPE (t1), TREE_TYPE (t2))
>> +	      && wi::to_offset  (t1) == wi::to_offset  (t2);
> 
>   tree_int_cst_equal
> 
>> +    case FIELD_DECL:
>> +      {
>> +	tree fctx1 = DECL_FCONTEXT (t1);
>> +	tree fctx2 = DECL_FCONTEXT (t2);
> 
> DECL_FCONTEXT has no semantic meaning; so you can skip comparing it.
>> +
>> +	tree offset1 = DECL_FIELD_OFFSET (t1);
>> +	tree offset2 = DECL_FIELD_OFFSET (t2);
>> +
>> +	tree bit_offset1 = DECL_FIELD_BIT_OFFSET (t1);
>> +	tree bit_offset2 = DECL_FIELD_BIT_OFFSET (t2);
>> +
>> +	ret = compare_operand (fctx1, fctx2)
>> +	      && compare_operand (offset1, offset2)
>> +	      && compare_operand (bit_offset1, bit_offset2);
> 
> You probably want to compare type here?
>> +    case CONSTRUCTOR:
>> +      {
>> +	unsigned len1 = vec_safe_length (CONSTRUCTOR_ELTS (t1));
>> +	unsigned len2 = vec_safe_length (CONSTRUCTOR_ELTS (t2));
>> +
>> +	if (len1 != len2)
>> +	  return false;
>> +
>> +	for (unsigned i = 0; i < len1; i++)
>> +	  if (!sem_variable::equals (CONSTRUCTOR_ELT (t1, i)->value,
>> +				     CONSTRUCTOR_ELT (t2, i)->value))
>> +	    return false;
> 
> You want to compare ->index, too.
>> +    case INTEGER_CST:
>> +      return func_checker::types_are_compatible_p (TREE_TYPE (t1), TREE_TYPE (t2),
>> +	     true)
>> +	     && wi::to_offset (t1) == wi::to_offset (t2);
> again ;)
> 
> This is where I stopped for now.  Generally the patch seems OK to me with few of these
> details fixed.
> 
> Honza
> 

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 3/5] IPA ICF pass
  2014-10-11  0:02                       ` Martin Liška
@ 2014-10-11  8:23                         ` Jan Hubicka
  2014-10-13 13:20                           ` Martin Liška
  0 siblings, 1 reply; 70+ messages in thread
From: Jan Hubicka @ 2014-10-11  8:23 UTC (permalink / raw)
  To: Martin Liška; +Cc: gcc-patches, hubicka >> Jan Hubicka

> 
> After few days of measurement and tuning, I was able to get numbers to the following shape:
> Execution times (seconds)
>  phase setup             :   0.00 ( 0%) usr   0.00 ( 0%) sys   0.00 ( 0%) wall    1412 kB ( 0%) ggc
>  phase opt and generate  :  27.83 (59%) usr   0.66 (19%) sys  28.52 (37%) wall 1028813 kB (24%) ggc
>  phase stream in         :  16.90 (36%) usr   0.63 (18%) sys  17.60 (23%) wall 3246453 kB (76%) ggc
>  phase stream out        :   2.76 ( 6%) usr   2.19 (63%) sys  31.34 (40%) wall       2 kB ( 0%) ggc
>  callgraph optimization  :   0.36 ( 1%) usr   0.00 ( 0%) sys   0.35 ( 0%) wall      40 kB ( 0%) ggc
>  ipa dead code removal   :   3.31 ( 7%) usr   0.01 ( 0%) sys   3.25 ( 4%) wall       0 kB ( 0%) ggc
>  ipa virtual call target :   3.69 ( 8%) usr   0.03 ( 1%) sys   3.80 ( 5%) wall      21 kB ( 0%) ggc
>  ipa devirtualization    :   0.12 ( 0%) usr   0.00 ( 0%) sys   0.15 ( 0%) wall   13704 kB ( 0%) ggc
>  ipa cp                  :   1.11 ( 2%) usr   0.07 ( 2%) sys   1.17 ( 2%) wall  188558 kB ( 4%) ggc
>  ipa inlining heuristics :   8.17 (17%) usr   0.14 ( 4%) sys   8.27 (11%) wall  494738 kB (12%) ggc
>  ipa comdats             :   0.12 ( 0%) usr   0.00 ( 0%) sys   0.12 ( 0%) wall       0 kB ( 0%) ggc
>  ipa lto gimple in       :   1.86 ( 4%) usr   0.40 (11%) sys   2.20 ( 3%) wall  537970 kB (13%) ggc
>  ipa lto gimple out      :   0.19 ( 0%) usr   0.08 ( 2%) sys   0.27 ( 0%) wall       2 kB ( 0%) ggc
>  ipa lto decl in         :  12.20 (26%) usr   0.37 (11%) sys  12.64 (16%) wall 2441687 kB (57%) ggc
>  ipa lto decl out        :   2.51 ( 5%) usr   0.21 ( 6%) sys   2.71 ( 3%) wall       0 kB ( 0%) ggc
>  ipa lto constructors in :   0.13 ( 0%) usr   0.02 ( 1%) sys   0.17 ( 0%) wall   15692 kB ( 0%) ggc
>  ipa lto constructors out:   0.03 ( 0%) usr   0.00 ( 0%) sys   0.03 ( 0%) wall       0 kB ( 0%) ggc
>  ipa lto cgraph I/O      :   0.54 ( 1%) usr   0.09 ( 3%) sys   0.63 ( 1%) wall  407182 kB (10%) ggc
>  ipa lto decl merge      :   1.34 ( 3%) usr   0.00 ( 0%) sys   1.34 ( 2%) wall    8220 kB ( 0%) ggc
>  ipa lto cgraph merge    :   1.00 ( 2%) usr   0.00 ( 0%) sys   1.00 ( 1%) wall   14605 kB ( 0%) ggc
>  whopr wpa               :   0.92 ( 2%) usr   0.00 ( 0%) sys   0.89 ( 1%) wall       1 kB ( 0%) ggc
>  whopr wpa I/O           :   0.01 ( 0%) usr   1.90 (55%) sys  28.31 (37%) wall       0 kB ( 0%) ggc
>  whopr partitioning      :   2.81 ( 6%) usr   0.01 ( 0%) sys   2.83 ( 4%) wall    4943 kB ( 0%) ggc
>  ipa reference           :   1.34 ( 3%) usr   0.00 ( 0%) sys   1.35 ( 2%) wall       0 kB ( 0%) ggc
>  ipa profile             :   0.20 ( 0%) usr   0.01 ( 0%) sys   0.21 ( 0%) wall       0 kB ( 0%) ggc
>  ipa pure const          :   1.62 ( 3%) usr   0.00 ( 0%) sys   1.63 ( 2%) wall       0 kB ( 0%) ggc
>  ipa icf                 :   2.65 ( 6%) usr   0.02 ( 1%) sys   2.68 ( 3%) wall    1352 kB ( 0%) ggc
>  inline parameters       :   0.00 ( 0%) usr   0.01 ( 0%) sys   0.00 ( 0%) wall       0 kB ( 0%) ggc
>  tree SSA rewrite        :   0.11 ( 0%) usr   0.01 ( 0%) sys   0.08 ( 0%) wall   18919 kB ( 0%) ggc
>  tree SSA other          :   0.01 ( 0%) usr   0.00 ( 0%) sys   0.01 ( 0%) wall       0 kB ( 0%) ggc
>  tree SSA incremental    :   0.24 ( 1%) usr   0.01 ( 0%) sys   0.32 ( 0%) wall   11325 kB ( 0%) ggc
>  tree operand scan       :   0.15 ( 0%) usr   0.02 ( 1%) sys   0.18 ( 0%) wall  116283 kB ( 3%) ggc
>  dominance frontiers     :   0.01 ( 0%) usr   0.00 ( 0%) sys   0.02 ( 0%) wall       0 kB ( 0%) ggc
>  dominance computation   :   0.13 ( 0%) usr   0.01 ( 0%) sys   0.16 ( 0%) wall       0 kB ( 0%) ggc
>  varconst                :   0.01 ( 0%) usr   0.02 ( 1%) sys   0.01 ( 0%) wall       0 kB ( 0%) ggc
>  loop fini               :   0.02 ( 0%) usr   0.00 ( 0%) sys   0.04 ( 0%) wall       0 kB ( 0%) ggc
>  unaccounted todo        :   0.55 ( 1%) usr   0.00 ( 0%) sys   0.56 ( 1%) wall       0 kB ( 0%) ggc
>  TOTAL                 :  47.49             3.48            77.46            4276682 kB
> 
> and I was able to reduce function bodies loaded in WPA to 35% (from previous 55%). The main problem

35% means that 35% of all function bodies are compared with something else? That feels pretty high.
but overall numbers are not so terrible.

> with speed was hidden in work list for congruence classes, where hash_set was used. I chose the data
> structure to support delete operation, but it was really slow. Thus, hash_set was replaced with linked list
> and a flag is used to identify if a set is removed or not.

Interesting, I would not expect bottleneck in a congruence solving :)
> 
> I have no clue who complicated can it be to implement release_body function to an operation that
> really releases the memory?

I suppose one can keep the caches from streamer and free trees read.  Freeing
gimple statemnts, cfg should be relatively easy. 

Lets however first try to tune the implementation rather than try to this hack
implemented. Explicit ggc_free calls traditionally tended to cause some negative
reactions wrt memory fragmentation concerns.

> 
> Markus' problem with -fprofile-use has been removed, IPA-ICF is preceding devirtualization pass. I hope it is fine?

Yes, I think devirtualization should actually work better with identical
virutal methods merged.  We just need to be sure it sees through the newly
introduced aliases (there should be no thunks for virutal methods)

Honza

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 3/5] IPA ICF pass
  2014-10-11  0:08               ` Martin Liška
@ 2014-10-11  8:26                 ` Jan Hubicka
  2014-10-13 15:17                 ` Martin Liška
  1 sibling, 0 replies; 70+ messages in thread
From: Jan Hubicka @ 2014-10-11  8:26 UTC (permalink / raw)
  To: Martin Liška; +Cc: Jan Hubicka, gcc-patches

> > +/* Verifies for given GIMPLEs S1 and S2 that
> > +   goto statements are semantically equivalent.  */
> > +
> > +bool
> > +func_checker::compare_gimple_goto (gimple g1, gimple g2)
> > +{
> > +  tree dest1, dest2;
> > +
> > +  dest1 = gimple_goto_dest (g1);
> > +  dest2 = gimple_goto_dest (g2);
> > +
> > +  if (TREE_CODE (dest1) != TREE_CODE (dest2) || TREE_CODE (dest1) != SSA_NAME)
> > +    return false;
> > +
> > +  return compare_operand (dest1, dest2);
> > 
> > You probably need to care only about indirect gotos, the direct ones are checked by
> > CFG compare.  So is the condtional jump.
> 
> It looks that this code is visited quite rare.

Hmm, perhaps it is called only for indirect calls, because all others are not represented as statements.
> 
> > +
> > +/* Verifies for given GIMPLEs S1 and S2 that ASM statements are equivalent.
> > +   For the beginning, the pass only supports equality for
> > +   '__asm__ __volatile__ ("", "", "", "memory")'.  */
> > +
> > +bool
> > +func_checker::compare_gimple_asm (gimple g1, gimple g2)
> > +{
> > +  if (gimple_asm_volatile_p (g1) != gimple_asm_volatile_p (g2))
> > +    return false;
> > +
> > +  if (gimple_asm_ninputs (g1) || gimple_asm_ninputs (g2))
> > +    return false;
> > +
> > +  if (gimple_asm_noutputs (g1) || gimple_asm_noutputs (g2))
> > +    return false;
> > +
> > +  if (gimple_asm_nlabels (g1) || gimple_asm_nlabels (g2))
> > +    return false;
> > +
> > +  if (gimple_asm_nclobbers (g1) != gimple_asm_nclobbers (g2))
> > +    return false;
> > +
> > +  for (unsigned i = 0; i < gimple_asm_nclobbers (g1); i++)
> > +    {
> > +      tree clobber1 = TREE_VALUE (gimple_asm_clobber_op (g1, i));
> > +      tree clobber2 = TREE_VALUE (gimple_asm_clobber_op (g2, i));
> > +
> > +      if (!operand_equal_p (clobber1, clobber2, OEP_ONLY_CONST))
> > +	return false;
> > +    }
> > +
> > 
> > Even asm statements with no inputs or outputs can differ by the actual
> > asm statement. Compare it too.
> > 
> > Comparing inputs/outputs/labels should be very easy to do.
> > 
> > Compare all gimple_asm_n* for equivalency.
> 
> This makes fully sense, but I don't understand what kind of operands do you mean?

You can look some other code dealing with gimple asm statements.  You can just compare
gimple_op for 0.... gimple_num_ops and be ready to deal with TREE_LIST as described
bellow. 

Honza
> 
> > At the end walk operands and watch the case they are TREE_LIST.
> > THen compare TREE_VALUE (op) of the list for operand_equal_p
> > and TREE_VALUE (TREE_PURPOSE (op)) for equivalency
> > (those are the constraints)
> > 
> > If they are not (clobbers are not, those are just strings), operand_equal_p
> > should do.
> > 
> > +  return true;
> > +}
> > +
> > +} // ipa_icf namespace
> > 
> > Otherwise I think ipa-gimple-icf is quite fine now.
> > Please send updated version and I think it can go to mainline before the actual ipa-icf.
> 
> I renamed both files and put them to a newly created namespace ipa_icf_gimple.
> 
> Thank you,
> Martin

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 3/5] IPA ICF pass
  2014-10-11  0:23               ` Martin Liška
@ 2014-10-11  9:05                 ` Jan Hubicka
  0 siblings, 0 replies; 70+ messages in thread
From: Jan Hubicka @ 2014-10-11  9:05 UTC (permalink / raw)
  To: Martin Liška; +Cc: Jan Hubicka, gcc-patches

> 
> Hello.
> 
> Yeah, you are right. But even Richard advised me to put it to a single place. Maybe we are a bit
> more strict than it would be necessary. But I hope that's fine ;)

OK, lets do extra checking now and play with this incrementally.
> 
> Good point. Do you mean cases like, foo (alias_foo) and bar (alias_bar). If we prove that foo equals to bar, can we also merge aliases?
> I am curious if such comparison can really save something? Are there any interesting cases?

What probably matters is that you recognize the equivalence to know that uses of alias_foo can be merged with uses alias_bar.
Similarly for thunks.  Again something to do incrementally I guess.

Honza
> 
> Martin
> 
> 
> >> +    case INTEGER_CST:
> >> +      {
> >> +	ret = types_are_compatible_p (TREE_TYPE (t1), TREE_TYPE (t2))
> >> +	      && wi::to_offset  (t1) == wi::to_offset  (t2);
> > 
> >   tree_int_cst_equal
> > 
> >> +    case FIELD_DECL:
> >> +      {
> >> +	tree fctx1 = DECL_FCONTEXT (t1);
> >> +	tree fctx2 = DECL_FCONTEXT (t2);
> > 
> > DECL_FCONTEXT has no semantic meaning; so you can skip comparing it.
> >> +
> >> +	tree offset1 = DECL_FIELD_OFFSET (t1);
> >> +	tree offset2 = DECL_FIELD_OFFSET (t2);
> >> +
> >> +	tree bit_offset1 = DECL_FIELD_BIT_OFFSET (t1);
> >> +	tree bit_offset2 = DECL_FIELD_BIT_OFFSET (t2);
> >> +
> >> +	ret = compare_operand (fctx1, fctx2)
> >> +	      && compare_operand (offset1, offset2)
> >> +	      && compare_operand (bit_offset1, bit_offset2);
> > 
> > You probably want to compare type here?
> >> +    case CONSTRUCTOR:
> >> +      {
> >> +	unsigned len1 = vec_safe_length (CONSTRUCTOR_ELTS (t1));
> >> +	unsigned len2 = vec_safe_length (CONSTRUCTOR_ELTS (t2));
> >> +
> >> +	if (len1 != len2)
> >> +	  return false;
> >> +
> >> +	for (unsigned i = 0; i < len1; i++)
> >> +	  if (!sem_variable::equals (CONSTRUCTOR_ELT (t1, i)->value,
> >> +				     CONSTRUCTOR_ELT (t2, i)->value))
> >> +	    return false;
> > 
> > You want to compare ->index, too.
> >> +    case INTEGER_CST:
> >> +      return func_checker::types_are_compatible_p (TREE_TYPE (t1), TREE_TYPE (t2),
> >> +	     true)
> >> +	     && wi::to_offset (t1) == wi::to_offset (t2);
> > again ;)
> > 
> > This is where I stopped for now.  Generally the patch seems OK to me with few of these
> > details fixed.
> > 
> > Honza
> > 

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 3/5] IPA ICF pass
  2014-10-11  8:23                         ` Jan Hubicka
@ 2014-10-13 13:20                           ` Martin Liška
  2014-10-13 13:29                             ` Jan Hubicka
  0 siblings, 1 reply; 70+ messages in thread
From: Martin Liška @ 2014-10-13 13:20 UTC (permalink / raw)
  To: Jan Hubicka; +Cc: gcc-patches

On 10/11/2014 10:19 AM, Jan Hubicka wrote:
>>
>> After few days of measurement and tuning, I was able to get numbers to the following shape:
>> Execution times (seconds)
>>   phase setup             :   0.00 ( 0%) usr   0.00 ( 0%) sys   0.00 ( 0%) wall    1412 kB ( 0%) ggc
>>   phase opt and generate  :  27.83 (59%) usr   0.66 (19%) sys  28.52 (37%) wall 1028813 kB (24%) ggc
>>   phase stream in         :  16.90 (36%) usr   0.63 (18%) sys  17.60 (23%) wall 3246453 kB (76%) ggc
>>   phase stream out        :   2.76 ( 6%) usr   2.19 (63%) sys  31.34 (40%) wall       2 kB ( 0%) ggc
>>   callgraph optimization  :   0.36 ( 1%) usr   0.00 ( 0%) sys   0.35 ( 0%) wall      40 kB ( 0%) ggc
>>   ipa dead code removal   :   3.31 ( 7%) usr   0.01 ( 0%) sys   3.25 ( 4%) wall       0 kB ( 0%) ggc
>>   ipa virtual call target :   3.69 ( 8%) usr   0.03 ( 1%) sys   3.80 ( 5%) wall      21 kB ( 0%) ggc
>>   ipa devirtualization    :   0.12 ( 0%) usr   0.00 ( 0%) sys   0.15 ( 0%) wall   13704 kB ( 0%) ggc
>>   ipa cp                  :   1.11 ( 2%) usr   0.07 ( 2%) sys   1.17 ( 2%) wall  188558 kB ( 4%) ggc
>>   ipa inlining heuristics :   8.17 (17%) usr   0.14 ( 4%) sys   8.27 (11%) wall  494738 kB (12%) ggc
>>   ipa comdats             :   0.12 ( 0%) usr   0.00 ( 0%) sys   0.12 ( 0%) wall       0 kB ( 0%) ggc
>>   ipa lto gimple in       :   1.86 ( 4%) usr   0.40 (11%) sys   2.20 ( 3%) wall  537970 kB (13%) ggc
>>   ipa lto gimple out      :   0.19 ( 0%) usr   0.08 ( 2%) sys   0.27 ( 0%) wall       2 kB ( 0%) ggc
>>   ipa lto decl in         :  12.20 (26%) usr   0.37 (11%) sys  12.64 (16%) wall 2441687 kB (57%) ggc
>>   ipa lto decl out        :   2.51 ( 5%) usr   0.21 ( 6%) sys   2.71 ( 3%) wall       0 kB ( 0%) ggc
>>   ipa lto constructors in :   0.13 ( 0%) usr   0.02 ( 1%) sys   0.17 ( 0%) wall   15692 kB ( 0%) ggc
>>   ipa lto constructors out:   0.03 ( 0%) usr   0.00 ( 0%) sys   0.03 ( 0%) wall       0 kB ( 0%) ggc
>>   ipa lto cgraph I/O      :   0.54 ( 1%) usr   0.09 ( 3%) sys   0.63 ( 1%) wall  407182 kB (10%) ggc
>>   ipa lto decl merge      :   1.34 ( 3%) usr   0.00 ( 0%) sys   1.34 ( 2%) wall    8220 kB ( 0%) ggc
>>   ipa lto cgraph merge    :   1.00 ( 2%) usr   0.00 ( 0%) sys   1.00 ( 1%) wall   14605 kB ( 0%) ggc
>>   whopr wpa               :   0.92 ( 2%) usr   0.00 ( 0%) sys   0.89 ( 1%) wall       1 kB ( 0%) ggc
>>   whopr wpa I/O           :   0.01 ( 0%) usr   1.90 (55%) sys  28.31 (37%) wall       0 kB ( 0%) ggc
>>   whopr partitioning      :   2.81 ( 6%) usr   0.01 ( 0%) sys   2.83 ( 4%) wall    4943 kB ( 0%) ggc
>>   ipa reference           :   1.34 ( 3%) usr   0.00 ( 0%) sys   1.35 ( 2%) wall       0 kB ( 0%) ggc
>>   ipa profile             :   0.20 ( 0%) usr   0.01 ( 0%) sys   0.21 ( 0%) wall       0 kB ( 0%) ggc
>>   ipa pure const          :   1.62 ( 3%) usr   0.00 ( 0%) sys   1.63 ( 2%) wall       0 kB ( 0%) ggc
>>   ipa icf                 :   2.65 ( 6%) usr   0.02 ( 1%) sys   2.68 ( 3%) wall    1352 kB ( 0%) ggc
>>   inline parameters       :   0.00 ( 0%) usr   0.01 ( 0%) sys   0.00 ( 0%) wall       0 kB ( 0%) ggc
>>   tree SSA rewrite        :   0.11 ( 0%) usr   0.01 ( 0%) sys   0.08 ( 0%) wall   18919 kB ( 0%) ggc
>>   tree SSA other          :   0.01 ( 0%) usr   0.00 ( 0%) sys   0.01 ( 0%) wall       0 kB ( 0%) ggc
>>   tree SSA incremental    :   0.24 ( 1%) usr   0.01 ( 0%) sys   0.32 ( 0%) wall   11325 kB ( 0%) ggc
>>   tree operand scan       :   0.15 ( 0%) usr   0.02 ( 1%) sys   0.18 ( 0%) wall  116283 kB ( 3%) ggc
>>   dominance frontiers     :   0.01 ( 0%) usr   0.00 ( 0%) sys   0.02 ( 0%) wall       0 kB ( 0%) ggc
>>   dominance computation   :   0.13 ( 0%) usr   0.01 ( 0%) sys   0.16 ( 0%) wall       0 kB ( 0%) ggc
>>   varconst                :   0.01 ( 0%) usr   0.02 ( 1%) sys   0.01 ( 0%) wall       0 kB ( 0%) ggc
>>   loop fini               :   0.02 ( 0%) usr   0.00 ( 0%) sys   0.04 ( 0%) wall       0 kB ( 0%) ggc
>>   unaccounted todo        :   0.55 ( 1%) usr   0.00 ( 0%) sys   0.56 ( 1%) wall       0 kB ( 0%) ggc
>>   TOTAL                 :  47.49             3.48            77.46            4276682 kB
>>
>> and I was able to reduce function bodies loaded in WPA to 35% (from previous 55%). The main problem
>
> 35% means that 35% of all function bodies are compared with something else? That feels pretty high.
> but overall numbers are not so terrible.

Currently, the pass is able to merge 32K functions. As you know, we group functions to so called classes.
According to stats, average non-singular class size contains at the end of comparison 7.39 candidates and we
have 5K such functions. Because we load body for each candidate in such groups, it gives us minimum number
of loaded bodies: 37K. As we load 70K function, we have still place to improve. But I guess WPA body-less
comparison is quite efficient.

>
>> with speed was hidden in work list for congruence classes, where hash_set was used. I chose the data
>> structure to support delete operation, but it was really slow. Thus, hash_set was replaced with linked list
>> and a flag is used to identify if a set is removed or not.
>
> Interesting, I would not expect bottleneck in a congruence solving :)

The problem was just the hash_set that showed to be slow data structure for a set of operations needed
in congruence solving.

>>
>> I have no clue who complicated can it be to implement release_body function to an operation that
>> really releases the memory?
>
> I suppose one can keep the caches from streamer and free trees read.  Freeing
> gimple statemnts, cfg should be relatively easy.
>
> Lets however first try to tune the implementation rather than try to this hack
> implemented. Explicit ggc_free calls traditionally tended to cause some negative
> reactions wrt memory fragmentation concerns.

Agree with suggested approach.

>
>>
>> Markus' problem with -fprofile-use has been removed, IPA-ICF is preceding devirtualization pass. I hope it is fine?
>
> Yes, I think devirtualization should actually work better with identical
> virutal methods merged.  We just need to be sure it sees through the newly
> introduced aliases (there should be no thunks for virutal methods)

Thanks,
Martin

>
> Honza
>

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 3/5] IPA ICF pass
  2014-10-13 13:20                           ` Martin Liška
@ 2014-10-13 13:29                             ` Jan Hubicka
  0 siblings, 0 replies; 70+ messages in thread
From: Jan Hubicka @ 2014-10-13 13:29 UTC (permalink / raw)
  To: Martin Liška; +Cc: Jan Hubicka, gcc-patches

> >
> >35% means that 35% of all function bodies are compared with something else? That feels pretty high.
> >but overall numbers are not so terrible.
> 
> Currently, the pass is able to merge 32K functions. As you know, we group functions to so called classes.
> According to stats, average non-singular class size contains at the end of comparison 7.39 candidates and we
> have 5K such functions. Because we load body for each candidate in such groups, it gives us minimum number
> of loaded bodies: 37K. As we load 70K function, we have still place to improve. But I guess WPA body-less
> comparison is quite efficient.

OK, that seems resonable.
> 
> >
> >>with speed was hidden in work list for congruence classes, where hash_set was used. I chose the data
> >>structure to support delete operation, but it was really slow. Thus, hash_set was replaced with linked list
> >>and a flag is used to identify if a set is removed or not.
> >
> >Interesting, I would not expect bottleneck in a congruence solving :)
> 
> The problem was just the hash_set that showed to be slow data structure for a set of operations needed
> in congruence solving.
> 
> >>
> >>I have no clue who complicated can it be to implement release_body function to an operation that
> >>really releases the memory?
> >
> >I suppose one can keep the caches from streamer and free trees read.  Freeing
> >gimple statemnts, cfg should be relatively easy.
> >
> >Lets however first try to tune the implementation rather than try to this hack
> >implemented. Explicit ggc_free calls traditionally tended to cause some negative
> >reactions wrt memory fragmentation concerns.
> 
> Agree with suggested approach.

In future we actually may keep the duplicated functions in WPA memory and use corresponding body
whenever the function is inlined to avoid disturbing debug info more than needed.

Honza
> 
> >
> >>
> >>Markus' problem with -fprofile-use has been removed, IPA-ICF is preceding devirtualization pass. I hope it is fine?
> >
> >Yes, I think devirtualization should actually work better with identical
> >virutal methods merged.  We just need to be sure it sees through the newly
> >introduced aliases (there should be no thunks for virutal methods)
> 
> Thanks,
> Martin
> 
> >
> >Honza
> >

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 3/5] IPA ICF pass
  2014-10-11  0:08               ` Martin Liška
  2014-10-11  8:26                 ` Jan Hubicka
@ 2014-10-13 15:17                 ` Martin Liška
  2014-10-14 16:12                   ` Jan Hubicka
  1 sibling, 1 reply; 70+ messages in thread
From: Martin Liška @ 2014-10-13 15:17 UTC (permalink / raw)
  To: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 10007 bytes --]

On 10/11/2014 02:05 AM, Martin Liška wrote:
> On 09/26/2014 09:46 PM, Jan Hubicka wrote:
>> Hi,
>> this is on ipa-icf-gimple.c
>>
>> @@ -2827,11 +2829,19 @@ cgraph_node::verify_node (void)
>>   			    {
>>   			      if (verify_edge_corresponds_to_fndecl (e, decl))
>>   				{
>> -				  error ("edge points to wrong declaration:");
>> -				  debug_tree (e->callee->decl);
>> -				  fprintf (stderr," Instead of:");
>> -				  debug_tree (decl);
>> -				  error_found = true;
>> +				  /* The edge can be redirected in WPA by IPA ICF.
>> +				     Following check really ensures that it's
>> +				     not the case.  */
>> +
>> +				  cgraph_node *current_node = cgraph_node::get (decl);
>> +				  if (!current_node || !current_node->icf_merged)
>>
>> I would move this into verify_edge_corresponds_to_fndecl.
>>
>> diff --git a/gcc/ipa-icf-gimple.c b/gcc/ipa-icf-gimple.c
>> new file mode 100644
>> index 0000000..7031eaa
>> --- /dev/null
>> +++ b/gcc/ipa-icf-gimple.c
>> @@ -0,0 +1,384 @@
>> +/* Interprocedural Identical Code Folding pass
>> +   Copyright (C) 2014 Free Software Foundation, Inc.
>> +
>> +   Contributed by Jan Hubicka <hubicka@ucw.cz> and Martin Liska <mliska@suse.cz>
>> +
>> +This file is part of GCC.
>> +
>> +GCC 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, or (at your option) any later
>> +version.
>> +
>> +GCC 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 GCC; see the file COPYING3.  If not see
>> +<http://www.gnu.org/licenses/>.  */
>>
>> Please add toplevel comment about what the code does and how to use it.
>>
>> +namespace ipa_icf {
>> +
>> +/* Basic block equivalence comparison function that returns true if
>> +   basic blocks BB1 and BB2 (from functions FUNC1 and FUNC2) correspond.  */
>> ... to each other?
>> I would add short comment that as comparsion goes you build voclabulary
>> of equivalences of variables/ssanames etc.
>> So people reading the code do not get lost at very beggining.
>>
>> +
>> +bool
>> +func_checker::compare_bb (sem_bb *bb1, sem_bb *bb2)
>> +{
>> +  unsigned i;
>> +  gimple_stmt_iterator gsi1, gsi2;
>> +  gimple s1, s2;
>> +
>> +  if (bb1->nondbg_stmt_count != bb2->nondbg_stmt_count
>> +      || bb1->edge_count != bb2->edge_count)
>> +    return RETURN_FALSE ();
>>
>> The UPPERCASE looks ugly.  I see that RETURN_FALSE is a warpper for return_false_with_msg
>> that outputs line and file information.
>>
>> I would make it lowercase even if it is macro. You may consider using
>> CXX_MEM_STAT_INFO style default argument to avoid function macro completely.
>> Probably not big win given that it won't save you from preprocesor mess.
>> +
>> +  gsi1 = gsi_start_bb (bb1->bb);
>> +  gsi2 = gsi_start_bb (bb2->bb);
>> +
>> +  for (i = 0; i < bb1->nondbg_stmt_count; i++)
>> +    {
>> +      if (is_gimple_debug (gsi_stmt (gsi1)))
>> +	gsi_next_nondebug (&gsi1);
>> +
>> +      if (is_gimple_debug (gsi_stmt (gsi2)))
>> +	gsi_next_nondebug (&gsi2);
>> +
>> +      s1 = gsi_stmt (gsi1);
>> +      s2 = gsi_stmt (gsi2);
>> +
>> +      if (gimple_code (s1) != gimple_code (s2))
>> +	return RETURN_FALSE_WITH_MSG ("gimple codes are different");
>>
>> I think you need to compare EH here.  Consider case where one unit
>> is compiled with -fno-exception and thus all EH regions are removed,
>> while other function has EH regions in it.  Those are not equivalent.
>>
>> EH region is obtained by lookup_stmt_eh and then you need to comapre
>> them for match as you do with gimple_resx_regoin.
>>
>> +  t1 = gimple_call_fndecl (s1);
>> +  t2 = gimple_call_fndecl (s2);
>> +
>> +  /* Function pointer variables are not supported yet.  */
>>
>> They seems to be, compare_operand seems just right.
>>
>> +
>> +/* Verifies for given GIMPLEs S1 and S2 that
>> +   label statements are semantically equivalent.  */
>> +
>> +bool
>> +func_checker::compare_gimple_label (gimple g1, gimple g2)
>> +{
>> +  if (m_ignore_labels)
>> +    return true;
>> +
>> +  tree t1 = gimple_label_label (g1);
>> +  tree t2 = gimple_label_label (g2);
>> +
>> +  return compare_tree_ssa_label (t1, t2);
>> +}
>>
>> I would expect the main BB loop to record BB in which label belongs to
>> and the BB assciatio neing checked here.
>> Otherwise I do not see how switch statements are compared to not have
>> different permutations of targets. Also note that one BB may have
>> multiple labels in them and they are equivalent.
>>
>> Also I would punt on occurence of FORCED_LABEL. Those are tricky as they
>> may be passed around and compared for address and no one really defines
>> what should happen.  Better to avoid those.
>
> Hi.
>
> I will remove this support in the pass.
>
>>
>> +
>> +/* Verifies for given GIMPLEs S1 and S2 that
>> +   switch statements are semantically equivalent.  */
>> +
>> +bool
>> +func_checker::compare_gimple_switch (gimple g1, gimple g2)
>> +{
>> +  unsigned lsize1, lsize2, i;
>> +  tree t1, t2, low1, low2, high1, high2;
>> +
>> +  lsize1 = gimple_switch_num_labels (g1);
>> +  lsize2 = gimple_switch_num_labels (g2);
>> +
>> +  if (lsize1 != lsize2)
>> +    return false;
>> +
>> +  t1 = gimple_switch_index (g1);
>> +  t2 = gimple_switch_index (g2);
>> +
>> +  if (TREE_CODE (t1) != SSA_NAME || TREE_CODE(t2) != SSA_NAME)
>> +    return false;
>>
>> Why non-SSA_NAMES are excluded? I see that constants should be optimized out,
>> but I do not see a need for specific test here.
>> +
>> +  if (!compare_operand (t1, t2))
>> +    return false;
>> +
>> +  for (i = 0; i < lsize1; i++)
>> +    {
>> +      low1 = CASE_LOW (gimple_switch_label (g1, i));
>> +      low2 = CASE_LOW (gimple_switch_label (g2, i));
>> +
>> +      if ((low1 != NULL) != (low2 != NULL)
>> +	  || (low1 && low2 && TREE_INT_CST_LOW (low1) != TREE_INT_CST_LOW (low2)))
>> +	return false;
>>
>> You want to compare integers for equivality.  Just use tree_int_cst_equal.
>> +
>> +      high1 = CASE_HIGH (gimple_switch_label (g1, i));
>> +      high2 = CASE_HIGH (gimple_switch_label (g2, i));
>> +
>> +      if ((high1 != NULL) != (high2 != NULL)
>> +	  || (high1 && high2
>> +	      && TREE_INT_CST_LOW (high1) != TREE_INT_CST_LOW (high2)))
>> +	return false;
>>
>> Same here.
>> +    }
>> +
>> +  return true;
>> +}
>> +
>> +/* Verifies for given GIMPLEs S1 and S2 that
>> +   return statements are semantically equivalent.  */
>> +
>> +bool
>> +func_checker::compare_gimple_return (gimple g1, gimple g2)
>> +{
>> +  tree t1, t2;
>> +
>> +  t1 = gimple_return_retval (g1);
>> +  t2 = gimple_return_retval (g2);
>> +
>> +  /* Void return type.  */
>> +  if (t1 == NULL && t2 == NULL)
>> +    return true;
>> +  else
>> +    return compare_operand (t1, t2);
>>
>> Do you check somewhere DECL_BY_REFERENCE (DECL_RESULT (struct function))? Those needs to match, too.
>> Perhaps it is always the case that SSA form differs, but I would test it.
>> +}
>> +
>> +/* Verifies for given GIMPLEs S1 and S2 that
>> +   goto statements are semantically equivalent.  */
>> +
>> +bool
>> +func_checker::compare_gimple_goto (gimple g1, gimple g2)
>> +{
>> +  tree dest1, dest2;
>> +
>> +  dest1 = gimple_goto_dest (g1);
>> +  dest2 = gimple_goto_dest (g2);
>> +
>> +  if (TREE_CODE (dest1) != TREE_CODE (dest2) || TREE_CODE (dest1) != SSA_NAME)
>> +    return false;
>> +
>> +  return compare_operand (dest1, dest2);
>>
>> You probably need to care only about indirect gotos, the direct ones are checked by
>> CFG compare.  So is the condtional jump.
>
> It looks that this code is visited quite rare.
>
>> +
>> +/* Verifies for given GIMPLEs S1 and S2 that ASM statements are equivalent.
>> +   For the beginning, the pass only supports equality for
>> +   '__asm__ __volatile__ ("", "", "", "memory")'.  */
>> +
>> +bool
>> +func_checker::compare_gimple_asm (gimple g1, gimple g2)
>> +{
>> +  if (gimple_asm_volatile_p (g1) != gimple_asm_volatile_p (g2))
>> +    return false;
>> +
>> +  if (gimple_asm_ninputs (g1) || gimple_asm_ninputs (g2))
>> +    return false;
>> +
>> +  if (gimple_asm_noutputs (g1) || gimple_asm_noutputs (g2))
>> +    return false;
>> +
>> +  if (gimple_asm_nlabels (g1) || gimple_asm_nlabels (g2))
>> +    return false;
>> +
>> +  if (gimple_asm_nclobbers (g1) != gimple_asm_nclobbers (g2))
>> +    return false;
>> +
>> +  for (unsigned i = 0; i < gimple_asm_nclobbers (g1); i++)
>> +    {
>> +      tree clobber1 = TREE_VALUE (gimple_asm_clobber_op (g1, i));
>> +      tree clobber2 = TREE_VALUE (gimple_asm_clobber_op (g2, i));
>> +
>> +      if (!operand_equal_p (clobber1, clobber2, OEP_ONLY_CONST))
>> +	return false;
>> +    }
>> +
>>
>> Even asm statements with no inputs or outputs can differ by the actual
>> asm statement. Compare it too.
>>
>> Comparing inputs/outputs/labels should be very easy to do.
>>
>> Compare all gimple_asm_n* for equivalency.
>
> This makes fully sense, but I don't understand what kind of operands do you mean?
>
>> At the end walk operands and watch the case they are TREE_LIST.
>> THen compare TREE_VALUE (op) of the list for operand_equal_p
>> and TREE_VALUE (TREE_PURPOSE (op)) for equivalency
>> (those are the constraints)
>>
>> If they are not (clobbers are not, those are just strings), operand_equal_p
>> should do.
>>
>> +  return true;
>> +}
>> +
>> +} // ipa_icf namespace
>>
>> Otherwise I think ipa-gimple-icf is quite fine now.
>> Please send updated version and I think it can go to mainline before the actual ipa-icf.
>
> I renamed both files and put them to a newly created namespace ipa_icf_gimple.
>
> Thank you,
> Martin
>

Hello.

There's latest version of the patch.

Changes:
+ gimple_asm should be correctly handled
+ FORCED_LABEL prevents function merging

Thanks,
Martin

[-- Attachment #2: ipa-icf-latest.patch --]
[-- Type: text/x-patch, Size: 124842 bytes --]

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index b38f8ef..f2580ba 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1265,6 +1265,8 @@ OBJS = \
 	ipa-profile.o \
 	ipa-prop.o \
 	ipa-pure-const.o \
+	ipa-icf.o \
+	ipa-icf-gimple.o \
 	ipa-reference.o \
 	ipa-ref.o \
 	ipa-utils.o \
diff --git a/gcc/cgraph.c b/gcc/cgraph.c
index fdcaf79..07e7ecb 100644
--- a/gcc/cgraph.c
+++ b/gcc/cgraph.c
@@ -1913,6 +1913,8 @@ cgraph_node::dump (FILE *f)
     fprintf (f, " only_called_at_exit");
   if (tm_clone)
     fprintf (f, " tm_clone");
+  if (icf_merged)
+    fprintf (f, " icf_merged");
   if (DECL_STATIC_CONSTRUCTOR (decl))
     fprintf (f," static_constructor (priority:%i)", get_init_priority ());
   if (DECL_STATIC_DESTRUCTOR (decl))
@@ -2561,6 +2563,7 @@ verify_edge_corresponds_to_fndecl (cgraph_edge *e, tree decl)
   if (!node
       || node->body_removed
       || node->in_other_partition
+      || node->icf_merged
       || e->callee->in_other_partition)
     return false;
 
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index fb41b01..2de98b4 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -172,6 +172,12 @@ public:
   /* Dump referring in list to FILE.  */
   void dump_referring (FILE *);
 
+  /* Get number of references for this node.  */
+  inline unsigned get_references_count (void)
+  {
+    return ref_list.references ? ref_list.references->length () : 0;
+  }
+
   /* Iterates I-th reference in the list, REF is also set.  */
   ipa_ref *iterate_reference (unsigned i, ipa_ref *&ref);
 
@@ -1230,6 +1236,8 @@ public:
   /* True if this decl calls a COMDAT-local function.  This is set up in
      compute_inline_parameters and inline_call.  */
   unsigned calls_comdat_local : 1;
+  /* True if node has been created by merge operation in IPA-ICF.  */
+  unsigned icf_merged: 1;
 };
 
 /* A cgraph node set is a collection of cgraph nodes.  A cgraph node
diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c
index d463505..7100cd7 100644
--- a/gcc/cgraphunit.c
+++ b/gcc/cgraphunit.c
@@ -1500,7 +1500,7 @@ cgraph_node::expand_thunk (bool output_asm_thunks, bool force_gimple_thunk)
       if (in_lto_p)
 	get_body ();
       a = DECL_ARGUMENTS (thunk_fndecl);
-      
+
       current_function_decl = thunk_fndecl;
 
       /* Ensure thunks are emitted in their correct sections.  */
diff --git a/gcc/common.opt b/gcc/common.opt
index b4f0ed4..5db5e1e 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -1443,6 +1443,18 @@ fipa-pure-const
 Common Report Var(flag_ipa_pure_const) Init(0) Optimization
 Discover pure and const functions
 
+fipa-icf
+Common Report Var(flag_ipa_icf) Optimization
+Perform Identical Code Folding for functions and read-only variables
+
+fipa-icf-functions
+Common Report Var(flag_ipa_icf_functions) Optimization
+Perform Identical Code Folding for functions
+
+fipa-icf-variables
+Common Report Var(flag_ipa_icf_variables) Optimization
+Perform Identical Code Folding for variables
+
 fipa-reference
 Common Report Var(flag_ipa_reference) Init(0) Optimization
 Discover readonly and non addressable static variables
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 5fe7e15..38356e4 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -382,7 +382,7 @@ Objective-C and Objective-C++ Dialects}.
 -fif-conversion2 -findirect-inlining @gol
 -finline-functions -finline-functions-called-once -finline-limit=@var{n} @gol
 -finline-small-functions -fipa-cp -fipa-cp-clone @gol
--fipa-pta -fipa-profile -fipa-pure-const -fipa-reference @gol
+-fipa-pta -fipa-profile -fipa-pure-const -fipa-reference -fipa-icf @gol
 -fira-algorithm=@var{algorithm} @gol
 -fira-region=@var{region} -fira-hoist-pressure @gol
 -fira-loop-pressure -fno-ira-share-save-slots @gol
@@ -7123,6 +7123,7 @@ also turns on the following optimization flags:
 -findirect-inlining @gol
 -fipa-cp @gol
 -fipa-sra @gol
+-fipa-icf @gol
 -fisolate-erroneous-paths-dereference @gol
 -foptimize-sibling-calls @gol
 -foptimize-strlen @gol
@@ -8068,6 +8069,19 @@ it may significantly increase code size
 (see @option{--param ipcp-unit-growth=@var{value}}).
 This flag is enabled by default at @option{-O3}.
 
+@item -fipa-icf
+@opindex fipa-icf
+Perform Identical Code Folding for functions and read-only variables.
+The optimization reduces code size and may disturb unwind stacks by replacing
+a function by equivalent one with a different name. The optimization works
+more effectively with link time optimization enabled.
+
+Nevertheless the behavior is similar to Gold Linker ICF optimization, GCC ICF
+works on different levels and thus the optimizations are not same - there are
+equivalences that are found only by GCC and equivalences found only by Gold.
+
+This flag is enabled by default at @option{-O2}.
+
 @item -fisolate-erroneous-paths-dereference
 Detect paths which trigger erroneous or undefined behaviour due to
 dereferencing a NULL pointer.  Isolate those paths from the main control
diff --git a/gcc/ipa-icf-gimple.c b/gcc/ipa-icf-gimple.c
new file mode 100644
index 0000000..0714f56
--- /dev/null
+++ b/gcc/ipa-icf-gimple.c
@@ -0,0 +1,913 @@
+/* Interprocedural Identical Code Folding pass
+   Copyright (C) 2014 Free Software Foundation, Inc.
+
+   Contributed by Jan Hubicka <hubicka@ucw.cz> and Martin Liska <mliska@suse.cz>
+
+This file is part of GCC.
+
+GCC 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, or (at your option) any later
+version.
+
+GCC 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 GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tree.h"
+#include "basic-block.h"
+#include "tree-ssa-alias.h"
+#include "internal-fn.h"
+#include "gimple-expr.h"
+#include "is-a.h"
+#include "gimple.h"
+#include "expr.h"
+#include "gimple-iterator.h"
+#include "gimple-ssa.h"
+#include "tree-cfg.h"
+#include "stringpool.h"
+#include "tree-dfa.h"
+#include "tree-pass.h"
+#include "gimple-pretty-print.h"
+#include "cfgloop.h"
+#include "except.h"
+#include "data-streamer.h"
+#include "ipa-utils.h"
+#include <list>
+#include "tree-ssanames.h"
+#include "tree-eh.h"
+
+#include "ipa-icf-gimple.h"
+#include "ipa-icf.h"
+
+namespace ipa_icf_gimple {
+
+/* Initialize internal structures for a given SOURCE_FUNC_DECL and
+   TARGET_FUNC_DECL. Strict polymorphic comparison is processed if
+   an option COMPARE_POLYMORPHIC is true. For special cases, one can
+   set IGNORE_LABELS to skip label comparison.
+   Similarly, IGNORE_SOURCE_DECLS and IGNORE_TARGET_DECLS are sets
+   of declarations that can be skipped.  */
+
+func_checker::func_checker (tree source_func_decl, tree target_func_decl,
+			    bool compare_polymorphic,
+			    bool ignore_labels,
+			    hash_set<symtab_node *> *ignored_source_nodes,
+			    hash_set<symtab_node *> *ignored_target_nodes)
+  : m_source_func_decl (source_func_decl), m_target_func_decl (target_func_decl),
+    m_ignored_source_nodes (ignored_source_nodes),
+    m_ignored_target_nodes (ignored_target_nodes),
+    m_compare_polymorphic (compare_polymorphic),
+    m_ignore_labels (ignore_labels)
+{
+  function *source_func = DECL_STRUCT_FUNCTION (source_func_decl);
+  function *target_func = DECL_STRUCT_FUNCTION (target_func_decl);
+
+  unsigned ssa_source = SSANAMES (source_func)->length ();
+  unsigned ssa_target = SSANAMES (target_func)->length ();
+
+  m_source_ssa_names.create (ssa_source);
+  m_target_ssa_names.create (ssa_target);
+
+  for (unsigned i = 0; i < ssa_source; i++)
+    m_source_ssa_names.safe_push (-1);
+
+  for (unsigned i = 0; i < ssa_target; i++)
+    m_target_ssa_names.safe_push (-1);
+}
+
+/* Memory release routine.  */
+
+func_checker::~func_checker ()
+{
+  m_source_ssa_names.release();
+  m_target_ssa_names.release();
+}
+
+/* Verifies that trees T1 and T2 are equivalent from perspective of ICF.  */
+
+bool
+func_checker::compare_ssa_name (tree t1, tree t2)
+{
+  unsigned i1 = SSA_NAME_VERSION (t1);
+  unsigned i2 = SSA_NAME_VERSION (t2);
+
+  if (m_source_ssa_names[i1] == -1)
+    m_source_ssa_names[i1] = i2;
+  else if (m_source_ssa_names[i1] != (int) i2)
+    return false;
+
+  if(m_target_ssa_names[i2] == -1)
+    m_target_ssa_names[i2] = i1;
+  else if (m_target_ssa_names[i2] != (int) i1)
+    return false;
+
+  return true;
+}
+
+/* Verification function for edges E1 and E2.  */
+
+bool
+func_checker::compare_edge (edge e1, edge e2)
+{
+  if (e1->flags != e2->flags)
+    return false;
+
+  bool existed_p;
+
+  edge &slot = m_edge_map.get_or_insert (e1, &existed_p);
+  if (existed_p)
+    return return_with_debug (slot == e2);
+  else
+    slot = e2;
+
+  /* TODO: filter edge probabilities for profile feedback match.  */
+
+  return true;
+}
+
+/* Verification function for declaration trees T1 and T2 that
+   come from functions FUNC1 and FUNC2.  */
+
+bool
+func_checker::compare_decl (tree t1, tree t2)
+{
+  if (!auto_var_in_fn_p (t1, m_source_func_decl)
+      || !auto_var_in_fn_p (t2, m_target_func_decl))
+    return return_with_debug (t1 == t2);
+
+  tree_code t = TREE_CODE (t1);
+  if ((t == VAR_DECL || t == PARM_DECL || t == RESULT_DECL)
+      && DECL_BY_REFERENCE (t1) != DECL_BY_REFERENCE (t2))
+    return return_false_with_msg ("DECL_BY_REFERENCE flags are different");
+
+  if (!compatible_types_p (TREE_TYPE (t1), TREE_TYPE (t2),
+			   m_compare_polymorphic))
+    return return_false ();
+
+  bool existed_p;
+
+  tree &slot = m_decl_map.get_or_insert (t1, &existed_p);
+  if (existed_p)
+    return return_with_debug (slot == t2);
+  else
+    slot = t2;
+
+  return true;
+}
+
+/* Return true if types are compatible from perspective of ICF.  */
+bool func_checker::compatible_types_p (tree t1, tree t2,
+				       bool compare_polymorphic,
+				       bool first_argument)
+{
+  if (TREE_CODE (t1) != TREE_CODE (t2))
+    return return_false_with_msg ("different tree types");
+
+  if (!types_compatible_p (t1, t2))
+    return return_false_with_msg ("types are not compatible");
+
+  if (get_alias_set (t1) != get_alias_set (t2))
+    return return_false_with_msg ("alias sets are different");
+
+  /* We call contains_polymorphic_type_p with this pointer type.  */
+  if (first_argument && TREE_CODE (t1) == POINTER_TYPE)
+    {
+      t1 = TREE_TYPE (t1);
+      t2 = TREE_TYPE (t2);
+    }
+
+  if (compare_polymorphic)
+    if (contains_polymorphic_type_p (t1) || contains_polymorphic_type_p (t2))
+      {
+	if (!contains_polymorphic_type_p (t1) || !contains_polymorphic_type_p (t2))
+	  return return_false_with_msg ("one type is not polymorphic");
+
+	if (!types_must_be_same_for_odr (t1, t2))
+	  return return_false_with_msg ("types are not same for ODR");
+      }
+
+  return true;
+}
+
+/* Function responsible for comparison of handled components T1 and T2.
+   If these components, from functions FUNC1 and FUNC2, are equal, true
+   is returned.  */
+
+bool
+func_checker::compare_operand (tree t1, tree t2)
+{
+  tree base1, base2, x1, x2, y1, y2, z1, z2;
+  HOST_WIDE_INT offset1 = 0, offset2 = 0;
+  bool ret;
+
+  if (!t1 && !t2)
+    return true;
+  else if (!t1 || !t2)
+    return false;
+
+  tree tt1 = TREE_TYPE (t1);
+  tree tt2 = TREE_TYPE (t2);
+
+  if (!func_checker::compatible_types_p (tt1, tt2))
+    return false;
+
+  base1 = get_addr_base_and_unit_offset (t1, &offset1);
+  base2 = get_addr_base_and_unit_offset (t2, &offset2);
+
+  if (base1 && base2)
+    {
+      if (offset1 != offset2)
+	return return_false_with_msg ("base offsets are different");
+
+      t1 = base1;
+      t2 = base2;
+    }
+
+  if (TREE_CODE (t1) != TREE_CODE (t2))
+    return return_false ();
+
+  switch (TREE_CODE (t1))
+    {
+    case CONSTRUCTOR:
+      {
+	unsigned length1 = vec_safe_length (CONSTRUCTOR_ELTS (t1));
+	unsigned length2 = vec_safe_length (CONSTRUCTOR_ELTS (t2));
+
+	if (length1 != length2)
+	  return return_false ();
+
+	for (unsigned i = 0; i < length1; i++)
+	  if (!compare_operand (CONSTRUCTOR_ELT (t1, i)->value,
+				CONSTRUCTOR_ELT (t2, i)->value))
+	    return return_false();
+
+	return true;
+      }
+    case ARRAY_REF:
+    case ARRAY_RANGE_REF:
+      {
+	x1 = TREE_OPERAND (t1, 0);
+	x2 = TREE_OPERAND (t2, 0);
+	y1 = TREE_OPERAND (t1, 1);
+	y2 = TREE_OPERAND (t2, 1);
+
+	if (!compare_operand (array_ref_low_bound (t1),
+			      array_ref_low_bound (t2)))
+	  return return_false_with_msg ("");
+	if (!compare_operand (array_ref_element_size (t1),
+			      array_ref_element_size (t2)))
+	  return return_false_with_msg ("");
+	if (!compare_operand (x1, x2))
+	  return return_false_with_msg ("");
+	return compare_operand (y1, y2);
+      }
+
+    case MEM_REF:
+      {
+	x1 = TREE_OPERAND (t1, 0);
+	x2 = TREE_OPERAND (t2, 0);
+	y1 = TREE_OPERAND (t1, 1);
+	y2 = TREE_OPERAND (t2, 1);
+
+	/* See if operand is an memory access (the test originate from
+	 gimple_load_p).
+
+	In this case the alias set of the function being replaced must
+	be subset of the alias set of the other function.  At the moment
+	we seek for equivalency classes, so simply require inclussion in
+	both directions.  */
+
+	if (!func_checker::compatible_types_p (TREE_TYPE (x1), TREE_TYPE (x2)))
+	  return return_false ();
+
+	if (!compare_operand (x1, x2))
+	  return return_false_with_msg ("");
+
+	if (get_alias_set (TREE_TYPE (y1)) != get_alias_set (TREE_TYPE (y2)))
+	  return return_false_with_msg ("alias set for MEM_REF offsets are different");
+
+	ao_ref r1, r2;
+	ao_ref_init (&r1, t1);
+	ao_ref_init (&r2, t2);
+	if (ao_ref_alias_set (&r1) != ao_ref_alias_set (&r2)
+	    || ao_ref_base_alias_set (&r1) != ao_ref_base_alias_set (&r2))
+	  return return_false_with_msg ("ao alias sets are different");
+
+	/* Type of the offset on MEM_REF does not matter.  */
+	return wi::to_offset  (y1) == wi::to_offset  (y2);
+      }
+    case COMPONENT_REF:
+      {
+	x1 = TREE_OPERAND (t1, 0);
+	x2 = TREE_OPERAND (t2, 0);
+	y1 = TREE_OPERAND (t1, 1);
+	y2 = TREE_OPERAND (t2, 1);
+
+	ret = compare_operand (x1, x2)
+	      && compare_operand (y1, y2);
+
+	return return_with_debug (ret);
+      }
+    /* Virtual table call.  */
+    case OBJ_TYPE_REF:
+      {
+	x1 = TREE_OPERAND (t1, 0);
+	x2 = TREE_OPERAND (t2, 0);
+	y1 = TREE_OPERAND (t1, 1);
+	y2 = TREE_OPERAND (t2, 1);
+	z1 = TREE_OPERAND (t1, 2);
+	z2 = TREE_OPERAND (t2, 2);
+
+	ret = compare_operand (x1, x2)
+	      && compare_operand (y1, y2)
+	      && compare_operand (z1, z2);
+
+	return return_with_debug (ret);
+      }
+    case ADDR_EXPR:
+      {
+	x1 = TREE_OPERAND (t1, 0);
+	x2 = TREE_OPERAND (t2, 0);
+
+	ret = compare_operand (x1, x2);
+	return return_with_debug (ret);
+      }
+    case SSA_NAME:
+      {
+	ret = compare_ssa_name (t1, t2);
+
+	if (!ret)
+	  return return_with_debug (ret);
+
+	if (SSA_NAME_IS_DEFAULT_DEF (t1))
+	  {
+	    tree b1 = SSA_NAME_VAR (t1);
+	    tree b2 = SSA_NAME_VAR (t2);
+
+	    if (b1 == NULL && b2 == NULL)
+	      return true;
+
+	    if (b1 == NULL || b2 == NULL || TREE_CODE (b1) != TREE_CODE (b2))
+	      return return_false ();
+
+	    switch (TREE_CODE (b1))
+	      {
+	      case VAR_DECL:
+		return return_with_debug (compare_variable_decl (t1, t2));
+	      case PARM_DECL:
+	      case RESULT_DECL:
+		ret = compare_decl (b1, b2);
+		return return_with_debug (ret);
+	      default:
+		return return_false_with_msg ("Unknown TREE code reached");
+	      }
+	  }
+	else
+	  return true;
+      }
+    case INTEGER_CST:
+      {
+	ret = compatible_types_p (TREE_TYPE (t1), TREE_TYPE (t2))
+	      && wi::to_offset  (t1) == wi::to_offset  (t2);
+
+	return return_with_debug (ret);
+      }
+    case COMPLEX_CST:
+    case VECTOR_CST:
+    case STRING_CST:
+    case REAL_CST:
+      {
+	ret = operand_equal_p (t1, t2, OEP_ONLY_CONST);
+	return return_with_debug (ret);
+      }
+    case FUNCTION_DECL:
+      {
+	ret = compare_function_decl (t1, t2);
+	return return_with_debug (ret);
+      }
+    case VAR_DECL:
+      return return_with_debug (compare_variable_decl (t1, t2));
+    case FIELD_DECL:
+      {
+	tree offset1 = DECL_FIELD_OFFSET (t1);
+	tree offset2 = DECL_FIELD_OFFSET (t2);
+
+	tree bit_offset1 = DECL_FIELD_BIT_OFFSET (t1);
+	tree bit_offset2 = DECL_FIELD_BIT_OFFSET (t2);
+
+	ret = compare_operand (offset1, offset2)
+	      && compare_operand (bit_offset1, bit_offset2);
+
+	return return_with_debug (ret);
+      }
+    case LABEL_DECL:
+      {
+	int *bb1 = m_label_bb_map.get (t1);
+	int *bb2 = m_label_bb_map.get (t2);
+
+	return return_with_debug (*bb1 == *bb2);
+      }
+    case PARM_DECL:
+    case RESULT_DECL:
+    case CONST_DECL:
+    case BIT_FIELD_REF:
+      {
+	ret = compare_decl (t1, t2);
+	return return_with_debug (ret);
+      }
+    default:
+      return return_false_with_msg ("Unknown TREE code reached");
+    }
+}
+
+/* Compares two tree list operands T1 and T2 and returns true if these
+   two trees are semantically equivalent.  */
+
+bool
+func_checker::compare_tree_list_operand (tree t1, tree t2)
+{
+  gcc_assert (TREE_CODE (t1) == TREE_LIST);
+  gcc_assert (TREE_CODE (t2) == TREE_LIST);
+
+  for (; t1; t1 = TREE_CHAIN (t1))
+    {
+      if (!t2)
+	return false;
+
+      if (!compare_operand (TREE_VALUE (t1), TREE_VALUE (t2)))
+	return return_false ();
+
+      t2 = TREE_CHAIN (t2);
+    }
+
+  if (t2)
+    return return_false ();
+
+  return true;
+}
+
+/* Verifies that trees T1 and T2, representing function declarations
+   are equivalent from perspective of ICF.  */
+
+bool
+func_checker::compare_function_decl (tree t1, tree t2)
+{
+  bool ret = false;
+
+  if (t1 == t2)
+    return true;
+
+  symtab_node *n1 = symtab_node::get (t1);
+  symtab_node *n2 = symtab_node::get (t2);
+
+  if (m_ignored_source_nodes != NULL && m_ignored_target_nodes != NULL)
+    {
+      ret = m_ignored_source_nodes->contains (n1)
+	    && m_ignored_target_nodes->contains (n2);
+
+      if (ret)
+	return true;
+    }
+
+  /* If function decl is WEAKREF, we compare targets.  */
+  cgraph_node *f1 = cgraph_node::get (t1);
+  cgraph_node *f2 = cgraph_node::get (t2);
+
+  if(f1 && f2 && f1->weakref && f2->weakref)
+    ret = f1->alias_target == f2->alias_target;
+
+  return ret;
+}
+
+/* Verifies that trees T1 and T2 do correspond.  */
+
+bool
+func_checker::compare_variable_decl (tree t1, tree t2)
+{
+  bool ret = false;
+
+  if (t1 == t2)
+    return true;
+
+  if (TREE_CODE (t1) == VAR_DECL && (DECL_EXTERNAL (t1) || TREE_STATIC (t1)))
+    {
+      symtab_node *n1 = symtab_node::get (t1);
+      symtab_node *n2 = symtab_node::get (t2);
+
+      if (m_ignored_source_nodes != NULL && m_ignored_target_nodes != NULL)
+	{
+	  ret = m_ignored_source_nodes->contains (n1)
+		&& m_ignored_target_nodes->contains (n2);
+
+	  if (ret)
+	    return true;
+	}
+    }
+  ret = compare_decl (t1, t2);
+
+  return return_with_debug (ret);
+}
+
+void
+func_checker::parse_labels (sem_bb *bb)
+{
+  for (gimple_stmt_iterator gsi = gsi_start_bb (bb->bb); !gsi_end_p (gsi);
+       gsi_next (&gsi))
+    {
+      gimple stmt = gsi_stmt (gsi);
+
+      if (gimple_code (stmt) == GIMPLE_LABEL)
+	{
+	  tree t = gimple_label_label (stmt);
+	  gcc_assert (TREE_CODE (t) == LABEL_DECL);
+
+	  m_label_bb_map.put (t, bb->bb->index);
+	}
+    }
+}
+
+/* Basic block equivalence comparison function that returns true if
+   basic blocks BB1 and BB2 (from functions FUNC1 and FUNC2) correspond.
+
+   In general, a collection of equivalence dictionaries is built for types
+   like SSA names, declarations (VAR_DECL, PARM_DECL, ..). This infrastructure
+   is utilized by every statement-by-stament comparison function.  */
+
+bool
+func_checker::compare_bb (sem_bb *bb1, sem_bb *bb2)
+{
+  unsigned i;
+  gimple_stmt_iterator gsi1, gsi2;
+  gimple s1, s2;
+
+  if (bb1->nondbg_stmt_count != bb2->nondbg_stmt_count
+      || bb1->edge_count != bb2->edge_count)
+    return return_false ();
+
+  gsi1 = gsi_start_bb (bb1->bb);
+  gsi2 = gsi_start_bb (bb2->bb);
+
+  for (i = 0; i < bb1->nondbg_stmt_count; i++)
+    {
+      if (is_gimple_debug (gsi_stmt (gsi1)))
+	gsi_next_nondebug (&gsi1);
+
+      if (is_gimple_debug (gsi_stmt (gsi2)))
+	gsi_next_nondebug (&gsi2);
+
+      s1 = gsi_stmt (gsi1);
+      s2 = gsi_stmt (gsi2);
+
+      int eh1 = lookup_stmt_eh_lp_fn
+		(DECL_STRUCT_FUNCTION (m_source_func_decl), s1);
+      int eh2 = lookup_stmt_eh_lp_fn
+		(DECL_STRUCT_FUNCTION (m_target_func_decl), s2);
+
+      if (eh1 != eh2)
+	return return_false_with_msg ("EH regions are different");
+
+      if (gimple_code (s1) != gimple_code (s2))
+	return return_false_with_msg ("gimple codes are different");
+
+      switch (gimple_code (s1))
+	{
+	case GIMPLE_CALL:
+	  if (!compare_gimple_call (s1, s2))
+	    return return_different_stmts (s1, s2, "GIMPLE_CALL");
+	  break;
+	case GIMPLE_ASSIGN:
+	  if (!compare_gimple_assign (s1, s2))
+	    return return_different_stmts (s1, s2, "GIMPLE_ASSIGN");
+	  break;
+	case GIMPLE_COND:
+	  if (!compare_gimple_cond (s1, s2))
+	    return return_different_stmts (s1, s2, "GIMPLE_COND");
+	  break;
+	case GIMPLE_SWITCH:
+	  if (!compare_gimple_switch (s1, s2))
+	    return return_different_stmts (s1, s2, "GIMPLE_SWITCH");
+	  break;
+	case GIMPLE_DEBUG:
+	case GIMPLE_EH_DISPATCH:
+	  break;
+	case GIMPLE_RESX:
+	  if (!compare_gimple_resx (s1, s2))
+	    return return_different_stmts (s1, s2, "GIMPLE_RESX");
+	  break;
+	case GIMPLE_LABEL:
+	  if (!compare_gimple_label (s1, s2))
+	    return return_different_stmts (s1, s2, "GIMPLE_LABEL");
+	  break;
+	case GIMPLE_RETURN:
+	  if (!compare_gimple_return (s1, s2))
+	    return return_different_stmts (s1, s2, "GIMPLE_RETURN");
+	  break;
+	case GIMPLE_GOTO:
+	  if (!compare_gimple_goto (s1, s2))
+	    return return_different_stmts (s1, s2, "GIMPLE_GOTO");
+	  break;
+	case GIMPLE_ASM:
+	  if (!compare_gimple_asm (s1, s2))
+	    return return_different_stmts (s1, s2, "GIMPLE_ASM");
+	  break;
+	case GIMPLE_PREDICT:
+	case GIMPLE_NOP:
+	  return true;
+	default:
+	  return return_false_with_msg ("Unknown GIMPLE code reached");
+	}
+
+      gsi_next (&gsi1);
+      gsi_next (&gsi2);
+    }
+
+  return true;
+}
+
+/* Verifies for given GIMPLEs S1 and S2 that
+   call statements are semantically equivalent.  */
+
+bool
+func_checker::compare_gimple_call (gimple s1, gimple s2)
+{
+  unsigned i;
+  tree t1, t2;
+
+  if (gimple_call_num_args (s1) != gimple_call_num_args (s2))
+    return false;
+
+  t1 = gimple_call_fndecl (s1);
+  t2 = gimple_call_fndecl (s2);
+
+  /* Function pointer variables are not supported yet.  */
+  if (t1 == NULL || t2 == NULL)
+    {
+      if (!compare_operand (t1, t2))
+	return return_false();
+    }
+  else if (!compare_function_decl (t1, t2))
+    return false;
+
+  /* Checking of argument.  */
+  for (i = 0; i < gimple_call_num_args (s1); ++i)
+    {
+      t1 = gimple_call_arg (s1, i);
+      t2 = gimple_call_arg (s2, i);
+
+      if (!compare_operand (t1, t2))
+	return false;
+    }
+
+  /* Return value checking.  */
+  t1 = gimple_get_lhs (s1);
+  t2 = gimple_get_lhs (s2);
+
+  return compare_operand (t1, t2);
+}
+
+
+/* Verifies for given GIMPLEs S1 and S2 that
+   assignment statements are semantically equivalent.  */
+
+bool
+func_checker::compare_gimple_assign (gimple s1, gimple s2)
+{
+  tree arg1, arg2;
+  tree_code code1, code2;
+  unsigned i;
+
+  code1 = gimple_expr_code (s1);
+  code2 = gimple_expr_code (s2);
+
+  if (code1 != code2)
+    return false;
+
+  code1 = gimple_assign_rhs_code (s1);
+  code2 = gimple_assign_rhs_code (s2);
+
+  if (code1 != code2)
+    return false;
+
+  for (i = 0; i < gimple_num_ops (s1); i++)
+    {
+      arg1 = gimple_op (s1, i);
+      arg2 = gimple_op (s2, i);
+
+      if (!compare_operand (arg1, arg2))
+	return false;
+    }
+
+
+  return true;
+}
+
+/* Verifies for given GIMPLEs S1 and S2 that
+   condition statements are semantically equivalent.  */
+
+bool
+func_checker::compare_gimple_cond (gimple s1, gimple s2)
+{
+  tree t1, t2;
+  tree_code code1, code2;
+
+  code1 = gimple_expr_code (s1);
+  code2 = gimple_expr_code (s2);
+
+  if (code1 != code2)
+    return false;
+
+  t1 = gimple_cond_lhs (s1);
+  t2 = gimple_cond_lhs (s2);
+
+  if (!compare_operand (t1, t2))
+    return false;
+
+  t1 = gimple_cond_rhs (s1);
+  t2 = gimple_cond_rhs (s2);
+
+  return compare_operand (t1, t2);
+}
+
+/* Verifies that tree labels T1 and T2 correspond in FUNC1 and FUNC2.  */
+
+bool
+func_checker::compare_tree_ssa_label (tree t1, tree t2)
+{
+  return compare_operand (t1, t2);
+}
+
+/* Verifies for given GIMPLEs S1 and S2 that
+   label statements are semantically equivalent.  */
+
+bool
+func_checker::compare_gimple_label (gimple g1, gimple g2)
+{
+  if (m_ignore_labels)
+    return true;
+
+  tree t1 = gimple_label_label (g1);
+  tree t2 = gimple_label_label (g2);
+
+  if (FORCED_LABEL (t1) || FORCED_LABEL (t2))
+    return return_false_with_msg ("FORCED_LABEL");
+
+  return compare_tree_ssa_label (t1, t2);
+}
+
+/* Verifies for given GIMPLEs S1 and S2 that
+   switch statements are semantically equivalent.  */
+
+bool
+func_checker::compare_gimple_switch (gimple g1, gimple g2)
+{
+  unsigned lsize1, lsize2, i;
+
+  lsize1 = gimple_switch_num_labels (g1);
+  lsize2 = gimple_switch_num_labels (g2);
+
+  if (lsize1 != lsize2)
+    return false;
+
+  tree t1 = gimple_switch_index (g1);
+  tree t2 = gimple_switch_index (g2);
+
+  if (!compare_operand (t1, t2))
+    return false;
+
+  for (i = 0; i < lsize1; i++)
+    {
+      tree label1 = gimple_switch_label (g1, i);
+      tree label2 = gimple_switch_label (g2, i);
+
+      if (TREE_CODE (label1) == CASE_LABEL_EXPR
+	  && TREE_CODE (label2) == CASE_LABEL_EXPR)
+	{
+	  label1 = CASE_LABEL (label1);
+	  label2 = CASE_LABEL (label2);
+
+	  if (!compare_operand (label1, label2))
+	    return return_false_with_msg ("switch label_exprs are different");
+	}
+      else if (!tree_int_cst_equal (label1, label2))
+	return return_false_with_msg ("switch labels are different");
+    }
+
+  return true;
+}
+
+/* Verifies for given GIMPLEs S1 and S2 that
+   return statements are semantically equivalent.  */
+
+bool
+func_checker::compare_gimple_return (gimple g1, gimple g2)
+{
+  tree t1, t2;
+
+  t1 = gimple_return_retval (g1);
+  t2 = gimple_return_retval (g2);
+
+  /* Void return type.  */
+  if (t1 == NULL && t2 == NULL)
+    return true;
+  else
+    return compare_operand (t1, t2);
+}
+
+/* Verifies for given GIMPLEs S1 and S2 that
+   goto statements are semantically equivalent.  */
+
+bool
+func_checker::compare_gimple_goto (gimple g1, gimple g2)
+{
+  tree dest1, dest2;
+
+  dest1 = gimple_goto_dest (g1);
+  dest2 = gimple_goto_dest (g2);
+
+  if (TREE_CODE (dest1) != TREE_CODE (dest2) || TREE_CODE (dest1) != SSA_NAME)
+    return false;
+
+  return compare_operand (dest1, dest2);
+}
+
+/* Verifies for given GIMPLEs S1 and S2 that
+   resx statements are semantically equivalent.  */
+
+bool
+func_checker::compare_gimple_resx (gimple g1, gimple g2)
+{
+  return gimple_resx_region (g1) == gimple_resx_region (g2);
+}
+
+/* Verifies for given GIMPLEs S1 and S2 that ASM statements are equivalent.
+   For the beginning, the pass only supports equality for
+   '__asm__ __volatile__ ("", "", "", "memory")'.  */
+
+bool
+func_checker::compare_gimple_asm (gimple g1, gimple g2)
+{
+  if (gimple_asm_volatile_p (g1) != gimple_asm_volatile_p (g2))
+    return false;
+
+  if (gimple_asm_ninputs (g1) != gimple_asm_ninputs (g2))
+    return false;
+
+  if (gimple_asm_noutputs (g1) != gimple_asm_noutputs (g2))
+    return false;
+
+  if (gimple_asm_nlabels (g1) != gimple_asm_nlabels (g2))
+    return false;
+
+  if (gimple_asm_nclobbers (g1) != gimple_asm_nclobbers (g2))
+    return false;
+
+  for (unsigned i = 0; i < gimple_asm_ninputs (g1); i++)
+    {
+      tree input1 = gimple_asm_input_op (g1, i);
+      tree input2 = gimple_asm_input_op (g2, i);
+
+      if (!compare_tree_list_operand (input1, input2))
+	return return_false_with_msg ("ASM input is different");
+    }
+
+  for (unsigned i = 0; i < gimple_asm_noutputs (g1); i++)
+    {
+      tree output1 = gimple_asm_output_op (g1, i);
+      tree output2 = gimple_asm_output_op (g2, i);
+
+      if (!compare_tree_list_operand (output1, output2))
+	return return_false_with_msg ("ASM output is different");
+    }
+
+  for (unsigned i = 0; i < gimple_asm_nlabels (g1); i++)
+    {
+      tree label1 = gimple_asm_label_op (g1, i);
+      tree label2 = gimple_asm_label_op (g2, i);
+
+      if (!compare_tree_list_operand (label1, label2))
+	return return_false_with_msg ("ASM label is different");
+    }
+
+  for (unsigned i = 0; i < gimple_asm_nclobbers (g1); i++)
+    {
+      tree clobber1 = gimple_asm_clobber_op (g1, i);
+      tree clobber2 = gimple_asm_clobber_op (g2, i);
+
+      if (!operand_equal_p (TREE_VALUE (clobber1), TREE_VALUE (clobber2),
+			    OEP_ONLY_CONST))
+	return return_false_with_msg ("ASM clobber is different");
+    }
+
+  return true;
+}
+
+} // ipa_icf_gimple namespace
diff --git a/gcc/ipa-icf-gimple.h b/gcc/ipa-icf-gimple.h
new file mode 100644
index 0000000..8487a2a
--- /dev/null
+++ b/gcc/ipa-icf-gimple.h
@@ -0,0 +1,264 @@
+/* Interprocedural semantic function equality pass
+   Copyright (C) 2014 Free Software Foundation, Inc.
+
+   Contributed by Jan Hubicka <hubicka@ucw.cz> and Martin Liska <mliska@suse.cz>
+
+This file is part of GCC.
+
+GCC 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, or (at your option) any later
+version.
+
+GCC 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 GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+/* Gimple identical code folding (class func_checker) is an infastructure
+   capable of comparing two given functions. The class compares every
+   gimple statement and uses many dictionaries to map source and target
+   SSA_NAMEs, declarations and other components.
+
+   To use the infrastructure, create an instanse of func_checker and call
+   a comparsion function based on type of gimple statement.  */
+
+/* Prints string STRING to a FILE with a given number of SPACE_COUNT.  */
+#define FPUTS_SPACES(file, space_count, string) \
+  fprintf (file, "%*s" string, space_count, " ");
+
+/* fprintf function wrapper that transforms given FORMAT to follow given
+   number for SPACE_COUNT and call fprintf for a FILE.  */
+#define FPRINTF_SPACES(file, space_count, format, ...) \
+  fprintf (file, "%*s" format, space_count, " ", ##__VA_ARGS__);
+
+/* Prints a MESSAGE to dump_file if exists. FUNC is name of function and
+   LINE is location in the source file.  */
+
+static inline void
+dump_message_1 (const char *message, const char *func, unsigned int line)
+{
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    fprintf (dump_file, "  debug message: %s (%s:%u)\n", message, func, line);
+}
+
+/* Prints a MESSAGE to dump_file if exists.  */
+#define dump_message(message) dump_message_1 (message, __func__, __LINE__)
+
+/* Logs a MESSAGE to dump_file if exists and returns false. FUNC is name
+   of function and LINE is location in the source file.  */
+
+static inline bool
+return_false_with_message_1 (const char *message, const char *func,
+			     unsigned int line)
+{
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    fprintf (dump_file, "  false returned: '%s' (%s:%u)\n", message, func, line);
+  return false;
+}
+
+/* Logs a MESSAGE to dump_file if exists and returns false.  */
+#define return_false_with_msg(message) \
+  return_false_with_message_1 (message, __func__, __LINE__)
+
+/* Return false and log that false value is returned.  */
+#define return_false() return_false_with_msg ("")
+
+/* Logs return value if RESULT is false. FUNC is name of function and LINE
+   is location in the source file.  */
+
+static inline bool
+return_with_result (bool result, const char *func, unsigned int line)
+{
+  if (!result && dump_file && (dump_flags & TDF_DETAILS))
+    fprintf (dump_file, "  false returned (%s:%u)\n", func, line);
+
+  return result;
+}
+
+/* Logs return value if RESULT is false.  */
+#define return_with_debug(result) return_with_result (result, __func__, __LINE__)
+
+/* Verbose logging function logging statements S1 and S2 of a CODE.
+   FUNC is name of function and LINE is location in the source file.  */
+
+static inline bool
+return_different_stmts_1 (gimple s1, gimple s2, const char *code,
+			  const char *func, unsigned int line)
+{
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    {
+      fprintf (dump_file, "  different statement for code: %s (%s:%u):\n",
+	       code, func, line);
+
+      print_gimple_stmt (dump_file, s1, 3, TDF_DETAILS);
+      print_gimple_stmt (dump_file, s2, 3, TDF_DETAILS);
+    }
+
+  return false;
+}
+
+/* Verbose logging function logging statements S1 and S2 of a CODE.  */
+#define return_different_stmts(s1, s2, code) \
+  return_different_stmts_1 (s1, s2, code, __func__, __LINE__)
+
+namespace ipa_icf_gimple {
+
+/* Basic block struct for semantic equality pass.  */
+class sem_bb
+{
+public:
+  sem_bb (basic_block bb_, unsigned nondbg_stmt_count_, unsigned edge_count_):
+    bb (bb_), nondbg_stmt_count (nondbg_stmt_count_), edge_count (edge_count_) {}
+
+  /* Basic block the structure belongs to.  */
+  basic_block bb;
+
+  /* Number of non-debug statements in the basic block.  */
+  unsigned nondbg_stmt_count;
+
+  /* Number of edges connected to the block.  */
+  unsigned edge_count;
+};
+
+/* A class aggregating all connections and semantic equivalents
+   for a given pair of semantic function candidates.  */
+class func_checker
+{
+public:
+  /* Initialize internal structures for a given SOURCE_FUNC_DECL and
+     TARGET_FUNC_DECL. Strict polymorphic comparison is processed if
+     an option COMPARE_POLYMORPHIC is true. For special cases, one can
+     set IGNORE_LABELS to skip label comparison.
+     Similarly, IGNORE_SOURCE_DECLS and IGNORE_TARGET_DECLS are sets
+     of declarations that can be skipped.  */
+  func_checker (tree source_func_decl, tree target_func_decl,
+		bool compare_polymorphic,
+		bool ignore_labels = false,
+		hash_set<symtab_node *> *ignored_source_nodes = NULL,
+		hash_set<symtab_node *> *ignored_target_nodes = NULL);
+
+  /* Memory release routine.  */
+  ~func_checker();
+
+  void parse_labels (sem_bb *bb);
+
+  /* Basic block equivalence comparison function that returns true if
+     basic blocks BB1 and BB2 correspond.  */
+  bool compare_bb (sem_bb *bb1, sem_bb *bb2);
+
+  /* Verifies that trees T1 and T2 are equivalent from perspective of ICF.  */
+  bool compare_ssa_name (tree t1, tree t2);
+
+  /* Verification function for edges E1 and E2.  */
+  bool compare_edge (edge e1, edge e2);
+
+  /* Verifies for given GIMPLEs S1 and S2 that
+     call statements are semantically equivalent.  */
+  bool compare_gimple_call (gimple s1, gimple s2);
+
+  /* Verifies for given GIMPLEs S1 and S2 that
+     assignment statements are semantically equivalent.  */
+  bool compare_gimple_assign (gimple s1, gimple s2);
+
+  /* Verifies for given GIMPLEs S1 and S2 that
+     condition statements are semantically equivalent.  */
+  bool compare_gimple_cond (gimple s1, gimple s2);
+
+  /* Verifies for given GIMPLEs S1 and S2 that
+     label statements are semantically equivalent.  */
+  bool compare_gimple_label (gimple s1, gimple s2);
+
+  /* Verifies for given GIMPLEs S1 and S2 that
+     switch statements are semantically equivalent.  */
+  bool compare_gimple_switch (gimple s1, gimple s2);
+
+  /* Verifies for given GIMPLEs S1 and S2 that
+     return statements are semantically equivalent.  */
+  bool compare_gimple_return (gimple s1, gimple s2);
+
+  /* Verifies for given GIMPLEs S1 and S2 that
+     goto statements are semantically equivalent.  */
+  bool compare_gimple_goto (gimple s1, gimple s2);
+
+  /* Verifies for given GIMPLEs S1 and S2 that
+     resx statements are semantically equivalent.  */
+  bool compare_gimple_resx (gimple s1, gimple s2);
+
+  /* Verifies for given GIMPLEs S1 and S2 that ASM statements are equivalent.
+     For the beginning, the pass only supports equality for
+     '__asm__ __volatile__ ("", "", "", "memory")'.  */
+  bool compare_gimple_asm (gimple s1, gimple s2);
+
+  /* Verification function for declaration trees T1 and T2.  */
+  bool compare_decl (tree t1, tree t2);
+
+  /* Verifies that tree labels T1 and T2 correspond.  */
+  bool compare_tree_ssa_label (tree t1, tree t2);
+
+  /* Function responsible for comparison of handled components T1 and T2.
+     If these components, from functions FUNC1 and FUNC2, are equal, true
+     is returned.  */
+  bool compare_operand (tree t1, tree t2);
+
+  /* Compares two tree list operands T1 and T2 and returns true if these
+     two trees are semantically equivalent.  */
+  bool compare_tree_list_operand (tree t1, tree t2);
+
+  /* Verifies that trees T1 and T2, representing function declarations
+     are equivalent from perspective of ICF.  */
+  bool compare_function_decl (tree t1, tree t2);
+
+  /* Verifies that trees T1 and T2 do correspond.  */
+  bool compare_variable_decl (tree t1, tree t2);
+
+  /* Return true if types are compatible from perspective of ICF.
+     FIRST_ARGUMENT indicates if the comparison is called for
+     first parameter of a function.  */
+  static bool compatible_types_p (tree t1, tree t2,
+				  bool compare_polymorphic = true,
+				  bool first_argument = false);
+
+
+private:
+  /* Vector mapping source SSA names to target ones.  */
+  vec <int> m_source_ssa_names;
+
+  /* Vector mapping target SSA names to source ones.  */
+  vec <int> m_target_ssa_names;
+
+  /* Source TREE function declaration.  */
+  tree m_source_func_decl;
+
+  /* Target TREE function declaration.  */
+  tree m_target_func_decl;
+
+  /* Source symbol nodes that should be skipped by
+     declaration comparison.  */
+  hash_set<symtab_node *> *m_ignored_source_nodes;
+
+  /* Target symbol nodes that should be skipped by
+     declaration comparison.  */
+  hash_set<symtab_node *> *m_ignored_target_nodes;
+
+  /* Source to target edge map.  */
+  hash_map <edge, edge> m_edge_map;
+
+  /* Source to target declaration map.  */
+  hash_map <tree, tree> m_decl_map;
+
+  /* Label to basic block index mapping.  */
+  hash_map <tree, int> m_label_bb_map;
+
+  /* Flag if polymorphic comparison should be executed.  */
+  bool m_compare_polymorphic;
+
+  /* Flag if ignore labels in comparison.  */
+  bool m_ignore_labels;
+};
+
+} // ipa_icf_gimple namespace
diff --git a/gcc/ipa-icf.c b/gcc/ipa-icf.c
new file mode 100644
index 0000000..05068c8
--- /dev/null
+++ b/gcc/ipa-icf.c
@@ -0,0 +1,2376 @@
+/* Interprocedural Identical Code Folding pass
+   Copyright (C) 2014 Free Software Foundation, Inc.
+
+   Contributed by Jan Hubicka <hubicka@ucw.cz> and Martin Liska <mliska@suse.cz>
+
+This file is part of GCC.
+
+GCC 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, or (at your option) any later
+version.
+
+GCC 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 GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+/* Interprocedural Identical Code Folding for functions and
+   read-only variables.
+
+   The goal of this transformation is to discover functions and read-only
+   variables which do have exactly the same semantics.
+
+   In case of functions,
+   we could either create a virtual clone or do a simple function wrapper
+   that will call equivalent function. If the function is just locally visible,
+   all function calls can be redirected. For read-only variables, we create
+   aliases if possible.
+
+   Optimization pass arranges as follows:
+   1) All functions and read-only variables are visited and internal
+      data structure, either sem_function or sem_variables is created.
+   2) For every symbol from the previous step, VAR_DECL and FUNCTION_DECL are
+      saved and matched to corresponding sem_items.
+   3) These declaration are ignored for equality check and are solved
+      by Value Numbering algorithm published by Alpert, Zadeck in 1992.
+   4) We compute hash value for each symbol.
+   5) Congruence classes are created based on hash value. If hash value are
+      equal, equals function is called and symbols are deeply compared.
+      We must prove that all SSA names, declarations and other items
+      correspond.
+   6) Value Numbering is executed for these classes. At the end of the process
+      all symbol members in remaining classes can be merged.
+   7) Merge operation creates alias in case of read-only variables. For
+      callgraph node, we must decide if we can redirect local calls,
+      create an alias or a thunk.
+
+*/
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tree.h"
+#include "basic-block.h"
+#include "tree-ssa-alias.h"
+#include "internal-fn.h"
+#include "gimple-expr.h"
+#include "is-a.h"
+#include "gimple.h"
+#include "expr.h"
+#include "gimple-iterator.h"
+#include "gimple-ssa.h"
+#include "tree-cfg.h"
+#include "tree-phinodes.h"
+#include "stringpool.h"
+#include "tree-ssanames.h"
+#include "tree-dfa.h"
+#include "tree-pass.h"
+#include "gimple-pretty-print.h"
+#include "ipa-inline.h"
+#include "cfgloop.h"
+#include "except.h"
+#include "hash-table.h"
+#include "coverage.h"
+#include "attribs.h"
+#include "print-tree.h"
+#include "lto-streamer.h"
+#include "data-streamer.h"
+#include "ipa-utils.h"
+#include <list>
+#include "ipa-icf-gimple.h"
+#include "ipa-icf.h"
+
+using namespace ipa_icf_gimple;
+
+namespace ipa_icf {
+/* Constructor for key value pair, where _ITEM is key and _INDEX is a target.  */
+
+sem_usage_pair::sem_usage_pair (sem_item *_item, unsigned int _index):
+  item (_item), index (_index)
+{
+}
+
+/* Semantic item constructor for a node of _TYPE, where STACK is used
+   for bitmap memory allocation.  */
+
+sem_item::sem_item (sem_item_type _type,
+		    bitmap_obstack *stack): type(_type), hash(0)
+{
+  setup (stack);
+}
+
+/* Semantic item constructor for a node of _TYPE, where STACK is used
+   for bitmap memory allocation. The item is based on symtab node _NODE
+   with computed _HASH.  */
+
+sem_item::sem_item (sem_item_type _type, symtab_node *_node,
+		    hashval_t _hash, bitmap_obstack *stack): type(_type),
+  node (_node), hash (_hash)
+{
+  decl = node->decl;
+  setup (stack);
+}
+
+/* Add reference to a semantic TARGET.  */
+
+void
+sem_item::add_reference (sem_item *target)
+{
+  refs.safe_push (target);
+  unsigned index = refs.length ();
+  target->usages.safe_push (new sem_usage_pair(this, index));
+  bitmap_set_bit (target->usage_index_bitmap, index);
+  refs_set.add (target->node);
+}
+
+/* Initialize internal data structures. Bitmap STACK is used for
+   bitmap memory allocation process.  */
+
+void
+sem_item::setup (bitmap_obstack *stack)
+{
+  gcc_checking_assert (node);
+
+  refs.create (0);
+  tree_refs.create (0);
+  usages.create (0);
+  usage_index_bitmap = BITMAP_ALLOC (stack);
+}
+
+sem_item::~sem_item ()
+{
+  for (unsigned i = 0; i < usages.length (); i++)
+    delete usages[i];
+
+  refs.release ();
+  tree_refs.release ();
+  usages.release ();
+
+  BITMAP_FREE (usage_index_bitmap);
+}
+
+/* Dump function for debugging purpose.  */
+
+DEBUG_FUNCTION void
+sem_item::dump (void)
+{
+  if (dump_file)
+    {
+      fprintf (dump_file, "[%s] %s (%u) (tree:%p)\n", type == FUNC ? "func" : "var",
+	       name(), node->order, (void *) node->decl);
+      fprintf (dump_file, "  hash: %u\n", get_hash ());
+      fprintf (dump_file, "  references: ");
+
+      for (unsigned i = 0; i < refs.length (); i++)
+	fprintf (dump_file, "%s%s ", refs[i]->name (),
+		 i < refs.length() - 1 ? "," : "");
+
+      fprintf (dump_file, "\n");
+    }
+}
+
+/* Semantic function constructor that uses STACK as bitmap memory stack.  */
+
+sem_function::sem_function (bitmap_obstack *stack): sem_item (FUNC, stack),
+  m_checker (NULL), m_compared_func (NULL)
+{
+  arg_types.create (0);
+  bb_sizes.create (0);
+  bb_sorted.create (0);
+}
+
+/*  Constructor based on callgraph node _NODE with computed hash _HASH.
+    Bitmap STACK is used for memory allocation.  */
+sem_function::sem_function (cgraph_node *node, hashval_t hash,
+			    bitmap_obstack *stack):
+  sem_item (FUNC, node, hash, stack),
+  m_checker (NULL), m_compared_func (NULL)
+{
+  arg_types.create (0);
+  bb_sizes.create (0);
+  bb_sorted.create (0);
+}
+
+sem_function::~sem_function ()
+{
+  for (unsigned i = 0; i < bb_sorted.length (); i++)
+    free (bb_sorted[i]);
+
+  arg_types.release ();
+  bb_sizes.release ();
+  bb_sorted.release ();
+}
+
+/* Calculates hash value based on a BASIC_BLOCK.  */
+
+hashval_t
+sem_function::get_bb_hash (const sem_bb *basic_block)
+{
+  inchash::hash hstate;
+
+  hstate.add_int (basic_block->nondbg_stmt_count);
+  hstate.add_int (basic_block->edge_count);
+
+  return hstate.end ();
+}
+
+/* References independent hash function.  */
+
+hashval_t
+sem_function::get_hash (void)
+{
+  if(!hash)
+    {
+      inchash::hash hstate;
+      hstate.add_int (177454); /* Random number for function type.  */
+
+      hstate.add_int (arg_count);
+      hstate.add_int (cfg_checksum);
+      hstate.add_int (gcode_hash);
+
+      for (unsigned i = 0; i < bb_sorted.length (); i++)
+	hstate.merge_hash (get_bb_hash (bb_sorted[i]));
+
+      for (unsigned i = 0; i < bb_sizes.length (); i++)
+	hstate.add_int (bb_sizes[i]);
+
+      hash = hstate.end ();
+    }
+
+  return hash;
+}
+
+/* For a given symbol table nodes N1 and N2, we check that FUNCTION_DECLs
+   point to a same function. Comparison can be skipped if IGNORED_NODES
+   contains these nodes.  */
+
+bool
+sem_function::compare_cgraph_references (hash_map <symtab_node *, sem_item *>
+    &ignored_nodes,
+    symtab_node *n1, symtab_node *n2)
+{
+  if (n1 == n2 || (ignored_nodes.get (n1) && ignored_nodes.get (n2)))
+    return true;
+
+  cgraph_node *cn1 = dyn_cast <cgraph_node *> (n1);
+  cgraph_node *cn2 = dyn_cast <cgraph_node *> (n2);
+
+  if (cn1 && cn2 && cn1->weakref && cn2->weakref
+      && cn1->alias_target == cn2->alias_target)
+    return true;
+
+  return return_false_with_msg ("different references");
+}
+
+/* If cgraph edges E1 and E2 are indirect calls, verify that
+   ECF flags are the same.  */
+
+bool sem_function::compare_edge_flags (cgraph_edge *e1, cgraph_edge *e2)
+{
+  if (e1->indirect_info && e2->indirect_info)
+    {
+      int e1_flags = e1->indirect_info->ecf_flags;
+      int e2_flags = e2->indirect_info->ecf_flags;
+
+      if (e1_flags != e2_flags)
+	return return_false_with_msg ("ICF flags are different");
+    }
+  else if (e1->indirect_info || e2->indirect_info)
+    return false;
+
+  return true;
+}
+
+/* Fast equality function based on knowledge known in WPA.  */
+
+bool
+sem_function::equals_wpa (sem_item *item,
+			  hash_map <symtab_node *, sem_item *> &ignored_nodes)
+{
+  gcc_assert (item->type == FUNC);
+
+  m_compared_func = static_cast<sem_function *> (item);
+
+  if (arg_types.length () != m_compared_func->arg_types.length ())
+    return return_false_with_msg ("different number of arguments");
+
+  /* Checking types of arguments.  */
+  for (unsigned i = 0; i < arg_types.length (); i++)
+    {
+      /* This guard is here for function pointer with attributes (pr59927.c).  */
+      if (!arg_types[i] || !m_compared_func->arg_types[i])
+	return return_false_with_msg ("NULL argument type");
+
+      /* Polymorphic comparison is executed just for non-leaf functions.  */
+      bool is_not_leaf = get_node ()->callees != NULL;
+
+      if (!func_checker::compatible_types_p (arg_types[i],
+					     m_compared_func->arg_types[i],
+					     is_not_leaf, i == 0))
+	return return_false_with_msg ("argument type is different");
+    }
+
+  /* Result type checking.  */
+  if (!func_checker::compatible_types_p (result_type,
+					 m_compared_func->result_type))
+    return return_false_with_msg ("result types are different");
+
+  if (node->get_references_count () != item->node->get_references_count ())
+    return return_false_with_msg ("different number of references");
+
+  ipa_ref *ref = NULL, *ref2 = NULL;
+  for (unsigned i = 0; node->iterate_reference (i, ref); i++)
+    {
+      item->node->iterate_reference (i, ref2);
+
+      if (!compare_cgraph_references (ignored_nodes, ref->referred, ref2->referred))
+	return false;
+    }
+
+  cgraph_edge *e1 = dyn_cast <cgraph_node *> (node)->callees;
+  cgraph_edge *e2 = dyn_cast <cgraph_node *> (item->node)->callees;
+
+  while (e1 && e2)
+    {
+      if (!compare_cgraph_references (ignored_nodes, e1->callee, e2->callee))
+	return false;
+
+      e1 = e1->next_callee;
+      e2 = e2->next_callee;
+    }
+
+  if (e1 || e2)
+    return return_false_with_msg ("different number of edges");
+
+  return true;
+}
+
+/* Returns true if the item equals to ITEM given as argument.  */
+
+bool
+sem_function::equals (sem_item *item,
+		      hash_map <symtab_node *, sem_item *> &ignored_nodes)
+{
+  gcc_assert (item->type == FUNC);
+  bool eq = equals_private (item, ignored_nodes);
+
+  if (m_checker != NULL)
+    {
+      delete m_checker;
+      m_checker = NULL;
+    }
+
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    fprintf (dump_file,
+	     "Equals called for:%s:%s (%u:%u) (%s:%s) with result: %s\n\n",
+	     name(), item->name (), node->order, item->node->order, asm_name (),
+	     item->asm_name (), eq ? "true" : "false");
+
+  return eq;
+}
+
+/* Processes function equality comparison.  */
+
+bool
+sem_function::equals_private (sem_item *item,
+			      hash_map <symtab_node *, sem_item *> &ignored_nodes)
+{
+  if (item->type != FUNC)
+    return false;
+
+  basic_block bb1, bb2;
+  edge e1, e2;
+  edge_iterator ei1, ei2;
+  int *bb_dict = NULL;
+  bool result = true;
+  tree arg1, arg2;
+
+  m_compared_func = static_cast<sem_function *> (item);
+
+  gcc_assert (decl != item->decl);
+
+  if (bb_sorted.length () != m_compared_func->bb_sorted.length ()
+      || edge_count != m_compared_func->edge_count
+      || cfg_checksum != m_compared_func->cfg_checksum)
+    return return_false ();
+
+  if (!equals_wpa (item, ignored_nodes))
+    return false;
+
+  /* Checking function arguments.  */
+  tree decl1 = DECL_ATTRIBUTES (decl);
+  tree decl2 = DECL_ATTRIBUTES (m_compared_func->decl);
+
+  m_checker = new func_checker (decl, m_compared_func->decl,
+				compare_polymorphic_p (),
+				false,
+				&refs_set,
+				&m_compared_func->refs_set);
+  while (decl1)
+    {
+      if (decl2 == NULL)
+	return return_false ();
+
+      if (get_attribute_name (decl1) != get_attribute_name (decl2))
+	return return_false ();
+
+      tree attr_value1 = TREE_VALUE (decl1);
+      tree attr_value2 = TREE_VALUE (decl2);
+
+      if (attr_value1 && attr_value2)
+	{
+	  bool ret = m_checker->compare_operand (TREE_VALUE (attr_value1),
+						 TREE_VALUE (attr_value2));
+	  if (!ret)
+	    return return_false_with_msg ("attribute values are different");
+	}
+      else if (!attr_value1 && !attr_value2)
+	{}
+      else
+	return return_false ();
+
+      decl1 = TREE_CHAIN (decl1);
+      decl2 = TREE_CHAIN (decl2);
+    }
+
+  if (decl1 != decl2)
+    return return_false();
+
+
+  for (arg1 = DECL_ARGUMENTS (decl),
+       arg2 = DECL_ARGUMENTS (m_compared_func->decl);
+       arg1; arg1 = DECL_CHAIN (arg1), arg2 = DECL_CHAIN (arg2))
+    if (!m_checker->compare_decl (arg1, arg2))
+      return return_false ();
+
+  /* Fill-up label dictionary.  */
+  for (unsigned i = 0; i < bb_sorted.length (); ++i)
+    {
+      m_checker->parse_labels (bb_sorted[i]);
+      m_checker->parse_labels (m_compared_func->bb_sorted[i]);
+    }
+
+  /* Checking all basic blocks.  */
+  for (unsigned i = 0; i < bb_sorted.length (); ++i)
+    if(!m_checker->compare_bb (bb_sorted[i], m_compared_func->bb_sorted[i]))
+      return return_false();
+
+  dump_message ("All BBs are equal\n");
+
+  /* Basic block edges check.  */
+  for (unsigned i = 0; i < bb_sorted.length (); ++i)
+    {
+      bb_dict = XNEWVEC (int, bb_sorted.length () + 2);
+      memset (bb_dict, -1, (bb_sorted.length () + 2) * sizeof (int));
+
+      bb1 = bb_sorted[i]->bb;
+      bb2 = m_compared_func->bb_sorted[i]->bb;
+
+      ei2 = ei_start (bb2->preds);
+
+      for (ei1 = ei_start (bb1->preds); ei_cond (ei1, &e1); ei_next (&ei1))
+	{
+	  ei_cond (ei2, &e2);
+
+	  if (e1->flags != e2->flags)
+	    return return_false_with_msg ("flags comparison returns false");
+
+	  if (!bb_dict_test (bb_dict, e1->src->index, e2->src->index))
+	    return return_false_with_msg ("edge comparison returns false");
+
+	  if (!bb_dict_test (bb_dict, e1->dest->index, e2->dest->index))
+	    return return_false_with_msg ("BB comparison returns false");
+
+	  if (!m_checker->compare_edge (e1, e2))
+	    return return_false_with_msg ("edge comparison returns false");
+
+	  ei_next (&ei2);
+	}
+    }
+
+  /* Basic block PHI nodes comparison.  */
+  for (unsigned i = 0; i < bb_sorted.length (); i++)
+    if (!compare_phi_node (bb_sorted[i]->bb, m_compared_func->bb_sorted[i]->bb))
+      return return_false_with_msg ("PHI node comparison returns false");
+
+  return result;
+}
+
+/* Merges instance with an ALIAS_ITEM, where alias, thunk or redirection can
+   be applied.  */
+bool
+sem_function::merge (sem_item *alias_item)
+{
+  gcc_assert (alias_item->type == FUNC);
+
+  sem_function *alias_func = static_cast<sem_function *> (alias_item);
+
+  cgraph_node *original = get_node ();
+  cgraph_node *local_original = original;
+  cgraph_node *alias = alias_func->get_node ();
+  bool original_address_matters;
+  bool alias_address_matters;
+
+  bool create_thunk = false;
+  bool create_alias = false;
+  bool redirect_callers = false;
+  bool original_discardable = false;
+
+  /* Do not attempt to mix functions from different user sections;
+     we do not know what user intends with those.  */
+  if (((DECL_SECTION_NAME (original->decl) && !original->implicit_section)
+       || (DECL_SECTION_NAME (alias->decl) && !alias->implicit_section))
+      && DECL_SECTION_NAME (original->decl) != DECL_SECTION_NAME (alias->decl))
+    {
+      if (dump_file)
+	fprintf (dump_file,
+		 "Not unifying; original and alias are in different sections.\n\n");
+      return false;
+    }
+
+  /* See if original is in a section that can be discarded if the main
+     symbol is not used.  */
+  if (DECL_EXTERNAL (original->decl))
+    original_discardable = true;
+  if (original->resolution == LDPR_PREEMPTED_REG
+      || original->resolution == LDPR_PREEMPTED_IR)
+    original_discardable = true;
+  if (original->can_be_discarded_p ())
+    original_discardable = true;
+
+  /* See if original and/or alias address can be compared for equality.  */
+  original_address_matters
+    = (!DECL_VIRTUAL_P (original->decl)
+       && (original->externally_visible
+	   || original->address_taken_from_non_vtable_p ()));
+  alias_address_matters
+    = (!DECL_VIRTUAL_P (alias->decl)
+       && (alias->externally_visible
+	   || alias->address_taken_from_non_vtable_p ()));
+
+  /* If alias and original can be compared for address equality, we need
+     to create a thunk.  Also we can not create extra aliases into discardable
+     section (or we risk link failures when section is discarded).  */
+  if ((original_address_matters
+       && alias_address_matters)
+      || original_discardable)
+    {
+      create_thunk = !stdarg_p (TREE_TYPE (alias->decl));
+      create_alias = false;
+      /* When both alias and original are not overwritable, we can save
+         the extra thunk wrapper for direct calls.  */
+      redirect_callers
+	= (!original_discardable
+	   && alias->get_availability () > AVAIL_INTERPOSABLE
+	   && original->get_availability () > AVAIL_INTERPOSABLE);
+    }
+  else
+    {
+      create_alias = true;
+      create_thunk = false;
+      redirect_callers = false;
+    }
+
+  if (create_alias && DECL_COMDAT_GROUP (alias->decl))
+    {
+      create_alias = false;
+      create_thunk = true;
+    }
+
+  /* We want thunk to always jump to the local function body
+     unless the body is comdat and may be optimized out.  */
+  if ((create_thunk || redirect_callers)
+      && (!original_discardable
+	  || (DECL_COMDAT_GROUP (original->decl)
+	      && (DECL_COMDAT_GROUP (original->decl)
+		  == DECL_COMDAT_GROUP (alias->decl)))))
+    local_original
+      = dyn_cast <cgraph_node *> (original->noninterposable_alias ());
+
+  if (redirect_callers)
+    {
+      /* If alias is non-overwritable then
+         all direct calls are safe to be redirected to the original.  */
+      bool redirected = false;
+      while (alias->callers)
+	{
+	  cgraph_edge *e = alias->callers;
+	  e->redirect_callee (local_original);
+	  push_cfun (DECL_STRUCT_FUNCTION (e->caller->decl));
+
+	  if (e->call_stmt)
+	    e->redirect_call_stmt_to_callee ();
+
+	  pop_cfun ();
+	  redirected = true;
+	}
+
+      alias->icf_merged = true;
+
+      /* The alias function is removed if symbol address
+         does not matter.  */
+      if (!alias_address_matters)
+	alias->remove ();
+
+      if (dump_file && redirected)
+	fprintf (dump_file, "Callgraph local calls have been redirected.\n\n");
+    }
+  /* If the condtion above is not met, we are lucky and can turn the
+     function into real alias.  */
+  else if (create_alias)
+    {
+      alias->icf_merged = true;
+
+      /* Remove the function's body.  */
+      ipa_merge_profiles (original, alias);
+      alias->release_body (true);
+      alias->reset ();
+
+      /* Create the alias.  */
+      cgraph_node::create_alias (alias_func->decl, decl);
+      alias->resolve_alias (original);
+
+      if (dump_file)
+	fprintf (dump_file, "Callgraph alias has been created.\n\n");
+    }
+  else if (create_thunk)
+    {
+      if (DECL_COMDAT_GROUP (alias->decl))
+	{
+	  if (dump_file)
+	    fprintf (dump_file, "Callgraph thunk cannot be created because of COMDAT\n");
+
+	  return 0;
+	}
+
+      alias->icf_merged = true;
+      ipa_merge_profiles (local_original, alias);
+      alias->create_wrapper (local_original);
+
+      if (dump_file)
+	fprintf (dump_file, "Callgraph thunk has been created.\n\n");
+    }
+  else if (dump_file)
+    fprintf (dump_file, "Callgraph merge operation cannot be performed.\n\n");
+
+  return true;
+}
+
+/* Semantic item initialization function.  */
+
+void
+sem_function::init (void)
+{
+  if (in_lto_p)
+    get_node ()->get_body ();
+
+  tree fndecl = node->decl;
+  function *func = DECL_STRUCT_FUNCTION (fndecl);
+
+  gcc_assert (func);
+  gcc_assert (SSANAMES (func));
+
+  ssa_names_size = SSANAMES (func)->length ();
+  node = node;
+
+  decl = fndecl;
+  region_tree = func->eh->region_tree;
+
+  /* iterating all function arguments.  */
+  arg_count = count_formal_params (fndecl);
+
+  edge_count = n_edges_for_fn (func);
+  cfg_checksum = coverage_compute_cfg_checksum (func);
+
+  inchash::hash hstate;
+
+  basic_block bb;
+  FOR_EACH_BB_FN (bb, func)
+  {
+    unsigned nondbg_stmt_count = 0;
+
+    edge e;
+    for (edge_iterator ei = ei_start (bb->preds); ei_cond (ei, &e); ei_next (&ei))
+      cfg_checksum = iterative_hash_host_wide_int (e->flags,
+		     cfg_checksum);
+
+    for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi);
+	 gsi_next (&gsi))
+      {
+	gimple stmt = gsi_stmt (gsi);
+
+	if (gimple_code (stmt) != GIMPLE_DEBUG)
+	  {
+	    hash_stmt (&hstate, stmt);
+	    nondbg_stmt_count++;
+	  }
+      }
+
+    gcode_hash = hstate.end ();
+    bb_sizes.safe_push (nondbg_stmt_count);
+
+    /* Inserting basic block to hash table.  */
+    sem_bb *semantic_bb = new sem_bb (bb, nondbg_stmt_count,
+				      EDGE_COUNT (bb->preds) + EDGE_COUNT (bb->succs));
+
+    bb_sorted.safe_push (semantic_bb);
+  }
+
+  parse_tree_args ();
+}
+
+/* Improve accumulated hash for HSTATE based on a gimple statement STMT.  */
+
+void
+sem_function::hash_stmt (inchash::hash *hstate, gimple stmt)
+{
+  enum gimple_code code = gimple_code (stmt);
+
+  hstate->add_int (code);
+
+  if (code == GIMPLE_CALL)
+    {
+      /* Checking of argument.  */
+      for (unsigned i = 0; i < gimple_call_num_args (stmt); ++i)
+	{
+	  tree argument = gimple_call_arg (stmt, i);
+
+	  switch (TREE_CODE (argument))
+	    {
+	    case INTEGER_CST:
+	      if (tree_fits_shwi_p (argument))
+		hstate->add_wide_int (tree_to_shwi (argument));
+	      else if (tree_fits_uhwi_p (argument))
+		hstate->add_wide_int (tree_to_uhwi (argument));
+	      break;
+	    case REAL_CST:
+	      REAL_VALUE_TYPE c;
+	      HOST_WIDE_INT n;
+
+	      c = TREE_REAL_CST (argument);
+	      n = real_to_integer (&c);
+
+	      hstate->add_wide_int (n);
+	      break;
+	    case ADDR_EXPR:
+	      {
+		tree addr_operand = TREE_OPERAND (argument, 0);
+
+		if (TREE_CODE (addr_operand) == STRING_CST)
+		  hstate->add (TREE_STRING_POINTER (addr_operand),
+			       TREE_STRING_LENGTH (addr_operand));
+		break;
+	      }
+	    default:
+	      break;
+	    }
+	}
+    }
+}
+
+
+/* Return true if polymorphic comparison must be processed.  */
+
+bool
+sem_function::compare_polymorphic_p (void)
+{
+  return get_node ()->callees != NULL
+	 || m_compared_func->get_node ()->callees != NULL;
+}
+
+/* For a given call graph NODE, the function constructs new
+   semantic function item.  */
+
+sem_function *
+sem_function::parse (cgraph_node *node, bitmap_obstack *stack)
+{
+  tree fndecl = node->decl;
+  function *func = DECL_STRUCT_FUNCTION (fndecl);
+
+  /* TODO: add support for thunks and aliases.  */
+
+  if (!func || !node->has_gimple_body_p ())
+    return NULL;
+
+  if (lookup_attribute_by_prefix ("omp ", DECL_ATTRIBUTES (node->decl)) != NULL)
+    return NULL;
+
+  sem_function *f = new sem_function (node, 0, stack);
+
+  f->init ();
+
+  return f;
+}
+
+/* Parses function arguments and result type.  */
+
+void
+sem_function::parse_tree_args (void)
+{
+  tree result;
+
+  if (arg_types.exists ())
+    arg_types.release ();
+
+  arg_types.create (4);
+  tree fnargs = DECL_ARGUMENTS (decl);
+
+  for (tree parm = fnargs; parm; parm = DECL_CHAIN (parm))
+    arg_types.safe_push (DECL_ARG_TYPE (parm));
+
+  /* Function result type.  */
+  result = DECL_RESULT (decl);
+  result_type = result ? TREE_TYPE (result) : NULL;
+
+  /* During WPA, we can get arguments by following method.  */
+  if (!fnargs)
+    {
+      tree type = TYPE_ARG_TYPES (TREE_TYPE (decl));
+      for (tree parm = type; parm; parm = TREE_CHAIN (parm))
+	arg_types.safe_push (TYPE_CANONICAL (TREE_VALUE (parm)));
+
+      result_type = TREE_TYPE (TREE_TYPE (decl));
+    }
+}
+
+/* For given basic blocks BB1 and BB2 (from functions FUNC1 and FUNC),
+   return true if phi nodes are semantically equivalent in these blocks .  */
+
+bool
+sem_function::compare_phi_node (basic_block bb1, basic_block bb2)
+{
+  gimple_stmt_iterator si1, si2;
+  gimple phi1, phi2;
+  unsigned size1, size2, i;
+  tree t1, t2;
+  edge e1, e2;
+
+  gcc_assert (bb1 != NULL);
+  gcc_assert (bb2 != NULL);
+
+  si2 = gsi_start_phis (bb2);
+  for (si1 = gsi_start_phis (bb1); !gsi_end_p (si1);
+       gsi_next (&si1))
+    {
+      gsi_next_nonvirtual_phi (&si1);
+      gsi_next_nonvirtual_phi (&si2);
+
+      if (gsi_end_p (si1) && gsi_end_p (si2))
+	break;
+
+      if (gsi_end_p (si1) || gsi_end_p (si2))
+	return return_false();
+
+      phi1 = gsi_stmt (si1);
+      phi2 = gsi_stmt (si2);
+
+      size1 = gimple_phi_num_args (phi1);
+      size2 = gimple_phi_num_args (phi2);
+
+      if (size1 != size2)
+	return return_false ();
+
+      for (i = 0; i < size1; ++i)
+	{
+	  t1 = gimple_phi_arg (phi1, i)->def;
+	  t2 = gimple_phi_arg (phi2, i)->def;
+
+	  if (!m_checker->compare_operand (t1, t2))
+	    return return_false ();
+
+	  e1 = gimple_phi_arg_edge (phi1, i);
+	  e2 = gimple_phi_arg_edge (phi2, i);
+
+	  if (!m_checker->compare_edge (e1, e2))
+	    return return_false ();
+	}
+
+      gsi_next (&si2);
+    }
+
+  return true;
+}
+
+/* Returns true if tree T can be compared as a handled component.  */
+
+bool
+sem_function::icf_handled_component_p (tree t)
+{
+  tree_code tc = TREE_CODE (t);
+
+  return ((handled_component_p (t))
+	  || tc == ADDR_EXPR || tc == MEM_REF || tc == REALPART_EXPR
+	  || tc == IMAGPART_EXPR || tc == OBJ_TYPE_REF);
+}
+
+/* Basic blocks dictionary BB_DICT returns true if SOURCE index BB
+   corresponds to TARGET.  */
+
+bool
+sem_function::bb_dict_test (int* bb_dict, int source, int target)
+{
+  if (bb_dict[source] == -1)
+    {
+      bb_dict[source] = target;
+      return true;
+    }
+  else
+    return bb_dict[source] == target;
+}
+
+/* Iterates all tree types in T1 and T2 and returns true if all types
+   are compatible. If COMPARE_POLYMORPHIC is set to true,
+   more strict comparison is executed.  */
+
+bool
+sem_function::compare_type_list (tree t1, tree t2, bool compare_polymorphic)
+{
+  tree tv1, tv2;
+  tree_code tc1, tc2;
+
+  if (!t1 && !t2)
+    return true;
+
+  while (t1 != NULL && t2 != NULL)
+    {
+      tv1 = TREE_VALUE (t1);
+      tv2 = TREE_VALUE (t2);
+
+      tc1 = TREE_CODE (tv1);
+      tc2 = TREE_CODE (tv2);
+
+      if (tc1 == NOP_EXPR && tc2 == NOP_EXPR)
+	{}
+      else if (tc1 == NOP_EXPR || tc2 == NOP_EXPR)
+	return false;
+      else if (!func_checker::compatible_types_p (tv1, tv2, compare_polymorphic))
+	return false;
+
+      t1 = TREE_CHAIN (t1);
+      t2 = TREE_CHAIN (t2);
+    }
+
+  return !(t1 || t2);
+}
+
+
+/* Semantic variable constructor that uses STACK as bitmap memory stack.  */
+
+sem_variable::sem_variable (bitmap_obstack *stack): sem_item (VAR, stack)
+{
+}
+
+/*  Constructor based on varpool node _NODE with computed hash _HASH.
+    Bitmap STACK is used for memory allocation.  */
+
+sem_variable::sem_variable (varpool_node *node, hashval_t _hash,
+			    bitmap_obstack *stack): sem_item(VAR,
+				  node, _hash, stack)
+{
+  gcc_checking_assert (node);
+  gcc_checking_assert (get_node ());
+}
+
+/* Returns true if the item equals to ITEM given as argument.  */
+
+bool
+sem_variable::equals (sem_item *item,
+		      hash_map <symtab_node *, sem_item *> & ARG_UNUSED (ignored_nodes))
+{
+  gcc_assert (item->type == VAR);
+
+  sem_variable *v = static_cast<sem_variable *>(item);
+
+  if (!ctor || !v->ctor)
+    return return_false_with_msg ("ctor is missing for semantic variable");
+
+  return sem_variable::equals (ctor, v->ctor);
+}
+
+/* Compares trees T1 and T2 for semantic equality.  */
+
+bool
+sem_variable::equals (tree t1, tree t2)
+{
+  tree_code tc1 = TREE_CODE (t1);
+  tree_code tc2 = TREE_CODE (t2);
+
+  if (tc1 != tc2)
+    return false;
+
+  switch (tc1)
+    {
+    case CONSTRUCTOR:
+      {
+	unsigned len1 = vec_safe_length (CONSTRUCTOR_ELTS (t1));
+	unsigned len2 = vec_safe_length (CONSTRUCTOR_ELTS (t2));
+
+	if (len1 != len2)
+	  return false;
+
+	for (unsigned i = 0; i < len1; i++)
+	  if (!sem_variable::equals (CONSTRUCTOR_ELT (t1, i)->value,
+				     CONSTRUCTOR_ELT (t2, i)->value)
+	      || CONSTRUCTOR_ELT (t1, i)->index != CONSTRUCTOR_ELT (t2, i)->index)
+	    return false;
+
+	return true;
+      }
+    case MEM_REF:
+      {
+	tree x1 = TREE_OPERAND (t1, 0);
+	tree x2 = TREE_OPERAND (t2, 0);
+	tree y1 = TREE_OPERAND (t1, 1);
+	tree y2 = TREE_OPERAND (t2, 1);
+
+	if (!func_checker::compatible_types_p (TREE_TYPE (x1), TREE_TYPE (x2),
+					       true))
+	  return return_false ();
+
+	/* Type of the offset on MEM_REF does not matter.  */
+	return sem_variable::equals (x1, x2)
+	       && wi::to_offset  (y1) == wi::to_offset  (y2);
+      }
+    case NOP_EXPR:
+    case ADDR_EXPR:
+      {
+	tree op1 = TREE_OPERAND (t1, 0);
+	tree op2 = TREE_OPERAND (t2, 0);
+	return sem_variable::equals (op1, op2);
+      }
+    case FUNCTION_DECL:
+    case VAR_DECL:
+    case FIELD_DECL:
+    case LABEL_DECL:
+      return t1 == t2;
+    case INTEGER_CST:
+      return func_checker::compatible_types_p (TREE_TYPE (t1), TREE_TYPE (t2),
+	     true)
+	     && wi::to_offset (t1) == wi::to_offset (t2);
+    case STRING_CST:
+    case REAL_CST:
+    case COMPLEX_CST:
+      return operand_equal_p (t1, t2, OEP_ONLY_CONST);
+    case COMPONENT_REF:
+    case ARRAY_REF:
+    case POINTER_PLUS_EXPR:
+      {
+	tree x1 = TREE_OPERAND (t1, 0);
+	tree x2 = TREE_OPERAND (t2, 0);
+	tree y1 = TREE_OPERAND (t1, 1);
+	tree y2 = TREE_OPERAND (t2, 1);
+
+	return sem_variable::equals (x1, x2) && sem_variable::equals (y1, y2);
+      }
+    case ERROR_MARK:
+      return return_false_with_msg ("ERROR_MARK");
+    default:
+      return return_false_with_msg ("Unknown TREE code reached");
+    }
+}
+
+/* Parser function that visits a varpool NODE.  */
+
+sem_variable *
+sem_variable::parse (varpool_node *node, bitmap_obstack *stack)
+{
+  tree decl = node->decl;
+
+  bool readonly = TYPE_P (decl) ? TYPE_READONLY (decl) : TREE_READONLY (decl);
+  bool can_handle = readonly && (DECL_VIRTUAL_P (decl)
+				 || !TREE_ADDRESSABLE (decl));
+
+  if (!can_handle)
+    return NULL;
+
+  tree ctor = ctor_for_folding (decl);
+  if (!ctor)
+    return NULL;
+
+  sem_variable *v = new sem_variable (node, 0, stack);
+
+  v->init ();
+
+  return v;
+}
+
+/* References independent hash function.  */
+
+hashval_t
+sem_variable::get_hash (void)
+{
+  if (hash)
+    return hash;
+
+  inchash::hash hstate;
+
+  hstate.add_int (456346417);
+  hstate.add_int (TREE_CODE (ctor));
+
+  if (TREE_CODE (ctor) == CONSTRUCTOR)
+    {
+      unsigned length = vec_safe_length (CONSTRUCTOR_ELTS (ctor));
+      hstate.add_int (length);
+    }
+
+  hash = hstate.end ();
+
+  return hash;
+}
+
+/* Merges instance with an ALIAS_ITEM, where alias, thunk or redirection can
+   be applied.  */
+
+bool
+sem_variable::merge (sem_item *alias_item)
+{
+  gcc_assert (alias_item->type == VAR);
+
+  sem_variable *alias_var = static_cast<sem_variable *> (alias_item);
+
+  varpool_node *original = get_node ();
+  varpool_node *alias = alias_var->get_node ();
+  bool original_discardable = false;
+
+  /* See if original is in a section that can be discarded if the main
+     symbol is not used.  */
+  if (DECL_EXTERNAL (original->decl))
+    original_discardable = true;
+  if (original->resolution == LDPR_PREEMPTED_REG
+      || original->resolution == LDPR_PREEMPTED_IR)
+    original_discardable = true;
+  if (original->can_be_discarded_p ())
+    original_discardable = true;
+
+  gcc_assert (!TREE_ASM_WRITTEN (alias->decl));
+
+  if (original_discardable || DECL_EXTERNAL (alias_var->decl) ||
+      !compare_sections (alias_var))
+    {
+      if (dump_file)
+	fprintf (dump_file, "Varpool alias cannot be created\n\n");
+
+      return false;
+    }
+  else
+    {
+      // alias cycle creation check
+      varpool_node *n = original;
+
+      while (n->alias)
+	{
+	  n = n->get_alias_target ();
+	  if (n == alias)
+	    {
+	      if (dump_file)
+		fprintf (dump_file, "Varpool alias cannot be created (alias cycle).\n\n");
+
+	      return false;
+	    }
+	}
+
+      alias->analyzed = false;
+
+      DECL_INITIAL (alias->decl) = NULL;
+      alias->remove_all_references ();
+
+      varpool_node::create_alias (alias_var->decl, decl);
+      alias->resolve_alias (original);
+
+      if (dump_file)
+	fprintf (dump_file, "Varpool alias has been created.\n\n");
+
+      return true;
+    }
+}
+
+bool
+sem_variable::compare_sections (sem_variable *alias)
+{
+  const char *source = node->get_section ();
+  const char *target = alias->node->get_section();
+
+  if (source == NULL && target == NULL)
+    return true;
+  else if(!source || !target)
+    return false;
+  else
+    return strcmp (source, target) == 0;
+}
+
+/* Dump symbol to FILE.  */
+
+void
+sem_variable::dump_to_file (FILE *file)
+{
+  gcc_assert (file);
+
+  print_node (file, "", decl, 0);
+  fprintf (file, "\n\n");
+}
+
+/* Iterates though a constructor and identifies tree references
+   we are interested in semantic function equality.  */
+
+void
+sem_variable::parse_tree_refs (tree t)
+{
+  switch (TREE_CODE (t))
+    {
+    case CONSTRUCTOR:
+      {
+	unsigned length = vec_safe_length (CONSTRUCTOR_ELTS (t));
+
+	for (unsigned i = 0; i < length; i++)
+	  parse_tree_refs(CONSTRUCTOR_ELT (t, i)->value);
+
+	break;
+      }
+    case NOP_EXPR:
+    case ADDR_EXPR:
+      {
+	tree op = TREE_OPERAND (t, 0);
+	parse_tree_refs (op);
+	break;
+      }
+    case FUNCTION_DECL:
+      {
+	tree_refs.safe_push (t);
+	break;
+      }
+    default:
+      break;
+    }
+}
+
+unsigned int sem_item_optimizer::class_id = 0;
+
+sem_item_optimizer::sem_item_optimizer (): worklist (0), m_classes (0),
+  m_classes_count (0), m_cgraph_node_hooks (NULL), m_varpool_node_hooks (NULL)
+{
+  m_items.create (0);
+  bitmap_obstack_initialize (&m_bmstack);
+}
+
+sem_item_optimizer::~sem_item_optimizer ()
+{
+  for (unsigned int i = 0; i < m_items.length (); i++)
+    delete m_items[i];
+
+  for (hash_table<congruence_class_group_hash>::iterator it = m_classes.begin ();
+       it != m_classes.end (); ++it)
+    {
+      for (unsigned int i = 0; i < (*it)->classes.length (); i++)
+	delete (*it)->classes[i];
+
+      (*it)->classes.release ();
+    }
+
+  m_items.release ();
+
+  bitmap_obstack_release (&m_bmstack);
+}
+
+/* Write IPA ICF summary for symbols.  */
+
+void
+sem_item_optimizer::write_summary (void)
+{
+  unsigned int count = 0;
+
+  output_block *ob = create_output_block (LTO_section_ipa_icf);
+  lto_symtab_encoder_t encoder = ob->decl_state->symtab_node_encoder;
+  ob->symbol = NULL;
+
+  /* Calculate number of symbols to be serialized.  */
+  for (lto_symtab_encoder_iterator lsei = lsei_start_in_partition (encoder);
+       !lsei_end_p (lsei);
+       lsei_next_in_partition (&lsei))
+    {
+      symtab_node *node = lsei_node (lsei);
+
+      if (m_symtab_node_map.get (node))
+	count++;
+    }
+
+  streamer_write_uhwi (ob, count);
+
+  /* Process all of the symbols.  */
+  for (lto_symtab_encoder_iterator lsei = lsei_start_in_partition (encoder);
+       !lsei_end_p (lsei);
+       lsei_next_in_partition (&lsei))
+    {
+      symtab_node *node = lsei_node (lsei);
+
+      sem_item **item = m_symtab_node_map.get (node);
+
+      if (item && *item)
+	{
+	  int node_ref = lto_symtab_encoder_encode (encoder, node);
+	  streamer_write_uhwi_stream (ob->main_stream, node_ref);
+
+	  streamer_write_uhwi (ob, (*item)->get_hash ());
+	}
+    }
+
+  streamer_write_char_stream (ob->main_stream, 0);
+  produce_asm (ob, NULL);
+  destroy_output_block (ob);
+}
+
+/* Reads a section from LTO stream file FILE_DATA. Input block for DATA
+   contains LEN bytes.  */
+
+void
+sem_item_optimizer::read_section (lto_file_decl_data *file_data,
+				  const char *data, size_t len)
+{
+  const lto_function_header *header =
+    (const lto_function_header *) data;
+  const int cfg_offset = sizeof (lto_function_header);
+  const int main_offset = cfg_offset + header->cfg_size;
+  const int string_offset = main_offset + header->main_size;
+  data_in *data_in;
+  unsigned int i;
+  unsigned int count;
+
+  lto_input_block ib_main ((const char *) data + main_offset, 0,
+			   header->main_size);
+
+  data_in =
+    lto_data_in_create (file_data, (const char *) data + string_offset,
+			header->string_size, vNULL);
+
+  count = streamer_read_uhwi (&ib_main);
+
+  for (i = 0; i < count; i++)
+    {
+      unsigned int index;
+      symtab_node *node;
+      lto_symtab_encoder_t encoder;
+
+      index = streamer_read_uhwi (&ib_main);
+      encoder = file_data->symtab_node_encoder;
+      node = lto_symtab_encoder_deref (encoder, index);
+
+      hashval_t hash = streamer_read_uhwi (&ib_main);
+
+      gcc_assert (node->definition);
+
+      if (dump_file)
+	fprintf (dump_file, "Symbol added:%s (tree: %p, uid:%u)\n", node->asm_name (),
+		 (void *) node->decl, node->order);
+
+      if (is_a<cgraph_node *> (node))
+	{
+	  cgraph_node *cnode = dyn_cast <cgraph_node *> (node);
+
+	  m_items.safe_push (new sem_function (cnode, hash, &m_bmstack));
+	}
+      else
+	{
+	  varpool_node *vnode = dyn_cast <varpool_node *> (node);
+
+	  m_items.safe_push (new sem_variable (vnode, hash, &m_bmstack));
+	}
+    }
+
+  lto_free_section_data (file_data, LTO_section_ipa_icf, NULL, data,
+			 len);
+  lto_data_in_delete (data_in);
+}
+
+/* Read IPA IPA ICF summary for symbols.  */
+
+void
+sem_item_optimizer::read_summary (void)
+{
+  lto_file_decl_data **file_data_vec = lto_get_file_decl_data ();
+  lto_file_decl_data *file_data;
+  unsigned int j = 0;
+
+  while ((file_data = file_data_vec[j++]))
+    {
+      size_t len;
+      const char *data = lto_get_section_data (file_data,
+			 LTO_section_ipa_icf, NULL, &len);
+
+      if (data)
+	read_section (file_data, data, len);
+    }
+}
+
+/* Register callgraph and varpool hooks.  */
+
+void
+sem_item_optimizer::register_hooks (void)
+{
+  m_cgraph_node_hooks = symtab->add_cgraph_removal_hook
+			(&sem_item_optimizer::cgraph_removal_hook, this);
+
+  m_varpool_node_hooks = symtab->add_varpool_removal_hook
+			 (&sem_item_optimizer::varpool_removal_hook, this);
+}
+
+/* Unregister callgraph and varpool hooks.  */
+
+void
+sem_item_optimizer::unregister_hooks (void)
+{
+  if (m_cgraph_node_hooks)
+    symtab->remove_cgraph_removal_hook (m_cgraph_node_hooks);
+
+  if (m_varpool_node_hooks)
+    symtab->remove_varpool_removal_hook (m_varpool_node_hooks);
+}
+
+/* Adds a CLS to hashtable associated by hash value.  */
+
+void
+sem_item_optimizer::add_class (congruence_class *cls)
+{
+  gcc_assert (cls->members.length ());
+
+  congruence_class_group *group = get_group_by_hash (
+				    cls->members[0]->get_hash (),
+				    cls->members[0]->type);
+  group->classes.safe_push (cls);
+}
+
+/* Gets a congruence class group based on given HASH value and TYPE.  */
+
+congruence_class_group *
+sem_item_optimizer::get_group_by_hash (hashval_t hash, sem_item_type type)
+{
+  congruence_class_group *item = XNEW (congruence_class_group);
+  item->hash = hash;
+  item->type = type;
+
+  congruence_class_group **slot = m_classes.find_slot (item, INSERT);
+
+  if (*slot)
+    free (item);
+  else
+    {
+      item->classes.create (1);
+      *slot = item;
+    }
+
+  return *slot;
+}
+
+/* Callgraph removal hook called for a NODE with a custom DATA.  */
+
+void
+sem_item_optimizer::cgraph_removal_hook (cgraph_node *node, void *data)
+{
+  sem_item_optimizer *optimizer = (sem_item_optimizer *) data;
+  optimizer->remove_symtab_node (node);
+}
+
+/* Varpool removal hook called for a NODE with a custom DATA.  */
+
+void
+sem_item_optimizer::varpool_removal_hook (varpool_node *node, void *data)
+{
+  sem_item_optimizer *optimizer = (sem_item_optimizer *) data;
+  optimizer->remove_symtab_node (node);
+}
+
+/* Remove symtab NODE triggered by symtab removal hooks.  */
+
+void
+sem_item_optimizer::remove_symtab_node (symtab_node *node)
+{
+  gcc_assert (!m_classes.elements());
+
+  m_removed_items_set.add (node);
+}
+
+void
+sem_item_optimizer::remove_item (sem_item *item)
+{
+  if (m_symtab_node_map.get (item->node))
+    m_symtab_node_map.remove (item->node);
+  delete item;
+}
+
+/* Removes all callgraph and varpool nodes that are marked by symtab
+   as deleted.  */
+
+void
+sem_item_optimizer::filter_removed_items (void)
+{
+  auto_vec <sem_item *> filtered;
+
+  for (unsigned int i = 0; i < m_items.length(); i++)
+    {
+      sem_item *item = m_items[i];
+
+      if (!flag_ipa_icf_functions && item->type == FUNC)
+	{
+	  remove_item (item);
+	  continue;
+	}
+
+      if (!flag_ipa_icf_variables && item->type == VAR)
+	{
+	  remove_item (item);
+	  continue;
+	}
+
+      bool no_body_function = false;
+
+      if (item->type == FUNC)
+	{
+	  cgraph_node *cnode = static_cast <sem_function *>(item)->get_node ();
+
+	  no_body_function = in_lto_p && (cnode->alias || cnode->body_removed);
+	}
+
+      if(!m_removed_items_set.contains (m_items[i]->node)
+	  && !no_body_function)
+	{
+	  if (item->type == VAR || (!DECL_CXX_CONSTRUCTOR_P (item->decl)
+				    && !DECL_CXX_DESTRUCTOR_P (item->decl)))
+	    {
+	      filtered.safe_push (m_items[i]);
+	      continue;
+	    }
+	}
+
+      remove_item (item);
+    }
+
+  /* Clean-up of released semantic items.  */
+
+  m_items.release ();
+  for (unsigned int i = 0; i < filtered.length(); i++)
+    m_items.safe_push (filtered[i]);
+}
+
+/* Optimizer entry point.  */
+
+void
+sem_item_optimizer::execute (void)
+{
+  filter_removed_items ();
+  build_hash_based_classes ();
+
+  if (dump_file)
+    fprintf (dump_file, "Dump after hash based groups\n");
+  dump_cong_classes ();
+
+  for (unsigned int i = 0; i < m_items.length(); i++)
+    m_items[i]->init_wpa ();
+
+  build_graph ();
+
+  subdivide_classes_by_equality (true);
+
+  if (dump_file)
+    fprintf (dump_file, "Dump after WPA based types groups\n");
+
+  dump_cong_classes ();
+
+  process_cong_reduction ();
+  verify_classes ();
+
+  if (dump_file)
+    fprintf (dump_file, "Dump after callgraph-based congruence reduction\n");
+
+  dump_cong_classes ();
+
+  parse_nonsingleton_classes ();
+  subdivide_classes_by_equality ();
+
+  if (dump_file)
+    fprintf (dump_file, "Dump after full equality comparison of groups\n");
+
+  dump_cong_classes ();
+
+  unsigned int prev_class_count = m_classes_count;
+
+  process_cong_reduction ();
+  dump_cong_classes ();
+  verify_classes ();
+  merge_classes (prev_class_count);
+
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    symtab_node::dump_table (dump_file);
+}
+
+/* Function responsible for visiting all potential functions and
+   read-only variables that can be merged.  */
+
+void
+sem_item_optimizer::parse_funcs_and_vars (void)
+{
+  cgraph_node *cnode;
+
+  if (flag_ipa_icf_functions)
+    FOR_EACH_DEFINED_FUNCTION (cnode)
+    {
+      sem_function *f = sem_function::parse (cnode, &m_bmstack);
+      if (f)
+	{
+	  m_items.safe_push (f);
+	  m_symtab_node_map.put (cnode, f);
+
+	  if (dump_file)
+	    fprintf (dump_file, "Parsed function:%s\n", f->asm_name ());
+
+	  if (dump_file && (dump_flags & TDF_DETAILS))
+	    f->dump_to_file (dump_file);
+	}
+      else if (dump_file)
+	fprintf (dump_file, "Not parsed function:%s\n", cnode->asm_name ());
+    }
+
+  varpool_node *vnode;
+
+  if (flag_ipa_icf_variables)
+    FOR_EACH_DEFINED_VARIABLE (vnode)
+    {
+      sem_variable *v = sem_variable::parse (vnode, &m_bmstack);
+
+      if (v)
+	{
+	  m_items.safe_push (v);
+	  m_symtab_node_map.put (vnode, v);
+	}
+    }
+}
+
+/* Makes pairing between a congruence class CLS and semantic ITEM.  */
+
+void
+sem_item_optimizer::add_item_to_class (congruence_class *cls, sem_item *item)
+{
+  item->index_in_class = cls->members.length ();
+  cls->members.safe_push (item);
+  item->cls = cls;
+}
+
+/* Congruence classes are built by hash value.  */
+
+void
+sem_item_optimizer::build_hash_based_classes (void)
+{
+  for (unsigned i = 0; i < m_items.length (); i++)
+    {
+      sem_item *item = m_items[i];
+
+      congruence_class_group *group = get_group_by_hash (item->get_hash (),
+				      item->type);
+
+      if (!group->classes.length ())
+	{
+	  m_classes_count++;
+	  group->classes.safe_push (new congruence_class (class_id++));
+	}
+
+      add_item_to_class (group->classes[0], item);
+    }
+}
+
+/* Build references according to call graph.  */
+
+void
+sem_item_optimizer::build_graph (void)
+{
+  for (unsigned i = 0; i < m_items.length (); i++)
+    {
+      sem_item *item = m_items[i];
+      m_symtab_node_map.put (item->node, item);
+    }
+
+  for (unsigned i = 0; i < m_items.length (); i++)
+    {
+      sem_item *item = m_items[i];
+
+      if (item->type == FUNC)
+	{
+	  cgraph_node *cnode = dyn_cast <cgraph_node *> (item->node);
+
+	  cgraph_edge *e = cnode->callees;
+	  while (e)
+	    {
+	      sem_item **slot = m_symtab_node_map.get (e->callee);
+	      if (slot)
+		item->add_reference (*slot);
+
+	      e = e->next_callee;
+	    }
+	}
+
+      ipa_ref *ref = NULL;
+      for (unsigned i = 0; item->node->iterate_reference (i, ref); i++)
+	{
+	  sem_item **slot = m_symtab_node_map.get (ref->referred);
+	  if (slot)
+	    item->add_reference (*slot);
+	}
+    }
+}
+
+/* Semantic items in classes having more than one element and initialized.
+   In case of WPA, we load function body.  */
+
+void
+sem_item_optimizer::parse_nonsingleton_classes (void)
+{
+  unsigned int init_called_count = 0;
+
+  for (unsigned i = 0; i < m_items.length (); i++)
+    if (m_items[i]->cls->members.length () > 1)
+      {
+	m_items[i]->init ();
+	init_called_count++;
+      }
+
+  if (dump_file)
+    fprintf (dump_file, "Init called for %u items (%.2f%%).\n", init_called_count,
+	     100.0f * init_called_count / m_items.length ());
+}
+
+/* Equality function for semantic items is used to subdivide existing
+   classes. If IN_WPA, fast equality function is invoked.  */
+
+void
+sem_item_optimizer::subdivide_classes_by_equality (bool in_wpa)
+{
+  for (hash_table <congruence_class_group_hash>::iterator it = m_classes.begin ();
+       it != m_classes.end (); ++it)
+    {
+      unsigned int class_count = (*it)->classes.length ();
+
+      for (unsigned i = 0; i < class_count; i++)
+	{
+	  congruence_class *c = (*it)->classes [i];
+
+	  if (c->members.length() > 1)
+	    {
+	      auto_vec <sem_item *> new_vector;
+
+	      sem_item *first = c->members[0];
+	      new_vector.safe_push (first);
+
+	      unsigned class_split_first = (*it)->classes.length ();
+
+	      for (unsigned j = 1; j < c->members.length (); j++)
+		{
+		  sem_item *item = c->members[j];
+
+		  bool equals = in_wpa ? first->equals_wpa (item,
+				m_symtab_node_map) : first->equals (item, m_symtab_node_map);
+
+		  if (equals)
+		    new_vector.safe_push (item);
+		  else
+		    {
+		      bool integrated = false;
+
+		      for (unsigned k = class_split_first; k < (*it)->classes.length (); k++)
+			{
+			  sem_item *x = (*it)->classes[k]->members[0];
+			  bool equals = in_wpa ? x->equals_wpa (item,
+								m_symtab_node_map) : x->equals (item, m_symtab_node_map);
+
+			  if (equals)
+			    {
+			      integrated = true;
+			      add_item_to_class ((*it)->classes[k], item);
+
+			      break;
+			    }
+			}
+
+		      if (!integrated)
+			{
+			  congruence_class *c = new congruence_class (class_id++);
+			  m_classes_count++;
+			  add_item_to_class (c, item);
+
+			  (*it)->classes.safe_push (c);
+			}
+		    }
+		}
+
+	      // we replace newly created new_vector for the class we've just splitted
+	      c->members.release ();
+	      c->members.create (new_vector.length ());
+
+	      for (unsigned int j = 0; j < new_vector.length (); j++)
+		add_item_to_class (c, new_vector[j]);
+	    }
+	}
+    }
+
+  verify_classes ();
+}
+
+/* Verify congruence classes if checking is enabled.  */
+
+void
+sem_item_optimizer::verify_classes (void)
+{
+#if ENABLE_CHECKING
+  for (hash_table <congruence_class_group_hash>::iterator it = m_classes.begin ();
+       it != m_classes.end (); ++it)
+    {
+      for (unsigned int i = 0; i < (*it)->classes.length (); i++)
+	{
+	  congruence_class *cls = (*it)->classes[i];
+
+	  gcc_checking_assert (cls);
+	  gcc_checking_assert (cls->members.length () > 0);
+
+	  for (unsigned int j = 0; j < cls->members.length (); j++)
+	    {
+	      sem_item *item = cls->members[j];
+
+	      gcc_checking_assert (item);
+	      gcc_checking_assert (item->cls == cls);
+
+	      for (unsigned k = 0; k < item->usages.length (); k++)
+		{
+		  sem_usage_pair *usage = item->usages[k];
+		  gcc_checking_assert (usage->item->index_in_class <
+				       usage->item->cls->members.length ());
+		}
+	    }
+	}
+    }
+#endif
+}
+
+/* Disposes split map traverse function. CLS_PTR is pointer to congruence
+   class, BSLOT is bitmap slot we want to release. DATA is mandatory,
+   but unused argument.  */
+
+bool
+sem_item_optimizer::release_split_map (congruence_class * const &,
+				       bitmap const &b, traverse_split_pair *)
+{
+  bitmap bmp = b;
+
+  BITMAP_FREE (bmp);
+
+  return true;
+}
+
+/* Process split operation for a class given as pointer CLS_PTR,
+   where bitmap B splits congruence class members. DATA is used
+   as argument of split pair.  */
+
+bool
+sem_item_optimizer::traverse_congruence_split (congruence_class * const &cls,
+    bitmap const &b, traverse_split_pair *pair)
+{
+  sem_item_optimizer *optimizer = pair->optimizer;
+  const congruence_class *splitter_cls = pair->cls;
+
+  /* If counted bits are greater than zero and less than the number of members
+     a group will be splitted.  */
+  unsigned popcount = bitmap_count_bits (b);
+
+  if (popcount > 0 && popcount < cls->members.length ())
+    {
+      congruence_class* newclasses[2] = { new congruence_class (class_id++), new congruence_class (class_id++) };
+
+      for (unsigned int i = 0; i < cls->members.length (); i++)
+	{
+	  int target = bitmap_bit_p (b, i);
+	  congruence_class *tc = newclasses[target];
+
+	  add_item_to_class (tc, cls->members[i]);
+	}
+
+#ifdef ENABLE_CHECKING
+      for (unsigned int i = 0; i < 2; i++)
+	gcc_checking_assert (newclasses[i]->members.length ());
+#endif
+
+      if (splitter_cls == cls)
+	optimizer->splitter_class_removed = true;
+
+      /* Remove old class from worklist if presented.  */
+      bool in_worklist = cls->in_worklist;
+
+      if (in_worklist)
+	cls->in_worklist = false;
+
+      congruence_class_group g;
+      g.hash = cls->members[0]->get_hash ();
+      g.type = cls->members[0]->type;
+
+      congruence_class_group *slot = optimizer->m_classes.find(&g);
+
+      for (unsigned int i = 0; i < slot->classes.length (); i++)
+	if (slot->classes[i] == cls)
+	  {
+	    slot->classes.ordered_remove (i);
+	    break;
+	  }
+
+      /* New class will be inserted and integrated to work list.  */
+      for (unsigned int i = 0; i < 2; i++)
+	optimizer->add_class (newclasses[i]);
+
+      /* Two classes replace one, so that increment just by one.  */
+      optimizer->m_classes_count++;
+
+      /* If OLD class was presented in the worklist, we remove the class
+         and replace it will both newly created classes.  */
+      if (in_worklist)
+	for (unsigned int i = 0; i < 2; i++)
+	  optimizer->worklist_push (newclasses[i]);
+      else /* Just smaller class is inserted.  */
+	{
+	  unsigned int smaller_index = newclasses[0]->members.length () <
+				       newclasses[1]->members.length () ?
+				       0 : 1;
+	  optimizer->worklist_push (newclasses[smaller_index]);
+	}
+
+      if (dump_file && (dump_flags & TDF_DETAILS))
+	{
+	  fprintf (dump_file, "  congruence class splitted:\n");
+	  cls->dump (dump_file, 4);
+
+	  fprintf (dump_file, "  newly created groups:\n");
+	  for (unsigned int i = 0; i < 2; i++)
+	    newclasses[i]->dump (dump_file, 4);
+	}
+
+      /* Release class if not presented in work list.  */
+      if (!in_worklist)
+	delete cls;
+    }
+
+
+  return true;
+}
+
+/* Tests if a class CLS used as INDEXth splits any congruence classes.
+   Bitmap stack BMSTACK is used for bitmap allocation.  */
+
+void
+sem_item_optimizer::do_congruence_step_for_index (congruence_class *cls,
+    unsigned int index)
+{
+  hash_map <congruence_class *, bitmap> split_map;
+
+  for (unsigned int i = 0; i < cls->members.length (); i++)
+    {
+      sem_item *item = cls->members[i];
+
+      /* Iterate all usages that have INDEX as usage of the item.  */
+      for (unsigned int j = 0; j < item->usages.length (); j++)
+	{
+	  sem_usage_pair *usage = item->usages[j];
+
+	  if (usage->index != index)
+	    continue;
+
+	  bitmap *slot = split_map.get (usage->item->cls);
+	  bitmap b;
+
+	  if(!slot)
+	    {
+	      b = BITMAP_ALLOC (&m_bmstack);
+	      split_map.put (usage->item->cls, b);
+	    }
+	  else
+	    b = *slot;
+
+#if ENABLE_CHECKING
+	  gcc_checking_assert (usage->item->cls);
+	  gcc_checking_assert (usage->item->index_in_class <
+			       usage->item->cls->members.length ());
+#endif
+
+	  bitmap_set_bit (b, usage->item->index_in_class);
+	}
+    }
+
+  traverse_split_pair pair;
+  pair.optimizer = this;
+  pair.cls = cls;
+
+  splitter_class_removed = false;
+  split_map.traverse
+  <traverse_split_pair *, sem_item_optimizer::traverse_congruence_split> (&pair);
+
+  /* Bitmap clean-up.  */
+  split_map.traverse
+  <traverse_split_pair *, sem_item_optimizer::release_split_map> (NULL);
+}
+
+/* Every usage of a congruence class CLS is a candidate that can split the
+   collection of classes. Bitmap stack BMSTACK is used for bitmap
+   allocation.  */
+
+void
+sem_item_optimizer::do_congruence_step (congruence_class *cls)
+{
+  bitmap_iterator bi;
+  unsigned int i;
+
+  bitmap usage = BITMAP_ALLOC (&m_bmstack);
+
+  for (unsigned int i = 0; i < cls->members.length (); i++)
+    bitmap_ior_into (usage, cls->members[i]->usage_index_bitmap);
+
+  EXECUTE_IF_SET_IN_BITMAP (usage, 0, i, bi)
+  {
+    if (dump_file && (dump_flags & TDF_DETAILS))
+      fprintf (dump_file, "  processing congruece step for class: %u, index: %u\n",
+	       cls->id, i);
+
+    do_congruence_step_for_index (cls, i);
+
+    if (splitter_class_removed)
+      break;
+  }
+
+  BITMAP_FREE (usage);
+}
+
+/* Adds a newly created congruence class CLS to worklist.  */
+
+void
+sem_item_optimizer::worklist_push (congruence_class *cls)
+{
+  /* Return if the class CLS is already presented in work list.  */
+  if (cls->in_worklist)
+    return;
+
+  cls->in_worklist = true;
+  worklist.push_back (cls);
+}
+
+/* Pops a class from worklist. */
+
+congruence_class *
+sem_item_optimizer::worklist_pop (void)
+{
+  congruence_class *cls;
+
+  while (!worklist.empty ())
+    {
+      cls = worklist.front ();
+      worklist.pop_front ();
+      if (cls->in_worklist)
+	{
+	  cls->in_worklist = false;
+
+	  return cls;
+	}
+      else
+	{
+	  /* Work list item was already intended to be removed.
+	     The only reason for doing it is to split a class.
+	     Thus, the class CLS is deleted.  */
+	  delete cls;
+	}
+    }
+
+  return NULL;
+}
+
+/* Iterative congruence reduction function.  */
+
+void
+sem_item_optimizer::process_cong_reduction (void)
+{
+  for (hash_table<congruence_class_group_hash>::iterator it = m_classes.begin ();
+       it != m_classes.end (); ++it)
+    for (unsigned i = 0; i < (*it)->classes.length (); i++)
+      if ((*it)->classes[i]->is_class_used ())
+	worklist_push ((*it)->classes[i]);
+
+  if (dump_file)
+    fprintf (dump_file, "Worklist has been filled with: %lu\n",
+	     worklist.size ());
+
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    fprintf (dump_file, "Congruence class reduction\n");
+
+  congruence_class *cls;
+  while ((cls = worklist_pop ()) != NULL)
+    do_congruence_step (cls);
+}
+
+/* Debug function prints all informations about congruence classes.  */
+
+void
+sem_item_optimizer::dump_cong_classes (void)
+{
+  if (!dump_file)
+    return;
+
+  fprintf (dump_file,
+	   "Congruence classes: %u (unique hash values: %lu), with total: %u items\n",
+	   m_classes_count, m_classes.elements(), m_items.length ());
+
+  /* Histogram calculation.  */
+  unsigned int max_index = 0;
+  unsigned int* histogram = XCNEWVEC (unsigned int, m_items.length () + 1);
+
+  for (hash_table<congruence_class_group_hash>::iterator it = m_classes.begin ();
+       it != m_classes.end (); ++it)
+
+    for (unsigned i = 0; i < (*it)->classes.length (); i++)
+      {
+	unsigned int c = (*it)->classes[i]->members.length ();
+	histogram[c]++;
+
+	if (c > max_index)
+	  max_index = c;
+      }
+
+  fprintf (dump_file,
+	   "Class size histogram [num of members]: number of classe number of classess\n");
+
+  for (unsigned int i = 0; i <= max_index; i++)
+    if (histogram[i])
+      fprintf (dump_file, "[%u]: %u classes\n", i, histogram[i]);
+
+  fprintf (dump_file, "\n\n");
+
+
+  if (dump_flags & TDF_DETAILS)
+    for (hash_table<congruence_class_group_hash>::iterator it = m_classes.begin ();
+	 it != m_classes.end (); ++it)
+      {
+	fprintf (dump_file, "  group: with %u classes:\n", (*it)->classes.length ());
+
+	for (unsigned i = 0; i < (*it)->classes.length (); i++)
+	  {
+	    (*it)->classes[i]->dump (dump_file, 4);
+
+	    if(i < (*it)->classes.length () - 1)
+	      fprintf (dump_file, " ");
+	  }
+      }
+
+  free (histogram);
+}
+
+/* After reduction is done, we can declare all items in a group
+   to be equal. PREV_CLASS_COUNT is start number of classes
+   before reduction.  */
+
+void
+sem_item_optimizer::merge_classes (unsigned int prev_class_count)
+{
+  unsigned int item_count = m_items.length ();
+  unsigned int class_count = m_classes_count;
+  unsigned int equal_items = item_count - class_count;
+
+  unsigned int non_singular_classes_count = 0;
+  unsigned int non_singular_classes_sum = 0;
+
+  for (hash_table<congruence_class_group_hash>::iterator it = m_classes.begin ();
+       it != m_classes.end (); ++it)
+    for (unsigned int i = 0; i < (*it)->classes.length (); i++)
+      {
+	congruence_class *c = (*it)->classes[i];
+	if (c->members.length () > 1)
+	{
+	  non_singular_classes_count++;
+	  non_singular_classes_sum += c->members.length ();
+	}
+      }
+
+  if (dump_file)
+    {
+      fprintf (dump_file, "\nItem count: %u\n", item_count);
+      fprintf (dump_file, "Congruent classes before: %u, after: %u\n",
+	       prev_class_count, class_count);
+      fprintf (dump_file, "Average class size before: %.2f, after: %.2f\n",
+	       1.0f * item_count / prev_class_count,
+	       1.0f * item_count / class_count);
+      fprintf (dump_file, "Average non-singular class size: %.2f, count: %u\n",
+	       1.0f * non_singular_classes_sum / non_singular_classes_count,
+	       non_singular_classes_count);
+      fprintf (dump_file, "Equal symbols: %u\n", equal_items);
+      fprintf (dump_file, "Fraction of visited symbols: %.2f%%\n\n",
+	       100.0f * equal_items / item_count);
+    }
+
+  for (hash_table<congruence_class_group_hash>::iterator it = m_classes.begin ();
+       it != m_classes.end (); ++it)
+    for (unsigned int i = 0; i < (*it)->classes.length (); i++)
+      {
+	congruence_class *c = (*it)->classes[i];
+
+	if (c->members.length () == 1)
+	  continue;
+
+	gcc_assert (c->members.length ());
+
+	sem_item *source = c->members[0];
+
+	for (unsigned int j = 1; j < c->members.length (); j++)
+	  {
+	    sem_item *alias = c->members[j];
+	    source->equals (alias, m_symtab_node_map);
+
+	    if (dump_file)
+	      {
+		fprintf (dump_file, "Semantic equality hit:%s->%s\n",
+			 source->name (), alias->name ());
+		fprintf (dump_file, "Assembler symbol names:%s->%s\n",
+			 source->asm_name (), alias->asm_name ());
+	      }
+
+	    if (dump_file && (dump_flags & TDF_DETAILS))
+	      {
+		source->dump_to_file (dump_file);
+		alias->dump_to_file (dump_file);
+	      }
+
+	    source->merge (alias);
+	  }
+      }
+}
+
+/* Dump function prints all class members to a FILE with an INDENT.  */
+
+void
+congruence_class::dump (FILE *file, unsigned int indent) const
+{
+  FPRINTF_SPACES (file, indent, "class with id: %u, hash: %u, items: %u\n",
+		  id, members[0]->get_hash (), members.length ());
+
+  FPUTS_SPACES (file, indent + 2, "");
+  for (unsigned i = 0; i < members.length (); i++)
+    fprintf (file, "%s(%p/%u) ", members[i]->asm_name (), (void *) members[i]->decl,
+	     members[i]->node->order);
+
+  fprintf (file, "\n");
+}
+
+/* Returns true if there's a member that is used from another group.  */
+
+bool
+congruence_class::is_class_used (void)
+{
+  for (unsigned int i = 0; i < members.length (); i++)
+    if (members[i]->usages.length ())
+      return true;
+
+  return false;
+}
+
+/* Initialization and computation of symtab node hash, there data
+   are propagated later on.  */
+
+static sem_item_optimizer *optimizer = NULL;
+
+/* Generate pass summary for IPA ICF pass.  */
+
+static void
+ipa_icf_generate_summary (void)
+{
+  if (!optimizer)
+    optimizer = new sem_item_optimizer ();
+
+  optimizer->parse_funcs_and_vars ();
+}
+
+/* Write pass summary for IPA ICF pass.  */
+
+static void
+ipa_icf_write_summary (void)
+{
+  gcc_assert (optimizer);
+
+  optimizer->write_summary ();
+}
+
+/* Read pass summary for IPA ICF pass.  */
+
+static void
+ipa_icf_read_summary (void)
+{
+  if (!optimizer)
+    optimizer = new sem_item_optimizer ();
+
+  optimizer->read_summary ();
+  optimizer->register_hooks ();
+}
+
+/* Semantic equality exection function.  */
+
+static unsigned int
+ipa_icf_driver (void)
+{
+  gcc_assert (optimizer);
+
+  optimizer->execute ();
+  optimizer->unregister_hooks ();
+
+  delete optimizer;
+
+  return 0;
+}
+
+const pass_data pass_data_ipa_icf =
+{
+  IPA_PASS,		    /* type */
+  "icf",		    /* name */
+  OPTGROUP_IPA,             /* optinfo_flags */
+  TV_IPA_ICF,		    /* tv_id */
+  0,                        /* properties_required */
+  0,                        /* properties_provided */
+  0,                        /* properties_destroyed */
+  0,                        /* todo_flags_start */
+  0,                        /* todo_flags_finish */
+};
+
+class pass_ipa_icf : public ipa_opt_pass_d
+{
+public:
+  pass_ipa_icf (gcc::context *ctxt)
+    : ipa_opt_pass_d (pass_data_ipa_icf, ctxt,
+		      ipa_icf_generate_summary, /* generate_summary */
+		      ipa_icf_write_summary, /* write_summary */
+		      ipa_icf_read_summary, /* read_summary */
+		      NULL, /*
+		      write_optimization_summary */
+		      NULL, /*
+		      read_optimization_summary */
+		      NULL, /* stmt_fixup */
+		      0, /* function_transform_todo_flags_start */
+		      NULL, /* function_transform */
+		      NULL) /* variable_transform */
+  {}
+
+  /* opt_pass methods: */
+  virtual bool gate (function *)
+  {
+    return flag_ipa_icf_variables || flag_ipa_icf_functions;
+  }
+
+  virtual unsigned int execute (function *)
+  {
+    return ipa_icf_driver();
+  }
+}; // class pass_ipa_icf
+
+} // ipa_icf namespace
+
+ipa_opt_pass_d *
+make_pass_ipa_icf (gcc::context *ctxt)
+{
+  return new ipa_icf::pass_ipa_icf (ctxt);
+}
diff --git a/gcc/ipa-icf.h b/gcc/ipa-icf.h
new file mode 100644
index 0000000..d8e7b16
--- /dev/null
+++ b/gcc/ipa-icf.h
@@ -0,0 +1,553 @@
+/* Interprocedural semantic function equality pass
+   Copyright (C) 2014 Free Software Foundation, Inc.
+
+   Contributed by Jan Hubicka <hubicka@ucw.cz> and Martin Liska <mliska@suse.cz>
+
+This file is part of GCC.
+
+GCC 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, or (at your option) any later
+version.
+
+GCC 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 GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+namespace ipa_icf {
+class sem_item;
+
+/* Congruence class encompasses a collection of either functions or
+   read-only variables. These items are considered to be equivalent
+   if not proved the oposite.  */
+class congruence_class
+{
+public:
+  /* Congruence class constructor for a new class with _ID.  */
+  congruence_class (unsigned int _id): in_worklist (false), id(_id)
+  {
+  }
+
+  /* Destructor.  */
+  ~congruence_class ()
+  {
+  }
+
+  /* Dump function prints all class members to a FILE with an INDENT.  */
+  void dump (FILE *file, unsigned int indent = 0) const;
+
+  /* Returns true if there's a member that is used from another group.  */
+  bool is_class_used (void);
+
+  /* Flag is used in case we want to remove a class from worklist and
+     delete operation is quite expensive for
+     the data structure (linked list).  */
+  bool in_worklist;
+
+  /* Vector of all group members.  */
+  auto_vec <sem_item *> members;
+
+  /* Global unique class identifier.  */
+  unsigned int id;
+};
+
+/* Semantic item type enum.  */
+enum sem_item_type
+{
+  FUNC,
+  VAR
+};
+
+/* Semantic item usage pair.  */
+class sem_usage_pair
+{
+public:
+  /* Constructor for key value pair, where _ITEM is key and _INDEX is a target.  */
+  sem_usage_pair (sem_item *_item, unsigned int _index);
+
+  /* Target semantic item where an item is used.  */
+  sem_item *item;
+
+  /* Index of usage of such an item.  */
+  unsigned int index;
+};
+
+/* Semantic item is a base class that encapsulates all shared functionality
+   for both semantic function and variable items.  */
+class sem_item
+{
+public:
+  /* Semantic item constructor for a node of _TYPE, where STACK is used
+     for bitmap memory allocation.  */
+  sem_item (sem_item_type _type, bitmap_obstack *stack);
+
+  /* Semantic item constructor for a node of _TYPE, where STACK is used
+     for bitmap memory allocation. The item is based on symtab node _NODE
+     with computed _HASH.  */
+  sem_item (sem_item_type _type, symtab_node *_node, hashval_t _hash,
+	    bitmap_obstack *stack);
+
+  virtual ~sem_item ();
+
+  /* Dump function for debugging purpose.  */
+  DEBUG_FUNCTION void dump (void);
+
+  /* Initialize semantic item by info reachable during LTO WPA phase.  */
+  virtual void init_wpa (void) = 0;
+
+  /* Semantic item initialization function.  */
+  virtual void init (void) = 0;
+
+  /* Add reference to a semantic TARGET.  */
+  void add_reference (sem_item *target);
+
+  /* Gets symbol name of the item.  */
+  const char *name (void)
+  {
+    return node->name ();
+  }
+
+  /* Gets assembler name of the item.  */
+  const char *asm_name (void)
+  {
+    return node->asm_name ();
+  }
+
+  /* Fast equality function based on knowledge known in WPA.  */
+  virtual bool equals_wpa (sem_item *item,
+			   hash_map <symtab_node *, sem_item *> &ignored_nodes) = 0;
+
+  /* Returns true if the item equals to ITEM given as arguemnt.  */
+  virtual bool equals (sem_item *item,
+		       hash_map <symtab_node *, sem_item *> &ignored_nodes) = 0;
+
+  /* References independent hash function.  */
+  virtual hashval_t get_hash (void) = 0;
+
+  /* Merges instance with an ALIAS_ITEM, where alias, thunk or redirection can
+     be applied.  */
+  virtual bool merge (sem_item *alias_item) = 0;
+
+  /* Dump symbol to FILE.  */
+  virtual void dump_to_file (FILE *file) = 0;
+
+  /* Return base tree that can be used for compatible_types_p and
+     contains_polymorphic_type_p comparison.  */
+
+  static bool get_base_types (tree *t1, tree *t2);
+
+  /* Item type.  */
+  sem_item_type type;
+
+  /* Symtab node.  */
+  symtab_node *node;
+
+  /* Declaration tree node.  */
+  tree decl;
+
+  /* Semantic references used that generate congruence groups.  */
+  vec <sem_item *> refs;
+
+  /* Pointer to a congruence class the item belongs to.  */
+  congruence_class *cls;
+
+  /* Index of the item in a class belonging to.  */
+  unsigned int index_in_class;
+
+  /* List of semantic items where the instance is used.  */
+  vec <sem_usage_pair *> usages;
+
+  /* A bitmap with indices of all classes referencing this item.  */
+  bitmap usage_index_bitmap;
+
+  /* List of tree references (either FUNC_DECL or VAR_DECL).  */
+  vec <tree> tree_refs;
+
+  /* A set with symbol table references.  */
+  hash_set <symtab_node *> refs_set;
+
+protected:
+  /* Cached, once calculated hash for the item.  */
+  hashval_t hash;
+
+private:
+  /* Initialize internal data structures. Bitmap STACK is used for
+     bitmap memory allocation process.  */
+  void setup (bitmap_obstack *stack);
+}; // class sem_item
+
+class sem_function: public sem_item
+{
+public:
+  /* Semantic function constructor that uses STACK as bitmap memory stack.  */
+  sem_function (bitmap_obstack *stack);
+
+  /*  Constructor based on callgraph node _NODE with computed hash _HASH.
+      Bitmap STACK is used for memory allocation.  */
+  sem_function (cgraph_node *_node, hashval_t _hash, bitmap_obstack *stack);
+
+  ~sem_function ();
+
+  inline virtual void init_wpa (void)
+  {
+    parse_tree_args ();
+  }
+
+  virtual void init (void);
+  virtual bool equals_wpa (sem_item *item,
+			   hash_map <symtab_node *, sem_item *> &ignored_nodes);
+  virtual hashval_t get_hash (void);
+  virtual bool equals (sem_item *item,
+		       hash_map <symtab_node *, sem_item *> &ignored_nodes);
+  virtual bool merge (sem_item *alias_item);
+
+  /* Dump symbol to FILE.  */
+  virtual void dump_to_file (FILE *file)
+  {
+    gcc_assert (file);
+    dump_function_to_file (decl, file, TDF_DETAILS);
+  }
+
+  /* Parses function arguments and result type.  */
+  void parse_tree_args (void);
+
+  /* Returns cgraph_node.  */
+  inline cgraph_node *get_node (void)
+  {
+    return dyn_cast <cgraph_node *> (node);
+  }
+
+  /* Improve accumulated hash for HSTATE based on a gimple statement STMT.  */
+  void hash_stmt (inchash::hash *inchash, gimple stmt);
+
+  /* Return true if polymorphic comparison must be processed.  */
+  bool compare_polymorphic_p (void);
+
+  /* For a given call graph NODE, the function constructs new
+     semantic function item.  */
+  static sem_function *parse (cgraph_node *node, bitmap_obstack *stack);
+
+  /* Exception handling region tree.  */
+  eh_region region_tree;
+
+  /* Result type tree node.  */
+  tree result_type;
+
+  /* Array of argument tree types.  */
+  vec <tree> arg_types;
+
+  /* Number of function arguments.  */
+  unsigned int arg_count;
+
+  /* Total amount of edges in the function.  */
+  unsigned int edge_count;
+
+  /* Vector of sizes of all basic blocks.  */
+  vec <unsigned int> bb_sizes;
+
+  /* Control flow graph checksum.  */
+  hashval_t cfg_checksum;
+
+  /* GIMPLE codes hash value.  */
+  hashval_t gcode_hash;
+
+  /* Total number of SSA names used in the function.  */
+  unsigned ssa_names_size;
+
+  /* Array of structures for all basic blocks.  */
+  vec <ipa_icf_gimple::sem_bb *> bb_sorted;
+
+private:
+  /* Calculates hash value based on a BASIC_BLOCK.  */
+  hashval_t get_bb_hash (const ipa_icf_gimple::sem_bb *basic_block);
+
+  /* For given basic blocks BB1 and BB2 (from functions FUNC1 and FUNC),
+     true value is returned if phi nodes are semantically
+     equivalent in these blocks .  */
+  bool compare_phi_node (basic_block bb1, basic_block bb2);
+
+  /* Basic blocks dictionary BB_DICT returns true if SOURCE index BB
+     corresponds to TARGET.  */
+  bool bb_dict_test (int* bb_dict, int source, int target);
+
+  /* Iterates all tree types in T1 and T2 and returns true if all types
+     are compatible. If COMPARE_POLYMORPHIC is set to true,
+     more strict comparison is executed.  */
+  bool compare_type_list (tree t1, tree t2, bool compare_polymorphic);
+
+  /* If cgraph edges E1 and E2 are indirect calls, verify that
+     ICF flags are the same.  */
+  bool compare_edge_flags (cgraph_edge *e1, cgraph_edge *e2);
+
+  /* For a given symbol table nodes N1 and N2, we check that FUNCTION_DECLs
+     point to a same function. Comparison can be skipped if IGNORED_NODES
+     contains these nodes.  */
+  bool compare_cgraph_references (hash_map <symtab_node *, sem_item *>
+				  &ignored_nodes,
+				  symtab_node *n1, symtab_node *n2);
+
+  /* Processes function equality comparison.  */
+  bool equals_private (sem_item *item,
+		       hash_map <symtab_node *, sem_item *> &ignored_nodes);
+
+  /* Returns true if tree T can be compared as a handled component.  */
+  static bool icf_handled_component_p (tree t);
+
+  /* Function checker stores binding between functions.   */
+  ipa_icf_gimple::func_checker *m_checker;
+
+  /* COMPARED_FUNC is a function that we compare to.  */
+  sem_function *m_compared_func;
+}; // class sem_function
+
+class sem_variable: public sem_item
+{
+public:
+  /* Semantic variable constructor that uses STACK as bitmap memory stack.  */
+  sem_variable (bitmap_obstack *stack);
+
+  /*  Constructor based on callgraph node _NODE with computed hash _HASH.
+      Bitmap STACK is used for memory allocation.  */
+
+  sem_variable (varpool_node *_node, hashval_t _hash, bitmap_obstack *stack);
+
+  inline virtual void init_wpa (void) {}
+
+  /* Semantic variable initialization function.  */
+  inline virtual void init (void)
+  {
+    decl = get_node ()->decl;
+    ctor = ctor_for_folding (decl);
+  }
+
+  virtual hashval_t get_hash (void);
+  virtual bool merge (sem_item *alias_item);
+  virtual void dump_to_file (FILE *file);
+  virtual bool equals (sem_item *item,
+		       hash_map <symtab_node *, sem_item *> &ignored_nodes);
+
+  /* Fast equality variable based on knowledge known in WPA.  */
+  inline virtual bool equals_wpa (sem_item *item,
+				  hash_map <symtab_node *, sem_item *> & ARG_UNUSED(ignored_nodes))
+  {
+    gcc_assert (item->type == VAR);
+    return true;
+  }
+
+  /* Returns varpool_node.  */
+  inline varpool_node *get_node (void)
+  {
+    return dyn_cast <varpool_node *> (node);
+  }
+
+  /* Parser function that visits a varpool NODE.  */
+  static sem_variable *parse (varpool_node *node, bitmap_obstack *stack);
+
+  /* Variable constructor.  */
+  tree ctor;
+
+private:
+  /* Iterates though a constructor and identifies tree references
+     we are interested in semantic function equality.  */
+  void parse_tree_refs (tree t);
+
+  /* Compares trees T1 and T2 for semantic equality.  */
+  static bool equals (tree t1, tree t2);
+
+  /* Compare that symbol sections are either NULL or have same name.  */
+  bool compare_sections (sem_variable *alias);
+
+}; // class sem_variable
+
+class sem_item_optimizer;
+
+struct congruence_class_group
+{
+  hashval_t hash;
+  sem_item_type type;
+  vec <congruence_class *> classes;
+};
+
+/* Congruence class set structure.  */
+struct congruence_class_group_hash: typed_noop_remove <congruence_class_group>
+{
+  typedef congruence_class_group value_type;
+  typedef congruence_class_group compare_type;
+
+  static inline hashval_t hash (const value_type *item)
+  {
+    return item->hash;
+  }
+
+  static inline int equal (const value_type *item1, const compare_type *item2)
+  {
+    return item1->hash == item2->hash && item1->type == item2->type;
+  }
+};
+
+struct traverse_split_pair
+{
+  sem_item_optimizer *optimizer;
+  class congruence_class *cls;
+};
+
+/* Semantic item optimizer includes all top-level logic
+   related to semantic equality comparison.  */
+class sem_item_optimizer
+{
+public:
+  sem_item_optimizer ();
+  ~sem_item_optimizer ();
+
+  /* Function responsible for visiting all potential functions and
+     read-only variables that can be merged.  */
+  void parse_funcs_and_vars (void);
+
+  /* Optimizer entry point.  */
+  void execute (void);
+
+  /* Dump function. */
+  void dump (void);
+
+  /* Verify congruence classes if checking is enabled.  */
+  void verify_classes (void);
+
+  /* Write IPA ICF summary for symbols.  */
+  void write_summary (void);
+
+  /* Read IPA IPA ICF summary for symbols.  */
+  void read_summary (void);
+
+  /* Callgraph removal hook called for a NODE with a custom DATA.  */
+  static void cgraph_removal_hook (cgraph_node *node, void *data);
+
+  /* Varpool removal hook called for a NODE with a custom DATA.  */
+  static void varpool_removal_hook (varpool_node *node, void *data);
+
+  /* Worklist of congruence classes that can potentially
+     refine classes of congruence.  */
+  std::list<congruence_class *> worklist;
+
+  /* Remove semantic ITEM and release memory.  */
+  void remove_item (sem_item *item);
+
+  /* Remove symtab NODE triggered by symtab removal hooks.  */
+  void remove_symtab_node (symtab_node *node);
+
+  /* Register callgraph and varpool hooks.  */
+  void register_hooks (void);
+
+  /* Unregister callgraph and varpool hooks.  */
+  void unregister_hooks (void);
+
+  /* Adds a CLS to hashtable associated by hash value.  */
+  void add_class (congruence_class *cls);
+
+  /* Gets a congruence class group based on given HASH value and TYPE.  */
+  congruence_class_group *get_group_by_hash (hashval_t hash,
+      sem_item_type type);
+
+private:
+
+  /* Congruence classes are built by hash value.  */
+  void build_hash_based_classes (void);
+
+  /* Semantic items in classes having more than one element and initialized.
+     In case of WPA, we load function body.  */
+  void parse_nonsingleton_classes (void);
+
+  /* Equality function for semantic items is used to subdivide existing
+     classes. If IN_WPA, fast equality function is invoked.  */
+  void subdivide_classes_by_equality (bool in_wpa = false);
+
+  /* Debug function prints all informations about congruence classes.  */
+  void dump_cong_classes (void);
+
+  /* Build references according to call graph.  */
+  void build_graph (void);
+
+  /* Iterative congruence reduction function.  */
+  void process_cong_reduction (void);
+
+  /* After reduction is done, we can declare all items in a group
+     to be equal. PREV_CLASS_COUNT is start number of classes
+     before reduction.  */
+  void merge_classes (unsigned int prev_class_count);
+
+  /* Adds a newly created congruence class CLS to worklist.  */
+  void worklist_push (congruence_class *cls);
+
+  /* Pops a class from worklist. */
+  congruence_class *worklist_pop ();
+
+  /* Every usage of a congruence class CLS is a candidate that can split the
+     collection of classes. Bitmap stack BMSTACK is used for bitmap
+     allocation.  */
+  void do_congruence_step (congruence_class *cls);
+
+  /* Tests if a class CLS used as INDEXth splits any congruence classes.
+     Bitmap stack BMSTACK is used for bitmap allocation.  */
+  void do_congruence_step_for_index (congruence_class *cls, unsigned int index);
+
+  /* Makes pairing between a congruence class CLS and semantic ITEM.  */
+  static void add_item_to_class (congruence_class *cls, sem_item *item);
+
+  /* Disposes split map traverse function. CLS is congruence
+     class, BSLOT is bitmap slot we want to release. DATA is mandatory,
+     but unused argument.  */
+  static bool release_split_map (congruence_class * const &cls, bitmap const &b,
+				 traverse_split_pair *pair);
+
+  /* Process split operation for a cognruence class CLS,
+     where bitmap B splits congruence class members. DATA is used
+     as argument of split pair.  */
+  static bool traverse_congruence_split (congruence_class * const &cls,
+					 bitmap const &b,
+					 traverse_split_pair *pair);
+
+  /* Reads a section from LTO stream file FILE_DATA. Input block for DATA
+     contains LEN bytes.  */
+  void read_section (lto_file_decl_data *file_data, const char *data,
+		     size_t len);
+
+  /* Removes all callgraph and varpool nodes that are marked by symtab
+     as deleted.  */
+  void filter_removed_items (void);
+
+  /* Vector of semantic items.  */
+  vec <sem_item *> m_items;
+
+  /* A set containing all items removed by hooks.  */
+  hash_set <symtab_node *> m_removed_items_set;
+
+  /* Hashtable of congruence classes */
+  hash_table <congruence_class_group_hash> m_classes;
+
+  /* Count of congruence classes.  */
+  unsigned int m_classes_count;
+
+  /* Map data structure maps symtab nodes to semantic items.  */
+  hash_map <symtab_node *, sem_item *> m_symtab_node_map;
+
+  /* Set to true if a splitter class is removed.  */
+  bool splitter_class_removed;
+
+  /* Global unique class id counter.  */
+  static unsigned int class_id;
+
+  /* Callgraph node removal hook holder.  */
+  cgraph_node_hook_list *m_cgraph_node_hooks;
+
+  /* Varpool node removal hook holder.  */
+  varpool_node_hook_list *m_varpool_node_hooks;
+
+  /* Bitmap stack.  */
+  bitmap_obstack m_bmstack;
+}; // class sem_item_optimizer
+
+} // ipa_icf namespace
diff --git a/gcc/lto-cgraph.c b/gcc/lto-cgraph.c
index 0584946..c5aa797 100644
--- a/gcc/lto-cgraph.c
+++ b/gcc/lto-cgraph.c
@@ -539,6 +539,7 @@ lto_output_node (struct lto_simple_output_block *ob, struct cgraph_node *node,
   bp_pack_value (&bp, node->only_called_at_exit, 1);
   bp_pack_value (&bp, node->tm_clone, 1);
   bp_pack_value (&bp, node->calls_comdat_local, 1);
+  bp_pack_value (&bp, node->icf_merged, 1);
   bp_pack_value (&bp, node->thunk.thunk_p && !boundary_p, 1);
   bp_pack_enum (&bp, ld_plugin_symbol_resolution,
 	        LDPR_NUM_KNOWN, node->resolution);
@@ -1079,6 +1080,7 @@ input_overwrite_node (struct lto_file_decl_data *file_data,
   node->only_called_at_exit = bp_unpack_value (bp, 1);
   node->tm_clone = bp_unpack_value (bp, 1);
   node->calls_comdat_local = bp_unpack_value (bp, 1);
+  node->icf_merged = bp_unpack_value (bp, 1);
   node->thunk.thunk_p = bp_unpack_value (bp, 1);
   node->resolution = bp_unpack_enum (bp, ld_plugin_symbol_resolution,
 				     LDPR_NUM_KNOWN);
diff --git a/gcc/lto-section-in.c b/gcc/lto-section-in.c
index 5623706..c053545 100644
--- a/gcc/lto-section-in.c
+++ b/gcc/lto-section-in.c
@@ -60,7 +60,8 @@ const char *lto_section_name[LTO_N_SECTION_TYPES] =
   "opts",
   "cgraphopt",
   "inline",
-  "ipcp_trans"
+  "ipcp_trans",
+  "icf"
 };
 
 
diff --git a/gcc/lto-streamer.h b/gcc/lto-streamer.h
index 4bec969..63e4b32 100644
--- a/gcc/lto-streamer.h
+++ b/gcc/lto-streamer.h
@@ -247,6 +247,7 @@ enum lto_section_type
   LTO_section_cgraph_opt_sum,
   LTO_section_inline_summary,
   LTO_section_ipcp_transform,
+  LTO_section_ipa_icf,
   LTO_N_SECTION_TYPES		/* Must be last.  */
 };
 
diff --git a/gcc/opts.c b/gcc/opts.c
index 5cb5a39..7e9374a 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -497,6 +497,7 @@ static const struct default_options default_options_table[] =
     { OPT_LEVELS_2_PLUS, OPT_fvect_cost_model_, NULL, VECT_COST_MODEL_CHEAP },
     { OPT_LEVELS_2_PLUS_SPEED_ONLY, OPT_foptimize_strlen, NULL, 1 },
     { OPT_LEVELS_2_PLUS, OPT_fhoist_adjacent_loads, NULL, 1 },
+    { OPT_LEVELS_2_PLUS, OPT_fipa_icf, NULL, 1 },
     { OPT_LEVELS_2_PLUS, OPT_fisolate_erroneous_paths_dereference, NULL, 1 },
     { OPT_LEVELS_2_PLUS, OPT_fuse_caller_save, NULL, 1 },
 
@@ -1978,6 +1979,11 @@ common_handle_option (struct gcc_options *opts,
 	opts->x_flag_wrapv = 0;
       break;
 
+    case OPT_fipa_icf:
+	opts->x_flag_ipa_icf_functions = value;
+	opts->x_flag_ipa_icf_variables = value;
+      break;
+
     default:
       /* If the flag was handled in a standard way, assume the lack of
 	 processing here is intentional.  */
diff --git a/gcc/passes.def b/gcc/passes.def
index 801998f..b8efc9c 100644
--- a/gcc/passes.def
+++ b/gcc/passes.def
@@ -103,6 +103,7 @@ along with GCC; see the file COPYING3.  If not see
   INSERT_PASSES_AFTER (all_regular_ipa_passes)
   NEXT_PASS (pass_ipa_whole_program_visibility);
   NEXT_PASS (pass_ipa_profile);
+  NEXT_PASS (pass_ipa_icf);
   NEXT_PASS (pass_ipa_devirt);
   NEXT_PASS (pass_ipa_cp);
   NEXT_PASS (pass_ipa_cdtor_merge);
diff --git a/gcc/timevar.def b/gcc/timevar.def
index a04d05c..55a230b 100644
--- a/gcc/timevar.def
+++ b/gcc/timevar.def
@@ -90,6 +90,7 @@ DEFTIMEVAR (TV_WHOPR_LTRANS          , "whopr ltrans")
 DEFTIMEVAR (TV_IPA_REFERENCE         , "ipa reference")
 DEFTIMEVAR (TV_IPA_PROFILE           , "ipa profile")
 DEFTIMEVAR (TV_IPA_PURE_CONST        , "ipa pure const")
+DEFTIMEVAR (TV_IPA_ICF		     , "ipa icf")
 DEFTIMEVAR (TV_IPA_PTA               , "ipa points-to")
 DEFTIMEVAR (TV_IPA_SRA               , "ipa SRA")
 DEFTIMEVAR (TV_IPA_FREE_LANG_DATA    , "ipa free lang data")
diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h
index ed109c3..aac6703 100644
--- a/gcc/tree-pass.h
+++ b/gcc/tree-pass.h
@@ -461,6 +461,7 @@ extern simple_ipa_opt_pass *make_pass_ipa_free_lang_data (gcc::context *ctxt);
 extern simple_ipa_opt_pass *make_pass_ipa_free_inline_summary (gcc::context
 							       *ctxt);
 extern ipa_opt_pass_d *make_pass_ipa_cp (gcc::context *ctxt);
+extern ipa_opt_pass_d *make_pass_ipa_icf (gcc::context *ctxt);
 extern ipa_opt_pass_d *make_pass_ipa_devirt (gcc::context *ctxt);
 extern ipa_opt_pass_d *make_pass_ipa_reference (gcc::context *ctxt);
 extern ipa_opt_pass_d *make_pass_ipa_pure_const (gcc::context *ctxt);

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 3/5] IPA ICF pass
  2014-10-13 15:17                 ` Martin Liška
@ 2014-10-14 16:12                   ` Jan Hubicka
  2014-10-15 17:06                     ` Martin Liška
  0 siblings, 1 reply; 70+ messages in thread
From: Jan Hubicka @ 2014-10-14 16:12 UTC (permalink / raw)
  To: Martin Liška; +Cc: gcc-patches

> diff --git a/gcc/cgraph.h b/gcc/cgraph.h
> index fb41b01..2de98b4 100644
> --- a/gcc/cgraph.h
> +++ b/gcc/cgraph.h
> @@ -172,6 +172,12 @@ public:
>    /* Dump referring in list to FILE.  */
>    void dump_referring (FILE *);
>  
> +  /* Get number of references for this node.  */
> +  inline unsigned get_references_count (void)
> +  {
> +    return ref_list.references ? ref_list.references->length () : 0;
> +  }

Probably better called num_references() (like we have num_edge in basic-block.h)
> @@ -8068,6 +8069,19 @@ it may significantly increase code size
>  (see @option{--param ipcp-unit-growth=@var{value}}).
>  This flag is enabled by default at @option{-O3}.
>  
> +@item -fipa-icf
> +@opindex fipa-icf
> +Perform Identical Code Folding for functions and read-only variables.
> +The optimization reduces code size and may disturb unwind stacks by replacing
> +a function by equivalent one with a different name. The optimization works
> +more effectively with link time optimization enabled.
> +
> +Nevertheless the behavior is similar to Gold Linker ICF optimization, GCC ICF
> +works on different levels and thus the optimizations are not same - there are
> +equivalences that are found only by GCC and equivalences found only by Gold.
> +
> +This flag is enabled by default at @option{-O2}.
... and -Os?
> +    case ARRAY_REF:
> +    case ARRAY_RANGE_REF:
> +      {
> +	x1 = TREE_OPERAND (t1, 0);
> +	x2 = TREE_OPERAND (t2, 0);
> +	y1 = TREE_OPERAND (t1, 1);
> +	y2 = TREE_OPERAND (t2, 1);
> +
> +	if (!compare_operand (array_ref_low_bound (t1),
> +			      array_ref_low_bound (t2)))
> +	  return return_false_with_msg ("");
> +	if (!compare_operand (array_ref_element_size (t1),
> +			      array_ref_element_size (t2)))
> +	  return return_false_with_msg ("");
> +	if (!compare_operand (x1, x2))
> +	  return return_false_with_msg ("");
> +	return compare_operand (y1, y2);
> +      }

No need for {...} if there are no local vars.
> +bool
> +func_checker::compare_function_decl (tree t1, tree t2)
> +{
> +  bool ret = false;
> +
> +  if (t1 == t2)
> +    return true;
> +
> +  symtab_node *n1 = symtab_node::get (t1);
> +  symtab_node *n2 = symtab_node::get (t2);
> +
> +  if (m_ignored_source_nodes != NULL && m_ignored_target_nodes != NULL)
> +    {
> +      ret = m_ignored_source_nodes->contains (n1)
> +	    && m_ignored_target_nodes->contains (n2);
> +
> +      if (ret)
> +	return true;
> +    }
> +
> +  /* If function decl is WEAKREF, we compare targets.  */
> +  cgraph_node *f1 = cgraph_node::get (t1);
> +  cgraph_node *f2 = cgraph_node::get (t2);
> +
> +  if(f1 && f2 && f1->weakref && f2->weakref)
> +    ret = f1->alias_target == f2->alias_target;
> +
> +  return ret;

Comparing aliases is bit more complicated than just handling weakrefs. I have
patch for symtab_node::equivalent_address_p somewhre in queue.  lets just drop
the fancy stuff for the moment and compare f1&&f2 for equivalence.
> +  ret = compare_decl (t1, t2);

Why functions are not compared with compare_decl while variables are?
> +
> +  return return_with_debug (ret);
> +}
> +
> +void
> +func_checker::parse_labels (sem_bb *bb)
> +{
> +  for (gimple_stmt_iterator gsi = gsi_start_bb (bb->bb); !gsi_end_p (gsi);
> +       gsi_next (&gsi))
> +    {
> +      gimple stmt = gsi_stmt (gsi);
> +
> +      if (gimple_code (stmt) == GIMPLE_LABEL)
> +	{
> +	  tree t = gimple_label_label (stmt);
> +	  gcc_assert (TREE_CODE (t) == LABEL_DECL);
> +
> +	  m_label_bb_map.put (t, bb->bb->index);
> +	}
> +    }
> +}
> +
> +/* Basic block equivalence comparison function that returns true if
> +   basic blocks BB1 and BB2 (from functions FUNC1 and FUNC2) correspond.
> +
> +   In general, a collection of equivalence dictionaries is built for types
> +   like SSA names, declarations (VAR_DECL, PARM_DECL, ..). This infrastructure
> +   is utilized by every statement-by-stament comparison function.  */
> +
> +bool
> +func_checker::compare_bb (sem_bb *bb1, sem_bb *bb2)
> +{
> +  unsigned i;
> +  gimple_stmt_iterator gsi1, gsi2;
> +  gimple s1, s2;
> +
> +  if (bb1->nondbg_stmt_count != bb2->nondbg_stmt_count
> +      || bb1->edge_count != bb2->edge_count)
> +    return return_false ();
> +
> +  gsi1 = gsi_start_bb (bb1->bb);
> +  gsi2 = gsi_start_bb (bb2->bb);
> +
> +  for (i = 0; i < bb1->nondbg_stmt_count; i++)
> +    {
> +      if (is_gimple_debug (gsi_stmt (gsi1)))
> +	gsi_next_nondebug (&gsi1);
> +
> +      if (is_gimple_debug (gsi_stmt (gsi2)))
> +	gsi_next_nondebug (&gsi2);
> +
> +      s1 = gsi_stmt (gsi1);
> +      s2 = gsi_stmt (gsi2);
> +
> +      int eh1 = lookup_stmt_eh_lp_fn
> +		(DECL_STRUCT_FUNCTION (m_source_func_decl), s1);
> +      int eh2 = lookup_stmt_eh_lp_fn
> +		(DECL_STRUCT_FUNCTION (m_target_func_decl), s2);
> +
> +      if (eh1 != eh2)
> +	return return_false_with_msg ("EH regions are different");
> +
> +      if (gimple_code (s1) != gimple_code (s2))
> +	return return_false_with_msg ("gimple codes are different");
> +
> +      switch (gimple_code (s1))
> +	{
> +	case GIMPLE_CALL:
> +	  if (!compare_gimple_call (s1, s2))
> +	    return return_different_stmts (s1, s2, "GIMPLE_CALL");
> +	  break;
> +	case GIMPLE_ASSIGN:
> +	  if (!compare_gimple_assign (s1, s2))
> +	    return return_different_stmts (s1, s2, "GIMPLE_ASSIGN");
> +	  break;
> +	case GIMPLE_COND:
> +	  if (!compare_gimple_cond (s1, s2))
> +	    return return_different_stmts (s1, s2, "GIMPLE_COND");
> +	  break;
> +	case GIMPLE_SWITCH:
> +	  if (!compare_gimple_switch (s1, s2))
> +	    return return_different_stmts (s1, s2, "GIMPLE_SWITCH");
> +	  break;
> +	case GIMPLE_DEBUG:
> +	case GIMPLE_EH_DISPATCH:
> +	  break;
> +	case GIMPLE_RESX:
> +	  if (!compare_gimple_resx (s1, s2))
> +	    return return_different_stmts (s1, s2, "GIMPLE_RESX");
> +	  break;
> +	case GIMPLE_LABEL:
> +	  if (!compare_gimple_label (s1, s2))
> +	    return return_different_stmts (s1, s2, "GIMPLE_LABEL");
> +	  break;
> +	case GIMPLE_RETURN:
> +	  if (!compare_gimple_return (s1, s2))
> +	    return return_different_stmts (s1, s2, "GIMPLE_RETURN");
> +	  break;
> +	case GIMPLE_GOTO:
> +	  if (!compare_gimple_goto (s1, s2))
> +	    return return_different_stmts (s1, s2, "GIMPLE_GOTO");
> +	  break;
> +	case GIMPLE_ASM:
> +	  if (!compare_gimple_asm (s1, s2))
> +	    return return_different_stmts (s1, s2, "GIMPLE_ASM");
> +	  break;
> +	case GIMPLE_PREDICT:
> +	case GIMPLE_NOP:
> +	  return true;
> +	default:
> +	  return return_false_with_msg ("Unknown GIMPLE code reached");
> +	}
> +
> +      gsi_next (&gsi1);
> +      gsi_next (&gsi2);
> +    }
> +
> +  return true;
> +}
> +
> +/* Verifies for given GIMPLEs S1 and S2 that
> +   call statements are semantically equivalent.  */
> +
> +bool
> +func_checker::compare_gimple_call (gimple s1, gimple s2)
> +{
> +  unsigned i;
> +  tree t1, t2;
> +
> +  if (gimple_call_num_args (s1) != gimple_call_num_args (s2))
> +    return false;
> +
> +  t1 = gimple_call_fndecl (s1);
> +  t2 = gimple_call_fndecl (s2);
> +
> +  /* Function pointer variables are not supported yet.  */
> +  if (t1 == NULL || t2 == NULL)
> +    {
> +      if (!compare_operand (t1, t2))
> +	return return_false();

I think the comment above is out of date. compare_operand should do the right
job for indirect calls.
> +
> +  if (cn1 && cn2 && cn1->weakref && cn2->weakref
> +      && cn1->alias_target == cn2->alias_target)
> +    return true;

Lets consistently drop the weakrefs handling and add full alias handling incrementally.
> +
> +  /* Checking function arguments.  */
attributes
> +  tree decl1 = DECL_ATTRIBUTES (decl);
> +  tree decl2 = DECL_ATTRIBUTES (m_compared_func->decl);

You can still do this as part of the wap_comparison, right?
> +
> +  m_checker = new func_checker (decl, m_compared_func->decl,
> +				compare_polymorphic_p (),
> +				false,
> +				&refs_set,
> +				&m_compared_func->refs_set);
> +  while (decl1)
> +    {
> +      if (decl2 == NULL)
> +	return return_false ();
> +
> +      if (get_attribute_name (decl1) != get_attribute_name (decl2))
> +	return return_false ();
> +
> +      tree attr_value1 = TREE_VALUE (decl1);
> +      tree attr_value2 = TREE_VALUE (decl2);
> +
> +      if (attr_value1 && attr_value2)
> +	{
> +	  bool ret = m_checker->compare_operand (TREE_VALUE (attr_value1),
> +						 TREE_VALUE (attr_value2));
> +	  if (!ret)
> +	    return return_false_with_msg ("attribute values are different");
> +	}
> +      else if (!attr_value1 && !attr_value2)
> +	{}
> +      else
> +	return return_false ();
> +
> +      decl1 = TREE_CHAIN (decl1);
> +      decl2 = TREE_CHAIN (decl2);
> +    }
> +
> +  if (decl1 != decl2)
> +    return return_false();
> +
> +
> +  for (arg1 = DECL_ARGUMENTS (decl),
> +       arg2 = DECL_ARGUMENTS (m_compared_func->decl);
> +       arg1; arg1 = DECL_CHAIN (arg1), arg2 = DECL_CHAIN (arg2))
> +    if (!m_checker->compare_decl (arg1, arg2))
> +      return return_false ();
> +
> +  /* Fill-up label dictionary.  */
> +  for (unsigned i = 0; i < bb_sorted.length (); ++i)
> +    {
> +      m_checker->parse_labels (bb_sorted[i]);
> +      m_checker->parse_labels (m_compared_func->bb_sorted[i]);
> +    }
> +
> +  /* Checking all basic blocks.  */
> +  for (unsigned i = 0; i < bb_sorted.length (); ++i)
> +    if(!m_checker->compare_bb (bb_sorted[i], m_compared_func->bb_sorted[i]))
> +      return return_false();
> +
> +  dump_message ("All BBs are equal\n");
> +
> +  /* Basic block edges check.  */
> +  for (unsigned i = 0; i < bb_sorted.length (); ++i)
> +    {
> +      bb_dict = XNEWVEC (int, bb_sorted.length () + 2);
> +      memset (bb_dict, -1, (bb_sorted.length () + 2) * sizeof (int));
> +
> +      bb1 = bb_sorted[i]->bb;
> +      bb2 = m_compared_func->bb_sorted[i]->bb;
> +
> +      ei2 = ei_start (bb2->preds);
> +
> +      for (ei1 = ei_start (bb1->preds); ei_cond (ei1, &e1); ei_next (&ei1))
> +	{
> +	  ei_cond (ei2, &e2);
> +
> +	  if (e1->flags != e2->flags)
> +	    return return_false_with_msg ("flags comparison returns false");
> +
> +	  if (!bb_dict_test (bb_dict, e1->src->index, e2->src->index))
> +	    return return_false_with_msg ("edge comparison returns false");
> +
> +	  if (!bb_dict_test (bb_dict, e1->dest->index, e2->dest->index))
> +	    return return_false_with_msg ("BB comparison returns false");
> +
> +	  if (!m_checker->compare_edge (e1, e2))
> +	    return return_false_with_msg ("edge comparison returns false");
> +
> +	  ei_next (&ei2);
> +	}
> +    }
> +
> +  /* Basic block PHI nodes comparison.  */
> +  for (unsigned i = 0; i < bb_sorted.length (); i++)
> +    if (!compare_phi_node (bb_sorted[i]->bb, m_compared_func->bb_sorted[i]->bb))
> +      return return_false_with_msg ("PHI node comparison returns false");
> +
> +  return result;
> +}

The rest of patch seems fine.  I think we went across enough of iteraitons, the patch is OK
with changes above and lets handle rest incrementally.

Thanks!
Honza

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 3/5] IPA ICF pass
  2014-10-14 16:12                   ` Jan Hubicka
@ 2014-10-15 17:06                     ` Martin Liška
  2014-10-22 21:20                       ` Jiong Wang
  2014-11-13 22:26                       ` H.J. Lu
  0 siblings, 2 replies; 70+ messages in thread
From: Martin Liška @ 2014-10-15 17:06 UTC (permalink / raw)
  To: gcc-patches; +Cc: hubicka >> Jan Hubicka

[-- Attachment #1: Type: text/plain, Size: 11421 bytes --]

On 10/14/2014 06:04 PM, Jan Hubicka wrote:
>> diff --git a/gcc/cgraph.h b/gcc/cgraph.h
>> index fb41b01..2de98b4 100644
>> --- a/gcc/cgraph.h
>> +++ b/gcc/cgraph.h
>> @@ -172,6 +172,12 @@ public:
>>     /* Dump referring in list to FILE.  */
>>     void dump_referring (FILE *);
>>
>> +  /* Get number of references for this node.  */
>> +  inline unsigned get_references_count (void)
>> +  {
>> +    return ref_list.references ? ref_list.references->length () : 0;
>> +  }
>
> Probably better called num_references() (like we have num_edge in basic-block.h)
>> @@ -8068,6 +8069,19 @@ it may significantly increase code size
>>   (see @option{--param ipcp-unit-growth=@var{value}}).
>>   This flag is enabled by default at @option{-O3}.
>>
>> +@item -fipa-icf
>> +@opindex fipa-icf
>> +Perform Identical Code Folding for functions and read-only variables.
>> +The optimization reduces code size and may disturb unwind stacks by replacing
>> +a function by equivalent one with a different name. The optimization works
>> +more effectively with link time optimization enabled.
>> +
>> +Nevertheless the behavior is similar to Gold Linker ICF optimization, GCC ICF
>> +works on different levels and thus the optimizations are not same - there are
>> +equivalences that are found only by GCC and equivalences found only by Gold.
>> +
>> +This flag is enabled by default at @option{-O2}.
> ... and -Os?
>> +    case ARRAY_REF:
>> +    case ARRAY_RANGE_REF:
>> +      {
>> +	x1 = TREE_OPERAND (t1, 0);
>> +	x2 = TREE_OPERAND (t2, 0);
>> +	y1 = TREE_OPERAND (t1, 1);
>> +	y2 = TREE_OPERAND (t2, 1);
>> +
>> +	if (!compare_operand (array_ref_low_bound (t1),
>> +			      array_ref_low_bound (t2)))
>> +	  return return_false_with_msg ("");
>> +	if (!compare_operand (array_ref_element_size (t1),
>> +			      array_ref_element_size (t2)))
>> +	  return return_false_with_msg ("");
>> +	if (!compare_operand (x1, x2))
>> +	  return return_false_with_msg ("");
>> +	return compare_operand (y1, y2);
>> +      }
>
> No need for {...} if there are no local vars.
>> +bool
>> +func_checker::compare_function_decl (tree t1, tree t2)
>> +{
>> +  bool ret = false;
>> +
>> +  if (t1 == t2)
>> +    return true;
>> +
>> +  symtab_node *n1 = symtab_node::get (t1);
>> +  symtab_node *n2 = symtab_node::get (t2);
>> +
>> +  if (m_ignored_source_nodes != NULL && m_ignored_target_nodes != NULL)
>> +    {
>> +      ret = m_ignored_source_nodes->contains (n1)
>> +	    && m_ignored_target_nodes->contains (n2);
>> +
>> +      if (ret)
>> +	return true;
>> +    }
>> +
>> +  /* If function decl is WEAKREF, we compare targets.  */
>> +  cgraph_node *f1 = cgraph_node::get (t1);
>> +  cgraph_node *f2 = cgraph_node::get (t2);
>> +
>> +  if(f1 && f2 && f1->weakref && f2->weakref)
>> +    ret = f1->alias_target == f2->alias_target;
>> +
>> +  return ret;
>
> Comparing aliases is bit more complicated than just handling weakrefs. I have
> patch for symtab_node::equivalent_address_p somewhre in queue.  lets just drop
> the fancy stuff for the moment and compare f1&&f2 for equivalence.
>> +  ret = compare_decl (t1, t2);
>
> Why functions are not compared with compare_decl while variables are?
>> +
>> +  return return_with_debug (ret);
>> +}
>> +
>> +void
>> +func_checker::parse_labels (sem_bb *bb)
>> +{
>> +  for (gimple_stmt_iterator gsi = gsi_start_bb (bb->bb); !gsi_end_p (gsi);
>> +       gsi_next (&gsi))
>> +    {
>> +      gimple stmt = gsi_stmt (gsi);
>> +
>> +      if (gimple_code (stmt) == GIMPLE_LABEL)
>> +	{
>> +	  tree t = gimple_label_label (stmt);
>> +	  gcc_assert (TREE_CODE (t) == LABEL_DECL);
>> +
>> +	  m_label_bb_map.put (t, bb->bb->index);
>> +	}
>> +    }
>> +}
>> +
>> +/* Basic block equivalence comparison function that returns true if
>> +   basic blocks BB1 and BB2 (from functions FUNC1 and FUNC2) correspond.
>> +
>> +   In general, a collection of equivalence dictionaries is built for types
>> +   like SSA names, declarations (VAR_DECL, PARM_DECL, ..). This infrastructure
>> +   is utilized by every statement-by-stament comparison function.  */
>> +
>> +bool
>> +func_checker::compare_bb (sem_bb *bb1, sem_bb *bb2)
>> +{
>> +  unsigned i;
>> +  gimple_stmt_iterator gsi1, gsi2;
>> +  gimple s1, s2;
>> +
>> +  if (bb1->nondbg_stmt_count != bb2->nondbg_stmt_count
>> +      || bb1->edge_count != bb2->edge_count)
>> +    return return_false ();
>> +
>> +  gsi1 = gsi_start_bb (bb1->bb);
>> +  gsi2 = gsi_start_bb (bb2->bb);
>> +
>> +  for (i = 0; i < bb1->nondbg_stmt_count; i++)
>> +    {
>> +      if (is_gimple_debug (gsi_stmt (gsi1)))
>> +	gsi_next_nondebug (&gsi1);
>> +
>> +      if (is_gimple_debug (gsi_stmt (gsi2)))
>> +	gsi_next_nondebug (&gsi2);
>> +
>> +      s1 = gsi_stmt (gsi1);
>> +      s2 = gsi_stmt (gsi2);
>> +
>> +      int eh1 = lookup_stmt_eh_lp_fn
>> +		(DECL_STRUCT_FUNCTION (m_source_func_decl), s1);
>> +      int eh2 = lookup_stmt_eh_lp_fn
>> +		(DECL_STRUCT_FUNCTION (m_target_func_decl), s2);
>> +
>> +      if (eh1 != eh2)
>> +	return return_false_with_msg ("EH regions are different");
>> +
>> +      if (gimple_code (s1) != gimple_code (s2))
>> +	return return_false_with_msg ("gimple codes are different");
>> +
>> +      switch (gimple_code (s1))
>> +	{
>> +	case GIMPLE_CALL:
>> +	  if (!compare_gimple_call (s1, s2))
>> +	    return return_different_stmts (s1, s2, "GIMPLE_CALL");
>> +	  break;
>> +	case GIMPLE_ASSIGN:
>> +	  if (!compare_gimple_assign (s1, s2))
>> +	    return return_different_stmts (s1, s2, "GIMPLE_ASSIGN");
>> +	  break;
>> +	case GIMPLE_COND:
>> +	  if (!compare_gimple_cond (s1, s2))
>> +	    return return_different_stmts (s1, s2, "GIMPLE_COND");
>> +	  break;
>> +	case GIMPLE_SWITCH:
>> +	  if (!compare_gimple_switch (s1, s2))
>> +	    return return_different_stmts (s1, s2, "GIMPLE_SWITCH");
>> +	  break;
>> +	case GIMPLE_DEBUG:
>> +	case GIMPLE_EH_DISPATCH:
>> +	  break;
>> +	case GIMPLE_RESX:
>> +	  if (!compare_gimple_resx (s1, s2))
>> +	    return return_different_stmts (s1, s2, "GIMPLE_RESX");
>> +	  break;
>> +	case GIMPLE_LABEL:
>> +	  if (!compare_gimple_label (s1, s2))
>> +	    return return_different_stmts (s1, s2, "GIMPLE_LABEL");
>> +	  break;
>> +	case GIMPLE_RETURN:
>> +	  if (!compare_gimple_return (s1, s2))
>> +	    return return_different_stmts (s1, s2, "GIMPLE_RETURN");
>> +	  break;
>> +	case GIMPLE_GOTO:
>> +	  if (!compare_gimple_goto (s1, s2))
>> +	    return return_different_stmts (s1, s2, "GIMPLE_GOTO");
>> +	  break;
>> +	case GIMPLE_ASM:
>> +	  if (!compare_gimple_asm (s1, s2))
>> +	    return return_different_stmts (s1, s2, "GIMPLE_ASM");
>> +	  break;
>> +	case GIMPLE_PREDICT:
>> +	case GIMPLE_NOP:
>> +	  return true;
>> +	default:
>> +	  return return_false_with_msg ("Unknown GIMPLE code reached");
>> +	}
>> +
>> +      gsi_next (&gsi1);
>> +      gsi_next (&gsi2);
>> +    }
>> +
>> +  return true;
>> +}
>> +
>> +/* Verifies for given GIMPLEs S1 and S2 that
>> +   call statements are semantically equivalent.  */
>> +
>> +bool
>> +func_checker::compare_gimple_call (gimple s1, gimple s2)
>> +{
>> +  unsigned i;
>> +  tree t1, t2;
>> +
>> +  if (gimple_call_num_args (s1) != gimple_call_num_args (s2))
>> +    return false;
>> +
>> +  t1 = gimple_call_fndecl (s1);
>> +  t2 = gimple_call_fndecl (s2);
>> +
>> +  /* Function pointer variables are not supported yet.  */
>> +  if (t1 == NULL || t2 == NULL)
>> +    {
>> +      if (!compare_operand (t1, t2))
>> +	return return_false();
>
> I think the comment above is out of date. compare_operand should do the right
> job for indirect calls.
>> +
>> +  if (cn1 && cn2 && cn1->weakref && cn2->weakref
>> +      && cn1->alias_target == cn2->alias_target)
>> +    return true;
>
> Lets consistently drop the weakrefs handling and add full alias handling incrementally.
>> +
>> +  /* Checking function arguments.  */
> attributes
>> +  tree decl1 = DECL_ATTRIBUTES (decl);
>> +  tree decl2 = DECL_ATTRIBUTES (m_compared_func->decl);
>
> You can still do this as part of the wap_comparison, right?
>> +
>> +  m_checker = new func_checker (decl, m_compared_func->decl,
>> +				compare_polymorphic_p (),
>> +				false,
>> +				&refs_set,
>> +				&m_compared_func->refs_set);
>> +  while (decl1)
>> +    {
>> +      if (decl2 == NULL)
>> +	return return_false ();
>> +
>> +      if (get_attribute_name (decl1) != get_attribute_name (decl2))
>> +	return return_false ();
>> +
>> +      tree attr_value1 = TREE_VALUE (decl1);
>> +      tree attr_value2 = TREE_VALUE (decl2);
>> +
>> +      if (attr_value1 && attr_value2)
>> +	{
>> +	  bool ret = m_checker->compare_operand (TREE_VALUE (attr_value1),
>> +						 TREE_VALUE (attr_value2));
>> +	  if (!ret)
>> +	    return return_false_with_msg ("attribute values are different");
>> +	}
>> +      else if (!attr_value1 && !attr_value2)
>> +	{}
>> +      else
>> +	return return_false ();
>> +
>> +      decl1 = TREE_CHAIN (decl1);
>> +      decl2 = TREE_CHAIN (decl2);
>> +    }
>> +
>> +  if (decl1 != decl2)
>> +    return return_false();
>> +
>> +
>> +  for (arg1 = DECL_ARGUMENTS (decl),
>> +       arg2 = DECL_ARGUMENTS (m_compared_func->decl);
>> +       arg1; arg1 = DECL_CHAIN (arg1), arg2 = DECL_CHAIN (arg2))
>> +    if (!m_checker->compare_decl (arg1, arg2))
>> +      return return_false ();
>> +
>> +  /* Fill-up label dictionary.  */
>> +  for (unsigned i = 0; i < bb_sorted.length (); ++i)
>> +    {
>> +      m_checker->parse_labels (bb_sorted[i]);
>> +      m_checker->parse_labels (m_compared_func->bb_sorted[i]);
>> +    }
>> +
>> +  /* Checking all basic blocks.  */
>> +  for (unsigned i = 0; i < bb_sorted.length (); ++i)
>> +    if(!m_checker->compare_bb (bb_sorted[i], m_compared_func->bb_sorted[i]))
>> +      return return_false();
>> +
>> +  dump_message ("All BBs are equal\n");
>> +
>> +  /* Basic block edges check.  */
>> +  for (unsigned i = 0; i < bb_sorted.length (); ++i)
>> +    {
>> +      bb_dict = XNEWVEC (int, bb_sorted.length () + 2);
>> +      memset (bb_dict, -1, (bb_sorted.length () + 2) * sizeof (int));
>> +
>> +      bb1 = bb_sorted[i]->bb;
>> +      bb2 = m_compared_func->bb_sorted[i]->bb;
>> +
>> +      ei2 = ei_start (bb2->preds);
>> +
>> +      for (ei1 = ei_start (bb1->preds); ei_cond (ei1, &e1); ei_next (&ei1))
>> +	{
>> +	  ei_cond (ei2, &e2);
>> +
>> +	  if (e1->flags != e2->flags)
>> +	    return return_false_with_msg ("flags comparison returns false");
>> +
>> +	  if (!bb_dict_test (bb_dict, e1->src->index, e2->src->index))
>> +	    return return_false_with_msg ("edge comparison returns false");
>> +
>> +	  if (!bb_dict_test (bb_dict, e1->dest->index, e2->dest->index))
>> +	    return return_false_with_msg ("BB comparison returns false");
>> +
>> +	  if (!m_checker->compare_edge (e1, e2))
>> +	    return return_false_with_msg ("edge comparison returns false");
>> +
>> +	  ei_next (&ei2);
>> +	}
>> +    }
>> +
>> +  /* Basic block PHI nodes comparison.  */
>> +  for (unsigned i = 0; i < bb_sorted.length (); i++)
>> +    if (!compare_phi_node (bb_sorted[i]->bb, m_compared_func->bb_sorted[i]->bb))
>> +      return return_false_with_msg ("PHI node comparison returns false");
>> +
>> +  return result;
>> +}
>
> The rest of patch seems fine.  I think we went across enough of iteraitons, the patch is OK
> with changes above and lets handle rest incrementally.
>
> Thanks!
> Honza
>

Hello

There's final version of the patch I'm going to commit tomorrow in the morning (CEST).
Thank you Honza for the review.

Martin

[-- Attachment #2: ipa-icf-final.patch --]
[-- Type: text/x-patch, Size: 123928 bytes --]

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 04ce0c0..de2adc7 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1269,6 +1269,8 @@ OBJS = \
 	ipa-profile.o \
 	ipa-prop.o \
 	ipa-pure-const.o \
+	ipa-icf.o \
+	ipa-icf-gimple.o \
 	ipa-reference.o \
 	ipa-ref.o \
 	ipa-utils.o \
diff --git a/gcc/cgraph.c b/gcc/cgraph.c
index 02224f3..f472ec5 100644
--- a/gcc/cgraph.c
+++ b/gcc/cgraph.c
@@ -1910,6 +1910,8 @@ cgraph_node::dump (FILE *f)
     fprintf (f, " only_called_at_exit");
   if (tm_clone)
     fprintf (f, " tm_clone");
+  if (icf_merged)
+    fprintf (f, " icf_merged");
   if (DECL_STATIC_CONSTRUCTOR (decl))
     fprintf (f," static_constructor (priority:%i)", get_init_priority ());
   if (DECL_STATIC_DESTRUCTOR (decl))
@@ -2560,6 +2562,7 @@ verify_edge_corresponds_to_fndecl (cgraph_edge *e, tree decl)
   if (!node
       || node->body_removed
       || node->in_other_partition
+      || node->icf_merged
       || e->callee->in_other_partition)
     return false;
 
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index a5777c2..69b5d0a 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -180,6 +180,12 @@ public:
   /* Dump referring in list to FILE.  */
   void dump_referring (FILE *);
 
+  /* Get number of references for this node.  */
+  inline unsigned num_references (void)
+  {
+    return ref_list.references ? ref_list.references->length () : 0;
+  }
+
   /* Iterates I-th reference in the list, REF is also set.  */
   ipa_ref *iterate_reference (unsigned i, ipa_ref *&ref);
 
@@ -1249,6 +1255,8 @@ public:
   /* True if this decl calls a COMDAT-local function.  This is set up in
      compute_inline_parameters and inline_call.  */
   unsigned calls_comdat_local : 1;
+  /* True if node has been created by merge operation in IPA-ICF.  */
+  unsigned icf_merged: 1;
 };
 
 /* A cgraph node set is a collection of cgraph nodes.  A cgraph node
diff --git a/gcc/common.opt b/gcc/common.opt
index b4f0ed4..5db5e1e 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -1443,6 +1443,18 @@ fipa-pure-const
 Common Report Var(flag_ipa_pure_const) Init(0) Optimization
 Discover pure and const functions
 
+fipa-icf
+Common Report Var(flag_ipa_icf) Optimization
+Perform Identical Code Folding for functions and read-only variables
+
+fipa-icf-functions
+Common Report Var(flag_ipa_icf_functions) Optimization
+Perform Identical Code Folding for functions
+
+fipa-icf-variables
+Common Report Var(flag_ipa_icf_variables) Optimization
+Perform Identical Code Folding for variables
+
 fipa-reference
 Common Report Var(flag_ipa_reference) Init(0) Optimization
 Discover readonly and non addressable static variables
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index f7055d0..6bc09d6 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -382,7 +382,7 @@ Objective-C and Objective-C++ Dialects}.
 -fif-conversion2 -findirect-inlining @gol
 -finline-functions -finline-functions-called-once -finline-limit=@var{n} @gol
 -finline-small-functions -fipa-cp -fipa-cp-clone @gol
--fipa-pta -fipa-profile -fipa-pure-const -fipa-reference @gol
+-fipa-pta -fipa-profile -fipa-pure-const -fipa-reference -fipa-icf @gol
 -fira-algorithm=@var{algorithm} @gol
 -fira-region=@var{region} -fira-hoist-pressure @gol
 -fira-loop-pressure -fno-ira-share-save-slots @gol
@@ -7142,6 +7142,7 @@ also turns on the following optimization flags:
 -findirect-inlining @gol
 -fipa-cp @gol
 -fipa-sra @gol
+-fipa-icf @gol
 -fisolate-erroneous-paths-dereference @gol
 -foptimize-sibling-calls @gol
 -foptimize-strlen @gol
@@ -8087,6 +8088,19 @@ it may significantly increase code size
 (see @option{--param ipcp-unit-growth=@var{value}}).
 This flag is enabled by default at @option{-O3}.
 
+@item -fipa-icf
+@opindex fipa-icf
+Perform Identical Code Folding for functions and read-only variables.
+The optimization reduces code size and may disturb unwind stacks by replacing
+a function by equivalent one with a different name. The optimization works
+more effectively with link time optimization enabled.
+
+Nevertheless the behavior is similar to Gold Linker ICF optimization, GCC ICF
+works on different levels and thus the optimizations are not same - there are
+equivalences that are found only by GCC and equivalences found only by Gold.
+
+This flag is enabled by default at @option{-O2} and @option{-Os}.
+
 @item -fisolate-erroneous-paths-dereference
 Detect paths which trigger erroneous or undefined behaviour due to
 dereferencing a NULL pointer.  Isolate those paths from the main control
diff --git a/gcc/ipa-icf-gimple.c b/gcc/ipa-icf-gimple.c
new file mode 100644
index 0000000..792a3e4
--- /dev/null
+++ b/gcc/ipa-icf-gimple.c
@@ -0,0 +1,897 @@
+/* Interprocedural Identical Code Folding pass
+   Copyright (C) 2014 Free Software Foundation, Inc.
+
+   Contributed by Jan Hubicka <hubicka@ucw.cz> and Martin Liska <mliska@suse.cz>
+
+This file is part of GCC.
+
+GCC 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, or (at your option) any later
+version.
+
+GCC 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 GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tree.h"
+#include "basic-block.h"
+#include "tree-ssa-alias.h"
+#include "internal-fn.h"
+#include "gimple-expr.h"
+#include "is-a.h"
+#include "gimple.h"
+#include "expr.h"
+#include "gimple-iterator.h"
+#include "gimple-ssa.h"
+#include "tree-cfg.h"
+#include "stringpool.h"
+#include "tree-dfa.h"
+#include "tree-pass.h"
+#include "gimple-pretty-print.h"
+#include "cfgloop.h"
+#include "except.h"
+#include "data-streamer.h"
+#include "ipa-utils.h"
+#include <list>
+#include "tree-ssanames.h"
+#include "tree-eh.h"
+
+#include "ipa-icf-gimple.h"
+#include "ipa-icf.h"
+
+namespace ipa_icf_gimple {
+
+/* Initialize internal structures for a given SOURCE_FUNC_DECL and
+   TARGET_FUNC_DECL. Strict polymorphic comparison is processed if
+   an option COMPARE_POLYMORPHIC is true. For special cases, one can
+   set IGNORE_LABELS to skip label comparison.
+   Similarly, IGNORE_SOURCE_DECLS and IGNORE_TARGET_DECLS are sets
+   of declarations that can be skipped.  */
+
+func_checker::func_checker (tree source_func_decl, tree target_func_decl,
+			    bool compare_polymorphic,
+			    bool ignore_labels,
+			    hash_set<symtab_node *> *ignored_source_nodes,
+			    hash_set<symtab_node *> *ignored_target_nodes)
+  : m_source_func_decl (source_func_decl), m_target_func_decl (target_func_decl),
+    m_ignored_source_nodes (ignored_source_nodes),
+    m_ignored_target_nodes (ignored_target_nodes),
+    m_compare_polymorphic (compare_polymorphic),
+    m_ignore_labels (ignore_labels)
+{
+  function *source_func = DECL_STRUCT_FUNCTION (source_func_decl);
+  function *target_func = DECL_STRUCT_FUNCTION (target_func_decl);
+
+  unsigned ssa_source = SSANAMES (source_func)->length ();
+  unsigned ssa_target = SSANAMES (target_func)->length ();
+
+  m_source_ssa_names.create (ssa_source);
+  m_target_ssa_names.create (ssa_target);
+
+  for (unsigned i = 0; i < ssa_source; i++)
+    m_source_ssa_names.safe_push (-1);
+
+  for (unsigned i = 0; i < ssa_target; i++)
+    m_target_ssa_names.safe_push (-1);
+}
+
+/* Memory release routine.  */
+
+func_checker::~func_checker ()
+{
+  m_source_ssa_names.release();
+  m_target_ssa_names.release();
+}
+
+/* Verifies that trees T1 and T2 are equivalent from perspective of ICF.  */
+
+bool
+func_checker::compare_ssa_name (tree t1, tree t2)
+{
+  unsigned i1 = SSA_NAME_VERSION (t1);
+  unsigned i2 = SSA_NAME_VERSION (t2);
+
+  if (m_source_ssa_names[i1] == -1)
+    m_source_ssa_names[i1] = i2;
+  else if (m_source_ssa_names[i1] != (int) i2)
+    return false;
+
+  if(m_target_ssa_names[i2] == -1)
+    m_target_ssa_names[i2] = i1;
+  else if (m_target_ssa_names[i2] != (int) i1)
+    return false;
+
+  return true;
+}
+
+/* Verification function for edges E1 and E2.  */
+
+bool
+func_checker::compare_edge (edge e1, edge e2)
+{
+  if (e1->flags != e2->flags)
+    return false;
+
+  bool existed_p;
+
+  edge &slot = m_edge_map.get_or_insert (e1, &existed_p);
+  if (existed_p)
+    return return_with_debug (slot == e2);
+  else
+    slot = e2;
+
+  /* TODO: filter edge probabilities for profile feedback match.  */
+
+  return true;
+}
+
+/* Verification function for declaration trees T1 and T2 that
+   come from functions FUNC1 and FUNC2.  */
+
+bool
+func_checker::compare_decl (tree t1, tree t2)
+{
+  if (!auto_var_in_fn_p (t1, m_source_func_decl)
+      || !auto_var_in_fn_p (t2, m_target_func_decl))
+    return return_with_debug (t1 == t2);
+
+  tree_code t = TREE_CODE (t1);
+  if ((t == VAR_DECL || t == PARM_DECL || t == RESULT_DECL)
+      && DECL_BY_REFERENCE (t1) != DECL_BY_REFERENCE (t2))
+    return return_false_with_msg ("DECL_BY_REFERENCE flags are different");
+
+  if (!compatible_types_p (TREE_TYPE (t1), TREE_TYPE (t2),
+			   m_compare_polymorphic))
+    return return_false ();
+
+  bool existed_p;
+
+  tree &slot = m_decl_map.get_or_insert (t1, &existed_p);
+  if (existed_p)
+    return return_with_debug (slot == t2);
+  else
+    slot = t2;
+
+  return true;
+}
+
+/* Return true if types are compatible from perspective of ICF.  */
+bool func_checker::compatible_types_p (tree t1, tree t2,
+				       bool compare_polymorphic,
+				       bool first_argument)
+{
+  if (TREE_CODE (t1) != TREE_CODE (t2))
+    return return_false_with_msg ("different tree types");
+
+  if (!types_compatible_p (t1, t2))
+    return return_false_with_msg ("types are not compatible");
+
+  if (get_alias_set (t1) != get_alias_set (t2))
+    return return_false_with_msg ("alias sets are different");
+
+  /* We call contains_polymorphic_type_p with this pointer type.  */
+  if (first_argument && TREE_CODE (t1) == POINTER_TYPE)
+    {
+      t1 = TREE_TYPE (t1);
+      t2 = TREE_TYPE (t2);
+    }
+
+  if (compare_polymorphic)
+    if (contains_polymorphic_type_p (t1) || contains_polymorphic_type_p (t2))
+      {
+	if (!contains_polymorphic_type_p (t1) || !contains_polymorphic_type_p (t2))
+	  return return_false_with_msg ("one type is not polymorphic");
+
+	if (!types_must_be_same_for_odr (t1, t2))
+	  return return_false_with_msg ("types are not same for ODR");
+      }
+
+  return true;
+}
+
+/* Function responsible for comparison of handled components T1 and T2.
+   If these components, from functions FUNC1 and FUNC2, are equal, true
+   is returned.  */
+
+bool
+func_checker::compare_operand (tree t1, tree t2)
+{
+  tree base1, base2, x1, x2, y1, y2, z1, z2;
+  HOST_WIDE_INT offset1 = 0, offset2 = 0;
+  bool ret;
+
+  if (!t1 && !t2)
+    return true;
+  else if (!t1 || !t2)
+    return false;
+
+  tree tt1 = TREE_TYPE (t1);
+  tree tt2 = TREE_TYPE (t2);
+
+  if (!func_checker::compatible_types_p (tt1, tt2))
+    return false;
+
+  base1 = get_addr_base_and_unit_offset (t1, &offset1);
+  base2 = get_addr_base_and_unit_offset (t2, &offset2);
+
+  if (base1 && base2)
+    {
+      if (offset1 != offset2)
+	return return_false_with_msg ("base offsets are different");
+
+      t1 = base1;
+      t2 = base2;
+    }
+
+  if (TREE_CODE (t1) != TREE_CODE (t2))
+    return return_false ();
+
+  switch (TREE_CODE (t1))
+    {
+    case CONSTRUCTOR:
+      {
+	unsigned length1 = vec_safe_length (CONSTRUCTOR_ELTS (t1));
+	unsigned length2 = vec_safe_length (CONSTRUCTOR_ELTS (t2));
+
+	if (length1 != length2)
+	  return return_false ();
+
+	for (unsigned i = 0; i < length1; i++)
+	  if (!compare_operand (CONSTRUCTOR_ELT (t1, i)->value,
+				CONSTRUCTOR_ELT (t2, i)->value))
+	    return return_false();
+
+	return true;
+      }
+    case ARRAY_REF:
+    case ARRAY_RANGE_REF:
+      x1 = TREE_OPERAND (t1, 0);
+      x2 = TREE_OPERAND (t2, 0);
+      y1 = TREE_OPERAND (t1, 1);
+      y2 = TREE_OPERAND (t2, 1);
+
+      if (!compare_operand (array_ref_low_bound (t1),
+			    array_ref_low_bound (t2)))
+	return return_false_with_msg ("");
+      if (!compare_operand (array_ref_element_size (t1),
+			    array_ref_element_size (t2)))
+	return return_false_with_msg ("");
+      if (!compare_operand (x1, x2))
+	return return_false_with_msg ("");
+      return compare_operand (y1, y2);
+    case MEM_REF:
+      {
+	x1 = TREE_OPERAND (t1, 0);
+	x2 = TREE_OPERAND (t2, 0);
+	y1 = TREE_OPERAND (t1, 1);
+	y2 = TREE_OPERAND (t2, 1);
+
+	/* See if operand is an memory access (the test originate from
+	 gimple_load_p).
+
+	In this case the alias set of the function being replaced must
+	be subset of the alias set of the other function.  At the moment
+	we seek for equivalency classes, so simply require inclussion in
+	both directions.  */
+
+	if (!func_checker::compatible_types_p (TREE_TYPE (x1), TREE_TYPE (x2)))
+	  return return_false ();
+
+	if (!compare_operand (x1, x2))
+	  return return_false_with_msg ("");
+
+	if (get_alias_set (TREE_TYPE (y1)) != get_alias_set (TREE_TYPE (y2)))
+	  return return_false_with_msg ("alias set for MEM_REF offsets are different");
+
+	ao_ref r1, r2;
+	ao_ref_init (&r1, t1);
+	ao_ref_init (&r2, t2);
+	if (ao_ref_alias_set (&r1) != ao_ref_alias_set (&r2)
+	    || ao_ref_base_alias_set (&r1) != ao_ref_base_alias_set (&r2))
+	  return return_false_with_msg ("ao alias sets are different");
+
+	/* Type of the offset on MEM_REF does not matter.  */
+	return wi::to_offset  (y1) == wi::to_offset  (y2);
+      }
+    case COMPONENT_REF:
+      {
+	x1 = TREE_OPERAND (t1, 0);
+	x2 = TREE_OPERAND (t2, 0);
+	y1 = TREE_OPERAND (t1, 1);
+	y2 = TREE_OPERAND (t2, 1);
+
+	ret = compare_operand (x1, x2)
+	      && compare_operand (y1, y2);
+
+	return return_with_debug (ret);
+      }
+    /* Virtual table call.  */
+    case OBJ_TYPE_REF:
+      {
+	x1 = TREE_OPERAND (t1, 0);
+	x2 = TREE_OPERAND (t2, 0);
+	y1 = TREE_OPERAND (t1, 1);
+	y2 = TREE_OPERAND (t2, 1);
+	z1 = TREE_OPERAND (t1, 2);
+	z2 = TREE_OPERAND (t2, 2);
+
+	ret = compare_operand (x1, x2)
+	      && compare_operand (y1, y2)
+	      && compare_operand (z1, z2);
+
+	return return_with_debug (ret);
+      }
+    case ADDR_EXPR:
+      {
+	x1 = TREE_OPERAND (t1, 0);
+	x2 = TREE_OPERAND (t2, 0);
+
+	ret = compare_operand (x1, x2);
+	return return_with_debug (ret);
+      }
+    case SSA_NAME:
+      {
+	ret = compare_ssa_name (t1, t2);
+
+	if (!ret)
+	  return return_with_debug (ret);
+
+	if (SSA_NAME_IS_DEFAULT_DEF (t1))
+	  {
+	    tree b1 = SSA_NAME_VAR (t1);
+	    tree b2 = SSA_NAME_VAR (t2);
+
+	    if (b1 == NULL && b2 == NULL)
+	      return true;
+
+	    if (b1 == NULL || b2 == NULL || TREE_CODE (b1) != TREE_CODE (b2))
+	      return return_false ();
+
+	    switch (TREE_CODE (b1))
+	      {
+	      case VAR_DECL:
+		return return_with_debug (compare_variable_decl (t1, t2));
+	      case PARM_DECL:
+	      case RESULT_DECL:
+		ret = compare_decl (b1, b2);
+		return return_with_debug (ret);
+	      default:
+		return return_false_with_msg ("Unknown TREE code reached");
+	      }
+	  }
+	else
+	  return true;
+      }
+    case INTEGER_CST:
+      {
+	ret = compatible_types_p (TREE_TYPE (t1), TREE_TYPE (t2))
+	      && wi::to_offset  (t1) == wi::to_offset  (t2);
+
+	return return_with_debug (ret);
+      }
+    case COMPLEX_CST:
+    case VECTOR_CST:
+    case STRING_CST:
+    case REAL_CST:
+      {
+	ret = operand_equal_p (t1, t2, OEP_ONLY_CONST);
+	return return_with_debug (ret);
+      }
+    case FUNCTION_DECL:
+      {
+	ret = compare_function_decl (t1, t2);
+	return return_with_debug (ret);
+      }
+    case VAR_DECL:
+      return return_with_debug (compare_variable_decl (t1, t2));
+    case FIELD_DECL:
+      {
+	tree offset1 = DECL_FIELD_OFFSET (t1);
+	tree offset2 = DECL_FIELD_OFFSET (t2);
+
+	tree bit_offset1 = DECL_FIELD_BIT_OFFSET (t1);
+	tree bit_offset2 = DECL_FIELD_BIT_OFFSET (t2);
+
+	ret = compare_operand (offset1, offset2)
+	      && compare_operand (bit_offset1, bit_offset2);
+
+	return return_with_debug (ret);
+      }
+    case LABEL_DECL:
+      {
+	int *bb1 = m_label_bb_map.get (t1);
+	int *bb2 = m_label_bb_map.get (t2);
+
+	return return_with_debug (*bb1 == *bb2);
+      }
+    case PARM_DECL:
+    case RESULT_DECL:
+    case CONST_DECL:
+    case BIT_FIELD_REF:
+      {
+	ret = compare_decl (t1, t2);
+	return return_with_debug (ret);
+      }
+    default:
+      return return_false_with_msg ("Unknown TREE code reached");
+    }
+}
+
+/* Compares two tree list operands T1 and T2 and returns true if these
+   two trees are semantically equivalent.  */
+
+bool
+func_checker::compare_tree_list_operand (tree t1, tree t2)
+{
+  gcc_assert (TREE_CODE (t1) == TREE_LIST);
+  gcc_assert (TREE_CODE (t2) == TREE_LIST);
+
+  for (; t1; t1 = TREE_CHAIN (t1))
+    {
+      if (!t2)
+	return false;
+
+      if (!compare_operand (TREE_VALUE (t1), TREE_VALUE (t2)))
+	return return_false ();
+
+      t2 = TREE_CHAIN (t2);
+    }
+
+  if (t2)
+    return return_false ();
+
+  return true;
+}
+
+/* Verifies that trees T1 and T2, representing function declarations
+   are equivalent from perspective of ICF.  */
+
+bool
+func_checker::compare_function_decl (tree t1, tree t2)
+{
+  bool ret = false;
+
+  if (t1 == t2)
+    return true;
+
+  symtab_node *n1 = symtab_node::get (t1);
+  symtab_node *n2 = symtab_node::get (t2);
+
+  if (m_ignored_source_nodes != NULL && m_ignored_target_nodes != NULL)
+    {
+      ret = m_ignored_source_nodes->contains (n1)
+	    && m_ignored_target_nodes->contains (n2);
+
+      if (ret)
+	return true;
+    }
+
+  /* If function decl is WEAKREF, we compare targets.  */
+  cgraph_node *f1 = cgraph_node::get (t1);
+  cgraph_node *f2 = cgraph_node::get (t2);
+
+  if(f1 && f2 && f1->weakref && f2->weakref)
+    ret = f1->alias_target == f2->alias_target;
+
+  return ret;
+}
+
+/* Verifies that trees T1 and T2 do correspond.  */
+
+bool
+func_checker::compare_variable_decl (tree t1, tree t2)
+{
+  bool ret = false;
+
+  if (t1 == t2)
+    return true;
+
+  if (TREE_CODE (t1) == VAR_DECL && (DECL_EXTERNAL (t1) || TREE_STATIC (t1)))
+    {
+      symtab_node *n1 = symtab_node::get (t1);
+      symtab_node *n2 = symtab_node::get (t2);
+
+      if (m_ignored_source_nodes != NULL && m_ignored_target_nodes != NULL)
+	{
+	  ret = m_ignored_source_nodes->contains (n1)
+		&& m_ignored_target_nodes->contains (n2);
+
+	  if (ret)
+	    return true;
+	}
+    }
+  ret = compare_decl (t1, t2);
+
+  return return_with_debug (ret);
+}
+
+void
+func_checker::parse_labels (sem_bb *bb)
+{
+  for (gimple_stmt_iterator gsi = gsi_start_bb (bb->bb); !gsi_end_p (gsi);
+       gsi_next (&gsi))
+    {
+      gimple stmt = gsi_stmt (gsi);
+
+      if (gimple_code (stmt) == GIMPLE_LABEL)
+	{
+	  tree t = gimple_label_label (stmt);
+	  gcc_assert (TREE_CODE (t) == LABEL_DECL);
+
+	  m_label_bb_map.put (t, bb->bb->index);
+	}
+    }
+}
+
+/* Basic block equivalence comparison function that returns true if
+   basic blocks BB1 and BB2 (from functions FUNC1 and FUNC2) correspond.
+
+   In general, a collection of equivalence dictionaries is built for types
+   like SSA names, declarations (VAR_DECL, PARM_DECL, ..). This infrastructure
+   is utilized by every statement-by-stament comparison function.  */
+
+bool
+func_checker::compare_bb (sem_bb *bb1, sem_bb *bb2)
+{
+  unsigned i;
+  gimple_stmt_iterator gsi1, gsi2;
+  gimple s1, s2;
+
+  if (bb1->nondbg_stmt_count != bb2->nondbg_stmt_count
+      || bb1->edge_count != bb2->edge_count)
+    return return_false ();
+
+  gsi1 = gsi_start_bb (bb1->bb);
+  gsi2 = gsi_start_bb (bb2->bb);
+
+  for (i = 0; i < bb1->nondbg_stmt_count; i++)
+    {
+      if (is_gimple_debug (gsi_stmt (gsi1)))
+	gsi_next_nondebug (&gsi1);
+
+      if (is_gimple_debug (gsi_stmt (gsi2)))
+	gsi_next_nondebug (&gsi2);
+
+      s1 = gsi_stmt (gsi1);
+      s2 = gsi_stmt (gsi2);
+
+      int eh1 = lookup_stmt_eh_lp_fn
+		(DECL_STRUCT_FUNCTION (m_source_func_decl), s1);
+      int eh2 = lookup_stmt_eh_lp_fn
+		(DECL_STRUCT_FUNCTION (m_target_func_decl), s2);
+
+      if (eh1 != eh2)
+	return return_false_with_msg ("EH regions are different");
+
+      if (gimple_code (s1) != gimple_code (s2))
+	return return_false_with_msg ("gimple codes are different");
+
+      switch (gimple_code (s1))
+	{
+	case GIMPLE_CALL:
+	  if (!compare_gimple_call (s1, s2))
+	    return return_different_stmts (s1, s2, "GIMPLE_CALL");
+	  break;
+	case GIMPLE_ASSIGN:
+	  if (!compare_gimple_assign (s1, s2))
+	    return return_different_stmts (s1, s2, "GIMPLE_ASSIGN");
+	  break;
+	case GIMPLE_COND:
+	  if (!compare_gimple_cond (s1, s2))
+	    return return_different_stmts (s1, s2, "GIMPLE_COND");
+	  break;
+	case GIMPLE_SWITCH:
+	  if (!compare_gimple_switch (s1, s2))
+	    return return_different_stmts (s1, s2, "GIMPLE_SWITCH");
+	  break;
+	case GIMPLE_DEBUG:
+	case GIMPLE_EH_DISPATCH:
+	  break;
+	case GIMPLE_RESX:
+	  if (!compare_gimple_resx (s1, s2))
+	    return return_different_stmts (s1, s2, "GIMPLE_RESX");
+	  break;
+	case GIMPLE_LABEL:
+	  if (!compare_gimple_label (s1, s2))
+	    return return_different_stmts (s1, s2, "GIMPLE_LABEL");
+	  break;
+	case GIMPLE_RETURN:
+	  if (!compare_gimple_return (s1, s2))
+	    return return_different_stmts (s1, s2, "GIMPLE_RETURN");
+	  break;
+	case GIMPLE_GOTO:
+	  if (!compare_gimple_goto (s1, s2))
+	    return return_different_stmts (s1, s2, "GIMPLE_GOTO");
+	  break;
+	case GIMPLE_ASM:
+	  if (!compare_gimple_asm (s1, s2))
+	    return return_different_stmts (s1, s2, "GIMPLE_ASM");
+	  break;
+	case GIMPLE_PREDICT:
+	case GIMPLE_NOP:
+	  return true;
+	default:
+	  return return_false_with_msg ("Unknown GIMPLE code reached");
+	}
+
+      gsi_next (&gsi1);
+      gsi_next (&gsi2);
+    }
+
+  return true;
+}
+
+/* Verifies for given GIMPLEs S1 and S2 that
+   call statements are semantically equivalent.  */
+
+bool
+func_checker::compare_gimple_call (gimple s1, gimple s2)
+{
+  unsigned i;
+  tree t1, t2;
+
+  if (gimple_call_num_args (s1) != gimple_call_num_args (s2))
+    return false;
+
+  t1 = gimple_call_fndecl (s1);
+  t2 = gimple_call_fndecl (s2);
+
+  /* Function pointer variables are not supported yet.  */
+  if (!compare_operand (t1, t2))
+    return return_false();
+
+  /* Checking of argument.  */
+  for (i = 0; i < gimple_call_num_args (s1); ++i)
+    {
+      t1 = gimple_call_arg (s1, i);
+      t2 = gimple_call_arg (s2, i);
+
+      if (!compare_operand (t1, t2))
+	return false;
+    }
+
+  /* Return value checking.  */
+  t1 = gimple_get_lhs (s1);
+  t2 = gimple_get_lhs (s2);
+
+  return compare_operand (t1, t2);
+}
+
+
+/* Verifies for given GIMPLEs S1 and S2 that
+   assignment statements are semantically equivalent.  */
+
+bool
+func_checker::compare_gimple_assign (gimple s1, gimple s2)
+{
+  tree arg1, arg2;
+  tree_code code1, code2;
+  unsigned i;
+
+  code1 = gimple_expr_code (s1);
+  code2 = gimple_expr_code (s2);
+
+  if (code1 != code2)
+    return false;
+
+  code1 = gimple_assign_rhs_code (s1);
+  code2 = gimple_assign_rhs_code (s2);
+
+  if (code1 != code2)
+    return false;
+
+  for (i = 0; i < gimple_num_ops (s1); i++)
+    {
+      arg1 = gimple_op (s1, i);
+      arg2 = gimple_op (s2, i);
+
+      if (!compare_operand (arg1, arg2))
+	return false;
+    }
+
+
+  return true;
+}
+
+/* Verifies for given GIMPLEs S1 and S2 that
+   condition statements are semantically equivalent.  */
+
+bool
+func_checker::compare_gimple_cond (gimple s1, gimple s2)
+{
+  tree t1, t2;
+  tree_code code1, code2;
+
+  code1 = gimple_expr_code (s1);
+  code2 = gimple_expr_code (s2);
+
+  if (code1 != code2)
+    return false;
+
+  t1 = gimple_cond_lhs (s1);
+  t2 = gimple_cond_lhs (s2);
+
+  if (!compare_operand (t1, t2))
+    return false;
+
+  t1 = gimple_cond_rhs (s1);
+  t2 = gimple_cond_rhs (s2);
+
+  return compare_operand (t1, t2);
+}
+
+/* Verifies that tree labels T1 and T2 correspond in FUNC1 and FUNC2.  */
+
+bool
+func_checker::compare_tree_ssa_label (tree t1, tree t2)
+{
+  return compare_operand (t1, t2);
+}
+
+/* Verifies for given GIMPLEs S1 and S2 that
+   label statements are semantically equivalent.  */
+
+bool
+func_checker::compare_gimple_label (gimple g1, gimple g2)
+{
+  if (m_ignore_labels)
+    return true;
+
+  tree t1 = gimple_label_label (g1);
+  tree t2 = gimple_label_label (g2);
+
+  if (FORCED_LABEL (t1) || FORCED_LABEL (t2))
+    return return_false_with_msg ("FORCED_LABEL");
+
+  return compare_tree_ssa_label (t1, t2);
+}
+
+/* Verifies for given GIMPLEs S1 and S2 that
+   switch statements are semantically equivalent.  */
+
+bool
+func_checker::compare_gimple_switch (gimple g1, gimple g2)
+{
+  unsigned lsize1, lsize2, i;
+
+  lsize1 = gimple_switch_num_labels (g1);
+  lsize2 = gimple_switch_num_labels (g2);
+
+  if (lsize1 != lsize2)
+    return false;
+
+  tree t1 = gimple_switch_index (g1);
+  tree t2 = gimple_switch_index (g2);
+
+  if (!compare_operand (t1, t2))
+    return false;
+
+  for (i = 0; i < lsize1; i++)
+    {
+      tree label1 = gimple_switch_label (g1, i);
+      tree label2 = gimple_switch_label (g2, i);
+
+      if (TREE_CODE (label1) == CASE_LABEL_EXPR
+	  && TREE_CODE (label2) == CASE_LABEL_EXPR)
+	{
+	  label1 = CASE_LABEL (label1);
+	  label2 = CASE_LABEL (label2);
+
+	  if (!compare_operand (label1, label2))
+	    return return_false_with_msg ("switch label_exprs are different");
+	}
+      else if (!tree_int_cst_equal (label1, label2))
+	return return_false_with_msg ("switch labels are different");
+    }
+
+  return true;
+}
+
+/* Verifies for given GIMPLEs S1 and S2 that
+   return statements are semantically equivalent.  */
+
+bool
+func_checker::compare_gimple_return (gimple g1, gimple g2)
+{
+  tree t1, t2;
+
+  t1 = gimple_return_retval (g1);
+  t2 = gimple_return_retval (g2);
+
+  /* Void return type.  */
+  if (t1 == NULL && t2 == NULL)
+    return true;
+  else
+    return compare_operand (t1, t2);
+}
+
+/* Verifies for given GIMPLEs S1 and S2 that
+   goto statements are semantically equivalent.  */
+
+bool
+func_checker::compare_gimple_goto (gimple g1, gimple g2)
+{
+  tree dest1, dest2;
+
+  dest1 = gimple_goto_dest (g1);
+  dest2 = gimple_goto_dest (g2);
+
+  if (TREE_CODE (dest1) != TREE_CODE (dest2) || TREE_CODE (dest1) != SSA_NAME)
+    return false;
+
+  return compare_operand (dest1, dest2);
+}
+
+/* Verifies for given GIMPLEs S1 and S2 that
+   resx statements are semantically equivalent.  */
+
+bool
+func_checker::compare_gimple_resx (gimple g1, gimple g2)
+{
+  return gimple_resx_region (g1) == gimple_resx_region (g2);
+}
+
+/* Verifies for given GIMPLEs S1 and S2 that ASM statements are equivalent.
+   For the beginning, the pass only supports equality for
+   '__asm__ __volatile__ ("", "", "", "memory")'.  */
+
+bool
+func_checker::compare_gimple_asm (gimple g1, gimple g2)
+{
+  if (gimple_asm_volatile_p (g1) != gimple_asm_volatile_p (g2))
+    return false;
+
+  if (gimple_asm_ninputs (g1) != gimple_asm_ninputs (g2))
+    return false;
+
+  if (gimple_asm_noutputs (g1) != gimple_asm_noutputs (g2))
+    return false;
+
+  /* We do not suppport goto ASM statement comparison.  */
+  if (gimple_asm_nlabels (g1) || gimple_asm_nlabels (g2))
+    return false;
+
+  if (gimple_asm_nclobbers (g1) != gimple_asm_nclobbers (g2))
+    return false;
+
+  for (unsigned i = 0; i < gimple_asm_ninputs (g1); i++)
+    {
+      tree input1 = gimple_asm_input_op (g1, i);
+      tree input2 = gimple_asm_input_op (g2, i);
+
+      if (!compare_tree_list_operand (input1, input2))
+	return return_false_with_msg ("ASM input is different");
+    }
+
+  for (unsigned i = 0; i < gimple_asm_noutputs (g1); i++)
+    {
+      tree output1 = gimple_asm_output_op (g1, i);
+      tree output2 = gimple_asm_output_op (g2, i);
+
+      if (!compare_tree_list_operand (output1, output2))
+	return return_false_with_msg ("ASM output is different");
+    }
+
+  for (unsigned i = 0; i < gimple_asm_nclobbers (g1); i++)
+    {
+      tree clobber1 = gimple_asm_clobber_op (g1, i);
+      tree clobber2 = gimple_asm_clobber_op (g2, i);
+
+      if (!operand_equal_p (TREE_VALUE (clobber1), TREE_VALUE (clobber2),
+			    OEP_ONLY_CONST))
+	return return_false_with_msg ("ASM clobber is different");
+    }
+
+  return true;
+}
+
+} // ipa_icf_gimple namespace
diff --git a/gcc/ipa-icf-gimple.h b/gcc/ipa-icf-gimple.h
new file mode 100644
index 0000000..8487a2a
--- /dev/null
+++ b/gcc/ipa-icf-gimple.h
@@ -0,0 +1,264 @@
+/* Interprocedural semantic function equality pass
+   Copyright (C) 2014 Free Software Foundation, Inc.
+
+   Contributed by Jan Hubicka <hubicka@ucw.cz> and Martin Liska <mliska@suse.cz>
+
+This file is part of GCC.
+
+GCC 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, or (at your option) any later
+version.
+
+GCC 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 GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+/* Gimple identical code folding (class func_checker) is an infastructure
+   capable of comparing two given functions. The class compares every
+   gimple statement and uses many dictionaries to map source and target
+   SSA_NAMEs, declarations and other components.
+
+   To use the infrastructure, create an instanse of func_checker and call
+   a comparsion function based on type of gimple statement.  */
+
+/* Prints string STRING to a FILE with a given number of SPACE_COUNT.  */
+#define FPUTS_SPACES(file, space_count, string) \
+  fprintf (file, "%*s" string, space_count, " ");
+
+/* fprintf function wrapper that transforms given FORMAT to follow given
+   number for SPACE_COUNT and call fprintf for a FILE.  */
+#define FPRINTF_SPACES(file, space_count, format, ...) \
+  fprintf (file, "%*s" format, space_count, " ", ##__VA_ARGS__);
+
+/* Prints a MESSAGE to dump_file if exists. FUNC is name of function and
+   LINE is location in the source file.  */
+
+static inline void
+dump_message_1 (const char *message, const char *func, unsigned int line)
+{
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    fprintf (dump_file, "  debug message: %s (%s:%u)\n", message, func, line);
+}
+
+/* Prints a MESSAGE to dump_file if exists.  */
+#define dump_message(message) dump_message_1 (message, __func__, __LINE__)
+
+/* Logs a MESSAGE to dump_file if exists and returns false. FUNC is name
+   of function and LINE is location in the source file.  */
+
+static inline bool
+return_false_with_message_1 (const char *message, const char *func,
+			     unsigned int line)
+{
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    fprintf (dump_file, "  false returned: '%s' (%s:%u)\n", message, func, line);
+  return false;
+}
+
+/* Logs a MESSAGE to dump_file if exists and returns false.  */
+#define return_false_with_msg(message) \
+  return_false_with_message_1 (message, __func__, __LINE__)
+
+/* Return false and log that false value is returned.  */
+#define return_false() return_false_with_msg ("")
+
+/* Logs return value if RESULT is false. FUNC is name of function and LINE
+   is location in the source file.  */
+
+static inline bool
+return_with_result (bool result, const char *func, unsigned int line)
+{
+  if (!result && dump_file && (dump_flags & TDF_DETAILS))
+    fprintf (dump_file, "  false returned (%s:%u)\n", func, line);
+
+  return result;
+}
+
+/* Logs return value if RESULT is false.  */
+#define return_with_debug(result) return_with_result (result, __func__, __LINE__)
+
+/* Verbose logging function logging statements S1 and S2 of a CODE.
+   FUNC is name of function and LINE is location in the source file.  */
+
+static inline bool
+return_different_stmts_1 (gimple s1, gimple s2, const char *code,
+			  const char *func, unsigned int line)
+{
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    {
+      fprintf (dump_file, "  different statement for code: %s (%s:%u):\n",
+	       code, func, line);
+
+      print_gimple_stmt (dump_file, s1, 3, TDF_DETAILS);
+      print_gimple_stmt (dump_file, s2, 3, TDF_DETAILS);
+    }
+
+  return false;
+}
+
+/* Verbose logging function logging statements S1 and S2 of a CODE.  */
+#define return_different_stmts(s1, s2, code) \
+  return_different_stmts_1 (s1, s2, code, __func__, __LINE__)
+
+namespace ipa_icf_gimple {
+
+/* Basic block struct for semantic equality pass.  */
+class sem_bb
+{
+public:
+  sem_bb (basic_block bb_, unsigned nondbg_stmt_count_, unsigned edge_count_):
+    bb (bb_), nondbg_stmt_count (nondbg_stmt_count_), edge_count (edge_count_) {}
+
+  /* Basic block the structure belongs to.  */
+  basic_block bb;
+
+  /* Number of non-debug statements in the basic block.  */
+  unsigned nondbg_stmt_count;
+
+  /* Number of edges connected to the block.  */
+  unsigned edge_count;
+};
+
+/* A class aggregating all connections and semantic equivalents
+   for a given pair of semantic function candidates.  */
+class func_checker
+{
+public:
+  /* Initialize internal structures for a given SOURCE_FUNC_DECL and
+     TARGET_FUNC_DECL. Strict polymorphic comparison is processed if
+     an option COMPARE_POLYMORPHIC is true. For special cases, one can
+     set IGNORE_LABELS to skip label comparison.
+     Similarly, IGNORE_SOURCE_DECLS and IGNORE_TARGET_DECLS are sets
+     of declarations that can be skipped.  */
+  func_checker (tree source_func_decl, tree target_func_decl,
+		bool compare_polymorphic,
+		bool ignore_labels = false,
+		hash_set<symtab_node *> *ignored_source_nodes = NULL,
+		hash_set<symtab_node *> *ignored_target_nodes = NULL);
+
+  /* Memory release routine.  */
+  ~func_checker();
+
+  void parse_labels (sem_bb *bb);
+
+  /* Basic block equivalence comparison function that returns true if
+     basic blocks BB1 and BB2 correspond.  */
+  bool compare_bb (sem_bb *bb1, sem_bb *bb2);
+
+  /* Verifies that trees T1 and T2 are equivalent from perspective of ICF.  */
+  bool compare_ssa_name (tree t1, tree t2);
+
+  /* Verification function for edges E1 and E2.  */
+  bool compare_edge (edge e1, edge e2);
+
+  /* Verifies for given GIMPLEs S1 and S2 that
+     call statements are semantically equivalent.  */
+  bool compare_gimple_call (gimple s1, gimple s2);
+
+  /* Verifies for given GIMPLEs S1 and S2 that
+     assignment statements are semantically equivalent.  */
+  bool compare_gimple_assign (gimple s1, gimple s2);
+
+  /* Verifies for given GIMPLEs S1 and S2 that
+     condition statements are semantically equivalent.  */
+  bool compare_gimple_cond (gimple s1, gimple s2);
+
+  /* Verifies for given GIMPLEs S1 and S2 that
+     label statements are semantically equivalent.  */
+  bool compare_gimple_label (gimple s1, gimple s2);
+
+  /* Verifies for given GIMPLEs S1 and S2 that
+     switch statements are semantically equivalent.  */
+  bool compare_gimple_switch (gimple s1, gimple s2);
+
+  /* Verifies for given GIMPLEs S1 and S2 that
+     return statements are semantically equivalent.  */
+  bool compare_gimple_return (gimple s1, gimple s2);
+
+  /* Verifies for given GIMPLEs S1 and S2 that
+     goto statements are semantically equivalent.  */
+  bool compare_gimple_goto (gimple s1, gimple s2);
+
+  /* Verifies for given GIMPLEs S1 and S2 that
+     resx statements are semantically equivalent.  */
+  bool compare_gimple_resx (gimple s1, gimple s2);
+
+  /* Verifies for given GIMPLEs S1 and S2 that ASM statements are equivalent.
+     For the beginning, the pass only supports equality for
+     '__asm__ __volatile__ ("", "", "", "memory")'.  */
+  bool compare_gimple_asm (gimple s1, gimple s2);
+
+  /* Verification function for declaration trees T1 and T2.  */
+  bool compare_decl (tree t1, tree t2);
+
+  /* Verifies that tree labels T1 and T2 correspond.  */
+  bool compare_tree_ssa_label (tree t1, tree t2);
+
+  /* Function responsible for comparison of handled components T1 and T2.
+     If these components, from functions FUNC1 and FUNC2, are equal, true
+     is returned.  */
+  bool compare_operand (tree t1, tree t2);
+
+  /* Compares two tree list operands T1 and T2 and returns true if these
+     two trees are semantically equivalent.  */
+  bool compare_tree_list_operand (tree t1, tree t2);
+
+  /* Verifies that trees T1 and T2, representing function declarations
+     are equivalent from perspective of ICF.  */
+  bool compare_function_decl (tree t1, tree t2);
+
+  /* Verifies that trees T1 and T2 do correspond.  */
+  bool compare_variable_decl (tree t1, tree t2);
+
+  /* Return true if types are compatible from perspective of ICF.
+     FIRST_ARGUMENT indicates if the comparison is called for
+     first parameter of a function.  */
+  static bool compatible_types_p (tree t1, tree t2,
+				  bool compare_polymorphic = true,
+				  bool first_argument = false);
+
+
+private:
+  /* Vector mapping source SSA names to target ones.  */
+  vec <int> m_source_ssa_names;
+
+  /* Vector mapping target SSA names to source ones.  */
+  vec <int> m_target_ssa_names;
+
+  /* Source TREE function declaration.  */
+  tree m_source_func_decl;
+
+  /* Target TREE function declaration.  */
+  tree m_target_func_decl;
+
+  /* Source symbol nodes that should be skipped by
+     declaration comparison.  */
+  hash_set<symtab_node *> *m_ignored_source_nodes;
+
+  /* Target symbol nodes that should be skipped by
+     declaration comparison.  */
+  hash_set<symtab_node *> *m_ignored_target_nodes;
+
+  /* Source to target edge map.  */
+  hash_map <edge, edge> m_edge_map;
+
+  /* Source to target declaration map.  */
+  hash_map <tree, tree> m_decl_map;
+
+  /* Label to basic block index mapping.  */
+  hash_map <tree, int> m_label_bb_map;
+
+  /* Flag if polymorphic comparison should be executed.  */
+  bool m_compare_polymorphic;
+
+  /* Flag if ignore labels in comparison.  */
+  bool m_ignore_labels;
+};
+
+} // ipa_icf_gimple namespace
diff --git a/gcc/ipa-icf.c b/gcc/ipa-icf.c
new file mode 100644
index 0000000..4e73849
--- /dev/null
+++ b/gcc/ipa-icf.c
@@ -0,0 +1,2371 @@
+/* Interprocedural Identical Code Folding pass
+   Copyright (C) 2014 Free Software Foundation, Inc.
+
+   Contributed by Jan Hubicka <hubicka@ucw.cz> and Martin Liska <mliska@suse.cz>
+
+This file is part of GCC.
+
+GCC 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, or (at your option) any later
+version.
+
+GCC 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 GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+/* Interprocedural Identical Code Folding for functions and
+   read-only variables.
+
+   The goal of this transformation is to discover functions and read-only
+   variables which do have exactly the same semantics.
+
+   In case of functions,
+   we could either create a virtual clone or do a simple function wrapper
+   that will call equivalent function. If the function is just locally visible,
+   all function calls can be redirected. For read-only variables, we create
+   aliases if possible.
+
+   Optimization pass arranges as follows:
+   1) All functions and read-only variables are visited and internal
+      data structure, either sem_function or sem_variables is created.
+   2) For every symbol from the previous step, VAR_DECL and FUNCTION_DECL are
+      saved and matched to corresponding sem_items.
+   3) These declaration are ignored for equality check and are solved
+      by Value Numbering algorithm published by Alpert, Zadeck in 1992.
+   4) We compute hash value for each symbol.
+   5) Congruence classes are created based on hash value. If hash value are
+      equal, equals function is called and symbols are deeply compared.
+      We must prove that all SSA names, declarations and other items
+      correspond.
+   6) Value Numbering is executed for these classes. At the end of the process
+      all symbol members in remaining classes can be merged.
+   7) Merge operation creates alias in case of read-only variables. For
+      callgraph node, we must decide if we can redirect local calls,
+      create an alias or a thunk.
+
+*/
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tree.h"
+#include "basic-block.h"
+#include "tree-ssa-alias.h"
+#include "internal-fn.h"
+#include "gimple-expr.h"
+#include "is-a.h"
+#include "gimple.h"
+#include "expr.h"
+#include "gimple-iterator.h"
+#include "gimple-ssa.h"
+#include "tree-cfg.h"
+#include "tree-phinodes.h"
+#include "stringpool.h"
+#include "tree-ssanames.h"
+#include "tree-dfa.h"
+#include "tree-pass.h"
+#include "gimple-pretty-print.h"
+#include "ipa-inline.h"
+#include "cfgloop.h"
+#include "except.h"
+#include "hash-table.h"
+#include "coverage.h"
+#include "attribs.h"
+#include "print-tree.h"
+#include "lto-streamer.h"
+#include "data-streamer.h"
+#include "ipa-utils.h"
+#include <list>
+#include "ipa-icf-gimple.h"
+#include "ipa-icf.h"
+
+using namespace ipa_icf_gimple;
+
+namespace ipa_icf {
+/* Constructor for key value pair, where _ITEM is key and _INDEX is a target.  */
+
+sem_usage_pair::sem_usage_pair (sem_item *_item, unsigned int _index):
+  item (_item), index (_index)
+{
+}
+
+/* Semantic item constructor for a node of _TYPE, where STACK is used
+   for bitmap memory allocation.  */
+
+sem_item::sem_item (sem_item_type _type,
+		    bitmap_obstack *stack): type(_type), hash(0)
+{
+  setup (stack);
+}
+
+/* Semantic item constructor for a node of _TYPE, where STACK is used
+   for bitmap memory allocation. The item is based on symtab node _NODE
+   with computed _HASH.  */
+
+sem_item::sem_item (sem_item_type _type, symtab_node *_node,
+		    hashval_t _hash, bitmap_obstack *stack): type(_type),
+  node (_node), hash (_hash)
+{
+  decl = node->decl;
+  setup (stack);
+}
+
+/* Add reference to a semantic TARGET.  */
+
+void
+sem_item::add_reference (sem_item *target)
+{
+  refs.safe_push (target);
+  unsigned index = refs.length ();
+  target->usages.safe_push (new sem_usage_pair(this, index));
+  bitmap_set_bit (target->usage_index_bitmap, index);
+  refs_set.add (target->node);
+}
+
+/* Initialize internal data structures. Bitmap STACK is used for
+   bitmap memory allocation process.  */
+
+void
+sem_item::setup (bitmap_obstack *stack)
+{
+  gcc_checking_assert (node);
+
+  refs.create (0);
+  tree_refs.create (0);
+  usages.create (0);
+  usage_index_bitmap = BITMAP_ALLOC (stack);
+}
+
+sem_item::~sem_item ()
+{
+  for (unsigned i = 0; i < usages.length (); i++)
+    delete usages[i];
+
+  refs.release ();
+  tree_refs.release ();
+  usages.release ();
+
+  BITMAP_FREE (usage_index_bitmap);
+}
+
+/* Dump function for debugging purpose.  */
+
+DEBUG_FUNCTION void
+sem_item::dump (void)
+{
+  if (dump_file)
+    {
+      fprintf (dump_file, "[%s] %s (%u) (tree:%p)\n", type == FUNC ? "func" : "var",
+	       name(), node->order, (void *) node->decl);
+      fprintf (dump_file, "  hash: %u\n", get_hash ());
+      fprintf (dump_file, "  references: ");
+
+      for (unsigned i = 0; i < refs.length (); i++)
+	fprintf (dump_file, "%s%s ", refs[i]->name (),
+		 i < refs.length() - 1 ? "," : "");
+
+      fprintf (dump_file, "\n");
+    }
+}
+
+/* Semantic function constructor that uses STACK as bitmap memory stack.  */
+
+sem_function::sem_function (bitmap_obstack *stack): sem_item (FUNC, stack),
+  m_checker (NULL), m_compared_func (NULL)
+{
+  arg_types.create (0);
+  bb_sizes.create (0);
+  bb_sorted.create (0);
+}
+
+/*  Constructor based on callgraph node _NODE with computed hash _HASH.
+    Bitmap STACK is used for memory allocation.  */
+sem_function::sem_function (cgraph_node *node, hashval_t hash,
+			    bitmap_obstack *stack):
+  sem_item (FUNC, node, hash, stack),
+  m_checker (NULL), m_compared_func (NULL)
+{
+  arg_types.create (0);
+  bb_sizes.create (0);
+  bb_sorted.create (0);
+}
+
+sem_function::~sem_function ()
+{
+  for (unsigned i = 0; i < bb_sorted.length (); i++)
+    free (bb_sorted[i]);
+
+  arg_types.release ();
+  bb_sizes.release ();
+  bb_sorted.release ();
+}
+
+/* Calculates hash value based on a BASIC_BLOCK.  */
+
+hashval_t
+sem_function::get_bb_hash (const sem_bb *basic_block)
+{
+  inchash::hash hstate;
+
+  hstate.add_int (basic_block->nondbg_stmt_count);
+  hstate.add_int (basic_block->edge_count);
+
+  return hstate.end ();
+}
+
+/* References independent hash function.  */
+
+hashval_t
+sem_function::get_hash (void)
+{
+  if(!hash)
+    {
+      inchash::hash hstate;
+      hstate.add_int (177454); /* Random number for function type.  */
+
+      hstate.add_int (arg_count);
+      hstate.add_int (cfg_checksum);
+      hstate.add_int (gcode_hash);
+
+      for (unsigned i = 0; i < bb_sorted.length (); i++)
+	hstate.merge_hash (get_bb_hash (bb_sorted[i]));
+
+      for (unsigned i = 0; i < bb_sizes.length (); i++)
+	hstate.add_int (bb_sizes[i]);
+
+      hash = hstate.end ();
+    }
+
+  return hash;
+}
+
+/* For a given symbol table nodes N1 and N2, we check that FUNCTION_DECLs
+   point to a same function. Comparison can be skipped if IGNORED_NODES
+   contains these nodes.  */
+
+bool
+sem_function::compare_cgraph_references (hash_map <symtab_node *, sem_item *>
+    &ignored_nodes,
+    symtab_node *n1, symtab_node *n2)
+{
+  if (n1 == n2 || (ignored_nodes.get (n1) && ignored_nodes.get (n2)))
+    return true;
+
+  /* TODO: add more precise comparison for weakrefs, etc.  */
+
+  return return_false_with_msg ("different references");
+}
+
+/* If cgraph edges E1 and E2 are indirect calls, verify that
+   ECF flags are the same.  */
+
+bool sem_function::compare_edge_flags (cgraph_edge *e1, cgraph_edge *e2)
+{
+  if (e1->indirect_info && e2->indirect_info)
+    {
+      int e1_flags = e1->indirect_info->ecf_flags;
+      int e2_flags = e2->indirect_info->ecf_flags;
+
+      if (e1_flags != e2_flags)
+	return return_false_with_msg ("ICF flags are different");
+    }
+  else if (e1->indirect_info || e2->indirect_info)
+    return false;
+
+  return true;
+}
+
+/* Fast equality function based on knowledge known in WPA.  */
+
+bool
+sem_function::equals_wpa (sem_item *item,
+			  hash_map <symtab_node *, sem_item *> &ignored_nodes)
+{
+  gcc_assert (item->type == FUNC);
+
+  m_compared_func = static_cast<sem_function *> (item);
+
+  if (arg_types.length () != m_compared_func->arg_types.length ())
+    return return_false_with_msg ("different number of arguments");
+
+  /* Checking types of arguments.  */
+  for (unsigned i = 0; i < arg_types.length (); i++)
+    {
+      /* This guard is here for function pointer with attributes (pr59927.c).  */
+      if (!arg_types[i] || !m_compared_func->arg_types[i])
+	return return_false_with_msg ("NULL argument type");
+
+      /* Polymorphic comparison is executed just for non-leaf functions.  */
+      bool is_not_leaf = get_node ()->callees != NULL;
+
+      if (!func_checker::compatible_types_p (arg_types[i],
+					     m_compared_func->arg_types[i],
+					     is_not_leaf, i == 0))
+	return return_false_with_msg ("argument type is different");
+    }
+
+  /* Result type checking.  */
+  if (!func_checker::compatible_types_p (result_type,
+					 m_compared_func->result_type))
+    return return_false_with_msg ("result types are different");
+
+  if (node->num_references () != item->node->num_references ())
+    return return_false_with_msg ("different number of references");
+
+  ipa_ref *ref = NULL, *ref2 = NULL;
+  for (unsigned i = 0; node->iterate_reference (i, ref); i++)
+    {
+      item->node->iterate_reference (i, ref2);
+
+      if (!compare_cgraph_references (ignored_nodes, ref->referred, ref2->referred))
+	return false;
+    }
+
+  cgraph_edge *e1 = dyn_cast <cgraph_node *> (node)->callees;
+  cgraph_edge *e2 = dyn_cast <cgraph_node *> (item->node)->callees;
+
+  while (e1 && e2)
+    {
+      if (!compare_cgraph_references (ignored_nodes, e1->callee, e2->callee))
+	return false;
+
+      e1 = e1->next_callee;
+      e2 = e2->next_callee;
+    }
+
+  if (e1 || e2)
+    return return_false_with_msg ("different number of edges");
+
+  return true;
+}
+
+/* Returns true if the item equals to ITEM given as argument.  */
+
+bool
+sem_function::equals (sem_item *item,
+		      hash_map <symtab_node *, sem_item *> &ignored_nodes)
+{
+  gcc_assert (item->type == FUNC);
+  bool eq = equals_private (item, ignored_nodes);
+
+  if (m_checker != NULL)
+    {
+      delete m_checker;
+      m_checker = NULL;
+    }
+
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    fprintf (dump_file,
+	     "Equals called for:%s:%s (%u:%u) (%s:%s) with result: %s\n\n",
+	     name(), item->name (), node->order, item->node->order, asm_name (),
+	     item->asm_name (), eq ? "true" : "false");
+
+  return eq;
+}
+
+/* Processes function equality comparison.  */
+
+bool
+sem_function::equals_private (sem_item *item,
+			      hash_map <symtab_node *, sem_item *> &ignored_nodes)
+{
+  if (item->type != FUNC)
+    return false;
+
+  basic_block bb1, bb2;
+  edge e1, e2;
+  edge_iterator ei1, ei2;
+  int *bb_dict = NULL;
+  bool result = true;
+  tree arg1, arg2;
+
+  m_compared_func = static_cast<sem_function *> (item);
+
+  gcc_assert (decl != item->decl);
+
+  if (bb_sorted.length () != m_compared_func->bb_sorted.length ()
+      || edge_count != m_compared_func->edge_count
+      || cfg_checksum != m_compared_func->cfg_checksum)
+    return return_false ();
+
+  if (!equals_wpa (item, ignored_nodes))
+    return false;
+
+  /* Checking function arguments.  */
+  tree decl1 = DECL_ATTRIBUTES (decl);
+  tree decl2 = DECL_ATTRIBUTES (m_compared_func->decl);
+
+  m_checker = new func_checker (decl, m_compared_func->decl,
+				compare_polymorphic_p (),
+				false,
+				&refs_set,
+				&m_compared_func->refs_set);
+  while (decl1)
+    {
+      if (decl2 == NULL)
+	return return_false ();
+
+      if (get_attribute_name (decl1) != get_attribute_name (decl2))
+	return return_false ();
+
+      tree attr_value1 = TREE_VALUE (decl1);
+      tree attr_value2 = TREE_VALUE (decl2);
+
+      if (attr_value1 && attr_value2)
+	{
+	  bool ret = m_checker->compare_operand (TREE_VALUE (attr_value1),
+						 TREE_VALUE (attr_value2));
+	  if (!ret)
+	    return return_false_with_msg ("attribute values are different");
+	}
+      else if (!attr_value1 && !attr_value2)
+	{}
+      else
+	return return_false ();
+
+      decl1 = TREE_CHAIN (decl1);
+      decl2 = TREE_CHAIN (decl2);
+    }
+
+  if (decl1 != decl2)
+    return return_false();
+
+
+  for (arg1 = DECL_ARGUMENTS (decl),
+       arg2 = DECL_ARGUMENTS (m_compared_func->decl);
+       arg1; arg1 = DECL_CHAIN (arg1), arg2 = DECL_CHAIN (arg2))
+    if (!m_checker->compare_decl (arg1, arg2))
+      return return_false ();
+
+  /* Fill-up label dictionary.  */
+  for (unsigned i = 0; i < bb_sorted.length (); ++i)
+    {
+      m_checker->parse_labels (bb_sorted[i]);
+      m_checker->parse_labels (m_compared_func->bb_sorted[i]);
+    }
+
+  /* Checking all basic blocks.  */
+  for (unsigned i = 0; i < bb_sorted.length (); ++i)
+    if(!m_checker->compare_bb (bb_sorted[i], m_compared_func->bb_sorted[i]))
+      return return_false();
+
+  dump_message ("All BBs are equal\n");
+
+  /* Basic block edges check.  */
+  for (unsigned i = 0; i < bb_sorted.length (); ++i)
+    {
+      bb_dict = XNEWVEC (int, bb_sorted.length () + 2);
+      memset (bb_dict, -1, (bb_sorted.length () + 2) * sizeof (int));
+
+      bb1 = bb_sorted[i]->bb;
+      bb2 = m_compared_func->bb_sorted[i]->bb;
+
+      ei2 = ei_start (bb2->preds);
+
+      for (ei1 = ei_start (bb1->preds); ei_cond (ei1, &e1); ei_next (&ei1))
+	{
+	  ei_cond (ei2, &e2);
+
+	  if (e1->flags != e2->flags)
+	    return return_false_with_msg ("flags comparison returns false");
+
+	  if (!bb_dict_test (bb_dict, e1->src->index, e2->src->index))
+	    return return_false_with_msg ("edge comparison returns false");
+
+	  if (!bb_dict_test (bb_dict, e1->dest->index, e2->dest->index))
+	    return return_false_with_msg ("BB comparison returns false");
+
+	  if (!m_checker->compare_edge (e1, e2))
+	    return return_false_with_msg ("edge comparison returns false");
+
+	  ei_next (&ei2);
+	}
+    }
+
+  /* Basic block PHI nodes comparison.  */
+  for (unsigned i = 0; i < bb_sorted.length (); i++)
+    if (!compare_phi_node (bb_sorted[i]->bb, m_compared_func->bb_sorted[i]->bb))
+      return return_false_with_msg ("PHI node comparison returns false");
+
+  return result;
+}
+
+/* Merges instance with an ALIAS_ITEM, where alias, thunk or redirection can
+   be applied.  */
+bool
+sem_function::merge (sem_item *alias_item)
+{
+  gcc_assert (alias_item->type == FUNC);
+
+  sem_function *alias_func = static_cast<sem_function *> (alias_item);
+
+  cgraph_node *original = get_node ();
+  cgraph_node *local_original = original;
+  cgraph_node *alias = alias_func->get_node ();
+  bool original_address_matters;
+  bool alias_address_matters;
+
+  bool create_thunk = false;
+  bool create_alias = false;
+  bool redirect_callers = false;
+  bool original_discardable = false;
+
+  /* Do not attempt to mix functions from different user sections;
+     we do not know what user intends with those.  */
+  if (((DECL_SECTION_NAME (original->decl) && !original->implicit_section)
+       || (DECL_SECTION_NAME (alias->decl) && !alias->implicit_section))
+      && DECL_SECTION_NAME (original->decl) != DECL_SECTION_NAME (alias->decl))
+    {
+      if (dump_file)
+	fprintf (dump_file,
+		 "Not unifying; original and alias are in different sections.\n\n");
+      return false;
+    }
+
+  /* See if original is in a section that can be discarded if the main
+     symbol is not used.  */
+  if (DECL_EXTERNAL (original->decl))
+    original_discardable = true;
+  if (original->resolution == LDPR_PREEMPTED_REG
+      || original->resolution == LDPR_PREEMPTED_IR)
+    original_discardable = true;
+  if (original->can_be_discarded_p ())
+    original_discardable = true;
+
+  /* See if original and/or alias address can be compared for equality.  */
+  original_address_matters
+    = (!DECL_VIRTUAL_P (original->decl)
+       && (original->externally_visible
+	   || original->address_taken_from_non_vtable_p ()));
+  alias_address_matters
+    = (!DECL_VIRTUAL_P (alias->decl)
+       && (alias->externally_visible
+	   || alias->address_taken_from_non_vtable_p ()));
+
+  /* If alias and original can be compared for address equality, we need
+     to create a thunk.  Also we can not create extra aliases into discardable
+     section (or we risk link failures when section is discarded).  */
+  if ((original_address_matters
+       && alias_address_matters)
+      || original_discardable)
+    {
+      create_thunk = !stdarg_p (TREE_TYPE (alias->decl));
+      create_alias = false;
+      /* When both alias and original are not overwritable, we can save
+         the extra thunk wrapper for direct calls.  */
+      redirect_callers
+	= (!original_discardable
+	   && alias->get_availability () > AVAIL_INTERPOSABLE
+	   && original->get_availability () > AVAIL_INTERPOSABLE);
+    }
+  else
+    {
+      create_alias = true;
+      create_thunk = false;
+      redirect_callers = false;
+    }
+
+  if (create_alias && DECL_COMDAT_GROUP (alias->decl))
+    {
+      create_alias = false;
+      create_thunk = true;
+    }
+
+  /* We want thunk to always jump to the local function body
+     unless the body is comdat and may be optimized out.  */
+  if ((create_thunk || redirect_callers)
+      && (!original_discardable
+	  || (DECL_COMDAT_GROUP (original->decl)
+	      && (DECL_COMDAT_GROUP (original->decl)
+		  == DECL_COMDAT_GROUP (alias->decl)))))
+    local_original
+      = dyn_cast <cgraph_node *> (original->noninterposable_alias ());
+
+  if (redirect_callers)
+    {
+      /* If alias is non-overwritable then
+         all direct calls are safe to be redirected to the original.  */
+      bool redirected = false;
+      while (alias->callers)
+	{
+	  cgraph_edge *e = alias->callers;
+	  e->redirect_callee (local_original);
+	  push_cfun (DECL_STRUCT_FUNCTION (e->caller->decl));
+
+	  if (e->call_stmt)
+	    e->redirect_call_stmt_to_callee ();
+
+	  pop_cfun ();
+	  redirected = true;
+	}
+
+      alias->icf_merged = true;
+
+      /* The alias function is removed if symbol address
+         does not matter.  */
+      if (!alias_address_matters)
+	alias->remove ();
+
+      if (dump_file && redirected)
+	fprintf (dump_file, "Callgraph local calls have been redirected.\n\n");
+    }
+  /* If the condtion above is not met, we are lucky and can turn the
+     function into real alias.  */
+  else if (create_alias)
+    {
+      alias->icf_merged = true;
+
+      /* Remove the function's body.  */
+      ipa_merge_profiles (original, alias);
+      alias->release_body (true);
+      alias->reset ();
+
+      /* Create the alias.  */
+      cgraph_node::create_alias (alias_func->decl, decl);
+      alias->resolve_alias (original);
+
+      if (dump_file)
+	fprintf (dump_file, "Callgraph alias has been created.\n\n");
+    }
+  else if (create_thunk)
+    {
+      if (DECL_COMDAT_GROUP (alias->decl))
+	{
+	  if (dump_file)
+	    fprintf (dump_file, "Callgraph thunk cannot be created because of COMDAT\n");
+
+	  return 0;
+	}
+
+      alias->icf_merged = true;
+      ipa_merge_profiles (local_original, alias);
+      alias->create_wrapper (local_original);
+
+      if (dump_file)
+	fprintf (dump_file, "Callgraph thunk has been created.\n\n");
+    }
+  else if (dump_file)
+    fprintf (dump_file, "Callgraph merge operation cannot be performed.\n\n");
+
+  return true;
+}
+
+/* Semantic item initialization function.  */
+
+void
+sem_function::init (void)
+{
+  if (in_lto_p)
+    get_node ()->get_body ();
+
+  tree fndecl = node->decl;
+  function *func = DECL_STRUCT_FUNCTION (fndecl);
+
+  gcc_assert (func);
+  gcc_assert (SSANAMES (func));
+
+  ssa_names_size = SSANAMES (func)->length ();
+  node = node;
+
+  decl = fndecl;
+  region_tree = func->eh->region_tree;
+
+  /* iterating all function arguments.  */
+  arg_count = count_formal_params (fndecl);
+
+  edge_count = n_edges_for_fn (func);
+  cfg_checksum = coverage_compute_cfg_checksum (func);
+
+  inchash::hash hstate;
+
+  basic_block bb;
+  FOR_EACH_BB_FN (bb, func)
+  {
+    unsigned nondbg_stmt_count = 0;
+
+    edge e;
+    for (edge_iterator ei = ei_start (bb->preds); ei_cond (ei, &e); ei_next (&ei))
+      cfg_checksum = iterative_hash_host_wide_int (e->flags,
+		     cfg_checksum);
+
+    for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi);
+	 gsi_next (&gsi))
+      {
+	gimple stmt = gsi_stmt (gsi);
+
+	if (gimple_code (stmt) != GIMPLE_DEBUG)
+	  {
+	    hash_stmt (&hstate, stmt);
+	    nondbg_stmt_count++;
+	  }
+      }
+
+    gcode_hash = hstate.end ();
+    bb_sizes.safe_push (nondbg_stmt_count);
+
+    /* Inserting basic block to hash table.  */
+    sem_bb *semantic_bb = new sem_bb (bb, nondbg_stmt_count,
+				      EDGE_COUNT (bb->preds) + EDGE_COUNT (bb->succs));
+
+    bb_sorted.safe_push (semantic_bb);
+  }
+
+  parse_tree_args ();
+}
+
+/* Improve accumulated hash for HSTATE based on a gimple statement STMT.  */
+
+void
+sem_function::hash_stmt (inchash::hash *hstate, gimple stmt)
+{
+  enum gimple_code code = gimple_code (stmt);
+
+  hstate->add_int (code);
+
+  if (code == GIMPLE_CALL)
+    {
+      /* Checking of argument.  */
+      for (unsigned i = 0; i < gimple_call_num_args (stmt); ++i)
+	{
+	  tree argument = gimple_call_arg (stmt, i);
+
+	  switch (TREE_CODE (argument))
+	    {
+	    case INTEGER_CST:
+	      if (tree_fits_shwi_p (argument))
+		hstate->add_wide_int (tree_to_shwi (argument));
+	      else if (tree_fits_uhwi_p (argument))
+		hstate->add_wide_int (tree_to_uhwi (argument));
+	      break;
+	    case REAL_CST:
+	      REAL_VALUE_TYPE c;
+	      HOST_WIDE_INT n;
+
+	      c = TREE_REAL_CST (argument);
+	      n = real_to_integer (&c);
+
+	      hstate->add_wide_int (n);
+	      break;
+	    case ADDR_EXPR:
+	      {
+		tree addr_operand = TREE_OPERAND (argument, 0);
+
+		if (TREE_CODE (addr_operand) == STRING_CST)
+		  hstate->add (TREE_STRING_POINTER (addr_operand),
+			       TREE_STRING_LENGTH (addr_operand));
+		break;
+	      }
+	    default:
+	      break;
+	    }
+	}
+    }
+}
+
+
+/* Return true if polymorphic comparison must be processed.  */
+
+bool
+sem_function::compare_polymorphic_p (void)
+{
+  return get_node ()->callees != NULL
+	 || m_compared_func->get_node ()->callees != NULL;
+}
+
+/* For a given call graph NODE, the function constructs new
+   semantic function item.  */
+
+sem_function *
+sem_function::parse (cgraph_node *node, bitmap_obstack *stack)
+{
+  tree fndecl = node->decl;
+  function *func = DECL_STRUCT_FUNCTION (fndecl);
+
+  /* TODO: add support for thunks and aliases.  */
+
+  if (!func || !node->has_gimple_body_p ())
+    return NULL;
+
+  if (lookup_attribute_by_prefix ("omp ", DECL_ATTRIBUTES (node->decl)) != NULL)
+    return NULL;
+
+  sem_function *f = new sem_function (node, 0, stack);
+
+  f->init ();
+
+  return f;
+}
+
+/* Parses function arguments and result type.  */
+
+void
+sem_function::parse_tree_args (void)
+{
+  tree result;
+
+  if (arg_types.exists ())
+    arg_types.release ();
+
+  arg_types.create (4);
+  tree fnargs = DECL_ARGUMENTS (decl);
+
+  for (tree parm = fnargs; parm; parm = DECL_CHAIN (parm))
+    arg_types.safe_push (DECL_ARG_TYPE (parm));
+
+  /* Function result type.  */
+  result = DECL_RESULT (decl);
+  result_type = result ? TREE_TYPE (result) : NULL;
+
+  /* During WPA, we can get arguments by following method.  */
+  if (!fnargs)
+    {
+      tree type = TYPE_ARG_TYPES (TREE_TYPE (decl));
+      for (tree parm = type; parm; parm = TREE_CHAIN (parm))
+	arg_types.safe_push (TYPE_CANONICAL (TREE_VALUE (parm)));
+
+      result_type = TREE_TYPE (TREE_TYPE (decl));
+    }
+}
+
+/* For given basic blocks BB1 and BB2 (from functions FUNC1 and FUNC),
+   return true if phi nodes are semantically equivalent in these blocks .  */
+
+bool
+sem_function::compare_phi_node (basic_block bb1, basic_block bb2)
+{
+  gimple_stmt_iterator si1, si2;
+  gimple phi1, phi2;
+  unsigned size1, size2, i;
+  tree t1, t2;
+  edge e1, e2;
+
+  gcc_assert (bb1 != NULL);
+  gcc_assert (bb2 != NULL);
+
+  si2 = gsi_start_phis (bb2);
+  for (si1 = gsi_start_phis (bb1); !gsi_end_p (si1);
+       gsi_next (&si1))
+    {
+      gsi_next_nonvirtual_phi (&si1);
+      gsi_next_nonvirtual_phi (&si2);
+
+      if (gsi_end_p (si1) && gsi_end_p (si2))
+	break;
+
+      if (gsi_end_p (si1) || gsi_end_p (si2))
+	return return_false();
+
+      phi1 = gsi_stmt (si1);
+      phi2 = gsi_stmt (si2);
+
+      size1 = gimple_phi_num_args (phi1);
+      size2 = gimple_phi_num_args (phi2);
+
+      if (size1 != size2)
+	return return_false ();
+
+      for (i = 0; i < size1; ++i)
+	{
+	  t1 = gimple_phi_arg (phi1, i)->def;
+	  t2 = gimple_phi_arg (phi2, i)->def;
+
+	  if (!m_checker->compare_operand (t1, t2))
+	    return return_false ();
+
+	  e1 = gimple_phi_arg_edge (phi1, i);
+	  e2 = gimple_phi_arg_edge (phi2, i);
+
+	  if (!m_checker->compare_edge (e1, e2))
+	    return return_false ();
+	}
+
+      gsi_next (&si2);
+    }
+
+  return true;
+}
+
+/* Returns true if tree T can be compared as a handled component.  */
+
+bool
+sem_function::icf_handled_component_p (tree t)
+{
+  tree_code tc = TREE_CODE (t);
+
+  return ((handled_component_p (t))
+	  || tc == ADDR_EXPR || tc == MEM_REF || tc == REALPART_EXPR
+	  || tc == IMAGPART_EXPR || tc == OBJ_TYPE_REF);
+}
+
+/* Basic blocks dictionary BB_DICT returns true if SOURCE index BB
+   corresponds to TARGET.  */
+
+bool
+sem_function::bb_dict_test (int* bb_dict, int source, int target)
+{
+  if (bb_dict[source] == -1)
+    {
+      bb_dict[source] = target;
+      return true;
+    }
+  else
+    return bb_dict[source] == target;
+}
+
+/* Iterates all tree types in T1 and T2 and returns true if all types
+   are compatible. If COMPARE_POLYMORPHIC is set to true,
+   more strict comparison is executed.  */
+
+bool
+sem_function::compare_type_list (tree t1, tree t2, bool compare_polymorphic)
+{
+  tree tv1, tv2;
+  tree_code tc1, tc2;
+
+  if (!t1 && !t2)
+    return true;
+
+  while (t1 != NULL && t2 != NULL)
+    {
+      tv1 = TREE_VALUE (t1);
+      tv2 = TREE_VALUE (t2);
+
+      tc1 = TREE_CODE (tv1);
+      tc2 = TREE_CODE (tv2);
+
+      if (tc1 == NOP_EXPR && tc2 == NOP_EXPR)
+	{}
+      else if (tc1 == NOP_EXPR || tc2 == NOP_EXPR)
+	return false;
+      else if (!func_checker::compatible_types_p (tv1, tv2, compare_polymorphic))
+	return false;
+
+      t1 = TREE_CHAIN (t1);
+      t2 = TREE_CHAIN (t2);
+    }
+
+  return !(t1 || t2);
+}
+
+
+/* Semantic variable constructor that uses STACK as bitmap memory stack.  */
+
+sem_variable::sem_variable (bitmap_obstack *stack): sem_item (VAR, stack)
+{
+}
+
+/*  Constructor based on varpool node _NODE with computed hash _HASH.
+    Bitmap STACK is used for memory allocation.  */
+
+sem_variable::sem_variable (varpool_node *node, hashval_t _hash,
+			    bitmap_obstack *stack): sem_item(VAR,
+				  node, _hash, stack)
+{
+  gcc_checking_assert (node);
+  gcc_checking_assert (get_node ());
+}
+
+/* Returns true if the item equals to ITEM given as argument.  */
+
+bool
+sem_variable::equals (sem_item *item,
+		      hash_map <symtab_node *, sem_item *> & ARG_UNUSED (ignored_nodes))
+{
+  gcc_assert (item->type == VAR);
+
+  sem_variable *v = static_cast<sem_variable *>(item);
+
+  if (!ctor || !v->ctor)
+    return return_false_with_msg ("ctor is missing for semantic variable");
+
+  return sem_variable::equals (ctor, v->ctor);
+}
+
+/* Compares trees T1 and T2 for semantic equality.  */
+
+bool
+sem_variable::equals (tree t1, tree t2)
+{
+  tree_code tc1 = TREE_CODE (t1);
+  tree_code tc2 = TREE_CODE (t2);
+
+  if (tc1 != tc2)
+    return false;
+
+  switch (tc1)
+    {
+    case CONSTRUCTOR:
+      {
+	unsigned len1 = vec_safe_length (CONSTRUCTOR_ELTS (t1));
+	unsigned len2 = vec_safe_length (CONSTRUCTOR_ELTS (t2));
+
+	if (len1 != len2)
+	  return false;
+
+	for (unsigned i = 0; i < len1; i++)
+	  if (!sem_variable::equals (CONSTRUCTOR_ELT (t1, i)->value,
+				     CONSTRUCTOR_ELT (t2, i)->value)
+	      || CONSTRUCTOR_ELT (t1, i)->index != CONSTRUCTOR_ELT (t2, i)->index)
+	    return false;
+
+	return true;
+      }
+    case MEM_REF:
+      {
+	tree x1 = TREE_OPERAND (t1, 0);
+	tree x2 = TREE_OPERAND (t2, 0);
+	tree y1 = TREE_OPERAND (t1, 1);
+	tree y2 = TREE_OPERAND (t2, 1);
+
+	if (!func_checker::compatible_types_p (TREE_TYPE (x1), TREE_TYPE (x2),
+					       true))
+	  return return_false ();
+
+	/* Type of the offset on MEM_REF does not matter.  */
+	return sem_variable::equals (x1, x2)
+	       && wi::to_offset  (y1) == wi::to_offset  (y2);
+      }
+    case NOP_EXPR:
+    case ADDR_EXPR:
+      {
+	tree op1 = TREE_OPERAND (t1, 0);
+	tree op2 = TREE_OPERAND (t2, 0);
+	return sem_variable::equals (op1, op2);
+      }
+    case FUNCTION_DECL:
+    case VAR_DECL:
+    case FIELD_DECL:
+    case LABEL_DECL:
+      return t1 == t2;
+    case INTEGER_CST:
+      return func_checker::compatible_types_p (TREE_TYPE (t1), TREE_TYPE (t2),
+	     true)
+	     && wi::to_offset (t1) == wi::to_offset (t2);
+    case STRING_CST:
+    case REAL_CST:
+    case COMPLEX_CST:
+      return operand_equal_p (t1, t2, OEP_ONLY_CONST);
+    case COMPONENT_REF:
+    case ARRAY_REF:
+    case POINTER_PLUS_EXPR:
+      {
+	tree x1 = TREE_OPERAND (t1, 0);
+	tree x2 = TREE_OPERAND (t2, 0);
+	tree y1 = TREE_OPERAND (t1, 1);
+	tree y2 = TREE_OPERAND (t2, 1);
+
+	return sem_variable::equals (x1, x2) && sem_variable::equals (y1, y2);
+      }
+    case ERROR_MARK:
+      return return_false_with_msg ("ERROR_MARK");
+    default:
+      return return_false_with_msg ("Unknown TREE code reached");
+    }
+}
+
+/* Parser function that visits a varpool NODE.  */
+
+sem_variable *
+sem_variable::parse (varpool_node *node, bitmap_obstack *stack)
+{
+  tree decl = node->decl;
+
+  bool readonly = TYPE_P (decl) ? TYPE_READONLY (decl) : TREE_READONLY (decl);
+  bool can_handle = readonly && (DECL_VIRTUAL_P (decl)
+				 || !TREE_ADDRESSABLE (decl));
+
+  if (!can_handle)
+    return NULL;
+
+  tree ctor = ctor_for_folding (decl);
+  if (!ctor)
+    return NULL;
+
+  sem_variable *v = new sem_variable (node, 0, stack);
+
+  v->init ();
+
+  return v;
+}
+
+/* References independent hash function.  */
+
+hashval_t
+sem_variable::get_hash (void)
+{
+  if (hash)
+    return hash;
+
+  inchash::hash hstate;
+
+  hstate.add_int (456346417);
+  hstate.add_int (TREE_CODE (ctor));
+
+  if (TREE_CODE (ctor) == CONSTRUCTOR)
+    {
+      unsigned length = vec_safe_length (CONSTRUCTOR_ELTS (ctor));
+      hstate.add_int (length);
+    }
+
+  hash = hstate.end ();
+
+  return hash;
+}
+
+/* Merges instance with an ALIAS_ITEM, where alias, thunk or redirection can
+   be applied.  */
+
+bool
+sem_variable::merge (sem_item *alias_item)
+{
+  gcc_assert (alias_item->type == VAR);
+
+  sem_variable *alias_var = static_cast<sem_variable *> (alias_item);
+
+  varpool_node *original = get_node ();
+  varpool_node *alias = alias_var->get_node ();
+  bool original_discardable = false;
+
+  /* See if original is in a section that can be discarded if the main
+     symbol is not used.  */
+  if (DECL_EXTERNAL (original->decl))
+    original_discardable = true;
+  if (original->resolution == LDPR_PREEMPTED_REG
+      || original->resolution == LDPR_PREEMPTED_IR)
+    original_discardable = true;
+  if (original->can_be_discarded_p ())
+    original_discardable = true;
+
+  gcc_assert (!TREE_ASM_WRITTEN (alias->decl));
+
+  if (original_discardable || DECL_EXTERNAL (alias_var->decl) ||
+      !compare_sections (alias_var))
+    {
+      if (dump_file)
+	fprintf (dump_file, "Varpool alias cannot be created\n\n");
+
+      return false;
+    }
+  else
+    {
+      // alias cycle creation check
+      varpool_node *n = original;
+
+      while (n->alias)
+	{
+	  n = n->get_alias_target ();
+	  if (n == alias)
+	    {
+	      if (dump_file)
+		fprintf (dump_file, "Varpool alias cannot be created (alias cycle).\n\n");
+
+	      return false;
+	    }
+	}
+
+      alias->analyzed = false;
+
+      DECL_INITIAL (alias->decl) = NULL;
+      alias->remove_all_references ();
+
+      varpool_node::create_alias (alias_var->decl, decl);
+      alias->resolve_alias (original);
+
+      if (dump_file)
+	fprintf (dump_file, "Varpool alias has been created.\n\n");
+
+      return true;
+    }
+}
+
+bool
+sem_variable::compare_sections (sem_variable *alias)
+{
+  const char *source = node->get_section ();
+  const char *target = alias->node->get_section();
+
+  if (source == NULL && target == NULL)
+    return true;
+  else if(!source || !target)
+    return false;
+  else
+    return strcmp (source, target) == 0;
+}
+
+/* Dump symbol to FILE.  */
+
+void
+sem_variable::dump_to_file (FILE *file)
+{
+  gcc_assert (file);
+
+  print_node (file, "", decl, 0);
+  fprintf (file, "\n\n");
+}
+
+/* Iterates though a constructor and identifies tree references
+   we are interested in semantic function equality.  */
+
+void
+sem_variable::parse_tree_refs (tree t)
+{
+  switch (TREE_CODE (t))
+    {
+    case CONSTRUCTOR:
+      {
+	unsigned length = vec_safe_length (CONSTRUCTOR_ELTS (t));
+
+	for (unsigned i = 0; i < length; i++)
+	  parse_tree_refs(CONSTRUCTOR_ELT (t, i)->value);
+
+	break;
+      }
+    case NOP_EXPR:
+    case ADDR_EXPR:
+      {
+	tree op = TREE_OPERAND (t, 0);
+	parse_tree_refs (op);
+	break;
+      }
+    case FUNCTION_DECL:
+      {
+	tree_refs.safe_push (t);
+	break;
+      }
+    default:
+      break;
+    }
+}
+
+unsigned int sem_item_optimizer::class_id = 0;
+
+sem_item_optimizer::sem_item_optimizer (): worklist (0), m_classes (0),
+  m_classes_count (0), m_cgraph_node_hooks (NULL), m_varpool_node_hooks (NULL)
+{
+  m_items.create (0);
+  bitmap_obstack_initialize (&m_bmstack);
+}
+
+sem_item_optimizer::~sem_item_optimizer ()
+{
+  for (unsigned int i = 0; i < m_items.length (); i++)
+    delete m_items[i];
+
+  for (hash_table<congruence_class_group_hash>::iterator it = m_classes.begin ();
+       it != m_classes.end (); ++it)
+    {
+      for (unsigned int i = 0; i < (*it)->classes.length (); i++)
+	delete (*it)->classes[i];
+
+      (*it)->classes.release ();
+    }
+
+  m_items.release ();
+
+  bitmap_obstack_release (&m_bmstack);
+}
+
+/* Write IPA ICF summary for symbols.  */
+
+void
+sem_item_optimizer::write_summary (void)
+{
+  unsigned int count = 0;
+
+  output_block *ob = create_output_block (LTO_section_ipa_icf);
+  lto_symtab_encoder_t encoder = ob->decl_state->symtab_node_encoder;
+  ob->symbol = NULL;
+
+  /* Calculate number of symbols to be serialized.  */
+  for (lto_symtab_encoder_iterator lsei = lsei_start_in_partition (encoder);
+       !lsei_end_p (lsei);
+       lsei_next_in_partition (&lsei))
+    {
+      symtab_node *node = lsei_node (lsei);
+
+      if (m_symtab_node_map.get (node))
+	count++;
+    }
+
+  streamer_write_uhwi (ob, count);
+
+  /* Process all of the symbols.  */
+  for (lto_symtab_encoder_iterator lsei = lsei_start_in_partition (encoder);
+       !lsei_end_p (lsei);
+       lsei_next_in_partition (&lsei))
+    {
+      symtab_node *node = lsei_node (lsei);
+
+      sem_item **item = m_symtab_node_map.get (node);
+
+      if (item && *item)
+	{
+	  int node_ref = lto_symtab_encoder_encode (encoder, node);
+	  streamer_write_uhwi_stream (ob->main_stream, node_ref);
+
+	  streamer_write_uhwi (ob, (*item)->get_hash ());
+	}
+    }
+
+  streamer_write_char_stream (ob->main_stream, 0);
+  produce_asm (ob, NULL);
+  destroy_output_block (ob);
+}
+
+/* Reads a section from LTO stream file FILE_DATA. Input block for DATA
+   contains LEN bytes.  */
+
+void
+sem_item_optimizer::read_section (lto_file_decl_data *file_data,
+				  const char *data, size_t len)
+{
+  const lto_function_header *header =
+    (const lto_function_header *) data;
+  const int cfg_offset = sizeof (lto_function_header);
+  const int main_offset = cfg_offset + header->cfg_size;
+  const int string_offset = main_offset + header->main_size;
+  data_in *data_in;
+  unsigned int i;
+  unsigned int count;
+
+  lto_input_block ib_main ((const char *) data + main_offset, 0,
+			   header->main_size);
+
+  data_in =
+    lto_data_in_create (file_data, (const char *) data + string_offset,
+			header->string_size, vNULL);
+
+  count = streamer_read_uhwi (&ib_main);
+
+  for (i = 0; i < count; i++)
+    {
+      unsigned int index;
+      symtab_node *node;
+      lto_symtab_encoder_t encoder;
+
+      index = streamer_read_uhwi (&ib_main);
+      encoder = file_data->symtab_node_encoder;
+      node = lto_symtab_encoder_deref (encoder, index);
+
+      hashval_t hash = streamer_read_uhwi (&ib_main);
+
+      gcc_assert (node->definition);
+
+      if (dump_file)
+	fprintf (dump_file, "Symbol added:%s (tree: %p, uid:%u)\n", node->asm_name (),
+		 (void *) node->decl, node->order);
+
+      if (is_a<cgraph_node *> (node))
+	{
+	  cgraph_node *cnode = dyn_cast <cgraph_node *> (node);
+
+	  m_items.safe_push (new sem_function (cnode, hash, &m_bmstack));
+	}
+      else
+	{
+	  varpool_node *vnode = dyn_cast <varpool_node *> (node);
+
+	  m_items.safe_push (new sem_variable (vnode, hash, &m_bmstack));
+	}
+    }
+
+  lto_free_section_data (file_data, LTO_section_ipa_icf, NULL, data,
+			 len);
+  lto_data_in_delete (data_in);
+}
+
+/* Read IPA IPA ICF summary for symbols.  */
+
+void
+sem_item_optimizer::read_summary (void)
+{
+  lto_file_decl_data **file_data_vec = lto_get_file_decl_data ();
+  lto_file_decl_data *file_data;
+  unsigned int j = 0;
+
+  while ((file_data = file_data_vec[j++]))
+    {
+      size_t len;
+      const char *data = lto_get_section_data (file_data,
+			 LTO_section_ipa_icf, NULL, &len);
+
+      if (data)
+	read_section (file_data, data, len);
+    }
+}
+
+/* Register callgraph and varpool hooks.  */
+
+void
+sem_item_optimizer::register_hooks (void)
+{
+  m_cgraph_node_hooks = symtab->add_cgraph_removal_hook
+			(&sem_item_optimizer::cgraph_removal_hook, this);
+
+  m_varpool_node_hooks = symtab->add_varpool_removal_hook
+			 (&sem_item_optimizer::varpool_removal_hook, this);
+}
+
+/* Unregister callgraph and varpool hooks.  */
+
+void
+sem_item_optimizer::unregister_hooks (void)
+{
+  if (m_cgraph_node_hooks)
+    symtab->remove_cgraph_removal_hook (m_cgraph_node_hooks);
+
+  if (m_varpool_node_hooks)
+    symtab->remove_varpool_removal_hook (m_varpool_node_hooks);
+}
+
+/* Adds a CLS to hashtable associated by hash value.  */
+
+void
+sem_item_optimizer::add_class (congruence_class *cls)
+{
+  gcc_assert (cls->members.length ());
+
+  congruence_class_group *group = get_group_by_hash (
+				    cls->members[0]->get_hash (),
+				    cls->members[0]->type);
+  group->classes.safe_push (cls);
+}
+
+/* Gets a congruence class group based on given HASH value and TYPE.  */
+
+congruence_class_group *
+sem_item_optimizer::get_group_by_hash (hashval_t hash, sem_item_type type)
+{
+  congruence_class_group *item = XNEW (congruence_class_group);
+  item->hash = hash;
+  item->type = type;
+
+  congruence_class_group **slot = m_classes.find_slot (item, INSERT);
+
+  if (*slot)
+    free (item);
+  else
+    {
+      item->classes.create (1);
+      *slot = item;
+    }
+
+  return *slot;
+}
+
+/* Callgraph removal hook called for a NODE with a custom DATA.  */
+
+void
+sem_item_optimizer::cgraph_removal_hook (cgraph_node *node, void *data)
+{
+  sem_item_optimizer *optimizer = (sem_item_optimizer *) data;
+  optimizer->remove_symtab_node (node);
+}
+
+/* Varpool removal hook called for a NODE with a custom DATA.  */
+
+void
+sem_item_optimizer::varpool_removal_hook (varpool_node *node, void *data)
+{
+  sem_item_optimizer *optimizer = (sem_item_optimizer *) data;
+  optimizer->remove_symtab_node (node);
+}
+
+/* Remove symtab NODE triggered by symtab removal hooks.  */
+
+void
+sem_item_optimizer::remove_symtab_node (symtab_node *node)
+{
+  gcc_assert (!m_classes.elements());
+
+  m_removed_items_set.add (node);
+}
+
+void
+sem_item_optimizer::remove_item (sem_item *item)
+{
+  if (m_symtab_node_map.get (item->node))
+    m_symtab_node_map.remove (item->node);
+  delete item;
+}
+
+/* Removes all callgraph and varpool nodes that are marked by symtab
+   as deleted.  */
+
+void
+sem_item_optimizer::filter_removed_items (void)
+{
+  auto_vec <sem_item *> filtered;
+
+  for (unsigned int i = 0; i < m_items.length(); i++)
+    {
+      sem_item *item = m_items[i];
+
+      if (!flag_ipa_icf_functions && item->type == FUNC)
+	{
+	  remove_item (item);
+	  continue;
+	}
+
+      if (!flag_ipa_icf_variables && item->type == VAR)
+	{
+	  remove_item (item);
+	  continue;
+	}
+
+      bool no_body_function = false;
+
+      if (item->type == FUNC)
+	{
+	  cgraph_node *cnode = static_cast <sem_function *>(item)->get_node ();
+
+	  no_body_function = in_lto_p && (cnode->alias || cnode->body_removed);
+	}
+
+      if(!m_removed_items_set.contains (m_items[i]->node)
+	  && !no_body_function)
+	{
+	  if (item->type == VAR || (!DECL_CXX_CONSTRUCTOR_P (item->decl)
+				    && !DECL_CXX_DESTRUCTOR_P (item->decl)))
+	    {
+	      filtered.safe_push (m_items[i]);
+	      continue;
+	    }
+	}
+
+      remove_item (item);
+    }
+
+  /* Clean-up of released semantic items.  */
+
+  m_items.release ();
+  for (unsigned int i = 0; i < filtered.length(); i++)
+    m_items.safe_push (filtered[i]);
+}
+
+/* Optimizer entry point.  */
+
+void
+sem_item_optimizer::execute (void)
+{
+  filter_removed_items ();
+  build_hash_based_classes ();
+
+  if (dump_file)
+    fprintf (dump_file, "Dump after hash based groups\n");
+  dump_cong_classes ();
+
+  for (unsigned int i = 0; i < m_items.length(); i++)
+    m_items[i]->init_wpa ();
+
+  build_graph ();
+
+  subdivide_classes_by_equality (true);
+
+  if (dump_file)
+    fprintf (dump_file, "Dump after WPA based types groups\n");
+
+  dump_cong_classes ();
+
+  process_cong_reduction ();
+  verify_classes ();
+
+  if (dump_file)
+    fprintf (dump_file, "Dump after callgraph-based congruence reduction\n");
+
+  dump_cong_classes ();
+
+  parse_nonsingleton_classes ();
+  subdivide_classes_by_equality ();
+
+  if (dump_file)
+    fprintf (dump_file, "Dump after full equality comparison of groups\n");
+
+  dump_cong_classes ();
+
+  unsigned int prev_class_count = m_classes_count;
+
+  process_cong_reduction ();
+  dump_cong_classes ();
+  verify_classes ();
+  merge_classes (prev_class_count);
+
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    symtab_node::dump_table (dump_file);
+}
+
+/* Function responsible for visiting all potential functions and
+   read-only variables that can be merged.  */
+
+void
+sem_item_optimizer::parse_funcs_and_vars (void)
+{
+  cgraph_node *cnode;
+
+  if (flag_ipa_icf_functions)
+    FOR_EACH_DEFINED_FUNCTION (cnode)
+    {
+      sem_function *f = sem_function::parse (cnode, &m_bmstack);
+      if (f)
+	{
+	  m_items.safe_push (f);
+	  m_symtab_node_map.put (cnode, f);
+
+	  if (dump_file)
+	    fprintf (dump_file, "Parsed function:%s\n", f->asm_name ());
+
+	  if (dump_file && (dump_flags & TDF_DETAILS))
+	    f->dump_to_file (dump_file);
+	}
+      else if (dump_file)
+	fprintf (dump_file, "Not parsed function:%s\n", cnode->asm_name ());
+    }
+
+  varpool_node *vnode;
+
+  if (flag_ipa_icf_variables)
+    FOR_EACH_DEFINED_VARIABLE (vnode)
+    {
+      sem_variable *v = sem_variable::parse (vnode, &m_bmstack);
+
+      if (v)
+	{
+	  m_items.safe_push (v);
+	  m_symtab_node_map.put (vnode, v);
+	}
+    }
+}
+
+/* Makes pairing between a congruence class CLS and semantic ITEM.  */
+
+void
+sem_item_optimizer::add_item_to_class (congruence_class *cls, sem_item *item)
+{
+  item->index_in_class = cls->members.length ();
+  cls->members.safe_push (item);
+  item->cls = cls;
+}
+
+/* Congruence classes are built by hash value.  */
+
+void
+sem_item_optimizer::build_hash_based_classes (void)
+{
+  for (unsigned i = 0; i < m_items.length (); i++)
+    {
+      sem_item *item = m_items[i];
+
+      congruence_class_group *group = get_group_by_hash (item->get_hash (),
+				      item->type);
+
+      if (!group->classes.length ())
+	{
+	  m_classes_count++;
+	  group->classes.safe_push (new congruence_class (class_id++));
+	}
+
+      add_item_to_class (group->classes[0], item);
+    }
+}
+
+/* Build references according to call graph.  */
+
+void
+sem_item_optimizer::build_graph (void)
+{
+  for (unsigned i = 0; i < m_items.length (); i++)
+    {
+      sem_item *item = m_items[i];
+      m_symtab_node_map.put (item->node, item);
+    }
+
+  for (unsigned i = 0; i < m_items.length (); i++)
+    {
+      sem_item *item = m_items[i];
+
+      if (item->type == FUNC)
+	{
+	  cgraph_node *cnode = dyn_cast <cgraph_node *> (item->node);
+
+	  cgraph_edge *e = cnode->callees;
+	  while (e)
+	    {
+	      sem_item **slot = m_symtab_node_map.get (e->callee);
+	      if (slot)
+		item->add_reference (*slot);
+
+	      e = e->next_callee;
+	    }
+	}
+
+      ipa_ref *ref = NULL;
+      for (unsigned i = 0; item->node->iterate_reference (i, ref); i++)
+	{
+	  sem_item **slot = m_symtab_node_map.get (ref->referred);
+	  if (slot)
+	    item->add_reference (*slot);
+	}
+    }
+}
+
+/* Semantic items in classes having more than one element and initialized.
+   In case of WPA, we load function body.  */
+
+void
+sem_item_optimizer::parse_nonsingleton_classes (void)
+{
+  unsigned int init_called_count = 0;
+
+  for (unsigned i = 0; i < m_items.length (); i++)
+    if (m_items[i]->cls->members.length () > 1)
+      {
+	m_items[i]->init ();
+	init_called_count++;
+      }
+
+  if (dump_file)
+    fprintf (dump_file, "Init called for %u items (%.2f%%).\n", init_called_count,
+	     100.0f * init_called_count / m_items.length ());
+}
+
+/* Equality function for semantic items is used to subdivide existing
+   classes. If IN_WPA, fast equality function is invoked.  */
+
+void
+sem_item_optimizer::subdivide_classes_by_equality (bool in_wpa)
+{
+  for (hash_table <congruence_class_group_hash>::iterator it = m_classes.begin ();
+       it != m_classes.end (); ++it)
+    {
+      unsigned int class_count = (*it)->classes.length ();
+
+      for (unsigned i = 0; i < class_count; i++)
+	{
+	  congruence_class *c = (*it)->classes [i];
+
+	  if (c->members.length() > 1)
+	    {
+	      auto_vec <sem_item *> new_vector;
+
+	      sem_item *first = c->members[0];
+	      new_vector.safe_push (first);
+
+	      unsigned class_split_first = (*it)->classes.length ();
+
+	      for (unsigned j = 1; j < c->members.length (); j++)
+		{
+		  sem_item *item = c->members[j];
+
+		  bool equals = in_wpa ? first->equals_wpa (item,
+				m_symtab_node_map) : first->equals (item, m_symtab_node_map);
+
+		  if (equals)
+		    new_vector.safe_push (item);
+		  else
+		    {
+		      bool integrated = false;
+
+		      for (unsigned k = class_split_first; k < (*it)->classes.length (); k++)
+			{
+			  sem_item *x = (*it)->classes[k]->members[0];
+			  bool equals = in_wpa ? x->equals_wpa (item,
+								m_symtab_node_map) : x->equals (item, m_symtab_node_map);
+
+			  if (equals)
+			    {
+			      integrated = true;
+			      add_item_to_class ((*it)->classes[k], item);
+
+			      break;
+			    }
+			}
+
+		      if (!integrated)
+			{
+			  congruence_class *c = new congruence_class (class_id++);
+			  m_classes_count++;
+			  add_item_to_class (c, item);
+
+			  (*it)->classes.safe_push (c);
+			}
+		    }
+		}
+
+	      // we replace newly created new_vector for the class we've just splitted
+	      c->members.release ();
+	      c->members.create (new_vector.length ());
+
+	      for (unsigned int j = 0; j < new_vector.length (); j++)
+		add_item_to_class (c, new_vector[j]);
+	    }
+	}
+    }
+
+  verify_classes ();
+}
+
+/* Verify congruence classes if checking is enabled.  */
+
+void
+sem_item_optimizer::verify_classes (void)
+{
+#if ENABLE_CHECKING
+  for (hash_table <congruence_class_group_hash>::iterator it = m_classes.begin ();
+       it != m_classes.end (); ++it)
+    {
+      for (unsigned int i = 0; i < (*it)->classes.length (); i++)
+	{
+	  congruence_class *cls = (*it)->classes[i];
+
+	  gcc_checking_assert (cls);
+	  gcc_checking_assert (cls->members.length () > 0);
+
+	  for (unsigned int j = 0; j < cls->members.length (); j++)
+	    {
+	      sem_item *item = cls->members[j];
+
+	      gcc_checking_assert (item);
+	      gcc_checking_assert (item->cls == cls);
+
+	      for (unsigned k = 0; k < item->usages.length (); k++)
+		{
+		  sem_usage_pair *usage = item->usages[k];
+		  gcc_checking_assert (usage->item->index_in_class <
+				       usage->item->cls->members.length ());
+		}
+	    }
+	}
+    }
+#endif
+}
+
+/* Disposes split map traverse function. CLS_PTR is pointer to congruence
+   class, BSLOT is bitmap slot we want to release. DATA is mandatory,
+   but unused argument.  */
+
+bool
+sem_item_optimizer::release_split_map (congruence_class * const &,
+				       bitmap const &b, traverse_split_pair *)
+{
+  bitmap bmp = b;
+
+  BITMAP_FREE (bmp);
+
+  return true;
+}
+
+/* Process split operation for a class given as pointer CLS_PTR,
+   where bitmap B splits congruence class members. DATA is used
+   as argument of split pair.  */
+
+bool
+sem_item_optimizer::traverse_congruence_split (congruence_class * const &cls,
+    bitmap const &b, traverse_split_pair *pair)
+{
+  sem_item_optimizer *optimizer = pair->optimizer;
+  const congruence_class *splitter_cls = pair->cls;
+
+  /* If counted bits are greater than zero and less than the number of members
+     a group will be splitted.  */
+  unsigned popcount = bitmap_count_bits (b);
+
+  if (popcount > 0 && popcount < cls->members.length ())
+    {
+      congruence_class* newclasses[2] = { new congruence_class (class_id++), new congruence_class (class_id++) };
+
+      for (unsigned int i = 0; i < cls->members.length (); i++)
+	{
+	  int target = bitmap_bit_p (b, i);
+	  congruence_class *tc = newclasses[target];
+
+	  add_item_to_class (tc, cls->members[i]);
+	}
+
+#ifdef ENABLE_CHECKING
+      for (unsigned int i = 0; i < 2; i++)
+	gcc_checking_assert (newclasses[i]->members.length ());
+#endif
+
+      if (splitter_cls == cls)
+	optimizer->splitter_class_removed = true;
+
+      /* Remove old class from worklist if presented.  */
+      bool in_worklist = cls->in_worklist;
+
+      if (in_worklist)
+	cls->in_worklist = false;
+
+      congruence_class_group g;
+      g.hash = cls->members[0]->get_hash ();
+      g.type = cls->members[0]->type;
+
+      congruence_class_group *slot = optimizer->m_classes.find(&g);
+
+      for (unsigned int i = 0; i < slot->classes.length (); i++)
+	if (slot->classes[i] == cls)
+	  {
+	    slot->classes.ordered_remove (i);
+	    break;
+	  }
+
+      /* New class will be inserted and integrated to work list.  */
+      for (unsigned int i = 0; i < 2; i++)
+	optimizer->add_class (newclasses[i]);
+
+      /* Two classes replace one, so that increment just by one.  */
+      optimizer->m_classes_count++;
+
+      /* If OLD class was presented in the worklist, we remove the class
+         and replace it will both newly created classes.  */
+      if (in_worklist)
+	for (unsigned int i = 0; i < 2; i++)
+	  optimizer->worklist_push (newclasses[i]);
+      else /* Just smaller class is inserted.  */
+	{
+	  unsigned int smaller_index = newclasses[0]->members.length () <
+				       newclasses[1]->members.length () ?
+				       0 : 1;
+	  optimizer->worklist_push (newclasses[smaller_index]);
+	}
+
+      if (dump_file && (dump_flags & TDF_DETAILS))
+	{
+	  fprintf (dump_file, "  congruence class splitted:\n");
+	  cls->dump (dump_file, 4);
+
+	  fprintf (dump_file, "  newly created groups:\n");
+	  for (unsigned int i = 0; i < 2; i++)
+	    newclasses[i]->dump (dump_file, 4);
+	}
+
+      /* Release class if not presented in work list.  */
+      if (!in_worklist)
+	delete cls;
+    }
+
+
+  return true;
+}
+
+/* Tests if a class CLS used as INDEXth splits any congruence classes.
+   Bitmap stack BMSTACK is used for bitmap allocation.  */
+
+void
+sem_item_optimizer::do_congruence_step_for_index (congruence_class *cls,
+    unsigned int index)
+{
+  hash_map <congruence_class *, bitmap> split_map;
+
+  for (unsigned int i = 0; i < cls->members.length (); i++)
+    {
+      sem_item *item = cls->members[i];
+
+      /* Iterate all usages that have INDEX as usage of the item.  */
+      for (unsigned int j = 0; j < item->usages.length (); j++)
+	{
+	  sem_usage_pair *usage = item->usages[j];
+
+	  if (usage->index != index)
+	    continue;
+
+	  bitmap *slot = split_map.get (usage->item->cls);
+	  bitmap b;
+
+	  if(!slot)
+	    {
+	      b = BITMAP_ALLOC (&m_bmstack);
+	      split_map.put (usage->item->cls, b);
+	    }
+	  else
+	    b = *slot;
+
+#if ENABLE_CHECKING
+	  gcc_checking_assert (usage->item->cls);
+	  gcc_checking_assert (usage->item->index_in_class <
+			       usage->item->cls->members.length ());
+#endif
+
+	  bitmap_set_bit (b, usage->item->index_in_class);
+	}
+    }
+
+  traverse_split_pair pair;
+  pair.optimizer = this;
+  pair.cls = cls;
+
+  splitter_class_removed = false;
+  split_map.traverse
+  <traverse_split_pair *, sem_item_optimizer::traverse_congruence_split> (&pair);
+
+  /* Bitmap clean-up.  */
+  split_map.traverse
+  <traverse_split_pair *, sem_item_optimizer::release_split_map> (NULL);
+}
+
+/* Every usage of a congruence class CLS is a candidate that can split the
+   collection of classes. Bitmap stack BMSTACK is used for bitmap
+   allocation.  */
+
+void
+sem_item_optimizer::do_congruence_step (congruence_class *cls)
+{
+  bitmap_iterator bi;
+  unsigned int i;
+
+  bitmap usage = BITMAP_ALLOC (&m_bmstack);
+
+  for (unsigned int i = 0; i < cls->members.length (); i++)
+    bitmap_ior_into (usage, cls->members[i]->usage_index_bitmap);
+
+  EXECUTE_IF_SET_IN_BITMAP (usage, 0, i, bi)
+  {
+    if (dump_file && (dump_flags & TDF_DETAILS))
+      fprintf (dump_file, "  processing congruece step for class: %u, index: %u\n",
+	       cls->id, i);
+
+    do_congruence_step_for_index (cls, i);
+
+    if (splitter_class_removed)
+      break;
+  }
+
+  BITMAP_FREE (usage);
+}
+
+/* Adds a newly created congruence class CLS to worklist.  */
+
+void
+sem_item_optimizer::worklist_push (congruence_class *cls)
+{
+  /* Return if the class CLS is already presented in work list.  */
+  if (cls->in_worklist)
+    return;
+
+  cls->in_worklist = true;
+  worklist.push_back (cls);
+}
+
+/* Pops a class from worklist. */
+
+congruence_class *
+sem_item_optimizer::worklist_pop (void)
+{
+  congruence_class *cls;
+
+  while (!worklist.empty ())
+    {
+      cls = worklist.front ();
+      worklist.pop_front ();
+      if (cls->in_worklist)
+	{
+	  cls->in_worklist = false;
+
+	  return cls;
+	}
+      else
+	{
+	  /* Work list item was already intended to be removed.
+	     The only reason for doing it is to split a class.
+	     Thus, the class CLS is deleted.  */
+	  delete cls;
+	}
+    }
+
+  return NULL;
+}
+
+/* Iterative congruence reduction function.  */
+
+void
+sem_item_optimizer::process_cong_reduction (void)
+{
+  for (hash_table<congruence_class_group_hash>::iterator it = m_classes.begin ();
+       it != m_classes.end (); ++it)
+    for (unsigned i = 0; i < (*it)->classes.length (); i++)
+      if ((*it)->classes[i]->is_class_used ())
+	worklist_push ((*it)->classes[i]);
+
+  if (dump_file)
+    fprintf (dump_file, "Worklist has been filled with: %lu\n",
+	     worklist.size ());
+
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    fprintf (dump_file, "Congruence class reduction\n");
+
+  congruence_class *cls;
+  while ((cls = worklist_pop ()) != NULL)
+    do_congruence_step (cls);
+}
+
+/* Debug function prints all informations about congruence classes.  */
+
+void
+sem_item_optimizer::dump_cong_classes (void)
+{
+  if (!dump_file)
+    return;
+
+  fprintf (dump_file,
+	   "Congruence classes: %u (unique hash values: %lu), with total: %u items\n",
+	   m_classes_count, m_classes.elements(), m_items.length ());
+
+  /* Histogram calculation.  */
+  unsigned int max_index = 0;
+  unsigned int* histogram = XCNEWVEC (unsigned int, m_items.length () + 1);
+
+  for (hash_table<congruence_class_group_hash>::iterator it = m_classes.begin ();
+       it != m_classes.end (); ++it)
+
+    for (unsigned i = 0; i < (*it)->classes.length (); i++)
+      {
+	unsigned int c = (*it)->classes[i]->members.length ();
+	histogram[c]++;
+
+	if (c > max_index)
+	  max_index = c;
+      }
+
+  fprintf (dump_file,
+	   "Class size histogram [num of members]: number of classe number of classess\n");
+
+  for (unsigned int i = 0; i <= max_index; i++)
+    if (histogram[i])
+      fprintf (dump_file, "[%u]: %u classes\n", i, histogram[i]);
+
+  fprintf (dump_file, "\n\n");
+
+
+  if (dump_flags & TDF_DETAILS)
+    for (hash_table<congruence_class_group_hash>::iterator it = m_classes.begin ();
+	 it != m_classes.end (); ++it)
+      {
+	fprintf (dump_file, "  group: with %u classes:\n", (*it)->classes.length ());
+
+	for (unsigned i = 0; i < (*it)->classes.length (); i++)
+	  {
+	    (*it)->classes[i]->dump (dump_file, 4);
+
+	    if(i < (*it)->classes.length () - 1)
+	      fprintf (dump_file, " ");
+	  }
+      }
+
+  free (histogram);
+}
+
+/* After reduction is done, we can declare all items in a group
+   to be equal. PREV_CLASS_COUNT is start number of classes
+   before reduction.  */
+
+void
+sem_item_optimizer::merge_classes (unsigned int prev_class_count)
+{
+  unsigned int item_count = m_items.length ();
+  unsigned int class_count = m_classes_count;
+  unsigned int equal_items = item_count - class_count;
+
+  unsigned int non_singular_classes_count = 0;
+  unsigned int non_singular_classes_sum = 0;
+
+  for (hash_table<congruence_class_group_hash>::iterator it = m_classes.begin ();
+       it != m_classes.end (); ++it)
+    for (unsigned int i = 0; i < (*it)->classes.length (); i++)
+      {
+	congruence_class *c = (*it)->classes[i];
+	if (c->members.length () > 1)
+	  {
+	    non_singular_classes_count++;
+	    non_singular_classes_sum += c->members.length ();
+	  }
+      }
+
+  if (dump_file)
+    {
+      fprintf (dump_file, "\nItem count: %u\n", item_count);
+      fprintf (dump_file, "Congruent classes before: %u, after: %u\n",
+	       prev_class_count, class_count);
+      fprintf (dump_file, "Average class size before: %.2f, after: %.2f\n",
+	       1.0f * item_count / prev_class_count,
+	       1.0f * item_count / class_count);
+      fprintf (dump_file, "Average non-singular class size: %.2f, count: %u\n",
+	       1.0f * non_singular_classes_sum / non_singular_classes_count,
+	       non_singular_classes_count);
+      fprintf (dump_file, "Equal symbols: %u\n", equal_items);
+      fprintf (dump_file, "Fraction of visited symbols: %.2f%%\n\n",
+	       100.0f * equal_items / item_count);
+    }
+
+  for (hash_table<congruence_class_group_hash>::iterator it = m_classes.begin ();
+       it != m_classes.end (); ++it)
+    for (unsigned int i = 0; i < (*it)->classes.length (); i++)
+      {
+	congruence_class *c = (*it)->classes[i];
+
+	if (c->members.length () == 1)
+	  continue;
+
+	gcc_assert (c->members.length ());
+
+	sem_item *source = c->members[0];
+
+	for (unsigned int j = 1; j < c->members.length (); j++)
+	  {
+	    sem_item *alias = c->members[j];
+	    source->equals (alias, m_symtab_node_map);
+
+	    if (dump_file)
+	      {
+		fprintf (dump_file, "Semantic equality hit:%s->%s\n",
+			 source->name (), alias->name ());
+		fprintf (dump_file, "Assembler symbol names:%s->%s\n",
+			 source->asm_name (), alias->asm_name ());
+	      }
+
+	    if (dump_file && (dump_flags & TDF_DETAILS))
+	      {
+		source->dump_to_file (dump_file);
+		alias->dump_to_file (dump_file);
+	      }
+
+	    source->merge (alias);
+	  }
+      }
+}
+
+/* Dump function prints all class members to a FILE with an INDENT.  */
+
+void
+congruence_class::dump (FILE *file, unsigned int indent) const
+{
+  FPRINTF_SPACES (file, indent, "class with id: %u, hash: %u, items: %u\n",
+		  id, members[0]->get_hash (), members.length ());
+
+  FPUTS_SPACES (file, indent + 2, "");
+  for (unsigned i = 0; i < members.length (); i++)
+    fprintf (file, "%s(%p/%u) ", members[i]->asm_name (), (void *) members[i]->decl,
+	     members[i]->node->order);
+
+  fprintf (file, "\n");
+}
+
+/* Returns true if there's a member that is used from another group.  */
+
+bool
+congruence_class::is_class_used (void)
+{
+  for (unsigned int i = 0; i < members.length (); i++)
+    if (members[i]->usages.length ())
+      return true;
+
+  return false;
+}
+
+/* Initialization and computation of symtab node hash, there data
+   are propagated later on.  */
+
+static sem_item_optimizer *optimizer = NULL;
+
+/* Generate pass summary for IPA ICF pass.  */
+
+static void
+ipa_icf_generate_summary (void)
+{
+  if (!optimizer)
+    optimizer = new sem_item_optimizer ();
+
+  optimizer->parse_funcs_and_vars ();
+}
+
+/* Write pass summary for IPA ICF pass.  */
+
+static void
+ipa_icf_write_summary (void)
+{
+  gcc_assert (optimizer);
+
+  optimizer->write_summary ();
+}
+
+/* Read pass summary for IPA ICF pass.  */
+
+static void
+ipa_icf_read_summary (void)
+{
+  if (!optimizer)
+    optimizer = new sem_item_optimizer ();
+
+  optimizer->read_summary ();
+  optimizer->register_hooks ();
+}
+
+/* Semantic equality exection function.  */
+
+static unsigned int
+ipa_icf_driver (void)
+{
+  gcc_assert (optimizer);
+
+  optimizer->execute ();
+  optimizer->unregister_hooks ();
+
+  delete optimizer;
+
+  return 0;
+}
+
+const pass_data pass_data_ipa_icf =
+{
+  IPA_PASS,		    /* type */
+  "icf",		    /* name */
+  OPTGROUP_IPA,             /* optinfo_flags */
+  TV_IPA_ICF,		    /* tv_id */
+  0,                        /* properties_required */
+  0,                        /* properties_provided */
+  0,                        /* properties_destroyed */
+  0,                        /* todo_flags_start */
+  0,                        /* todo_flags_finish */
+};
+
+class pass_ipa_icf : public ipa_opt_pass_d
+{
+public:
+  pass_ipa_icf (gcc::context *ctxt)
+    : ipa_opt_pass_d (pass_data_ipa_icf, ctxt,
+		      ipa_icf_generate_summary, /* generate_summary */
+		      ipa_icf_write_summary, /* write_summary */
+		      ipa_icf_read_summary, /* read_summary */
+		      NULL, /*
+		      write_optimization_summary */
+		      NULL, /*
+		      read_optimization_summary */
+		      NULL, /* stmt_fixup */
+		      0, /* function_transform_todo_flags_start */
+		      NULL, /* function_transform */
+		      NULL) /* variable_transform */
+  {}
+
+  /* opt_pass methods: */
+  virtual bool gate (function *)
+  {
+    return flag_ipa_icf_variables || flag_ipa_icf_functions;
+  }
+
+  virtual unsigned int execute (function *)
+  {
+    return ipa_icf_driver();
+  }
+}; // class pass_ipa_icf
+
+} // ipa_icf namespace
+
+ipa_opt_pass_d *
+make_pass_ipa_icf (gcc::context *ctxt)
+{
+  return new ipa_icf::pass_ipa_icf (ctxt);
+}
diff --git a/gcc/ipa-icf.h b/gcc/ipa-icf.h
new file mode 100644
index 0000000..d8e7b16
--- /dev/null
+++ b/gcc/ipa-icf.h
@@ -0,0 +1,553 @@
+/* Interprocedural semantic function equality pass
+   Copyright (C) 2014 Free Software Foundation, Inc.
+
+   Contributed by Jan Hubicka <hubicka@ucw.cz> and Martin Liska <mliska@suse.cz>
+
+This file is part of GCC.
+
+GCC 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, or (at your option) any later
+version.
+
+GCC 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 GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+namespace ipa_icf {
+class sem_item;
+
+/* Congruence class encompasses a collection of either functions or
+   read-only variables. These items are considered to be equivalent
+   if not proved the oposite.  */
+class congruence_class
+{
+public:
+  /* Congruence class constructor for a new class with _ID.  */
+  congruence_class (unsigned int _id): in_worklist (false), id(_id)
+  {
+  }
+
+  /* Destructor.  */
+  ~congruence_class ()
+  {
+  }
+
+  /* Dump function prints all class members to a FILE with an INDENT.  */
+  void dump (FILE *file, unsigned int indent = 0) const;
+
+  /* Returns true if there's a member that is used from another group.  */
+  bool is_class_used (void);
+
+  /* Flag is used in case we want to remove a class from worklist and
+     delete operation is quite expensive for
+     the data structure (linked list).  */
+  bool in_worklist;
+
+  /* Vector of all group members.  */
+  auto_vec <sem_item *> members;
+
+  /* Global unique class identifier.  */
+  unsigned int id;
+};
+
+/* Semantic item type enum.  */
+enum sem_item_type
+{
+  FUNC,
+  VAR
+};
+
+/* Semantic item usage pair.  */
+class sem_usage_pair
+{
+public:
+  /* Constructor for key value pair, where _ITEM is key and _INDEX is a target.  */
+  sem_usage_pair (sem_item *_item, unsigned int _index);
+
+  /* Target semantic item where an item is used.  */
+  sem_item *item;
+
+  /* Index of usage of such an item.  */
+  unsigned int index;
+};
+
+/* Semantic item is a base class that encapsulates all shared functionality
+   for both semantic function and variable items.  */
+class sem_item
+{
+public:
+  /* Semantic item constructor for a node of _TYPE, where STACK is used
+     for bitmap memory allocation.  */
+  sem_item (sem_item_type _type, bitmap_obstack *stack);
+
+  /* Semantic item constructor for a node of _TYPE, where STACK is used
+     for bitmap memory allocation. The item is based on symtab node _NODE
+     with computed _HASH.  */
+  sem_item (sem_item_type _type, symtab_node *_node, hashval_t _hash,
+	    bitmap_obstack *stack);
+
+  virtual ~sem_item ();
+
+  /* Dump function for debugging purpose.  */
+  DEBUG_FUNCTION void dump (void);
+
+  /* Initialize semantic item by info reachable during LTO WPA phase.  */
+  virtual void init_wpa (void) = 0;
+
+  /* Semantic item initialization function.  */
+  virtual void init (void) = 0;
+
+  /* Add reference to a semantic TARGET.  */
+  void add_reference (sem_item *target);
+
+  /* Gets symbol name of the item.  */
+  const char *name (void)
+  {
+    return node->name ();
+  }
+
+  /* Gets assembler name of the item.  */
+  const char *asm_name (void)
+  {
+    return node->asm_name ();
+  }
+
+  /* Fast equality function based on knowledge known in WPA.  */
+  virtual bool equals_wpa (sem_item *item,
+			   hash_map <symtab_node *, sem_item *> &ignored_nodes) = 0;
+
+  /* Returns true if the item equals to ITEM given as arguemnt.  */
+  virtual bool equals (sem_item *item,
+		       hash_map <symtab_node *, sem_item *> &ignored_nodes) = 0;
+
+  /* References independent hash function.  */
+  virtual hashval_t get_hash (void) = 0;
+
+  /* Merges instance with an ALIAS_ITEM, where alias, thunk or redirection can
+     be applied.  */
+  virtual bool merge (sem_item *alias_item) = 0;
+
+  /* Dump symbol to FILE.  */
+  virtual void dump_to_file (FILE *file) = 0;
+
+  /* Return base tree that can be used for compatible_types_p and
+     contains_polymorphic_type_p comparison.  */
+
+  static bool get_base_types (tree *t1, tree *t2);
+
+  /* Item type.  */
+  sem_item_type type;
+
+  /* Symtab node.  */
+  symtab_node *node;
+
+  /* Declaration tree node.  */
+  tree decl;
+
+  /* Semantic references used that generate congruence groups.  */
+  vec <sem_item *> refs;
+
+  /* Pointer to a congruence class the item belongs to.  */
+  congruence_class *cls;
+
+  /* Index of the item in a class belonging to.  */
+  unsigned int index_in_class;
+
+  /* List of semantic items where the instance is used.  */
+  vec <sem_usage_pair *> usages;
+
+  /* A bitmap with indices of all classes referencing this item.  */
+  bitmap usage_index_bitmap;
+
+  /* List of tree references (either FUNC_DECL or VAR_DECL).  */
+  vec <tree> tree_refs;
+
+  /* A set with symbol table references.  */
+  hash_set <symtab_node *> refs_set;
+
+protected:
+  /* Cached, once calculated hash for the item.  */
+  hashval_t hash;
+
+private:
+  /* Initialize internal data structures. Bitmap STACK is used for
+     bitmap memory allocation process.  */
+  void setup (bitmap_obstack *stack);
+}; // class sem_item
+
+class sem_function: public sem_item
+{
+public:
+  /* Semantic function constructor that uses STACK as bitmap memory stack.  */
+  sem_function (bitmap_obstack *stack);
+
+  /*  Constructor based on callgraph node _NODE with computed hash _HASH.
+      Bitmap STACK is used for memory allocation.  */
+  sem_function (cgraph_node *_node, hashval_t _hash, bitmap_obstack *stack);
+
+  ~sem_function ();
+
+  inline virtual void init_wpa (void)
+  {
+    parse_tree_args ();
+  }
+
+  virtual void init (void);
+  virtual bool equals_wpa (sem_item *item,
+			   hash_map <symtab_node *, sem_item *> &ignored_nodes);
+  virtual hashval_t get_hash (void);
+  virtual bool equals (sem_item *item,
+		       hash_map <symtab_node *, sem_item *> &ignored_nodes);
+  virtual bool merge (sem_item *alias_item);
+
+  /* Dump symbol to FILE.  */
+  virtual void dump_to_file (FILE *file)
+  {
+    gcc_assert (file);
+    dump_function_to_file (decl, file, TDF_DETAILS);
+  }
+
+  /* Parses function arguments and result type.  */
+  void parse_tree_args (void);
+
+  /* Returns cgraph_node.  */
+  inline cgraph_node *get_node (void)
+  {
+    return dyn_cast <cgraph_node *> (node);
+  }
+
+  /* Improve accumulated hash for HSTATE based on a gimple statement STMT.  */
+  void hash_stmt (inchash::hash *inchash, gimple stmt);
+
+  /* Return true if polymorphic comparison must be processed.  */
+  bool compare_polymorphic_p (void);
+
+  /* For a given call graph NODE, the function constructs new
+     semantic function item.  */
+  static sem_function *parse (cgraph_node *node, bitmap_obstack *stack);
+
+  /* Exception handling region tree.  */
+  eh_region region_tree;
+
+  /* Result type tree node.  */
+  tree result_type;
+
+  /* Array of argument tree types.  */
+  vec <tree> arg_types;
+
+  /* Number of function arguments.  */
+  unsigned int arg_count;
+
+  /* Total amount of edges in the function.  */
+  unsigned int edge_count;
+
+  /* Vector of sizes of all basic blocks.  */
+  vec <unsigned int> bb_sizes;
+
+  /* Control flow graph checksum.  */
+  hashval_t cfg_checksum;
+
+  /* GIMPLE codes hash value.  */
+  hashval_t gcode_hash;
+
+  /* Total number of SSA names used in the function.  */
+  unsigned ssa_names_size;
+
+  /* Array of structures for all basic blocks.  */
+  vec <ipa_icf_gimple::sem_bb *> bb_sorted;
+
+private:
+  /* Calculates hash value based on a BASIC_BLOCK.  */
+  hashval_t get_bb_hash (const ipa_icf_gimple::sem_bb *basic_block);
+
+  /* For given basic blocks BB1 and BB2 (from functions FUNC1 and FUNC),
+     true value is returned if phi nodes are semantically
+     equivalent in these blocks .  */
+  bool compare_phi_node (basic_block bb1, basic_block bb2);
+
+  /* Basic blocks dictionary BB_DICT returns true if SOURCE index BB
+     corresponds to TARGET.  */
+  bool bb_dict_test (int* bb_dict, int source, int target);
+
+  /* Iterates all tree types in T1 and T2 and returns true if all types
+     are compatible. If COMPARE_POLYMORPHIC is set to true,
+     more strict comparison is executed.  */
+  bool compare_type_list (tree t1, tree t2, bool compare_polymorphic);
+
+  /* If cgraph edges E1 and E2 are indirect calls, verify that
+     ICF flags are the same.  */
+  bool compare_edge_flags (cgraph_edge *e1, cgraph_edge *e2);
+
+  /* For a given symbol table nodes N1 and N2, we check that FUNCTION_DECLs
+     point to a same function. Comparison can be skipped if IGNORED_NODES
+     contains these nodes.  */
+  bool compare_cgraph_references (hash_map <symtab_node *, sem_item *>
+				  &ignored_nodes,
+				  symtab_node *n1, symtab_node *n2);
+
+  /* Processes function equality comparison.  */
+  bool equals_private (sem_item *item,
+		       hash_map <symtab_node *, sem_item *> &ignored_nodes);
+
+  /* Returns true if tree T can be compared as a handled component.  */
+  static bool icf_handled_component_p (tree t);
+
+  /* Function checker stores binding between functions.   */
+  ipa_icf_gimple::func_checker *m_checker;
+
+  /* COMPARED_FUNC is a function that we compare to.  */
+  sem_function *m_compared_func;
+}; // class sem_function
+
+class sem_variable: public sem_item
+{
+public:
+  /* Semantic variable constructor that uses STACK as bitmap memory stack.  */
+  sem_variable (bitmap_obstack *stack);
+
+  /*  Constructor based on callgraph node _NODE with computed hash _HASH.
+      Bitmap STACK is used for memory allocation.  */
+
+  sem_variable (varpool_node *_node, hashval_t _hash, bitmap_obstack *stack);
+
+  inline virtual void init_wpa (void) {}
+
+  /* Semantic variable initialization function.  */
+  inline virtual void init (void)
+  {
+    decl = get_node ()->decl;
+    ctor = ctor_for_folding (decl);
+  }
+
+  virtual hashval_t get_hash (void);
+  virtual bool merge (sem_item *alias_item);
+  virtual void dump_to_file (FILE *file);
+  virtual bool equals (sem_item *item,
+		       hash_map <symtab_node *, sem_item *> &ignored_nodes);
+
+  /* Fast equality variable based on knowledge known in WPA.  */
+  inline virtual bool equals_wpa (sem_item *item,
+				  hash_map <symtab_node *, sem_item *> & ARG_UNUSED(ignored_nodes))
+  {
+    gcc_assert (item->type == VAR);
+    return true;
+  }
+
+  /* Returns varpool_node.  */
+  inline varpool_node *get_node (void)
+  {
+    return dyn_cast <varpool_node *> (node);
+  }
+
+  /* Parser function that visits a varpool NODE.  */
+  static sem_variable *parse (varpool_node *node, bitmap_obstack *stack);
+
+  /* Variable constructor.  */
+  tree ctor;
+
+private:
+  /* Iterates though a constructor and identifies tree references
+     we are interested in semantic function equality.  */
+  void parse_tree_refs (tree t);
+
+  /* Compares trees T1 and T2 for semantic equality.  */
+  static bool equals (tree t1, tree t2);
+
+  /* Compare that symbol sections are either NULL or have same name.  */
+  bool compare_sections (sem_variable *alias);
+
+}; // class sem_variable
+
+class sem_item_optimizer;
+
+struct congruence_class_group
+{
+  hashval_t hash;
+  sem_item_type type;
+  vec <congruence_class *> classes;
+};
+
+/* Congruence class set structure.  */
+struct congruence_class_group_hash: typed_noop_remove <congruence_class_group>
+{
+  typedef congruence_class_group value_type;
+  typedef congruence_class_group compare_type;
+
+  static inline hashval_t hash (const value_type *item)
+  {
+    return item->hash;
+  }
+
+  static inline int equal (const value_type *item1, const compare_type *item2)
+  {
+    return item1->hash == item2->hash && item1->type == item2->type;
+  }
+};
+
+struct traverse_split_pair
+{
+  sem_item_optimizer *optimizer;
+  class congruence_class *cls;
+};
+
+/* Semantic item optimizer includes all top-level logic
+   related to semantic equality comparison.  */
+class sem_item_optimizer
+{
+public:
+  sem_item_optimizer ();
+  ~sem_item_optimizer ();
+
+  /* Function responsible for visiting all potential functions and
+     read-only variables that can be merged.  */
+  void parse_funcs_and_vars (void);
+
+  /* Optimizer entry point.  */
+  void execute (void);
+
+  /* Dump function. */
+  void dump (void);
+
+  /* Verify congruence classes if checking is enabled.  */
+  void verify_classes (void);
+
+  /* Write IPA ICF summary for symbols.  */
+  void write_summary (void);
+
+  /* Read IPA IPA ICF summary for symbols.  */
+  void read_summary (void);
+
+  /* Callgraph removal hook called for a NODE with a custom DATA.  */
+  static void cgraph_removal_hook (cgraph_node *node, void *data);
+
+  /* Varpool removal hook called for a NODE with a custom DATA.  */
+  static void varpool_removal_hook (varpool_node *node, void *data);
+
+  /* Worklist of congruence classes that can potentially
+     refine classes of congruence.  */
+  std::list<congruence_class *> worklist;
+
+  /* Remove semantic ITEM and release memory.  */
+  void remove_item (sem_item *item);
+
+  /* Remove symtab NODE triggered by symtab removal hooks.  */
+  void remove_symtab_node (symtab_node *node);
+
+  /* Register callgraph and varpool hooks.  */
+  void register_hooks (void);
+
+  /* Unregister callgraph and varpool hooks.  */
+  void unregister_hooks (void);
+
+  /* Adds a CLS to hashtable associated by hash value.  */
+  void add_class (congruence_class *cls);
+
+  /* Gets a congruence class group based on given HASH value and TYPE.  */
+  congruence_class_group *get_group_by_hash (hashval_t hash,
+      sem_item_type type);
+
+private:
+
+  /* Congruence classes are built by hash value.  */
+  void build_hash_based_classes (void);
+
+  /* Semantic items in classes having more than one element and initialized.
+     In case of WPA, we load function body.  */
+  void parse_nonsingleton_classes (void);
+
+  /* Equality function for semantic items is used to subdivide existing
+     classes. If IN_WPA, fast equality function is invoked.  */
+  void subdivide_classes_by_equality (bool in_wpa = false);
+
+  /* Debug function prints all informations about congruence classes.  */
+  void dump_cong_classes (void);
+
+  /* Build references according to call graph.  */
+  void build_graph (void);
+
+  /* Iterative congruence reduction function.  */
+  void process_cong_reduction (void);
+
+  /* After reduction is done, we can declare all items in a group
+     to be equal. PREV_CLASS_COUNT is start number of classes
+     before reduction.  */
+  void merge_classes (unsigned int prev_class_count);
+
+  /* Adds a newly created congruence class CLS to worklist.  */
+  void worklist_push (congruence_class *cls);
+
+  /* Pops a class from worklist. */
+  congruence_class *worklist_pop ();
+
+  /* Every usage of a congruence class CLS is a candidate that can split the
+     collection of classes. Bitmap stack BMSTACK is used for bitmap
+     allocation.  */
+  void do_congruence_step (congruence_class *cls);
+
+  /* Tests if a class CLS used as INDEXth splits any congruence classes.
+     Bitmap stack BMSTACK is used for bitmap allocation.  */
+  void do_congruence_step_for_index (congruence_class *cls, unsigned int index);
+
+  /* Makes pairing between a congruence class CLS and semantic ITEM.  */
+  static void add_item_to_class (congruence_class *cls, sem_item *item);
+
+  /* Disposes split map traverse function. CLS is congruence
+     class, BSLOT is bitmap slot we want to release. DATA is mandatory,
+     but unused argument.  */
+  static bool release_split_map (congruence_class * const &cls, bitmap const &b,
+				 traverse_split_pair *pair);
+
+  /* Process split operation for a cognruence class CLS,
+     where bitmap B splits congruence class members. DATA is used
+     as argument of split pair.  */
+  static bool traverse_congruence_split (congruence_class * const &cls,
+					 bitmap const &b,
+					 traverse_split_pair *pair);
+
+  /* Reads a section from LTO stream file FILE_DATA. Input block for DATA
+     contains LEN bytes.  */
+  void read_section (lto_file_decl_data *file_data, const char *data,
+		     size_t len);
+
+  /* Removes all callgraph and varpool nodes that are marked by symtab
+     as deleted.  */
+  void filter_removed_items (void);
+
+  /* Vector of semantic items.  */
+  vec <sem_item *> m_items;
+
+  /* A set containing all items removed by hooks.  */
+  hash_set <symtab_node *> m_removed_items_set;
+
+  /* Hashtable of congruence classes */
+  hash_table <congruence_class_group_hash> m_classes;
+
+  /* Count of congruence classes.  */
+  unsigned int m_classes_count;
+
+  /* Map data structure maps symtab nodes to semantic items.  */
+  hash_map <symtab_node *, sem_item *> m_symtab_node_map;
+
+  /* Set to true if a splitter class is removed.  */
+  bool splitter_class_removed;
+
+  /* Global unique class id counter.  */
+  static unsigned int class_id;
+
+  /* Callgraph node removal hook holder.  */
+  cgraph_node_hook_list *m_cgraph_node_hooks;
+
+  /* Varpool node removal hook holder.  */
+  varpool_node_hook_list *m_varpool_node_hooks;
+
+  /* Bitmap stack.  */
+  bitmap_obstack m_bmstack;
+}; // class sem_item_optimizer
+
+} // ipa_icf namespace
diff --git a/gcc/lto-cgraph.c b/gcc/lto-cgraph.c
index a48e61e..136fc86 100644
--- a/gcc/lto-cgraph.c
+++ b/gcc/lto-cgraph.c
@@ -540,6 +540,7 @@ lto_output_node (struct lto_simple_output_block *ob, struct cgraph_node *node,
   bp_pack_value (&bp, node->only_called_at_exit, 1);
   bp_pack_value (&bp, node->tm_clone, 1);
   bp_pack_value (&bp, node->calls_comdat_local, 1);
+  bp_pack_value (&bp, node->icf_merged, 1);
   bp_pack_value (&bp, node->thunk.thunk_p && !boundary_p, 1);
   bp_pack_enum (&bp, ld_plugin_symbol_resolution,
 	        LDPR_NUM_KNOWN, node->resolution);
@@ -1080,6 +1081,7 @@ input_overwrite_node (struct lto_file_decl_data *file_data,
   node->only_called_at_exit = bp_unpack_value (bp, 1);
   node->tm_clone = bp_unpack_value (bp, 1);
   node->calls_comdat_local = bp_unpack_value (bp, 1);
+  node->icf_merged = bp_unpack_value (bp, 1);
   node->thunk.thunk_p = bp_unpack_value (bp, 1);
   node->resolution = bp_unpack_enum (bp, ld_plugin_symbol_resolution,
 				     LDPR_NUM_KNOWN);
diff --git a/gcc/lto-section-in.c b/gcc/lto-section-in.c
index 5623706..c053545 100644
--- a/gcc/lto-section-in.c
+++ b/gcc/lto-section-in.c
@@ -60,7 +60,8 @@ const char *lto_section_name[LTO_N_SECTION_TYPES] =
   "opts",
   "cgraphopt",
   "inline",
-  "ipcp_trans"
+  "ipcp_trans",
+  "icf"
 };
 
 
diff --git a/gcc/lto-streamer.h b/gcc/lto-streamer.h
index 4bec969..63e4b32 100644
--- a/gcc/lto-streamer.h
+++ b/gcc/lto-streamer.h
@@ -247,6 +247,7 @@ enum lto_section_type
   LTO_section_cgraph_opt_sum,
   LTO_section_inline_summary,
   LTO_section_ipcp_transform,
+  LTO_section_ipa_icf,
   LTO_N_SECTION_TYPES		/* Must be last.  */
 };
 
diff --git a/gcc/opts.c b/gcc/opts.c
index 3d9b6a7..dc8ddf4 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -497,6 +497,7 @@ static const struct default_options default_options_table[] =
     { OPT_LEVELS_2_PLUS, OPT_fvect_cost_model_, NULL, VECT_COST_MODEL_CHEAP },
     { OPT_LEVELS_2_PLUS_SPEED_ONLY, OPT_foptimize_strlen, NULL, 1 },
     { OPT_LEVELS_2_PLUS, OPT_fhoist_adjacent_loads, NULL, 1 },
+    { OPT_LEVELS_2_PLUS, OPT_fipa_icf, NULL, 1 },
     { OPT_LEVELS_2_PLUS, OPT_fisolate_erroneous_paths_dereference, NULL, 1 },
     { OPT_LEVELS_2_PLUS, OPT_fuse_caller_save, NULL, 1 },
 
@@ -1980,6 +1981,11 @@ common_handle_option (struct gcc_options *opts,
 	opts->x_flag_wrapv = 0;
       break;
 
+    case OPT_fipa_icf:
+	opts->x_flag_ipa_icf_functions = value;
+	opts->x_flag_ipa_icf_variables = value;
+      break;
+
     default:
       /* If the flag was handled in a standard way, assume the lack of
 	 processing here is intentional.  */
diff --git a/gcc/passes.def b/gcc/passes.def
index 3dca635..57c2c13 100644
--- a/gcc/passes.def
+++ b/gcc/passes.def
@@ -103,6 +103,7 @@ along with GCC; see the file COPYING3.  If not see
   INSERT_PASSES_AFTER (all_regular_ipa_passes)
   NEXT_PASS (pass_ipa_whole_program_visibility);
   NEXT_PASS (pass_ipa_profile);
+  NEXT_PASS (pass_ipa_icf);
   NEXT_PASS (pass_ipa_devirt);
   NEXT_PASS (pass_ipa_cp);
   NEXT_PASS (pass_ipa_cdtor_merge);
diff --git a/gcc/timevar.def b/gcc/timevar.def
index a04d05c..55a230b 100644
--- a/gcc/timevar.def
+++ b/gcc/timevar.def
@@ -90,6 +90,7 @@ DEFTIMEVAR (TV_WHOPR_LTRANS          , "whopr ltrans")
 DEFTIMEVAR (TV_IPA_REFERENCE         , "ipa reference")
 DEFTIMEVAR (TV_IPA_PROFILE           , "ipa profile")
 DEFTIMEVAR (TV_IPA_PURE_CONST        , "ipa pure const")
+DEFTIMEVAR (TV_IPA_ICF		     , "ipa icf")
 DEFTIMEVAR (TV_IPA_PTA               , "ipa points-to")
 DEFTIMEVAR (TV_IPA_SRA               , "ipa SRA")
 DEFTIMEVAR (TV_IPA_FREE_LANG_DATA    , "ipa free lang data")
diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h
index bfccf98..2ff52cf 100644
--- a/gcc/tree-pass.h
+++ b/gcc/tree-pass.h
@@ -461,6 +461,7 @@ extern simple_ipa_opt_pass *make_pass_ipa_free_lang_data (gcc::context *ctxt);
 extern simple_ipa_opt_pass *make_pass_ipa_free_inline_summary (gcc::context
 							       *ctxt);
 extern ipa_opt_pass_d *make_pass_ipa_cp (gcc::context *ctxt);
+extern ipa_opt_pass_d *make_pass_ipa_icf (gcc::context *ctxt);
 extern ipa_opt_pass_d *make_pass_ipa_devirt (gcc::context *ctxt);
 extern ipa_opt_pass_d *make_pass_ipa_reference (gcc::context *ctxt);
 extern ipa_opt_pass_d *make_pass_ipa_pure_const (gcc::context *ctxt);

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 5/5] New tests introduction
  2014-06-30 12:14     ` Martin Liška
@ 2014-10-19  8:19       ` Andreas Schwab
  2014-10-23 12:34         ` Martin Liška
  0 siblings, 1 reply; 70+ messages in thread
From: Andreas Schwab @ 2014-10-19  8:19 UTC (permalink / raw)
  To: Martin Liška; +Cc: gcc-patches

Martin Liška <mliska@suse.cz> writes:

> diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-icf-21.c b/gcc/testsuite/gcc.dg/ipa/ipa-icf-21.c
> new file mode 100644
> index 0000000..7358e43
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/ipa/ipa-icf-21.c
> @@ -0,0 +1,27 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fdump-ipa-icf"  } */
> +
> +#include <xmmintrin.h>
> +
> +__attribute__ ((noinline))
> +void foo()
> +{
> +  float x = 1.2345f;
> +  __m128 v =_mm_load1_ps(&x);
> +}
> +
> +__attribute__ ((noinline))
> +void bar()
> +{
> +  float x = 1.2345f;
> +  __m128 v =_mm_load1_ps(&x);
> +}
> +
> +int main()
> +{
> +  return 2;
> +}
> +
> +/* { dg-final { scan-ipa-dump "Semantic equality hit:bar->foo" "icf"  } } */
> +/* { dg-final { scan-ipa-dump "Equal symbols: 1" "icf"  } } */
> +/* { dg-final { cleanup-ipa-dump "icf" } } */

FAIL: gcc.dg/ipa/ipa-icf-21.c (test for excess errors)
Excess errors:
/usr/local/gcc/gcc-20141019/gcc/testsuite/gcc.dg/ipa/ipa-icf-21.c:4:23: fatal e\
rror: xmmintrin.h: No such file or directory
compilation terminated.

Andreas.

-- 
Andreas Schwab, schwab@linux-m68k.org
GPG Key fingerprint = 58CA 54C7 6D53 942B 1756  01D3 44D5 214B 8276 4ED5
"And now for something completely different."

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 3/5] IPA ICF pass
  2014-10-15 17:06                     ` Martin Liška
@ 2014-10-22 21:20                       ` Jiong Wang
  2014-11-06  3:01                         ` Joey Ye
  2014-11-13 22:26                       ` H.J. Lu
  1 sibling, 1 reply; 70+ messages in thread
From: Jiong Wang @ 2014-10-22 21:20 UTC (permalink / raw)
  To: Martin Liška; +Cc: gcc-patches, hubicka >> Jan Hubicka

PR 63574 ICE building libjava (segfault) on arm-linux-gnueabihf is
caused by this commit.

from the backtrace, the ICF pass is trying to compare two label tree
node without type info.

while looks like "compare_operand" expect the type info always be not
empty before invoking "func_checker::compatible_types_p".

I think we should add a similiar t1/t2 check at the start of
"func_checker::compatible_types_p", just
like what has been done at the head of "func_checker::compare_operand".

And I verified if we add that check, the crash gone away.

Regards,
Jiong


2014-10-15 18:03 GMT+01:00 Martin Liška <mliska@suse.cz>:
> On 10/14/2014 06:04 PM, Jan Hubicka wrote:
>>>
>>> diff --git a/gcc/cgraph.h b/gcc/cgraph.h
>>> index fb41b01..2de98b4 100644
>>> --- a/gcc/cgraph.h
>>> +++ b/gcc/cgraph.h
>>> @@ -172,6 +172,12 @@ public:
>>>     /* Dump referring in list to FILE.  */
>>>     void dump_referring (FILE *);
>>>
>>> +  /* Get number of references for this node.  */
>>> +  inline unsigned get_references_count (void)
>>> +  {
>>> +    return ref_list.references ? ref_list.references->length () : 0;
>>> +  }
>>
>>
>> Probably better called num_references() (like we have num_edge in
>> basic-block.h)
>>>
>>> @@ -8068,6 +8069,19 @@ it may significantly increase code size
>>>   (see @option{--param ipcp-unit-growth=@var{value}}).
>>>   This flag is enabled by default at @option{-O3}.
>>>
>>> +@item -fipa-icf
>>> +@opindex fipa-icf
>>> +Perform Identical Code Folding for functions and read-only variables.
>>> +The optimization reduces code size and may disturb unwind stacks by
>>> replacing
>>> +a function by equivalent one with a different name. The optimization
>>> works
>>> +more effectively with link time optimization enabled.
>>> +
>>> +Nevertheless the behavior is similar to Gold Linker ICF optimization,
>>> GCC ICF
>>> +works on different levels and thus the optimizations are not same -
>>> there are
>>> +equivalences that are found only by GCC and equivalences found only by
>>> Gold.
>>> +
>>> +This flag is enabled by default at @option{-O2}.
>>
>> ... and -Os?
>>>
>>> +    case ARRAY_REF:
>>> +    case ARRAY_RANGE_REF:
>>> +      {
>>> +       x1 = TREE_OPERAND (t1, 0);
>>> +       x2 = TREE_OPERAND (t2, 0);
>>> +       y1 = TREE_OPERAND (t1, 1);
>>> +       y2 = TREE_OPERAND (t2, 1);
>>> +
>>> +       if (!compare_operand (array_ref_low_bound (t1),
>>> +                             array_ref_low_bound (t2)))
>>> +         return return_false_with_msg ("");
>>> +       if (!compare_operand (array_ref_element_size (t1),
>>> +                             array_ref_element_size (t2)))
>>> +         return return_false_with_msg ("");
>>> +       if (!compare_operand (x1, x2))
>>> +         return return_false_with_msg ("");
>>> +       return compare_operand (y1, y2);
>>> +      }
>>
>>
>> No need for {...} if there are no local vars.
>>>
>>> +bool
>>> +func_checker::compare_function_decl (tree t1, tree t2)
>>> +{
>>> +  bool ret = false;
>>> +
>>> +  if (t1 == t2)
>>> +    return true;
>>> +
>>> +  symtab_node *n1 = symtab_node::get (t1);
>>> +  symtab_node *n2 = symtab_node::get (t2);
>>> +
>>> +  if (m_ignored_source_nodes != NULL && m_ignored_target_nodes != NULL)
>>> +    {
>>> +      ret = m_ignored_source_nodes->contains (n1)
>>> +           && m_ignored_target_nodes->contains (n2);
>>> +
>>> +      if (ret)
>>> +       return true;
>>> +    }
>>> +
>>> +  /* If function decl is WEAKREF, we compare targets.  */
>>> +  cgraph_node *f1 = cgraph_node::get (t1);
>>> +  cgraph_node *f2 = cgraph_node::get (t2);
>>> +
>>> +  if(f1 && f2 && f1->weakref && f2->weakref)
>>> +    ret = f1->alias_target == f2->alias_target;
>>> +
>>> +  return ret;
>>
>>
>> Comparing aliases is bit more complicated than just handling weakrefs. I
>> have
>> patch for symtab_node::equivalent_address_p somewhre in queue.  lets just
>> drop
>> the fancy stuff for the moment and compare f1&&f2 for equivalence.
>>>
>>> +  ret = compare_decl (t1, t2);
>>
>>
>> Why functions are not compared with compare_decl while variables are?
>>>
>>> +
>>> +  return return_with_debug (ret);
>>> +}
>>> +
>>> +void
>>> +func_checker::parse_labels (sem_bb *bb)
>>> +{
>>> +  for (gimple_stmt_iterator gsi = gsi_start_bb (bb->bb); !gsi_end_p
>>> (gsi);
>>> +       gsi_next (&gsi))
>>> +    {
>>> +      gimple stmt = gsi_stmt (gsi);
>>> +
>>> +      if (gimple_code (stmt) == GIMPLE_LABEL)
>>> +       {
>>> +         tree t = gimple_label_label (stmt);
>>> +         gcc_assert (TREE_CODE (t) == LABEL_DECL);
>>> +
>>> +         m_label_bb_map.put (t, bb->bb->index);
>>> +       }
>>> +    }
>>> +}
>>> +
>>> +/* Basic block equivalence comparison function that returns true if
>>> +   basic blocks BB1 and BB2 (from functions FUNC1 and FUNC2) correspond.
>>> +
>>> +   In general, a collection of equivalence dictionaries is built for
>>> types
>>> +   like SSA names, declarations (VAR_DECL, PARM_DECL, ..). This
>>> infrastructure
>>> +   is utilized by every statement-by-stament comparison function.  */
>>> +
>>> +bool
>>> +func_checker::compare_bb (sem_bb *bb1, sem_bb *bb2)
>>> +{
>>> +  unsigned i;
>>> +  gimple_stmt_iterator gsi1, gsi2;
>>> +  gimple s1, s2;
>>> +
>>> +  if (bb1->nondbg_stmt_count != bb2->nondbg_stmt_count
>>> +      || bb1->edge_count != bb2->edge_count)
>>> +    return return_false ();
>>> +
>>> +  gsi1 = gsi_start_bb (bb1->bb);
>>> +  gsi2 = gsi_start_bb (bb2->bb);
>>> +
>>> +  for (i = 0; i < bb1->nondbg_stmt_count; i++)
>>> +    {
>>> +      if (is_gimple_debug (gsi_stmt (gsi1)))
>>> +       gsi_next_nondebug (&gsi1);
>>> +
>>> +      if (is_gimple_debug (gsi_stmt (gsi2)))
>>> +       gsi_next_nondebug (&gsi2);
>>> +
>>> +      s1 = gsi_stmt (gsi1);
>>> +      s2 = gsi_stmt (gsi2);
>>> +
>>> +      int eh1 = lookup_stmt_eh_lp_fn
>>> +               (DECL_STRUCT_FUNCTION (m_source_func_decl), s1);
>>> +      int eh2 = lookup_stmt_eh_lp_fn
>>> +               (DECL_STRUCT_FUNCTION (m_target_func_decl), s2);
>>> +
>>> +      if (eh1 != eh2)
>>> +       return return_false_with_msg ("EH regions are different");
>>> +
>>> +      if (gimple_code (s1) != gimple_code (s2))
>>> +       return return_false_with_msg ("gimple codes are different");
>>> +
>>> +      switch (gimple_code (s1))
>>> +       {
>>> +       case GIMPLE_CALL:
>>> +         if (!compare_gimple_call (s1, s2))
>>> +           return return_different_stmts (s1, s2, "GIMPLE_CALL");
>>> +         break;
>>> +       case GIMPLE_ASSIGN:
>>> +         if (!compare_gimple_assign (s1, s2))
>>> +           return return_different_stmts (s1, s2, "GIMPLE_ASSIGN");
>>> +         break;
>>> +       case GIMPLE_COND:
>>> +         if (!compare_gimple_cond (s1, s2))
>>> +           return return_different_stmts (s1, s2, "GIMPLE_COND");
>>> +         break;
>>> +       case GIMPLE_SWITCH:
>>> +         if (!compare_gimple_switch (s1, s2))
>>> +           return return_different_stmts (s1, s2, "GIMPLE_SWITCH");
>>> +         break;
>>> +       case GIMPLE_DEBUG:
>>> +       case GIMPLE_EH_DISPATCH:
>>> +         break;
>>> +       case GIMPLE_RESX:
>>> +         if (!compare_gimple_resx (s1, s2))
>>> +           return return_different_stmts (s1, s2, "GIMPLE_RESX");
>>> +         break;
>>> +       case GIMPLE_LABEL:
>>> +         if (!compare_gimple_label (s1, s2))
>>> +           return return_different_stmts (s1, s2, "GIMPLE_LABEL");
>>> +         break;
>>> +       case GIMPLE_RETURN:
>>> +         if (!compare_gimple_return (s1, s2))
>>> +           return return_different_stmts (s1, s2, "GIMPLE_RETURN");
>>> +         break;
>>> +       case GIMPLE_GOTO:
>>> +         if (!compare_gimple_goto (s1, s2))
>>> +           return return_different_stmts (s1, s2, "GIMPLE_GOTO");
>>> +         break;
>>> +       case GIMPLE_ASM:
>>> +         if (!compare_gimple_asm (s1, s2))
>>> +           return return_different_stmts (s1, s2, "GIMPLE_ASM");
>>> +         break;
>>> +       case GIMPLE_PREDICT:
>>> +       case GIMPLE_NOP:
>>> +         return true;
>>> +       default:
>>> +         return return_false_with_msg ("Unknown GIMPLE code reached");
>>> +       }
>>> +
>>> +      gsi_next (&gsi1);
>>> +      gsi_next (&gsi2);
>>> +    }
>>> +
>>> +  return true;
>>> +}
>>> +
>>> +/* Verifies for given GIMPLEs S1 and S2 that
>>> +   call statements are semantically equivalent.  */
>>> +
>>> +bool
>>> +func_checker::compare_gimple_call (gimple s1, gimple s2)
>>> +{
>>> +  unsigned i;
>>> +  tree t1, t2;
>>> +
>>> +  if (gimple_call_num_args (s1) != gimple_call_num_args (s2))
>>> +    return false;
>>> +
>>> +  t1 = gimple_call_fndecl (s1);
>>> +  t2 = gimple_call_fndecl (s2);
>>> +
>>> +  /* Function pointer variables are not supported yet.  */
>>> +  if (t1 == NULL || t2 == NULL)
>>> +    {
>>> +      if (!compare_operand (t1, t2))
>>> +       return return_false();
>>
>>
>> I think the comment above is out of date. compare_operand should do the
>> right
>> job for indirect calls.
>>>
>>> +
>>> +  if (cn1 && cn2 && cn1->weakref && cn2->weakref
>>> +      && cn1->alias_target == cn2->alias_target)
>>> +    return true;
>>
>>
>> Lets consistently drop the weakrefs handling and add full alias handling
>> incrementally.
>>>
>>> +
>>> +  /* Checking function arguments.  */
>>
>> attributes
>>>
>>> +  tree decl1 = DECL_ATTRIBUTES (decl);
>>> +  tree decl2 = DECL_ATTRIBUTES (m_compared_func->decl);
>>
>>
>> You can still do this as part of the wap_comparison, right?
>>>
>>> +
>>> +  m_checker = new func_checker (decl, m_compared_func->decl,
>>> +                               compare_polymorphic_p (),
>>> +                               false,
>>> +                               &refs_set,
>>> +                               &m_compared_func->refs_set);
>>> +  while (decl1)
>>> +    {
>>> +      if (decl2 == NULL)
>>> +       return return_false ();
>>> +
>>> +      if (get_attribute_name (decl1) != get_attribute_name (decl2))
>>> +       return return_false ();
>>> +
>>> +      tree attr_value1 = TREE_VALUE (decl1);
>>> +      tree attr_value2 = TREE_VALUE (decl2);
>>> +
>>> +      if (attr_value1 && attr_value2)
>>> +       {
>>> +         bool ret = m_checker->compare_operand (TREE_VALUE
>>> (attr_value1),
>>> +                                                TREE_VALUE
>>> (attr_value2));
>>> +         if (!ret)
>>> +           return return_false_with_msg ("attribute values are
>>> different");
>>> +       }
>>> +      else if (!attr_value1 && !attr_value2)
>>> +       {}
>>> +      else
>>> +       return return_false ();
>>> +
>>> +      decl1 = TREE_CHAIN (decl1);
>>> +      decl2 = TREE_CHAIN (decl2);
>>> +    }
>>> +
>>> +  if (decl1 != decl2)
>>> +    return return_false();
>>> +
>>> +
>>> +  for (arg1 = DECL_ARGUMENTS (decl),
>>> +       arg2 = DECL_ARGUMENTS (m_compared_func->decl);
>>> +       arg1; arg1 = DECL_CHAIN (arg1), arg2 = DECL_CHAIN (arg2))
>>> +    if (!m_checker->compare_decl (arg1, arg2))
>>> +      return return_false ();
>>> +
>>> +  /* Fill-up label dictionary.  */
>>> +  for (unsigned i = 0; i < bb_sorted.length (); ++i)
>>> +    {
>>> +      m_checker->parse_labels (bb_sorted[i]);
>>> +      m_checker->parse_labels (m_compared_func->bb_sorted[i]);
>>> +    }
>>> +
>>> +  /* Checking all basic blocks.  */
>>> +  for (unsigned i = 0; i < bb_sorted.length (); ++i)
>>> +    if(!m_checker->compare_bb (bb_sorted[i],
>>> m_compared_func->bb_sorted[i]))
>>> +      return return_false();
>>> +
>>> +  dump_message ("All BBs are equal\n");
>>> +
>>> +  /* Basic block edges check.  */
>>> +  for (unsigned i = 0; i < bb_sorted.length (); ++i)
>>> +    {
>>> +      bb_dict = XNEWVEC (int, bb_sorted.length () + 2);
>>> +      memset (bb_dict, -1, (bb_sorted.length () + 2) * sizeof (int));
>>> +
>>> +      bb1 = bb_sorted[i]->bb;
>>> +      bb2 = m_compared_func->bb_sorted[i]->bb;
>>> +
>>> +      ei2 = ei_start (bb2->preds);
>>> +
>>> +      for (ei1 = ei_start (bb1->preds); ei_cond (ei1, &e1); ei_next
>>> (&ei1))
>>> +       {
>>> +         ei_cond (ei2, &e2);
>>> +
>>> +         if (e1->flags != e2->flags)
>>> +           return return_false_with_msg ("flags comparison returns
>>> false");
>>> +
>>> +         if (!bb_dict_test (bb_dict, e1->src->index, e2->src->index))
>>> +           return return_false_with_msg ("edge comparison returns
>>> false");
>>> +
>>> +         if (!bb_dict_test (bb_dict, e1->dest->index, e2->dest->index))
>>> +           return return_false_with_msg ("BB comparison returns false");
>>> +
>>> +         if (!m_checker->compare_edge (e1, e2))
>>> +           return return_false_with_msg ("edge comparison returns
>>> false");
>>> +
>>> +         ei_next (&ei2);
>>> +       }
>>> +    }
>>> +
>>> +  /* Basic block PHI nodes comparison.  */
>>> +  for (unsigned i = 0; i < bb_sorted.length (); i++)
>>> +    if (!compare_phi_node (bb_sorted[i]->bb,
>>> m_compared_func->bb_sorted[i]->bb))
>>> +      return return_false_with_msg ("PHI node comparison returns
>>> false");
>>> +
>>> +  return result;
>>> +}
>>
>>
>> The rest of patch seems fine.  I think we went across enough of
>> iteraitons, the patch is OK
>> with changes above and lets handle rest incrementally.
>>
>> Thanks!
>> Honza
>>
>
> Hello
>
> There's final version of the patch I'm going to commit tomorrow in the
> morning (CEST).
> Thank you Honza for the review.
>
> Martin

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 5/5] New tests introduction
  2014-10-19  8:19       ` Andreas Schwab
@ 2014-10-23 12:34         ` Martin Liška
  0 siblings, 0 replies; 70+ messages in thread
From: Martin Liška @ 2014-10-23 12:34 UTC (permalink / raw)
  To: Andreas Schwab; +Cc: gcc-patches

On 10/19/2014 09:50 AM, Andreas Schwab wrote:
> Martin Liška <mliska@suse.cz> writes:
>
>> diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-icf-21.c b/gcc/testsuite/gcc.dg/ipa/ipa-icf-21.c
>> new file mode 100644
>> index 0000000..7358e43
>> --- /dev/null
>> +++ b/gcc/testsuite/gcc.dg/ipa/ipa-icf-21.c
>> @@ -0,0 +1,27 @@
>> +/* { dg-do compile } */
>> +/* { dg-options "-O2 -fdump-ipa-icf"  } */
>> +
>> +#include <xmmintrin.h>
>> +
>> +__attribute__ ((noinline))
>> +void foo()
>> +{
>> +  float x = 1.2345f;
>> +  __m128 v =_mm_load1_ps(&x);
>> +}
>> +
>> +__attribute__ ((noinline))
>> +void bar()
>> +{
>> +  float x = 1.2345f;
>> +  __m128 v =_mm_load1_ps(&x);
>> +}
>> +
>> +int main()
>> +{
>> +  return 2;
>> +}
>> +
>> +/* { dg-final { scan-ipa-dump "Semantic equality hit:bar->foo" "icf"  } } */
>> +/* { dg-final { scan-ipa-dump "Equal symbols: 1" "icf"  } } */
>> +/* { dg-final { cleanup-ipa-dump "icf" } } */
>
> FAIL: gcc.dg/ipa/ipa-icf-21.c (test for excess errors)
> Excess errors:
> /usr/local/gcc/gcc-20141019/gcc/testsuite/gcc.dg/ipa/ipa-icf-21.c:4:23: fatal e\
> rror: xmmintrin.h: No such file or directory
> compilation terminated.
>
> Andreas.
>

Hello Andreas.

Starting from r216589 this problem should be fixed.

Thanks,
Martin

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 3/5] IPA ICF pass
  2014-10-22 21:20                       ` Jiong Wang
@ 2014-11-06  3:01                         ` Joey Ye
  2014-11-06  9:03                           ` Jan Hubicka
  0 siblings, 1 reply; 70+ messages in thread
From: Joey Ye @ 2014-11-06  3:01 UTC (permalink / raw)
  To: Jiong Wang; +Cc: Martin Liška, gcc-patches, hubicka >> Jan Hubicka

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=63747 is likely caused by
this patch. compare_gimple_switch does not check CASE_LOW and
CASE_HIGH, resulting merging functions not identical.

Interestingly in the first a few versions of this patch CASE_LOW/HIGH
were checked. But last versions only checks CASE_LABEL. What was the
intention?

Thanks,
Joey

On Thu, Oct 23, 2014 at 5:18 AM, Jiong Wang
<wong.kwongyuan.tools@gmail.com> wrote:
> PR 63574 ICE building libjava (segfault) on arm-linux-gnueabihf is
> caused by this commit.
>
> from the backtrace, the ICF pass is trying to compare two label tree
> node without type info.
>
> while looks like "compare_operand" expect the type info always be not
> empty before invoking "func_checker::compatible_types_p".
>
> I think we should add a similiar t1/t2 check at the start of
> "func_checker::compatible_types_p", just
> like what has been done at the head of "func_checker::compare_operand".
>
> And I verified if we add that check, the crash gone away.
>
> Regards,
> Jiong
>
>
> 2014-10-15 18:03 GMT+01:00 Martin Liška <mliska@suse.cz>:
>> On 10/14/2014 06:04 PM, Jan Hubicka wrote:
>>>>
>>>> diff --git a/gcc/cgraph.h b/gcc/cgraph.h
>>>> index fb41b01..2de98b4 100644
>>>> --- a/gcc/cgraph.h
>>>> +++ b/gcc/cgraph.h
>>>> @@ -172,6 +172,12 @@ public:
>>>>     /* Dump referring in list to FILE.  */
>>>>     void dump_referring (FILE *);
>>>>
>>>> +  /* Get number of references for this node.  */
>>>> +  inline unsigned get_references_count (void)
>>>> +  {
>>>> +    return ref_list.references ? ref_list.references->length () : 0;
>>>> +  }
>>>
>>>
>>> Probably better called num_references() (like we have num_edge in
>>> basic-block.h)
>>>>
>>>> @@ -8068,6 +8069,19 @@ it may significantly increase code size
>>>>   (see @option{--param ipcp-unit-growth=@var{value}}).
>>>>   This flag is enabled by default at @option{-O3}.
>>>>
>>>> +@item -fipa-icf
>>>> +@opindex fipa-icf
>>>> +Perform Identical Code Folding for functions and read-only variables.
>>>> +The optimization reduces code size and may disturb unwind stacks by
>>>> replacing
>>>> +a function by equivalent one with a different name. The optimization
>>>> works
>>>> +more effectively with link time optimization enabled.
>>>> +
>>>> +Nevertheless the behavior is similar to Gold Linker ICF optimization,
>>>> GCC ICF
>>>> +works on different levels and thus the optimizations are not same -
>>>> there are
>>>> +equivalences that are found only by GCC and equivalences found only by
>>>> Gold.
>>>> +
>>>> +This flag is enabled by default at @option{-O2}.
>>>
>>> ... and -Os?
>>>>
>>>> +    case ARRAY_REF:
>>>> +    case ARRAY_RANGE_REF:
>>>> +      {
>>>> +       x1 = TREE_OPERAND (t1, 0);
>>>> +       x2 = TREE_OPERAND (t2, 0);
>>>> +       y1 = TREE_OPERAND (t1, 1);
>>>> +       y2 = TREE_OPERAND (t2, 1);
>>>> +
>>>> +       if (!compare_operand (array_ref_low_bound (t1),
>>>> +                             array_ref_low_bound (t2)))
>>>> +         return return_false_with_msg ("");
>>>> +       if (!compare_operand (array_ref_element_size (t1),
>>>> +                             array_ref_element_size (t2)))
>>>> +         return return_false_with_msg ("");
>>>> +       if (!compare_operand (x1, x2))
>>>> +         return return_false_with_msg ("");
>>>> +       return compare_operand (y1, y2);
>>>> +      }
>>>
>>>
>>> No need for {...} if there are no local vars.
>>>>
>>>> +bool
>>>> +func_checker::compare_function_decl (tree t1, tree t2)
>>>> +{
>>>> +  bool ret = false;
>>>> +
>>>> +  if (t1 == t2)
>>>> +    return true;
>>>> +
>>>> +  symtab_node *n1 = symtab_node::get (t1);
>>>> +  symtab_node *n2 = symtab_node::get (t2);
>>>> +
>>>> +  if (m_ignored_source_nodes != NULL && m_ignored_target_nodes != NULL)
>>>> +    {
>>>> +      ret = m_ignored_source_nodes->contains (n1)
>>>> +           && m_ignored_target_nodes->contains (n2);
>>>> +
>>>> +      if (ret)
>>>> +       return true;
>>>> +    }
>>>> +
>>>> +  /* If function decl is WEAKREF, we compare targets.  */
>>>> +  cgraph_node *f1 = cgraph_node::get (t1);
>>>> +  cgraph_node *f2 = cgraph_node::get (t2);
>>>> +
>>>> +  if(f1 && f2 && f1->weakref && f2->weakref)
>>>> +    ret = f1->alias_target == f2->alias_target;
>>>> +
>>>> +  return ret;
>>>
>>>
>>> Comparing aliases is bit more complicated than just handling weakrefs. I
>>> have
>>> patch for symtab_node::equivalent_address_p somewhre in queue.  lets just
>>> drop
>>> the fancy stuff for the moment and compare f1&&f2 for equivalence.
>>>>
>>>> +  ret = compare_decl (t1, t2);
>>>
>>>
>>> Why functions are not compared with compare_decl while variables are?
>>>>
>>>> +
>>>> +  return return_with_debug (ret);
>>>> +}
>>>> +
>>>> +void
>>>> +func_checker::parse_labels (sem_bb *bb)
>>>> +{
>>>> +  for (gimple_stmt_iterator gsi = gsi_start_bb (bb->bb); !gsi_end_p
>>>> (gsi);
>>>> +       gsi_next (&gsi))
>>>> +    {
>>>> +      gimple stmt = gsi_stmt (gsi);
>>>> +
>>>> +      if (gimple_code (stmt) == GIMPLE_LABEL)
>>>> +       {
>>>> +         tree t = gimple_label_label (stmt);
>>>> +         gcc_assert (TREE_CODE (t) == LABEL_DECL);
>>>> +
>>>> +         m_label_bb_map.put (t, bb->bb->index);
>>>> +       }
>>>> +    }
>>>> +}
>>>> +
>>>> +/* Basic block equivalence comparison function that returns true if
>>>> +   basic blocks BB1 and BB2 (from functions FUNC1 and FUNC2) correspond.
>>>> +
>>>> +   In general, a collection of equivalence dictionaries is built for
>>>> types
>>>> +   like SSA names, declarations (VAR_DECL, PARM_DECL, ..). This
>>>> infrastructure
>>>> +   is utilized by every statement-by-stament comparison function.  */
>>>> +
>>>> +bool
>>>> +func_checker::compare_bb (sem_bb *bb1, sem_bb *bb2)
>>>> +{
>>>> +  unsigned i;
>>>> +  gimple_stmt_iterator gsi1, gsi2;
>>>> +  gimple s1, s2;
>>>> +
>>>> +  if (bb1->nondbg_stmt_count != bb2->nondbg_stmt_count
>>>> +      || bb1->edge_count != bb2->edge_count)
>>>> +    return return_false ();
>>>> +
>>>> +  gsi1 = gsi_start_bb (bb1->bb);
>>>> +  gsi2 = gsi_start_bb (bb2->bb);
>>>> +
>>>> +  for (i = 0; i < bb1->nondbg_stmt_count; i++)
>>>> +    {
>>>> +      if (is_gimple_debug (gsi_stmt (gsi1)))
>>>> +       gsi_next_nondebug (&gsi1);
>>>> +
>>>> +      if (is_gimple_debug (gsi_stmt (gsi2)))
>>>> +       gsi_next_nondebug (&gsi2);
>>>> +
>>>> +      s1 = gsi_stmt (gsi1);
>>>> +      s2 = gsi_stmt (gsi2);
>>>> +
>>>> +      int eh1 = lookup_stmt_eh_lp_fn
>>>> +               (DECL_STRUCT_FUNCTION (m_source_func_decl), s1);
>>>> +      int eh2 = lookup_stmt_eh_lp_fn
>>>> +               (DECL_STRUCT_FUNCTION (m_target_func_decl), s2);
>>>> +
>>>> +      if (eh1 != eh2)
>>>> +       return return_false_with_msg ("EH regions are different");
>>>> +
>>>> +      if (gimple_code (s1) != gimple_code (s2))
>>>> +       return return_false_with_msg ("gimple codes are different");
>>>> +
>>>> +      switch (gimple_code (s1))
>>>> +       {
>>>> +       case GIMPLE_CALL:
>>>> +         if (!compare_gimple_call (s1, s2))
>>>> +           return return_different_stmts (s1, s2, "GIMPLE_CALL");
>>>> +         break;
>>>> +       case GIMPLE_ASSIGN:
>>>> +         if (!compare_gimple_assign (s1, s2))
>>>> +           return return_different_stmts (s1, s2, "GIMPLE_ASSIGN");
>>>> +         break;
>>>> +       case GIMPLE_COND:
>>>> +         if (!compare_gimple_cond (s1, s2))
>>>> +           return return_different_stmts (s1, s2, "GIMPLE_COND");
>>>> +         break;
>>>> +       case GIMPLE_SWITCH:
>>>> +         if (!compare_gimple_switch (s1, s2))
>>>> +           return return_different_stmts (s1, s2, "GIMPLE_SWITCH");
>>>> +         break;
>>>> +       case GIMPLE_DEBUG:
>>>> +       case GIMPLE_EH_DISPATCH:
>>>> +         break;
>>>> +       case GIMPLE_RESX:
>>>> +         if (!compare_gimple_resx (s1, s2))
>>>> +           return return_different_stmts (s1, s2, "GIMPLE_RESX");
>>>> +         break;
>>>> +       case GIMPLE_LABEL:
>>>> +         if (!compare_gimple_label (s1, s2))
>>>> +           return return_different_stmts (s1, s2, "GIMPLE_LABEL");
>>>> +         break;
>>>> +       case GIMPLE_RETURN:
>>>> +         if (!compare_gimple_return (s1, s2))
>>>> +           return return_different_stmts (s1, s2, "GIMPLE_RETURN");
>>>> +         break;
>>>> +       case GIMPLE_GOTO:
>>>> +         if (!compare_gimple_goto (s1, s2))
>>>> +           return return_different_stmts (s1, s2, "GIMPLE_GOTO");
>>>> +         break;
>>>> +       case GIMPLE_ASM:
>>>> +         if (!compare_gimple_asm (s1, s2))
>>>> +           return return_different_stmts (s1, s2, "GIMPLE_ASM");
>>>> +         break;
>>>> +       case GIMPLE_PREDICT:
>>>> +       case GIMPLE_NOP:
>>>> +         return true;
>>>> +       default:
>>>> +         return return_false_with_msg ("Unknown GIMPLE code reached");
>>>> +       }
>>>> +
>>>> +      gsi_next (&gsi1);
>>>> +      gsi_next (&gsi2);
>>>> +    }
>>>> +
>>>> +  return true;
>>>> +}
>>>> +
>>>> +/* Verifies for given GIMPLEs S1 and S2 that
>>>> +   call statements are semantically equivalent.  */
>>>> +
>>>> +bool
>>>> +func_checker::compare_gimple_call (gimple s1, gimple s2)
>>>> +{
>>>> +  unsigned i;
>>>> +  tree t1, t2;
>>>> +
>>>> +  if (gimple_call_num_args (s1) != gimple_call_num_args (s2))
>>>> +    return false;
>>>> +
>>>> +  t1 = gimple_call_fndecl (s1);
>>>> +  t2 = gimple_call_fndecl (s2);
>>>> +
>>>> +  /* Function pointer variables are not supported yet.  */
>>>> +  if (t1 == NULL || t2 == NULL)
>>>> +    {
>>>> +      if (!compare_operand (t1, t2))
>>>> +       return return_false();
>>>
>>>
>>> I think the comment above is out of date. compare_operand should do the
>>> right
>>> job for indirect calls.
>>>>
>>>> +
>>>> +  if (cn1 && cn2 && cn1->weakref && cn2->weakref
>>>> +      && cn1->alias_target == cn2->alias_target)
>>>> +    return true;
>>>
>>>
>>> Lets consistently drop the weakrefs handling and add full alias handling
>>> incrementally.
>>>>
>>>> +
>>>> +  /* Checking function arguments.  */
>>>
>>> attributes
>>>>
>>>> +  tree decl1 = DECL_ATTRIBUTES (decl);
>>>> +  tree decl2 = DECL_ATTRIBUTES (m_compared_func->decl);
>>>
>>>
>>> You can still do this as part of the wap_comparison, right?
>>>>
>>>> +
>>>> +  m_checker = new func_checker (decl, m_compared_func->decl,
>>>> +                               compare_polymorphic_p (),
>>>> +                               false,
>>>> +                               &refs_set,
>>>> +                               &m_compared_func->refs_set);
>>>> +  while (decl1)
>>>> +    {
>>>> +      if (decl2 == NULL)
>>>> +       return return_false ();
>>>> +
>>>> +      if (get_attribute_name (decl1) != get_attribute_name (decl2))
>>>> +       return return_false ();
>>>> +
>>>> +      tree attr_value1 = TREE_VALUE (decl1);
>>>> +      tree attr_value2 = TREE_VALUE (decl2);
>>>> +
>>>> +      if (attr_value1 && attr_value2)
>>>> +       {
>>>> +         bool ret = m_checker->compare_operand (TREE_VALUE
>>>> (attr_value1),
>>>> +                                                TREE_VALUE
>>>> (attr_value2));
>>>> +         if (!ret)
>>>> +           return return_false_with_msg ("attribute values are
>>>> different");
>>>> +       }
>>>> +      else if (!attr_value1 && !attr_value2)
>>>> +       {}
>>>> +      else
>>>> +       return return_false ();
>>>> +
>>>> +      decl1 = TREE_CHAIN (decl1);
>>>> +      decl2 = TREE_CHAIN (decl2);
>>>> +    }
>>>> +
>>>> +  if (decl1 != decl2)
>>>> +    return return_false();
>>>> +
>>>> +
>>>> +  for (arg1 = DECL_ARGUMENTS (decl),
>>>> +       arg2 = DECL_ARGUMENTS (m_compared_func->decl);
>>>> +       arg1; arg1 = DECL_CHAIN (arg1), arg2 = DECL_CHAIN (arg2))
>>>> +    if (!m_checker->compare_decl (arg1, arg2))
>>>> +      return return_false ();
>>>> +
>>>> +  /* Fill-up label dictionary.  */
>>>> +  for (unsigned i = 0; i < bb_sorted.length (); ++i)
>>>> +    {
>>>> +      m_checker->parse_labels (bb_sorted[i]);
>>>> +      m_checker->parse_labels (m_compared_func->bb_sorted[i]);
>>>> +    }
>>>> +
>>>> +  /* Checking all basic blocks.  */
>>>> +  for (unsigned i = 0; i < bb_sorted.length (); ++i)
>>>> +    if(!m_checker->compare_bb (bb_sorted[i],
>>>> m_compared_func->bb_sorted[i]))
>>>> +      return return_false();
>>>> +
>>>> +  dump_message ("All BBs are equal\n");
>>>> +
>>>> +  /* Basic block edges check.  */
>>>> +  for (unsigned i = 0; i < bb_sorted.length (); ++i)
>>>> +    {
>>>> +      bb_dict = XNEWVEC (int, bb_sorted.length () + 2);
>>>> +      memset (bb_dict, -1, (bb_sorted.length () + 2) * sizeof (int));
>>>> +
>>>> +      bb1 = bb_sorted[i]->bb;
>>>> +      bb2 = m_compared_func->bb_sorted[i]->bb;
>>>> +
>>>> +      ei2 = ei_start (bb2->preds);
>>>> +
>>>> +      for (ei1 = ei_start (bb1->preds); ei_cond (ei1, &e1); ei_next
>>>> (&ei1))
>>>> +       {
>>>> +         ei_cond (ei2, &e2);
>>>> +
>>>> +         if (e1->flags != e2->flags)
>>>> +           return return_false_with_msg ("flags comparison returns
>>>> false");
>>>> +
>>>> +         if (!bb_dict_test (bb_dict, e1->src->index, e2->src->index))
>>>> +           return return_false_with_msg ("edge comparison returns
>>>> false");
>>>> +
>>>> +         if (!bb_dict_test (bb_dict, e1->dest->index, e2->dest->index))
>>>> +           return return_false_with_msg ("BB comparison returns false");
>>>> +
>>>> +         if (!m_checker->compare_edge (e1, e2))
>>>> +           return return_false_with_msg ("edge comparison returns
>>>> false");
>>>> +
>>>> +         ei_next (&ei2);
>>>> +       }
>>>> +    }
>>>> +
>>>> +  /* Basic block PHI nodes comparison.  */
>>>> +  for (unsigned i = 0; i < bb_sorted.length (); i++)
>>>> +    if (!compare_phi_node (bb_sorted[i]->bb,
>>>> m_compared_func->bb_sorted[i]->bb))
>>>> +      return return_false_with_msg ("PHI node comparison returns
>>>> false");
>>>> +
>>>> +  return result;
>>>> +}
>>>
>>>
>>> The rest of patch seems fine.  I think we went across enough of
>>> iteraitons, the patch is OK
>>> with changes above and lets handle rest incrementally.
>>>
>>> Thanks!
>>> Honza
>>>
>>
>> Hello
>>
>> There's final version of the patch I'm going to commit tomorrow in the
>> morning (CEST).
>> Thank you Honza for the review.
>>
>> Martin

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 3/5] IPA ICF pass
  2014-11-06  3:01                         ` Joey Ye
@ 2014-11-06  9:03                           ` Jan Hubicka
  0 siblings, 0 replies; 70+ messages in thread
From: Jan Hubicka @ 2014-11-06  9:03 UTC (permalink / raw)
  To: Joey Ye
  Cc: Jiong Wang, Martin Liška, gcc-patches, hubicka >> Jan Hubicka

> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=63747 is likely caused by
> this patch. compare_gimple_switch does not check CASE_LOW and
> CASE_HIGH, resulting merging functions not identical.
> 
> Interestingly in the first a few versions of this patch CASE_LOW/HIGH
> were checked. But last versions only checks CASE_LABEL. What was the
> intention?

It sounds like an accidental change.  CASE_LOW/CASE_HIGH needs to be
 compared ;)

Honza
> 
> Thanks,
> Joey
> 
> On Thu, Oct 23, 2014 at 5:18 AM, Jiong Wang
> <wong.kwongyuan.tools@gmail.com> wrote:
> > PR 63574 ICE building libjava (segfault) on arm-linux-gnueabihf is
> > caused by this commit.
> >
> > from the backtrace, the ICF pass is trying to compare two label tree
> > node without type info.
> >
> > while looks like "compare_operand" expect the type info always be not
> > empty before invoking "func_checker::compatible_types_p".
> >
> > I think we should add a similiar t1/t2 check at the start of
> > "func_checker::compatible_types_p", just
> > like what has been done at the head of "func_checker::compare_operand".
> >
> > And I verified if we add that check, the crash gone away.
> >
> > Regards,
> > Jiong
> >
> >
> > 2014-10-15 18:03 GMT+01:00 Martin Liška <mliska@suse.cz>:
> >> On 10/14/2014 06:04 PM, Jan Hubicka wrote:
> >>>>
> >>>> diff --git a/gcc/cgraph.h b/gcc/cgraph.h
> >>>> index fb41b01..2de98b4 100644
> >>>> --- a/gcc/cgraph.h
> >>>> +++ b/gcc/cgraph.h
> >>>> @@ -172,6 +172,12 @@ public:
> >>>>     /* Dump referring in list to FILE.  */
> >>>>     void dump_referring (FILE *);
> >>>>
> >>>> +  /* Get number of references for this node.  */
> >>>> +  inline unsigned get_references_count (void)
> >>>> +  {
> >>>> +    return ref_list.references ? ref_list.references->length () : 0;
> >>>> +  }
> >>>
> >>>
> >>> Probably better called num_references() (like we have num_edge in
> >>> basic-block.h)
> >>>>
> >>>> @@ -8068,6 +8069,19 @@ it may significantly increase code size
> >>>>   (see @option{--param ipcp-unit-growth=@var{value}}).
> >>>>   This flag is enabled by default at @option{-O3}.
> >>>>
> >>>> +@item -fipa-icf
> >>>> +@opindex fipa-icf
> >>>> +Perform Identical Code Folding for functions and read-only variables.
> >>>> +The optimization reduces code size and may disturb unwind stacks by
> >>>> replacing
> >>>> +a function by equivalent one with a different name. The optimization
> >>>> works
> >>>> +more effectively with link time optimization enabled.
> >>>> +
> >>>> +Nevertheless the behavior is similar to Gold Linker ICF optimization,
> >>>> GCC ICF
> >>>> +works on different levels and thus the optimizations are not same -
> >>>> there are
> >>>> +equivalences that are found only by GCC and equivalences found only by
> >>>> Gold.
> >>>> +
> >>>> +This flag is enabled by default at @option{-O2}.
> >>>
> >>> ... and -Os?
> >>>>
> >>>> +    case ARRAY_REF:
> >>>> +    case ARRAY_RANGE_REF:
> >>>> +      {
> >>>> +       x1 = TREE_OPERAND (t1, 0);
> >>>> +       x2 = TREE_OPERAND (t2, 0);
> >>>> +       y1 = TREE_OPERAND (t1, 1);
> >>>> +       y2 = TREE_OPERAND (t2, 1);
> >>>> +
> >>>> +       if (!compare_operand (array_ref_low_bound (t1),
> >>>> +                             array_ref_low_bound (t2)))
> >>>> +         return return_false_with_msg ("");
> >>>> +       if (!compare_operand (array_ref_element_size (t1),
> >>>> +                             array_ref_element_size (t2)))
> >>>> +         return return_false_with_msg ("");
> >>>> +       if (!compare_operand (x1, x2))
> >>>> +         return return_false_with_msg ("");
> >>>> +       return compare_operand (y1, y2);
> >>>> +      }
> >>>
> >>>
> >>> No need for {...} if there are no local vars.
> >>>>
> >>>> +bool
> >>>> +func_checker::compare_function_decl (tree t1, tree t2)
> >>>> +{
> >>>> +  bool ret = false;
> >>>> +
> >>>> +  if (t1 == t2)
> >>>> +    return true;
> >>>> +
> >>>> +  symtab_node *n1 = symtab_node::get (t1);
> >>>> +  symtab_node *n2 = symtab_node::get (t2);
> >>>> +
> >>>> +  if (m_ignored_source_nodes != NULL && m_ignored_target_nodes != NULL)
> >>>> +    {
> >>>> +      ret = m_ignored_source_nodes->contains (n1)
> >>>> +           && m_ignored_target_nodes->contains (n2);
> >>>> +
> >>>> +      if (ret)
> >>>> +       return true;
> >>>> +    }
> >>>> +
> >>>> +  /* If function decl is WEAKREF, we compare targets.  */
> >>>> +  cgraph_node *f1 = cgraph_node::get (t1);
> >>>> +  cgraph_node *f2 = cgraph_node::get (t2);
> >>>> +
> >>>> +  if(f1 && f2 && f1->weakref && f2->weakref)
> >>>> +    ret = f1->alias_target == f2->alias_target;
> >>>> +
> >>>> +  return ret;
> >>>
> >>>
> >>> Comparing aliases is bit more complicated than just handling weakrefs. I
> >>> have
> >>> patch for symtab_node::equivalent_address_p somewhre in queue.  lets just
> >>> drop
> >>> the fancy stuff for the moment and compare f1&&f2 for equivalence.
> >>>>
> >>>> +  ret = compare_decl (t1, t2);
> >>>
> >>>
> >>> Why functions are not compared with compare_decl while variables are?
> >>>>
> >>>> +
> >>>> +  return return_with_debug (ret);
> >>>> +}
> >>>> +
> >>>> +void
> >>>> +func_checker::parse_labels (sem_bb *bb)
> >>>> +{
> >>>> +  for (gimple_stmt_iterator gsi = gsi_start_bb (bb->bb); !gsi_end_p
> >>>> (gsi);
> >>>> +       gsi_next (&gsi))
> >>>> +    {
> >>>> +      gimple stmt = gsi_stmt (gsi);
> >>>> +
> >>>> +      if (gimple_code (stmt) == GIMPLE_LABEL)
> >>>> +       {
> >>>> +         tree t = gimple_label_label (stmt);
> >>>> +         gcc_assert (TREE_CODE (t) == LABEL_DECL);
> >>>> +
> >>>> +         m_label_bb_map.put (t, bb->bb->index);
> >>>> +       }
> >>>> +    }
> >>>> +}
> >>>> +
> >>>> +/* Basic block equivalence comparison function that returns true if
> >>>> +   basic blocks BB1 and BB2 (from functions FUNC1 and FUNC2) correspond.
> >>>> +
> >>>> +   In general, a collection of equivalence dictionaries is built for
> >>>> types
> >>>> +   like SSA names, declarations (VAR_DECL, PARM_DECL, ..). This
> >>>> infrastructure
> >>>> +   is utilized by every statement-by-stament comparison function.  */
> >>>> +
> >>>> +bool
> >>>> +func_checker::compare_bb (sem_bb *bb1, sem_bb *bb2)
> >>>> +{
> >>>> +  unsigned i;
> >>>> +  gimple_stmt_iterator gsi1, gsi2;
> >>>> +  gimple s1, s2;
> >>>> +
> >>>> +  if (bb1->nondbg_stmt_count != bb2->nondbg_stmt_count
> >>>> +      || bb1->edge_count != bb2->edge_count)
> >>>> +    return return_false ();
> >>>> +
> >>>> +  gsi1 = gsi_start_bb (bb1->bb);
> >>>> +  gsi2 = gsi_start_bb (bb2->bb);
> >>>> +
> >>>> +  for (i = 0; i < bb1->nondbg_stmt_count; i++)
> >>>> +    {
> >>>> +      if (is_gimple_debug (gsi_stmt (gsi1)))
> >>>> +       gsi_next_nondebug (&gsi1);
> >>>> +
> >>>> +      if (is_gimple_debug (gsi_stmt (gsi2)))
> >>>> +       gsi_next_nondebug (&gsi2);
> >>>> +
> >>>> +      s1 = gsi_stmt (gsi1);
> >>>> +      s2 = gsi_stmt (gsi2);
> >>>> +
> >>>> +      int eh1 = lookup_stmt_eh_lp_fn
> >>>> +               (DECL_STRUCT_FUNCTION (m_source_func_decl), s1);
> >>>> +      int eh2 = lookup_stmt_eh_lp_fn
> >>>> +               (DECL_STRUCT_FUNCTION (m_target_func_decl), s2);
> >>>> +
> >>>> +      if (eh1 != eh2)
> >>>> +       return return_false_with_msg ("EH regions are different");
> >>>> +
> >>>> +      if (gimple_code (s1) != gimple_code (s2))
> >>>> +       return return_false_with_msg ("gimple codes are different");
> >>>> +
> >>>> +      switch (gimple_code (s1))
> >>>> +       {
> >>>> +       case GIMPLE_CALL:
> >>>> +         if (!compare_gimple_call (s1, s2))
> >>>> +           return return_different_stmts (s1, s2, "GIMPLE_CALL");
> >>>> +         break;
> >>>> +       case GIMPLE_ASSIGN:
> >>>> +         if (!compare_gimple_assign (s1, s2))
> >>>> +           return return_different_stmts (s1, s2, "GIMPLE_ASSIGN");
> >>>> +         break;
> >>>> +       case GIMPLE_COND:
> >>>> +         if (!compare_gimple_cond (s1, s2))
> >>>> +           return return_different_stmts (s1, s2, "GIMPLE_COND");
> >>>> +         break;
> >>>> +       case GIMPLE_SWITCH:
> >>>> +         if (!compare_gimple_switch (s1, s2))
> >>>> +           return return_different_stmts (s1, s2, "GIMPLE_SWITCH");
> >>>> +         break;
> >>>> +       case GIMPLE_DEBUG:
> >>>> +       case GIMPLE_EH_DISPATCH:
> >>>> +         break;
> >>>> +       case GIMPLE_RESX:
> >>>> +         if (!compare_gimple_resx (s1, s2))
> >>>> +           return return_different_stmts (s1, s2, "GIMPLE_RESX");
> >>>> +         break;
> >>>> +       case GIMPLE_LABEL:
> >>>> +         if (!compare_gimple_label (s1, s2))
> >>>> +           return return_different_stmts (s1, s2, "GIMPLE_LABEL");
> >>>> +         break;
> >>>> +       case GIMPLE_RETURN:
> >>>> +         if (!compare_gimple_return (s1, s2))
> >>>> +           return return_different_stmts (s1, s2, "GIMPLE_RETURN");
> >>>> +         break;
> >>>> +       case GIMPLE_GOTO:
> >>>> +         if (!compare_gimple_goto (s1, s2))
> >>>> +           return return_different_stmts (s1, s2, "GIMPLE_GOTO");
> >>>> +         break;
> >>>> +       case GIMPLE_ASM:
> >>>> +         if (!compare_gimple_asm (s1, s2))
> >>>> +           return return_different_stmts (s1, s2, "GIMPLE_ASM");
> >>>> +         break;
> >>>> +       case GIMPLE_PREDICT:
> >>>> +       case GIMPLE_NOP:
> >>>> +         return true;
> >>>> +       default:
> >>>> +         return return_false_with_msg ("Unknown GIMPLE code reached");
> >>>> +       }
> >>>> +
> >>>> +      gsi_next (&gsi1);
> >>>> +      gsi_next (&gsi2);
> >>>> +    }
> >>>> +
> >>>> +  return true;
> >>>> +}
> >>>> +
> >>>> +/* Verifies for given GIMPLEs S1 and S2 that
> >>>> +   call statements are semantically equivalent.  */
> >>>> +
> >>>> +bool
> >>>> +func_checker::compare_gimple_call (gimple s1, gimple s2)
> >>>> +{
> >>>> +  unsigned i;
> >>>> +  tree t1, t2;
> >>>> +
> >>>> +  if (gimple_call_num_args (s1) != gimple_call_num_args (s2))
> >>>> +    return false;
> >>>> +
> >>>> +  t1 = gimple_call_fndecl (s1);
> >>>> +  t2 = gimple_call_fndecl (s2);
> >>>> +
> >>>> +  /* Function pointer variables are not supported yet.  */
> >>>> +  if (t1 == NULL || t2 == NULL)
> >>>> +    {
> >>>> +      if (!compare_operand (t1, t2))
> >>>> +       return return_false();
> >>>
> >>>
> >>> I think the comment above is out of date. compare_operand should do the
> >>> right
> >>> job for indirect calls.
> >>>>
> >>>> +
> >>>> +  if (cn1 && cn2 && cn1->weakref && cn2->weakref
> >>>> +      && cn1->alias_target == cn2->alias_target)
> >>>> +    return true;
> >>>
> >>>
> >>> Lets consistently drop the weakrefs handling and add full alias handling
> >>> incrementally.
> >>>>
> >>>> +
> >>>> +  /* Checking function arguments.  */
> >>>
> >>> attributes
> >>>>
> >>>> +  tree decl1 = DECL_ATTRIBUTES (decl);
> >>>> +  tree decl2 = DECL_ATTRIBUTES (m_compared_func->decl);
> >>>
> >>>
> >>> You can still do this as part of the wap_comparison, right?
> >>>>
> >>>> +
> >>>> +  m_checker = new func_checker (decl, m_compared_func->decl,
> >>>> +                               compare_polymorphic_p (),
> >>>> +                               false,
> >>>> +                               &refs_set,
> >>>> +                               &m_compared_func->refs_set);
> >>>> +  while (decl1)
> >>>> +    {
> >>>> +      if (decl2 == NULL)
> >>>> +       return return_false ();
> >>>> +
> >>>> +      if (get_attribute_name (decl1) != get_attribute_name (decl2))
> >>>> +       return return_false ();
> >>>> +
> >>>> +      tree attr_value1 = TREE_VALUE (decl1);
> >>>> +      tree attr_value2 = TREE_VALUE (decl2);
> >>>> +
> >>>> +      if (attr_value1 && attr_value2)
> >>>> +       {
> >>>> +         bool ret = m_checker->compare_operand (TREE_VALUE
> >>>> (attr_value1),
> >>>> +                                                TREE_VALUE
> >>>> (attr_value2));
> >>>> +         if (!ret)
> >>>> +           return return_false_with_msg ("attribute values are
> >>>> different");
> >>>> +       }
> >>>> +      else if (!attr_value1 && !attr_value2)
> >>>> +       {}
> >>>> +      else
> >>>> +       return return_false ();
> >>>> +
> >>>> +      decl1 = TREE_CHAIN (decl1);
> >>>> +      decl2 = TREE_CHAIN (decl2);
> >>>> +    }
> >>>> +
> >>>> +  if (decl1 != decl2)
> >>>> +    return return_false();
> >>>> +
> >>>> +
> >>>> +  for (arg1 = DECL_ARGUMENTS (decl),
> >>>> +       arg2 = DECL_ARGUMENTS (m_compared_func->decl);
> >>>> +       arg1; arg1 = DECL_CHAIN (arg1), arg2 = DECL_CHAIN (arg2))
> >>>> +    if (!m_checker->compare_decl (arg1, arg2))
> >>>> +      return return_false ();
> >>>> +
> >>>> +  /* Fill-up label dictionary.  */
> >>>> +  for (unsigned i = 0; i < bb_sorted.length (); ++i)
> >>>> +    {
> >>>> +      m_checker->parse_labels (bb_sorted[i]);
> >>>> +      m_checker->parse_labels (m_compared_func->bb_sorted[i]);
> >>>> +    }
> >>>> +
> >>>> +  /* Checking all basic blocks.  */
> >>>> +  for (unsigned i = 0; i < bb_sorted.length (); ++i)
> >>>> +    if(!m_checker->compare_bb (bb_sorted[i],
> >>>> m_compared_func->bb_sorted[i]))
> >>>> +      return return_false();
> >>>> +
> >>>> +  dump_message ("All BBs are equal\n");
> >>>> +
> >>>> +  /* Basic block edges check.  */
> >>>> +  for (unsigned i = 0; i < bb_sorted.length (); ++i)
> >>>> +    {
> >>>> +      bb_dict = XNEWVEC (int, bb_sorted.length () + 2);
> >>>> +      memset (bb_dict, -1, (bb_sorted.length () + 2) * sizeof (int));
> >>>> +
> >>>> +      bb1 = bb_sorted[i]->bb;
> >>>> +      bb2 = m_compared_func->bb_sorted[i]->bb;
> >>>> +
> >>>> +      ei2 = ei_start (bb2->preds);
> >>>> +
> >>>> +      for (ei1 = ei_start (bb1->preds); ei_cond (ei1, &e1); ei_next
> >>>> (&ei1))
> >>>> +       {
> >>>> +         ei_cond (ei2, &e2);
> >>>> +
> >>>> +         if (e1->flags != e2->flags)
> >>>> +           return return_false_with_msg ("flags comparison returns
> >>>> false");
> >>>> +
> >>>> +         if (!bb_dict_test (bb_dict, e1->src->index, e2->src->index))
> >>>> +           return return_false_with_msg ("edge comparison returns
> >>>> false");
> >>>> +
> >>>> +         if (!bb_dict_test (bb_dict, e1->dest->index, e2->dest->index))
> >>>> +           return return_false_with_msg ("BB comparison returns false");
> >>>> +
> >>>> +         if (!m_checker->compare_edge (e1, e2))
> >>>> +           return return_false_with_msg ("edge comparison returns
> >>>> false");
> >>>> +
> >>>> +         ei_next (&ei2);
> >>>> +       }
> >>>> +    }
> >>>> +
> >>>> +  /* Basic block PHI nodes comparison.  */
> >>>> +  for (unsigned i = 0; i < bb_sorted.length (); i++)
> >>>> +    if (!compare_phi_node (bb_sorted[i]->bb,
> >>>> m_compared_func->bb_sorted[i]->bb))
> >>>> +      return return_false_with_msg ("PHI node comparison returns
> >>>> false");
> >>>> +
> >>>> +  return result;
> >>>> +}
> >>>
> >>>
> >>> The rest of patch seems fine.  I think we went across enough of
> >>> iteraitons, the patch is OK
> >>> with changes above and lets handle rest incrementally.
> >>>
> >>> Thanks!
> >>> Honza
> >>>
> >>
> >> Hello
> >>
> >> There's final version of the patch I'm going to commit tomorrow in the
> >> morning (CEST).
> >> Thank you Honza for the review.
> >>
> >> Martin

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 3/5] IPA ICF pass
  2014-10-15 17:06                     ` Martin Liška
  2014-10-22 21:20                       ` Jiong Wang
@ 2014-11-13 22:26                       ` H.J. Lu
  2015-01-20 21:29                         ` H.J. Lu
  1 sibling, 1 reply; 70+ messages in thread
From: H.J. Lu @ 2014-11-13 22:26 UTC (permalink / raw)
  To: Martin Liška; +Cc: GCC Patches, hubicka >> Jan Hubicka

On Wed, Oct 15, 2014 at 10:03 AM, Martin Liška <mliska@suse.cz> wrote:
>
> Hello
>
> There's final version of the patch I'm going to commit tomorrow in the
> morning (CEST).
> Thank you Honza for the review.
>
> Martin

This caused:

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=63856

-- 
H.J.

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 3/5] IPA ICF pass
  2014-11-13 22:26                       ` H.J. Lu
@ 2015-01-20 21:29                         ` H.J. Lu
  0 siblings, 0 replies; 70+ messages in thread
From: H.J. Lu @ 2015-01-20 21:29 UTC (permalink / raw)
  To: Martin Liška; +Cc: GCC Patches, hubicka >> Jan Hubicka

On Thu, Nov 13, 2014 at 2:17 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
> On Wed, Oct 15, 2014 at 10:03 AM, Martin Liška <mliska@suse.cz> wrote:
>>
>> Hello
>>
>> There's final version of the patch I'm going to commit tomorrow in the
>> morning (CEST).
>> Thank you Honza for the review.
>>
>> Martin
>
> This caused:
>
> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=63856
>

This also caused:

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64693

-- 
H.J.

^ permalink raw reply	[flat|nested] 70+ messages in thread

end of thread, other threads:[~2015-01-20 20:47 UTC | newest]

Thread overview: 70+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-06-16 10:07 [PATCH 1/5] New Identical Code Folding IPA pass mliska
2014-06-16 10:07 ` [PATCH 4/5] Existing tests fix mliska
2014-06-17 19:52   ` Jeff Law
2014-06-17 20:50     ` Rainer Orth
2014-06-18  9:02       ` Martin Liška
2014-06-30 12:12     ` Martin Liška
2014-09-26 12:21       ` Martin Liška
2014-06-16 10:07 ` [PATCH 5/5] New tests introduction mliska
2014-06-17 19:53   ` Jeff Law
2014-06-30 12:14     ` Martin Liška
2014-10-19  8:19       ` Andreas Schwab
2014-10-23 12:34         ` Martin Liška
2014-06-16 10:07 ` [PATCH 2/5] Existing call graph infrastructure enhancement mliska
2014-06-17 20:00   ` Jeff Law
2014-06-30 11:49     ` Martin Liška
2014-06-30 18:54       ` Jeff Law
2014-07-17 14:54         ` Martin Liška
2014-08-25  9:56           ` Martin Liška
2014-08-25 16:12             ` Jan Hubicka
2014-08-27 21:12             ` Jeff Law
2014-09-24 14:23   ` Martin Liška
2014-09-24 15:01     ` Jan Hubicka
2014-09-26 10:19       ` Martin Liška
2014-06-16 10:07 ` [PATCH 3/5] IPA ICF pass mliska
2014-06-20  7:32   ` Trevor Saunders
2014-06-26 11:18     ` Martin Liška
2014-07-05 21:44     ` Gerald Pfeifer
2014-07-05 22:53       ` Jan Hubicka
2014-07-17 15:23         ` Martin Liška
2014-09-26 12:20           ` Martin Liška
2014-09-26 14:44             ` Markus Trippelsdorf
2014-09-26 23:27               ` Jan Hubicka
2014-09-27  5:59                 ` Markus Trippelsdorf
2014-09-27  7:47                   ` Markus Trippelsdorf
2014-09-27 10:46                     ` Martin Liška
2014-09-27 10:37                   ` Martin Liška
2014-09-28  2:21                     ` Jan Hubicka
2014-10-10 23:54                       ` Fakturace
2014-10-11  0:02                       ` Martin Liška
2014-10-11  8:23                         ` Jan Hubicka
2014-10-13 13:20                           ` Martin Liška
2014-10-13 13:29                             ` Jan Hubicka
2014-09-27 10:32                 ` Martin Liška
2014-09-26 20:47             ` Jan Hubicka
2014-10-11  0:08               ` Martin Liška
2014-10-11  8:26                 ` Jan Hubicka
2014-10-13 15:17                 ` Martin Liška
2014-10-14 16:12                   ` Jan Hubicka
2014-10-15 17:06                     ` Martin Liška
2014-10-22 21:20                       ` Jiong Wang
2014-11-06  3:01                         ` Joey Ye
2014-11-06  9:03                           ` Jan Hubicka
2014-11-13 22:26                       ` H.J. Lu
2015-01-20 21:29                         ` H.J. Lu
2014-09-26 22:27             ` Jan Hubicka
2014-10-11  0:23               ` Martin Liška
2014-10-11  9:05                 ` Jan Hubicka
2014-06-24 20:31   ` Jeff Law
2014-06-26 16:02     ` Martin Liška
2014-06-26 18:46     ` Jan Hubicka
2014-06-30 12:05       ` Martin Liška
2014-07-01 23:45         ` Trevor Saunders
2014-06-30 19:06       ` Jeff Law
2014-06-17 19:49 ` [PATCH 1/5] New Identical Code Folding IPA pass Jeff Law
2014-06-18 19:05   ` Jan Hubicka
2014-06-17 20:09 ` Paolo Carlini
2014-06-18  8:46   ` Martin Liška
2014-06-18  8:49     ` Paolo Carlini
2014-06-17 20:17 ` David Malcolm
2014-06-18  8:10   ` Martin Liška

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).