summaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
Diffstat (limited to 'gcc')
-rw-r--r--gcc/ipa-field-reorder.c3
-rw-r--r--gcc/ipa-type-escape-analysis.c152
-rw-r--r--gcc/ipa-type-escape-analysis.h70
3 files changed, 210 insertions, 15 deletions
diff --git a/gcc/ipa-field-reorder.c b/gcc/ipa-field-reorder.c
index 2f4b7942966..21f13f376b0 100644
--- a/gcc/ipa-field-reorder.c
+++ b/gcc/ipa-field-reorder.c
@@ -588,8 +588,9 @@ lto_fr_execute ()
log ("here in field reordering \n");
// Analysis.
detected_incompatible_syntax = false;
+ std::map<tree, bool> whitelisted = get_whitelisted_nodes();
tpartitions_t escaping_nonescaping_sets
- = partition_types_into_escaping_nonescaping ();
+ = partition_types_into_escaping_nonescaping (whitelisted);
record_field_map_t record_field_map = find_fields_accessed ();
record_field_offset_map_t record_field_offset_map
= obtain_nonescaping_unaccessed_fields (escaping_nonescaping_sets,
diff --git a/gcc/ipa-type-escape-analysis.c b/gcc/ipa-type-escape-analysis.c
index 25a82d7daff..b65ef9b4e4f 100644
--- a/gcc/ipa-type-escape-analysis.c
+++ b/gcc/ipa-type-escape-analysis.c
@@ -166,6 +166,7 @@ along with GCC; see the file COPYING3. If not see
#include <set>
#include <map>
#include <stack>
+#include <queue>
#include "ipa-type-escape-analysis.h"
#include "ipa-dfe.h"
@@ -249,6 +250,74 @@ lto_dfe_execute ()
return 0;
}
+std::map<tree, bool>
+get_whitelisted_nodes()
+{
+ cgraph_node *node = NULL;
+ std::set<cgraph_node *> nodes;
+ std::set<cgraph_node *> leaf_nodes;
+ std::set<tree> leaf_nodes_decl;
+ FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
+ {
+ node->get_untransformed_body();
+ nodes.insert(node);
+ if (node->callees) continue;
+
+ leaf_nodes.insert(node);
+ leaf_nodes_decl.insert(node->decl);
+ }
+
+ std::queue<cgraph_node *> worklist;
+ for (std::set<cgraph_node*>::iterator i = leaf_nodes.begin(), e = leaf_nodes.end(); i != e; ++i)
+ {
+ if (dump_file) fprintf(dump_file, "is a leaf node %s\n", (*i)->name());
+ worklist.push(*i);
+ }
+
+ for (std::set<cgraph_node*>::iterator i = nodes.begin(), e = nodes.end(); i != e; ++i)
+ {
+ worklist.push(*i);
+ }
+
+ std::map<tree, bool> map;
+ while (!worklist.empty())
+ {
+
+ if (detected_incompatible_syntax) return map;
+ cgraph_node *i = worklist.front();
+ worklist.pop();
+ if (dump_file) fprintf(dump_file, "analyzing %s %p\n", i->name(), i);
+ GimpleWhiteLister whitelister;
+ whitelister._walk_cnode(i);
+ bool no_external = whitelister.does_not_call_external_functions(i, map);
+ bool before_in_map = map.find(i->decl) != map.end();
+ bool place_callers_in_worklist = !before_in_map;
+ if (!before_in_map)
+ {
+ map.insert(std::pair<tree, bool>(i->decl, no_external));
+ } else
+ {
+ map[i->decl] = no_external;
+ }
+ bool previous_value = map[i->decl];
+ place_callers_in_worklist |= previous_value != no_external;
+ if (previous_value != no_external)
+ {
+ gcc_assert(!previous_value);
+ gcc_assert(no_external);
+ }
+ if (dump_file) fprintf(dump_file, "no external %s\n", no_external ? "T" : "F");
+
+ for (cgraph_edge *e = i->callers; place_callers_in_worklist && e; e = e->next_caller)
+ {
+ worklist.push(e->caller);
+ }
+ }
+
+ return map;
+
+}
+
/*
* Perform dead field elimination at link-time.
* This transformation is composed of two main stages:
@@ -261,11 +330,14 @@ lto_dead_field_elimination ()
{
// Analysis.
detected_incompatible_syntax = false;
+ std::map<tree, bool> whitelisted = get_whitelisted_nodes();
tpartitions_t escaping_nonescaping_sets
- = partition_types_into_escaping_nonescaping ();
+ = partition_types_into_escaping_nonescaping (whitelisted);
+ if (detected_incompatible_syntax) return;
record_field_map_t record_field_map = find_fields_accessed ();
+ if (detected_incompatible_syntax) return;
record_field_offset_map_t record_field_offset_map
- = obtain_nonescaping_unaccessed_fields (escaping_nonescaping_sets,
+ = obtain_nonescaping_unaccessed_fields (escaping_nonescaping_sets,
record_field_map, OPT_Wdfa);
if (detected_incompatible_syntax || record_field_offset_map.empty ())
return;
@@ -301,11 +373,12 @@ partition_types_into_record_reaching_or_non_record_reaching ()
* which types are escaping AND are being casted.
*/
tpartitions_t
-partition_types_into_escaping_nonescaping ()
+partition_types_into_escaping_nonescaping (std::map<tree, bool> &whitelisted)
{
tpartitions_t partitions
= partition_types_into_record_reaching_or_non_record_reaching ();
- GimpleCaster caster (partitions);
+ if (detected_incompatible_syntax) return partitions;
+ GimpleCaster caster (partitions, whitelisted);
caster.walk ();
// Dump for debugging.
@@ -1194,6 +1267,7 @@ GimpleWalker::walk ()
if (dump_file)
dump_function_to_file (node->decl, dump_file, TDF_NONE);
+
_walk_cnode (node);
fndecls.insert (decl);
}
@@ -1252,6 +1326,7 @@ void
GimpleWalker::_walk_cnode (cgraph_node *cnode)
{
gcc_assert (cnode);
+ currently_walking = cnode;
_walk_decl (cnode);
_walk_locals (cnode);
_walk_ssa_names (cnode);
@@ -1534,7 +1609,6 @@ GimpleWalker::_walk_gphi (gphi *g)
{
}
-
void
TypeCollector::collect (const_tree t)
{
@@ -1746,6 +1820,7 @@ TypeCollector::_walk_RECORD_TYPE_post (const_tree t)
i->second = true;
}
_collect_simple (t);
+
}
void
@@ -1793,9 +1868,28 @@ TypeCollector::_walk_METHOD_TYPE_pre (const_tree t)
inline void
ExprCollector::_walk_pre (const_tree e)
{
+ if (!e) return;
const_tree t = TREE_TYPE (e);
gcc_assert (t);
typeCollector.collect (t);
+
+ if (RECORD_TYPE != TREE_CODE(t)) return;
+
+ if (typeCollector.ptrset.records.empty()) {
+ typeCollector.ptrset.records.insert(TYPE_MAIN_VARIANT(t));
+ return;
+ }
+
+ for (std::set<const_tree>::iterator i = typeCollector.ptrset.records.begin(), e = typeCollector.ptrset.records.end(); i != e; ++i)
+ {
+ const_tree r = *i;
+ TypeIncompleteEquality structuralEquality;
+ bool is_same = structuralEquality.equal (TYPE_MAIN_VARIANT(r), TYPE_MAIN_VARIANT(t));
+ if (is_same) continue;
+
+ TypeStringifier stringifier;
+ typeCollector.ptrset.records.insert(TYPE_MAIN_VARIANT(t));
+ }
}
/*
@@ -1809,6 +1903,7 @@ ExprCollector::_walk_pre (const_tree e)
void
GimpleTypeCollector::_walk_pre_tree (const_tree t)
{
+ if (!t) return;
exprCollector.walk (t);
}
@@ -1885,6 +1980,17 @@ GimpleTypeCollector::_walk_pre_gcall (gcall *s)
exprCollector.walk (lhs);
}
+void
+GimpleTypeCollector::_walk_pre_gdebug (gdebug *s)
+{
+ if (!gimple_debug_bind_p(s)) return;
+ tree var = gimple_debug_bind_get_var(s);
+ if (var) {
+ gcc_assert(TREE_TYPE(var));
+ exprCollector.walk(var);
+ }
+}
+
// Print working set.
void
GimpleTypeCollector::print_collected ()
@@ -2453,18 +2559,28 @@ GimpleCaster::_walk_pre_gassign (gassign *s)
const_tree t_lhs = TREE_TYPE (lhs);
const_tree t_rhs = TREE_TYPE (rhs);
gcc_assert (t_lhs && t_rhs);
- bool is_cast = !equality.equal (t_lhs, t_rhs);
+ bool is_cast = !equality.equal (TYPE_MAIN_VARIANT(t_lhs), TYPE_MAIN_VARIANT(t_rhs));
// If it is cast, we might need to look at the definition of rhs
// If the definition comes from a known function... then we are good...
bool is_ssa = TREE_CODE (rhs) == SSA_NAME;
- is_cast = is_ssa ? follow_def_to_find_if_really_cast (rhs) : is_cast;
+ is_cast = is_cast && is_ssa ? follow_def_to_find_if_really_cast (rhs) : is_cast;
+ // we allow casts to pointers...
+ is_cast = is_cast && !(t_lhs == ptr_type_node);
+
+ TypeStringifier stringifier;
reason.type_is_casted = is_cast;
+ bool in_map = _whitelisted.find(currently_walking->decl) != _whitelisted.end();
+ bool whitelisted = in_map && _whitelisted[currently_walking->decl];
+ if (whitelisted) goto escaper_label;
+ log("lhs %s is casted: %s\n", stringifier.stringify(t_lhs).c_str(), is_cast ? "T" : "F");
+ log("rhs %s is casted: %s\n", stringifier.stringify(t_rhs).c_str(), is_cast ? "T" : "F");
if (!flag_ipa_type_escape_analysis_keep_casts)
exprEscaper.typeEscaper.update (TREE_TYPE (lhs), reason);
if (!flag_ipa_type_escape_analysis_keep_casts)
exprEscaper.typeEscaper.update (TREE_TYPE (rhs), reason);
+escaper_label:
GimpleEscaper::_walk_pre_gassign (s);
}
@@ -2476,7 +2592,7 @@ GimpleCaster::_walk_pre_gcall (gcall *s)
if (flag_ipa_type_escape_analysis_keep_casts)
return;
- const_tree fn = gimple_call_fndecl (s);
+ tree fn = gimple_call_fndecl (s);
// If there's no function declaration, how do we
// know the argument types?
if (!fn)
@@ -2488,13 +2604,25 @@ GimpleCaster::_walk_pre_gcall (gcall *s)
if (known_function)
return;
+ if (_whitelisted.find(fn) != _whitelisted.end() && _whitelisted[fn]) return;
+ bool in_map = _whitelisted.find(currently_walking->decl) != _whitelisted.end();
+ bool whitelisted = in_map && _whitelisted[currently_walking->decl];
+ if (whitelisted) return;
+
+ if (!currently_walking->callers) return;
+
+ if (!node && currently_walking->inlined_to) return;
+
const_tree f_t = TREE_TYPE (fn);
TypeIncompleteEquality equality;
unsigned i = 0;
+ TypeStringifier stringifier;
for (tree a = TYPE_ARG_TYPES (f_t); NULL_TREE != a; a = TREE_CHAIN (a))
{
const_tree formal_t = TREE_VALUE (a);
+ //if (formal_t == ptr_type_node) continue;
+ //if (formal_t == void_type_node) continue;
// There seems to be a final VOID_TYPE at the end of some functions?
const enum tree_code code = TREE_CODE (formal_t);
const bool is_void = VOID_TYPE == code;
@@ -2504,6 +2632,7 @@ GimpleCaster::_walk_pre_gcall (gcall *s)
const_tree real = gimple_call_arg (s, i);
const_tree real_t = TREE_TYPE (real);
const bool is_casted = !equality.equal (formal_t, real_t);
+ log("arg %s is casted: %s\n", stringifier.stringify(real_t).c_str(), is_casted ? "T" : "F");
Reason arg_reason;
arg_reason.type_is_casted = is_casted;
exprEscaper.typeEscaper.update (TREE_TYPE (real), arg_reason);
@@ -3262,7 +3391,10 @@ TypeStructuralEquality::_equal_code (const_tree l, const_tree r)
const enum tree_code code_l = TREE_CODE (l);
const enum tree_code code_r = TREE_CODE (r);
const bool equal = code_l == code_r;
- return equal;
+ bool array_or_ptr_l = TREE_CODE(l) == ARRAY_TYPE || TREE_CODE(l) == POINTER_TYPE;
+ bool array_or_ptr_r = TREE_CODE(r) == ARRAY_TYPE || TREE_CODE(r) == POINTER_TYPE;
+ bool array_or_ptr = array_or_ptr_l && array_or_ptr_r;
+ return equal || array_or_ptr;
}
#define TSE_FUNC_DEF_SIMPLE(code) \
@@ -3284,7 +3416,9 @@ bool
TypeStructuralEquality::_equal_wrapper (const_tree l, const_tree r)
{
const_tree inner_l = TREE_TYPE (l);
+ if (TREE_CODE(inner_l) == ARRAY_TYPE && TREE_CODE(TREE_TYPE(inner_l)) == POINTER_TYPE ) inner_l = TREE_TYPE(inner_l);
const_tree inner_r = TREE_TYPE (r);
+ if (TREE_CODE(inner_r) == ARRAY_TYPE && TREE_CODE(TREE_TYPE(inner_r)) == POINTER_TYPE ) inner_r = TREE_TYPE(inner_r);
return _equal (inner_l, inner_r);
}
diff --git a/gcc/ipa-type-escape-analysis.h b/gcc/ipa-type-escape-analysis.h
index 8b9911ace1e..cbd07701199 100644
--- a/gcc/ipa-type-escape-analysis.h
+++ b/gcc/ipa-type-escape-analysis.h
@@ -316,6 +316,8 @@ protected:
*/
bool _deleted;
+ cgraph_node *currently_walking;
+
/* Walk global variables. */
void _walk_globals ();
@@ -323,7 +325,7 @@ protected:
virtual void _walk_global (varpool_node *v);
/* Will walk declarations, locals, ssa names, and basic blocks. */
- void _walk_cnode (cgraph_node *cnode);
+ virtual void _walk_cnode (cgraph_node *cnode);
/* This will walk the CNODE->decl. */
void _walk_decl (cgraph_node *cnode);
@@ -439,6 +441,9 @@ struct type_partitions_s
/* The set of all non escaping types. */
tset_t non_escaping;
+ /* The set of all records. */
+ tset_t records;
+
/* Determine if we have seen this type before. */
bool in_universe (const_tree) const;
@@ -494,8 +499,11 @@ private:
* from PTR.
* This datastructure persists across calls to collect.
*/
+public:
tpartitions_t ptrset;
+private:
+
/* Sanity check determines that the partitions are mutually
* exclusive.
*/
@@ -648,9 +656,9 @@ public:
return typeCollector.get_record_reaching_trees ();
}
-private:
TypeCollector typeCollector;
+private:
/* Catch all callback for all nested expressions E. */
virtual void _walk_pre (const_tree e);
};
@@ -676,9 +684,10 @@ public:
// TODO: I believe this could be made const.
void print_collected ();
-private:
ExprCollector exprCollector;
+private:
+
/* Call back for global variables. */
virtual void _walk_pre_tree (const_tree);
@@ -693,6 +702,53 @@ private:
/* Call back for gcall. */
virtual void _walk_pre_gcall (gcall *s);
+
+ /* Call back for gdebug. */
+ virtual void _walk_pre_gdebug (gdebug *s);
+
+};
+
+class GimpleWhiteLister : GimpleTypeCollector
+{
+public:
+ GimpleWhiteLister()
+ {};
+
+ bool _no_external = true;
+ bool does_not_call_external_functions(cgraph_node *c, std::map<tree, bool> &whitelisted)
+ {
+ gcc_assert(c);
+
+ for (cgraph_edge *edge = c->callees; edge; edge = edge->next_callee)
+ {
+ cgraph_node *callee = edge->callee;
+ if (callee == c) continue;
+ bool in_map = whitelisted.find(callee->decl) != whitelisted.end();
+ if (!in_map) {
+ return false;
+ }
+ bool w = whitelisted[callee->decl];
+ if (!w) {
+ return false;
+ }
+ }
+
+ unsigned int how_many_records = exprCollector.typeCollector.ptrset.records.size();
+ return how_many_records <= 1;
+
+ }
+
+ /* Will walk declarations, locals, ssa names, and basic blocks. */
+ virtual void _walk_cnode (cgraph_node *cnode)
+ {
+ GimpleTypeCollector::_walk_cnode(cnode);
+ }
+
+private:
+ virtual void _walk_pre_gcall (gcall *s)
+ {
+ this->_no_external = false;
+ }
};
// Reason for why a type is escaping
@@ -1004,10 +1060,13 @@ protected:
class GimpleCaster : public GimpleEscaper
{
public:
- GimpleCaster (tpartitions_t &types) : GimpleEscaper (types)
+ GimpleCaster (tpartitions_t &types, std::map<tree, bool> &whitelisted) : GimpleEscaper (types), _whitelisted(whitelisted)
{};
private:
+
+ std::map<tree, bool> &_whitelisted;
+
/* Determine if cast comes from a known function. */
static bool follow_def_to_find_if_really_cast (const_tree);
@@ -1157,7 +1216,7 @@ typedef std::map<const_tree, field_offsets_t> record_field_offset_map_t;
// Partition types into escaping or non escaping sets.
tpartitions_t
-partition_types_into_escaping_nonescaping ();
+partition_types_into_escaping_nonescaping (std::map<tree, bool>&);
// Compute set of not escaping unaccessed fields
record_field_offset_map_t
@@ -1166,5 +1225,6 @@ obtain_nonescaping_unaccessed_fields (tpartitions_t casting,
int warning);
extern bool detected_incompatible_syntax;
+std::map<tree, bool> get_whitelisted_nodes();
#endif /* GCC_IPA_TYPE_ESCAPE_ANALYSIS_H */