#include "ipa-utils.h"
#include "lto-partition.h"
+/* Classifcation of symbols into classes WRT partitioning. */
+enum symbol_class
+{
+ /* External declarations are ignored by partitioning algorithms and they are
+ added into the boundary later via compute_ltrans_boundary. */
+ SYMBOL_EXTERNAL,
+ /* Partitioned symbols are pur into one of partitions. */
+ SYMBOL_PARTITION,
+ /* Duplicated symbols (such as comdat or constant pool references) are
+ copied into every node needing them via add_symbol_to_partition. */
+ SYMBOL_DUPLICATE
+};
+
VEC(ltrans_partition, heap) *ltrans_partitions;
-static void add_cgraph_node_to_partition (ltrans_partition part, struct cgraph_node *node);
-static void add_varpool_node_to_partition (ltrans_partition part, struct varpool_node *vnode);
-static bool partition_symbol_p (symtab_node node);
+static void add_symbol_to_partition (ltrans_partition part, symtab_node node);
+
+/* Classify symbol NODE. */
+
+enum symbol_class
+get_symbol_class (symtab_node node)
+{
+ /* Inline clones are always duplicated.
+ This include external delcarations. */
+ if (symtab_function_p (node)
+ && cgraph (node)->global.inlined_to)
+ return SYMBOL_DUPLICATE;
+
+ /* External declarations are external. */
+ if (DECL_EXTERNAL (node->symbol.decl))
+ return SYMBOL_EXTERNAL;
+
+ if (symtab_variable_p (node))
+ {
+ /* Constant pool references use local symbol names that can not
+ be promoted global. We should never put into a constant pool
+ objects that can not be duplicated across partitions. */
+ if (DECL_IN_CONSTANT_POOL (node->symbol.decl))
+ return SYMBOL_DUPLICATE;
+ gcc_checking_assert (varpool (node)->analyzed);
+ }
+ /* Functions that are cloned may stay in callgraph even if they are unused.
+ Handle them as external; compute_ltrans_boundary take care to make
+ proper things to happen (i.e. to make them appear in the boundary but
+ with body streamed, so clone can me materialized). */
+ else if (!cgraph (node)->analyzed)
+ return SYMBOL_EXTERNAL;
+
+ /* Weakref aliases are always duplicated. */
+ if (lookup_attribute ("weakref", DECL_ATTRIBUTES (node->symbol.decl)))
+ return SYMBOL_DUPLICATE;
+
+ /* Comdats are duplicated to every use unless they are keyed.
+ Those do not need duplication. */
+ if (DECL_COMDAT (node->symbol.decl)
+ && !node->symbol.force_output
+ && !symtab_used_from_object_file_p ((symtab_node) node))
+ return SYMBOL_DUPLICATE;
+
+ return SYMBOL_PARTITION;
+}
/* Create new partition with name NAME. */
+
static ltrans_partition
new_partition (const char *name)
{
}
/* Free memory used by ltrans datastructures. */
+
void
free_ltrans_partitions (void)
{
ltrans_partition part;
for (idx = 0; VEC_iterate (ltrans_partition, ltrans_partitions, idx, part); idx++)
{
+ if (part->initializers_visited)
+ pointer_set_destroy (part->initializers_visited);
/* Symtab encoder is freed after streaming. */
free (part);
}
VEC_free (ltrans_partition, heap, ltrans_partitions);
}
-/* Add all referenced symbols referenced by REFS that are not external and not
- partitioned into PART. */
-static void
-add_references_to_partition (ltrans_partition part, struct ipa_ref_list *refs)
+/* Return true if symbol is already in some partition. */
+
+static inline bool
+symbol_partitioned_p (symtab_node node)
{
- int i;
- struct ipa_ref *ref;
- for (i = 0; ipa_ref_list_reference_iterate (refs, i, ref); i++)
- {
- if (DECL_EXTERNAL (ref->referred->symbol.decl)
- || partition_symbol_p (ref->referred)
- || lto_symtab_encoder_in_partition_p (part->encoder, ref->referred))
- continue;
- if (symtab_function_p (ref->referred))
- add_cgraph_node_to_partition (part, ipa_ref_node (ref));
- else
- add_varpool_node_to_partition (part, ipa_ref_varpool_node (ref));
- }
+ return node->symbol.aux;
}
-/* Look for all (nonweakref) aliases in REFS and add them into PART. */
+/* Add references into the partition. */
static void
-add_aliases_to_partition (ltrans_partition part, struct ipa_ref_list *refs)
+add_references_to_partition (ltrans_partition part, symtab_node node)
{
int i;
struct ipa_ref *ref;
- for (i = 0; ipa_ref_list_referring_iterate (refs, i, ref); i++)
- if (ref->use == IPA_REF_ALIAS
- && !lto_symtab_encoder_in_partition_p (part->encoder,
- ref->referring)
- && !lookup_attribute ("weakref",
- DECL_ATTRIBUTES
- (ref->referring->symbol.decl)))
+ /* Add all duplicated references to the partition. */
+ for (i = 0; ipa_ref_list_reference_iterate (&node->symbol.ref_list, i, ref); i++)
+ if (get_symbol_class (ref->referred) == SYMBOL_DUPLICATE)
+ add_symbol_to_partition (part, ref->referred);
+ /* References to a readonly variable may be constant foled into its value.
+ Recursively look into the initializers of the constant variable and add
+ references, too. */
+ else if (symtab_variable_p (ref->referred)
+ && const_value_known_p (ref->referred->symbol.decl)
+ && !lto_symtab_encoder_in_partition_p (part->encoder, ref->referred))
{
- if (symtab_function_p (ref->referring))
- add_cgraph_node_to_partition (part, ipa_ref_referring_node (ref));
- else
- add_varpool_node_to_partition (part,
- ipa_ref_referring_varpool_node (ref));
+ if (!part->initializers_visited)
+ part->initializers_visited = pointer_set_create ();
+ if (!pointer_set_insert (part->initializers_visited, ref->referred))
+ add_references_to_partition (part, ref->referred);
}
}
-/* Worker for add_cgraph_node_to_partition. */
+/* Helper function for add_symbol_to_partition doing the actual dirty work
+ of adding NODE to PART. */
static bool
-add_cgraph_node_to_partition_1 (struct cgraph_node *node, void *data)
+add_symbol_to_partition_1 (ltrans_partition part, symtab_node node)
{
- ltrans_partition part = (ltrans_partition) data;
+ enum symbol_class c = get_symbol_class (node);
+ int i;
+ struct ipa_ref *ref;
+ symtab_node node1;
+ /* If NODE is already there, we have nothing to do. */
if (lto_symtab_encoder_in_partition_p (part->encoder, (symtab_node) node))
+ return true;
+
+ /* non-duplicated aliases or tunks of a duplicated symbol needs to be output
+ just once.
+
+ Be lax about comdats; they may or may not be duplicated and we may
+ end up in need to duplicate keyed comdat because it has unkeyed alias. */
+ if (c == SYMBOL_PARTITION && !DECL_COMDAT (node->symbol.decl)
+ && symbol_partitioned_p (node))
return false;
- /* non-COMDAT aliases of COMDAT functions needs to be output just once. */
- if (!DECL_COMDAT (node->symbol.decl)
- && !node->global.inlined_to
- && node->symbol.aux)
- {
- gcc_assert (node->thunk.thunk_p || node->alias);
- return false;
- }
+ /* Be sure that we never try to duplicate partitioned symbol
+ or add external symbol. */
+ gcc_assert (c != SYMBOL_EXTERNAL
+ && (c == SYMBOL_DUPLICATE || !symbol_partitioned_p (node)));
+
+ lto_set_symtab_encoder_in_partition (part->encoder, (symtab_node) node);
- if (node->symbol.aux)
+ if (symbol_partitioned_p (node))
{
node->symbol.in_other_partition = 1;
if (cgraph_dump_file)
- fprintf (cgraph_dump_file, "Node %s/%i now used in multiple partitions\n",
- cgraph_node_name (node), node->uid);
+ fprintf (cgraph_dump_file, "Symbol node %s now used in multiple partitions\n",
+ symtab_node_name (node));
}
node->symbol.aux = (void *)((size_t)node->symbol.aux + 1);
- lto_set_symtab_encoder_in_partition (part->encoder, (symtab_node)node);
- return false;
-}
-
-/* Add NODE to partition as well as the inline callees and referred comdats into partition PART. */
-
-static void
-add_cgraph_node_to_partition (ltrans_partition part, struct cgraph_node *node)
-{
- struct cgraph_edge *e;
- struct cgraph_node *n;
-
- /* If NODE is already there, we have nothing to do. */
- if (lto_symtab_encoder_in_partition_p (part->encoder, (symtab_node) node))
- return;
-
- cgraph_for_node_thunks_and_aliases (node, add_cgraph_node_to_partition_1, part, true);
- part->insns += inline_summary (node)->self_size;
+ if (symtab_function_p (node))
+ {
+ struct cgraph_node *cnode = cgraph (node);
+ struct cgraph_edge *e;
+ part->insns += inline_summary (cnode)->self_size;
+
+ /* Add all inline clones and callees that are duplicated. */
+ for (e = cnode->callees; e; e = e->next_callee)
+ if (!e->inline_failed)
+ add_symbol_to_partition_1 (part, (symtab_node) e->callee);
+ else if (get_symbol_class ((symtab_node) e->callee) == SYMBOL_DUPLICATE)
+ add_symbol_to_partition (part, (symtab_node) e->callee);
+
+ /* Add all thunks associated with the function. */
+ for (e = cnode->callers; e; e = e->next_caller)
+ if (e->caller->thunk.thunk_p)
+ add_symbol_to_partition_1 (part, (symtab_node) e->caller);
+ }
- for (e = node->callees; e; e = e->next_callee)
- if ((!e->inline_failed
- || (!DECL_EXTERNAL (e->callee->symbol.decl)
- && !partition_symbol_p ((symtab_node) e->callee))))
- add_cgraph_node_to_partition (part, e->callee);
+ add_references_to_partition (part, node);
- /* The only way to assemble non-weakref alias is to add the aliased object into
- the unit. */
- add_references_to_partition (part, &node->symbol.ref_list);
- n = cgraph_function_node (node, NULL);
- if (n != node
- && !lookup_attribute ("weakref",
- DECL_ATTRIBUTES (node->symbol.decl)))
- add_cgraph_node_to_partition (part, n);
+ /* Add all aliases associated with the symbol. */
+ for (i = 0; ipa_ref_list_referring_iterate (&node->symbol.ref_list, i, ref); i++)
+ if (ref->use == IPA_REF_ALIAS
+ && !lookup_attribute ("weakref",
+ DECL_ATTRIBUTES
+ (ref->referring->symbol.decl)))
+ add_symbol_to_partition_1 (part, ref->referring);
+ /* Ensure that SAME_COMDAT_GROUP lists all allways added in a group. */
if (node->symbol.same_comdat_group)
- for (n = cgraph (node->symbol.same_comdat_group);
- n != node; n = cgraph (n->symbol.same_comdat_group))
- add_cgraph_node_to_partition (part, n);
+ for (node1 = node->symbol.same_comdat_group;
+ node1 != node; node1 = node1->symbol.same_comdat_group)
+ {
+ bool added = add_symbol_to_partition_1 (part, node1);
+ gcc_assert (added);
+ }
+ return true;
}
-/* Add VNODE to partition as well as comdat references partition PART. */
+/* If symbol NODE is really part of other symbol's definition (i.e. it is
+ internal label, thunk, alias or so), return the outer symbol.
+ When add_symbol_to_partition_1 is called on the outer symbol it must
+ eventually add NODE, too. */
+static symtab_node
+contained_in_symbol (symtab_node node)
+{
+ /* Weakrefs are never contained in anything. */
+ if (lookup_attribute ("weakref",
+ DECL_ATTRIBUTES (node->symbol.decl)))
+ return node;
+ if (symtab_function_p (node))
+ {
+ struct cgraph_node *cnode = cgraph_function_node (cgraph (node), NULL);
+ if (cnode->global.inlined_to)
+ cnode = cnode->global.inlined_to;
+ return (symtab_node) cnode;
+ }
+ else if (symtab_variable_p (node))
+ return (symtab_node) varpool_variable_node (varpool (node), NULL);
+ return node;
+}
+
+/* Add symbol NODE to partition. When definition of NODE is part
+ of other symbol definition, add the other symbol, too. */
static void
-add_varpool_node_to_partition (ltrans_partition part, struct varpool_node *vnode)
+add_symbol_to_partition (ltrans_partition part, symtab_node node)
{
- struct varpool_node *v;
+ symtab_node node1;
- /* If NODE is already there, we have nothing to do. */
- if (lto_symtab_encoder_in_partition_p (part->encoder, (symtab_node) vnode))
- return;
+ /* Verify that we do not try to duplicate something that can not be. */
+ gcc_checking_assert (get_symbol_class (node) == SYMBOL_DUPLICATE
+ || !symbol_partitioned_p (node));
- lto_set_symtab_encoder_in_partition (part->encoder, (symtab_node) vnode);
+ while ((node1 = contained_in_symbol (node)) != node)
+ node = node1;
- if (vnode->symbol.aux)
- {
- vnode->symbol.in_other_partition = 1;
- if (cgraph_dump_file)
- fprintf (cgraph_dump_file, "Varpool node %s now used in multiple partitions\n",
- varpool_node_name (vnode));
- }
- vnode->symbol.aux = (void *)((size_t)vnode->symbol.aux + 1);
-
- /* The only way to assemble non-weakref alias is to add the aliased object into
- the unit. */
- v = varpool_variable_node (vnode, NULL);
- if (v != vnode
- && !lookup_attribute ("weakref",
- DECL_ATTRIBUTES (vnode->symbol.decl)))
- add_varpool_node_to_partition (part, v);
-
- add_references_to_partition (part, &vnode->symbol.ref_list);
- add_aliases_to_partition (part, &vnode->symbol.ref_list);
-
- if (vnode->symbol.same_comdat_group
- && !lto_symtab_encoder_in_partition_p (part->encoder,
- vnode->symbol.same_comdat_group))
- add_varpool_node_to_partition (part, varpool (vnode->symbol.same_comdat_group));
+ /* If we have duplicated symbol contained in something we can not duplicate,
+ we are very badly screwed. The other way is possible, so we do not
+ assert this in add_symbol_to_partition_1.
+
+ Be lax about comdats; they may or may not be duplicated and we may
+ end up in need to duplicate keyed comdat because it has unkeyed alias. */
+ gcc_assert (get_symbol_class (node) == SYMBOL_DUPLICATE
+ || DECL_COMDAT (node->symbol.decl)
+ || !symbol_partitioned_p (node));
+ add_symbol_to_partition_1 (part, node);
}
/* Undo all additions until number of cgraph nodes in PARITION is N_CGRAPH_NODES
{
symtab_node node = lto_symtab_encoder_deref (partition->encoder,
n_nodes);
+
+ /* After UNDO we no longer know what was visited. */
+ if (partition->initializers_visited)
+ pointer_set_destroy (partition->initializers_visited);
+ partition->initializers_visited = NULL;
+
if (symtab_function_p (node))
partition->insns -= inline_summary (cgraph (node))->self_size;
lto_symtab_encoder_delete_node (partition->encoder, node);
}
}
-/* Return true if NODE should be partitioned.
- This means that partitioning algorithm should put NODE into one of partitions.
- This apply to most functions with bodies. Functions that are not partitions
- are put into every unit needing them. This is the case of i.e. COMDATs. */
-
-static bool
-partition_cgraph_node_p (struct cgraph_node *node)
-{
- /* We will get proper partition based on function they are inlined to. */
- if (node->global.inlined_to)
- return false;
- /* Nodes without a body do not need partitioning. */
- if (!node->analyzed)
- return false;
- /* Extern inlines and comdat are always only in partitions they are needed. */
- if (DECL_EXTERNAL (node->symbol.decl)
- || (DECL_COMDAT (node->symbol.decl)
- && !node->symbol.force_output
- && !symtab_used_from_object_file_p ((symtab_node) node)))
- return false;
- if (lookup_attribute ("weakref", DECL_ATTRIBUTES (node->symbol.decl)))
- return false;
- return true;
-}
-
-/* Return true if VNODE should be partitioned.
- This means that partitioning algorithm should put VNODE into one of partitions. */
-
-static bool
-partition_varpool_node_p (struct varpool_node *vnode)
-{
- if (!vnode->analyzed)
- return false;
- /* Constant pool and comdat are always only in partitions they are needed. */
- if (DECL_IN_CONSTANT_POOL (vnode->symbol.decl)
- || DECL_EXTERNAL (vnode->symbol.decl)
- || (DECL_COMDAT (vnode->symbol.decl)
- && !vnode->symbol.force_output
- && !symtab_used_from_object_file_p ((symtab_node) vnode)))
- return false;
- if (lookup_attribute ("weakref", DECL_ATTRIBUTES (vnode->symbol.decl)))
- return false;
- return true;
-}
-
-/* Return true if NODE should be partitioned.
- This means that partitioning algorithm should put NODE into one of partitions. */
-
-static bool
-partition_symbol_p (symtab_node node)
-{
- if (symtab_function_p (node))
- return partition_cgraph_node_p (cgraph (node));
- else
- return partition_varpool_node_p (varpool (node));
-}
-
/* Group cgrah nodes by input files. This is used mainly for testing
right now. */
void
lto_1_to_1_map (void)
{
- struct cgraph_node *node;
- struct varpool_node *vnode;
+ symtab_node node;
struct lto_file_decl_data *file_data;
struct pointer_map_t *pmap;
ltrans_partition partition;
void **slot;
int npartitions = 0;
- timevar_push (TV_WHOPR_WPA);
-
pmap = pointer_map_create ();
- FOR_EACH_DEFINED_FUNCTION (node)
+ FOR_EACH_SYMBOL (node)
{
- if (!partition_cgraph_node_p (node)
- || node->symbol.aux)
+ if (get_symbol_class (node) != SYMBOL_PARTITION
+ || symbol_partitioned_p (node))
continue;
file_data = node->symbol.lto_file_data;
npartitions++;
}
- add_cgraph_node_to_partition (partition, node);
- }
-
- FOR_EACH_VARIABLE (vnode)
- {
- if (!partition_varpool_node_p (vnode)
- || vnode->symbol.aux)
- continue;
- file_data = vnode->symbol.lto_file_data;
- slot = pointer_map_contains (pmap, file_data);
- if (slot)
- partition = (ltrans_partition) *slot;
- else
- {
- partition = new_partition (file_data->file_name);
- slot = pointer_map_insert (pmap, file_data);
- *slot = partition;
- npartitions++;
- }
-
- add_varpool_node_to_partition (partition, vnode);
+ add_symbol_to_partition (partition, (symtab_node) node);
}
- FOR_EACH_FUNCTION (node)
- node->symbol.aux = NULL;
- FOR_EACH_VARIABLE (vnode)
- vnode->symbol.aux = NULL;
/* If the cgraph is empty, create one cgraph node set so that there is still
an output file for any variables that need to be exported in a DSO. */
pointer_map_destroy (pmap);
- timevar_pop (TV_WHOPR_WPA);
+}
+
+/* Maximal partitioning. Put every new symbol into new partition if possible. */
- lto_stats.num_cgraph_partitions += VEC_length (ltrans_partition,
- ltrans_partitions);
+void
+lto_max_map (void)
+{
+ symtab_node node;
+ ltrans_partition partition;
+ int npartitions = 0;
+
+ FOR_EACH_SYMBOL (node)
+ {
+ if (get_symbol_class (node) != SYMBOL_PARTITION
+ || symbol_partitioned_p (node))
+ continue;
+ partition = new_partition (symtab_node_asm_name (node));
+ add_symbol_to_partition (partition, (symtab_node) node);
+ npartitions++;
+ }
+ if (!npartitions)
+ new_partition ("empty");
}
/* Helper function for qsort; sort nodes by order. */
for (i = 0; i < postorder_len; i++)
{
node = postorder[i];
- if (partition_cgraph_node_p (node))
+ if (get_symbol_class ((symtab_node) node) == SYMBOL_PARTITION)
{
order[n_nodes++] = node;
total_size += inline_summary (node)->size;
qsort (order, n_nodes, sizeof (struct cgraph_node *), node_cmp);
FOR_EACH_VARIABLE (vnode)
- if (partition_varpool_node_p (vnode))
+ if (get_symbol_class ((symtab_node) vnode) == SYMBOL_PARTITION)
n_varpool_nodes++;
varpool_order = XNEWVEC (struct varpool_node *, n_varpool_nodes);
n_varpool_nodes = 0;
FOR_EACH_VARIABLE (vnode)
- if (partition_varpool_node_p (vnode))
+ if (get_symbol_class ((symtab_node) vnode) == SYMBOL_PARTITION)
varpool_order[n_varpool_nodes++] = vnode;
qsort (varpool_order, n_varpool_nodes, sizeof (struct varpool_node *),
varpool_node_cmp);
for (i = 0; i < n_nodes; i++)
{
- if (order[i]->symbol.aux)
+ if (symbol_partitioned_p ((symtab_node) order[i]))
continue;
current_order = order[i]->symbol.order;
while (varpool_pos < n_varpool_nodes
&& varpool_order[varpool_pos]->symbol.order < current_order)
{
- if (!varpool_order[varpool_pos]->symbol.aux)
- add_varpool_node_to_partition (partition, varpool_order[varpool_pos]);
+ if (!symbol_partitioned_p ((symtab_node) varpool_order[varpool_pos]))
+ add_symbol_to_partition (partition, (symtab_node) varpool_order[varpool_pos]);
varpool_pos++;
}
- add_cgraph_node_to_partition (partition, order[i]);
+ add_symbol_to_partition (partition, (symtab_node) order[i]);
total_size -= inline_summary (order[i])->size;
/* Once we added a new node to the partition, we also want to add
all referenced variables unless they was already added into some
earlier partition.
- add_cgraph_node_to_partition adds possibly multiple nodes and
+ add_symbol_to_partition adds possibly multiple nodes and
variables that are needed to satisfy needs of ORDER[i].
We remember last visited cgraph and varpool node from last iteration
of outer loop that allows us to process every new addition.
vnode = ipa_ref_varpool_node (ref);
if (!vnode->finalized)
continue;
- if (!vnode->symbol.aux && flag_toplevel_reorder
- && partition_varpool_node_p (vnode))
- add_varpool_node_to_partition (partition, vnode);
+ if (!symbol_partitioned_p ((symtab_node) vnode) && flag_toplevel_reorder
+ && get_symbol_class ((symtab_node) vnode) == SYMBOL_PARTITION)
+ add_symbol_to_partition (partition, (symtab_node) vnode);
index = lto_symtab_encoder_lookup (partition->encoder,
(symtab_node)vnode);
if (index != LCC_NOT_FOUND
vnode = ipa_ref_referring_varpool_node (ref);
gcc_assert (vnode->finalized);
- if (!vnode->symbol.aux && flag_toplevel_reorder
- && partition_varpool_node_p (vnode))
- add_varpool_node_to_partition (partition, vnode);
+ if (!symbol_partitioned_p ((symtab_node) vnode) && flag_toplevel_reorder
+ && get_symbol_class ((symtab_node) vnode) == SYMBOL_PARTITION)
+ add_symbol_to_partition (partition, (symtab_node) vnode);
index = lto_symtab_encoder_lookup (partition->encoder,
(symtab_node)vnode);
if (index != LCC_NOT_FOUND
}
i = best_i;
/* When we are finished, avoid creating empty partition. */
- while (i < n_nodes - 1 && order[i + 1]->symbol.aux)
+ while (i < n_nodes - 1 && symbol_partitioned_p ((symtab_node) order[i + 1]))
i++;
if (i == n_nodes - 1)
break;
if (flag_toplevel_reorder)
{
FOR_EACH_VARIABLE (vnode)
- if (partition_varpool_node_p (vnode) && !vnode->symbol.aux)
- add_varpool_node_to_partition (partition, vnode);
+ if (get_symbol_class ((symtab_node) vnode) == SYMBOL_PARTITION
+ && !symbol_partitioned_p ((symtab_node) vnode))
+ add_symbol_to_partition (partition, (symtab_node) vnode);
}
else
{
while (varpool_pos < n_varpool_nodes)
{
- if (!varpool_order[varpool_pos]->symbol.aux)
- add_varpool_node_to_partition (partition, varpool_order[varpool_pos]);
+ if (!symbol_partitioned_p ((symtab_node) varpool_order[varpool_pos]))
+ add_symbol_to_partition (partition, (symtab_node) varpool_order[varpool_pos]);
varpool_pos++;
}
free (varpool_order);
|| lto_symtab_encoder_in_partition_p (encoder, node)
/* ... or if we do not partition it. This mean that it will
appear in every partition refernecing it. */
- || !partition_symbol_p (node))
+ || get_symbol_class ((symtab_node) node) != SYMBOL_PARTITION)
continue;
promote_symbol (node);