IPA ICF pass, part 3/5

* Makefile.in: New object files included.
	* cgraph.c (cgraph_node::dump): New cgraph_node flag icf_merged
	is printed.
	(verify_edge_corresponds_to_fndecl): More sensitive verification
	of nodes that are merged by IPA ICF.
	* cgraph.h (cgraph_node::num_references): New function.
	* cgraphunit.c (cgraph_node::expand_thunk): White space fixed.
	* common.opt: New options ipa-icf, ipa-icf-functions and
	ipa-icf-variables introduced.
	* doc/invoke.texi: Documentation of new options introduced.
	* ipa-icf-gimple.c: New file.
	* ipa-icf-gimple.h: New file.
	* ipa-icf.c: New file.
	* ipa-icf.h: New file.
	* lto-cgraph.c (lto_output_node): Streaming of icf_merged flag added.
	(input_overwrite_node): Likewise.
	* lto-section-in.c: New icf section added.
	* lto-streamer.h (enum lto_section_type): Likewise.
	* opts.c (common_handle_option): New option added.
	* passes.def: New pass included.
	* timevar.def: Time variable for IPA ICF added.
	* tree-pass.h: New IPA ICF pass entry point added.


Co-Authored-By: Jan Hubicka <hubicka@ucw.cz>

From-SVN: r216305
This commit is contained in:
Martin Liska 2014-10-16 12:47:55 +02:00 committed by Martin Liska
parent 93a95abe92
commit b84d4347ac
18 changed files with 4166 additions and 3 deletions

View File

@ -1,3 +1,29 @@
2014-10-16 Martin Liska <mliska@suse.cz>
Jan Hubicka <hubicka@ucw.cz>
* Makefile.in: New object files included.
* cgraph.c (cgraph_node::dump): New cgraph_node flag icf_merged
is printed.
(verify_edge_corresponds_to_fndecl): More sensitive verification
of nodes that are merged by IPA ICF.
* cgraph.h (cgraph_node::num_references): New function.
* cgraphunit.c (cgraph_node::expand_thunk): White space fixed.
* common.opt: New options ipa-icf, ipa-icf-functions and
ipa-icf-variables introduced.
* doc/invoke.texi: Documentation of new options introduced.
* ipa-icf-gimple.c: New file.
* ipa-icf-gimple.h: New file.
* ipa-icf.c: New file.
* ipa-icf.h: New file.
* lto-cgraph.c (lto_output_node): Streaming of icf_merged flag added.
(input_overwrite_node): Likewise.
* lto-section-in.c: New icf section added.
* lto-streamer.h (enum lto_section_type): Likewise.
* opts.c (common_handle_option): New option added.
* passes.def: New pass included.
* timevar.def: Time variable for IPA ICF added.
* tree-pass.h: New IPA ICF pass entry point added.
2014-10-16 Richard Biener <rguenther@suse.de>
PR tree-optimization/63168

View File

@ -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 \

View File

@ -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;

View File

@ -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

View File

@ -1501,7 +1501,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. */

View File

@ -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

View File

@ -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

897
gcc/ipa-icf-gimple.c Normal file
View File

@ -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

264
gcc/ipa-icf-gimple.h Normal file
View File

@ -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

2371
gcc/ipa-icf.c Normal file

File diff suppressed because it is too large Load Diff

553
gcc/ipa-icf.h Normal file
View File

@ -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

View File

@ -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);

View File

@ -60,7 +60,8 @@ const char *lto_section_name[LTO_N_SECTION_TYPES] =
"opts",
"cgraphopt",
"inline",
"ipcp_trans"
"ipcp_trans",
"icf"
};

View File

@ -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. */
};

View File

@ -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. */

View File

@ -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);

View File

@ -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")

View File

@ -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);