xref: /dragonfly/contrib/gcc-8.0/gcc/ipa.c (revision 58e805e6)
138fd1498Szrj /* Basic IPA optimizations and utilities.
238fd1498Szrj    Copyright (C) 2003-2018 Free Software Foundation, Inc.
338fd1498Szrj 
438fd1498Szrj This file is part of GCC.
538fd1498Szrj 
638fd1498Szrj GCC is free software; you can redistribute it and/or modify it under
738fd1498Szrj the terms of the GNU General Public License as published by the Free
838fd1498Szrj Software Foundation; either version 3, or (at your option) any later
938fd1498Szrj version.
1038fd1498Szrj 
1138fd1498Szrj GCC is distributed in the hope that it will be useful, but WITHOUT ANY
1238fd1498Szrj WARRANTY; without even the implied warranty of MERCHANTABILITY or
1338fd1498Szrj FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
1438fd1498Szrj for more details.
1538fd1498Szrj 
1638fd1498Szrj You should have received a copy of the GNU General Public License
1738fd1498Szrj along with GCC; see the file COPYING3.  If not see
1838fd1498Szrj <http://www.gnu.org/licenses/>.  */
1938fd1498Szrj 
2038fd1498Szrj #include "config.h"
2138fd1498Szrj #include "system.h"
2238fd1498Szrj #include "coretypes.h"
2338fd1498Szrj #include "backend.h"
2438fd1498Szrj #include "target.h"
2538fd1498Szrj #include "tree.h"
2638fd1498Szrj #include "gimple.h"
2738fd1498Szrj #include "alloc-pool.h"
2838fd1498Szrj #include "tree-pass.h"
2938fd1498Szrj #include "stringpool.h"
3038fd1498Szrj #include "cgraph.h"
3138fd1498Szrj #include "gimplify.h"
3238fd1498Szrj #include "tree-iterator.h"
3338fd1498Szrj #include "ipa-utils.h"
3438fd1498Szrj #include "symbol-summary.h"
3538fd1498Szrj #include "tree-vrp.h"
3638fd1498Szrj #include "ipa-prop.h"
3738fd1498Szrj #include "ipa-fnsummary.h"
3838fd1498Szrj #include "dbgcnt.h"
3938fd1498Szrj #include "debug.h"
4038fd1498Szrj #include "stringpool.h"
4138fd1498Szrj #include "attribs.h"
4238fd1498Szrj 
4338fd1498Szrj /* Return true when NODE has ADDR reference.  */
4438fd1498Szrj 
4538fd1498Szrj static bool
has_addr_references_p(struct cgraph_node * node,void *)4638fd1498Szrj has_addr_references_p (struct cgraph_node *node,
4738fd1498Szrj 		       void *)
4838fd1498Szrj {
4938fd1498Szrj   int i;
5038fd1498Szrj   struct ipa_ref *ref = NULL;
5138fd1498Szrj 
5238fd1498Szrj   for (i = 0; node->iterate_referring (i, ref); i++)
5338fd1498Szrj     if (ref->use == IPA_REF_ADDR)
5438fd1498Szrj       return true;
5538fd1498Szrj   return false;
5638fd1498Szrj }
5738fd1498Szrj 
5838fd1498Szrj /* Return true when NODE can be target of an indirect call.  */
5938fd1498Szrj 
6038fd1498Szrj static bool
is_indirect_call_target_p(struct cgraph_node * node,void *)6138fd1498Szrj is_indirect_call_target_p (struct cgraph_node *node, void *)
6238fd1498Szrj {
6338fd1498Szrj   return node->indirect_call_target;
6438fd1498Szrj }
6538fd1498Szrj 
6638fd1498Szrj /* Look for all functions inlined to NODE and update their inlined_to pointers
6738fd1498Szrj    to INLINED_TO.  */
6838fd1498Szrj 
6938fd1498Szrj static void
update_inlined_to_pointer(struct cgraph_node * node,struct cgraph_node * inlined_to)7038fd1498Szrj update_inlined_to_pointer (struct cgraph_node *node, struct cgraph_node *inlined_to)
7138fd1498Szrj {
7238fd1498Szrj   struct cgraph_edge *e;
7338fd1498Szrj   for (e = node->callees; e; e = e->next_callee)
7438fd1498Szrj     if (e->callee->global.inlined_to)
7538fd1498Szrj       {
7638fd1498Szrj         e->callee->global.inlined_to = inlined_to;
7738fd1498Szrj 	update_inlined_to_pointer (e->callee, inlined_to);
7838fd1498Szrj       }
7938fd1498Szrj }
8038fd1498Szrj 
8138fd1498Szrj /* Add symtab NODE to queue starting at FIRST.
8238fd1498Szrj 
8338fd1498Szrj    The queue is linked via AUX pointers and terminated by pointer to 1.
8438fd1498Szrj    We enqueue nodes at two occasions: when we find them reachable or when we find
8538fd1498Szrj    their bodies needed for further clonning.  In the second case we mark them
8638fd1498Szrj    by pointer to 2 after processing so they are re-queue when they become
8738fd1498Szrj    reachable.  */
8838fd1498Szrj 
8938fd1498Szrj static void
enqueue_node(symtab_node * node,symtab_node ** first,hash_set<symtab_node * > * reachable)9038fd1498Szrj enqueue_node (symtab_node *node, symtab_node **first,
9138fd1498Szrj 	      hash_set<symtab_node *> *reachable)
9238fd1498Szrj {
9338fd1498Szrj   /* Node is still in queue; do nothing.  */
9438fd1498Szrj   if (node->aux && node->aux != (void *) 2)
9538fd1498Szrj     return;
9638fd1498Szrj   /* Node was already processed as unreachable, re-enqueue
9738fd1498Szrj      only if it became reachable now.  */
9838fd1498Szrj   if (node->aux == (void *)2 && !reachable->contains (node))
9938fd1498Szrj     return;
10038fd1498Szrj   node->aux = *first;
10138fd1498Szrj   *first = node;
10238fd1498Szrj }
10338fd1498Szrj 
10438fd1498Szrj /* Process references.  */
10538fd1498Szrj 
10638fd1498Szrj static void
process_references(symtab_node * snode,symtab_node ** first,bool before_inlining_p,hash_set<symtab_node * > * reachable)10738fd1498Szrj process_references (symtab_node *snode,
10838fd1498Szrj 		    symtab_node **first,
10938fd1498Szrj 		    bool before_inlining_p,
11038fd1498Szrj 		    hash_set<symtab_node *> *reachable)
11138fd1498Szrj {
11238fd1498Szrj   int i;
11338fd1498Szrj   struct ipa_ref *ref = NULL;
11438fd1498Szrj   for (i = 0; snode->iterate_reference (i, ref); i++)
11538fd1498Szrj     {
11638fd1498Szrj       symtab_node *node = ref->referred;
11738fd1498Szrj       symtab_node *body = node->ultimate_alias_target ();
11838fd1498Szrj 
11938fd1498Szrj       if (node->definition && !node->in_other_partition
12038fd1498Szrj 	  && ((!DECL_EXTERNAL (node->decl) || node->alias)
12138fd1498Szrj 	      || (((before_inlining_p
12238fd1498Szrj 		    && (TREE_CODE (node->decl) != FUNCTION_DECL
12338fd1498Szrj 			|| (TREE_CODE (node->decl) == FUNCTION_DECL
12438fd1498Szrj 			    && opt_for_fn (body->decl, optimize))
12538fd1498Szrj 		        || (symtab->state < IPA_SSA
12638fd1498Szrj 		            && lookup_attribute
12738fd1498Szrj 				 ("always_inline",
12838fd1498Szrj 			          DECL_ATTRIBUTES (body->decl))))))
12938fd1498Szrj 		  /* We use variable constructors during late compilation for
13038fd1498Szrj 		     constant folding.  Keep references alive so partitioning
13138fd1498Szrj 		     knows about potential references.  */
13238fd1498Szrj 		  || (VAR_P (node->decl)
13338fd1498Szrj 		      && flag_wpa
13438fd1498Szrj 		      && ctor_for_folding (node->decl)
13538fd1498Szrj 		         != error_mark_node))))
13638fd1498Szrj 	{
13738fd1498Szrj 	  /* Be sure that we will not optimize out alias target
13838fd1498Szrj 	     body.  */
13938fd1498Szrj 	  if (DECL_EXTERNAL (node->decl)
14038fd1498Szrj 	      && node->alias
14138fd1498Szrj 	      && before_inlining_p)
14238fd1498Szrj 	    reachable->add (body);
14338fd1498Szrj 	  reachable->add (node);
14438fd1498Szrj 	}
14538fd1498Szrj       enqueue_node (node, first, reachable);
14638fd1498Szrj     }
14738fd1498Szrj }
14838fd1498Szrj 
14938fd1498Szrj /* EDGE is an polymorphic call.  If BEFORE_INLINING_P is set, mark
15038fd1498Szrj    all its potential targets as reachable to permit later inlining if
15138fd1498Szrj    devirtualization happens.  After inlining still keep their declarations
15238fd1498Szrj    around, so we can devirtualize to a direct call.
15338fd1498Szrj 
15438fd1498Szrj    Also try to make trivial devirutalization when no or only one target is
15538fd1498Szrj    possible.  */
15638fd1498Szrj 
15738fd1498Szrj static void
walk_polymorphic_call_targets(hash_set<void * > * reachable_call_targets,struct cgraph_edge * edge,symtab_node ** first,hash_set<symtab_node * > * reachable,bool before_inlining_p)15838fd1498Szrj walk_polymorphic_call_targets (hash_set<void *> *reachable_call_targets,
15938fd1498Szrj 			       struct cgraph_edge *edge,
16038fd1498Szrj 			       symtab_node **first,
16138fd1498Szrj 			       hash_set<symtab_node *> *reachable,
16238fd1498Szrj 			       bool before_inlining_p)
16338fd1498Szrj {
16438fd1498Szrj   unsigned int i;
16538fd1498Szrj   void *cache_token;
16638fd1498Szrj   bool final;
16738fd1498Szrj   vec <cgraph_node *>targets
16838fd1498Szrj     = possible_polymorphic_call_targets
16938fd1498Szrj 	(edge, &final, &cache_token);
17038fd1498Szrj 
17138fd1498Szrj   if (!reachable_call_targets->add (cache_token))
17238fd1498Szrj     {
17338fd1498Szrj       for (i = 0; i < targets.length (); i++)
17438fd1498Szrj 	{
17538fd1498Szrj 	  struct cgraph_node *n = targets[i];
17638fd1498Szrj 
17738fd1498Szrj 	  /* Do not bother to mark virtual methods in anonymous namespace;
17838fd1498Szrj 	     either we will find use of virtual table defining it, or it is
17938fd1498Szrj 	     unused.  */
18038fd1498Szrj 	  if (TREE_CODE (TREE_TYPE (n->decl)) == METHOD_TYPE
18138fd1498Szrj 	      && type_in_anonymous_namespace_p
18238fd1498Szrj 		    (TYPE_METHOD_BASETYPE (TREE_TYPE (n->decl))))
18338fd1498Szrj 	    continue;
18438fd1498Szrj 
18538fd1498Szrj 	  n->indirect_call_target = true;
18638fd1498Szrj 	  symtab_node *body = n->function_symbol ();
18738fd1498Szrj 
18838fd1498Szrj 	  /* Prior inlining, keep alive bodies of possible targets for
18938fd1498Szrj 	     devirtualization.  */
19038fd1498Szrj 	  if (n->definition
19138fd1498Szrj 	      && (before_inlining_p
19238fd1498Szrj 		  && opt_for_fn (body->decl, optimize)
19338fd1498Szrj 		  && opt_for_fn (body->decl, flag_devirtualize)))
19438fd1498Szrj 	     {
19538fd1498Szrj 		/* Be sure that we will not optimize out alias target
19638fd1498Szrj 		   body.  */
19738fd1498Szrj 		if (DECL_EXTERNAL (n->decl)
19838fd1498Szrj 		    && n->alias
19938fd1498Szrj 		    && before_inlining_p)
20038fd1498Szrj 		  reachable->add (body);
20138fd1498Szrj 	       reachable->add (n);
20238fd1498Szrj 	     }
20338fd1498Szrj 	  /* Even after inlining we want to keep the possible targets in the
20438fd1498Szrj 	     boundary, so late passes can still produce direct call even if
20538fd1498Szrj 	     the chance for inlining is lost.  */
20638fd1498Szrj 	  enqueue_node (n, first, reachable);
20738fd1498Szrj 	}
20838fd1498Szrj     }
20938fd1498Szrj 
21038fd1498Szrj   /* Very trivial devirtualization; when the type is
21138fd1498Szrj      final or anonymous (so we know all its derivation)
21238fd1498Szrj      and there is only one possible virtual call target,
21338fd1498Szrj      make the edge direct.  */
21438fd1498Szrj   if (final)
21538fd1498Szrj     {
21638fd1498Szrj       if (targets.length () <= 1 && dbg_cnt (devirt))
21738fd1498Szrj 	{
21838fd1498Szrj 	  cgraph_node *target, *node = edge->caller;
21938fd1498Szrj 	  if (targets.length () == 1)
22038fd1498Szrj 	    target = targets[0];
22138fd1498Szrj 	  else
22238fd1498Szrj 	    target = cgraph_node::get_create
22338fd1498Szrj 		       (builtin_decl_implicit (BUILT_IN_UNREACHABLE));
22438fd1498Szrj 
22538fd1498Szrj 	  if (dump_enabled_p ())
22638fd1498Szrj             {
22738fd1498Szrj 	      location_t locus;
22838fd1498Szrj 	      if (edge->call_stmt)
22938fd1498Szrj 		locus = gimple_location (edge->call_stmt);
23038fd1498Szrj 	      else
23138fd1498Szrj 		locus = UNKNOWN_LOCATION;
23238fd1498Szrj 	      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, locus,
23338fd1498Szrj 			       "devirtualizing call in %s to %s\n",
23438fd1498Szrj 			       edge->caller->dump_name (),
23538fd1498Szrj 			       target->dump_name ());
23638fd1498Szrj 	    }
23738fd1498Szrj 	  edge = edge->make_direct (target);
23838fd1498Szrj 	  if (ipa_fn_summaries)
23938fd1498Szrj 	    ipa_update_overall_fn_summary (node);
24038fd1498Szrj 	  else if (edge->call_stmt)
24138fd1498Szrj 	    {
24238fd1498Szrj 	      edge->redirect_call_stmt_to_callee ();
24338fd1498Szrj 
24438fd1498Szrj 	      /* Call to __builtin_unreachable shouldn't be instrumented.  */
24538fd1498Szrj 	      if (!targets.length ())
24638fd1498Szrj 		gimple_call_set_with_bounds (edge->call_stmt, false);
24738fd1498Szrj 	    }
24838fd1498Szrj 	}
24938fd1498Szrj     }
25038fd1498Szrj }
25138fd1498Szrj 
25238fd1498Szrj /* Perform reachability analysis and reclaim all unreachable nodes.
25338fd1498Szrj 
25438fd1498Szrj    The algorithm is basically mark&sweep but with some extra refinements:
25538fd1498Szrj 
25638fd1498Szrj    - reachable extern inline functions needs special handling; the bodies needs
25738fd1498Szrj      to stay in memory until inlining in hope that they will be inlined.
25838fd1498Szrj      After inlining we release their bodies and turn them into unanalyzed
25938fd1498Szrj      nodes even when they are reachable.
26038fd1498Szrj 
26138fd1498Szrj    - virtual functions are kept in callgraph even if they seem unreachable in
26238fd1498Szrj      hope calls to them will be devirtualized.
26338fd1498Szrj 
26438fd1498Szrj      Again we remove them after inlining.  In late optimization some
26538fd1498Szrj      devirtualization may happen, but it is not important since we won't inline
26638fd1498Szrj      the call. In theory early opts and IPA should work out all important cases.
26738fd1498Szrj 
26838fd1498Szrj    - virtual clones needs bodies of their origins for later materialization;
26938fd1498Szrj      this means that we want to keep the body even if the origin is unreachable
27038fd1498Szrj      otherwise.  To avoid origin from sitting in the callgraph and being
27138fd1498Szrj      walked by IPA passes, we turn them into unanalyzed nodes with body
27238fd1498Szrj      defined.
27338fd1498Szrj 
27438fd1498Szrj      We maintain set of function declaration where body needs to stay in
27538fd1498Szrj      body_needed_for_clonning
27638fd1498Szrj 
27738fd1498Szrj      Inline clones represent special case: their declaration match the
27838fd1498Szrj      declaration of origin and cgraph_remove_node already knows how to
27938fd1498Szrj      reshape callgraph and preserve body when offline copy of function or
28038fd1498Szrj      inline clone is being removed.
28138fd1498Szrj 
28238fd1498Szrj    - C++ virtual tables keyed to other unit are represented as DECL_EXTERNAL
28338fd1498Szrj      variables with DECL_INITIAL set.  We finalize these and keep reachable
28438fd1498Szrj      ones around for constant folding purposes.  After inlining we however
28538fd1498Szrj      stop walking their references to let everything static referneced by them
28638fd1498Szrj      to be removed when it is otherwise unreachable.
28738fd1498Szrj 
28838fd1498Szrj    We maintain queue of both reachable symbols (i.e. defined symbols that needs
28938fd1498Szrj    to stay) and symbols that are in boundary (i.e. external symbols referenced
29038fd1498Szrj    by reachable symbols or origins of clones).  The queue is represented
29138fd1498Szrj    as linked list by AUX pointer terminated by 1.
29238fd1498Szrj 
29338fd1498Szrj    At the end we keep all reachable symbols. For symbols in boundary we always
29438fd1498Szrj    turn definition into a declaration, but we may keep function body around
29538fd1498Szrj    based on body_needed_for_clonning
29638fd1498Szrj 
29738fd1498Szrj    All symbols that enter the queue have AUX pointer non-zero and are in the
29838fd1498Szrj    boundary.  Pointer set REACHABLE is used to track reachable symbols.
29938fd1498Szrj 
30038fd1498Szrj    Every symbol can be visited twice - once as part of boundary and once
30138fd1498Szrj    as real reachable symbol. enqueue_node needs to decide whether the
30238fd1498Szrj    node needs to be re-queued for second processing.  For this purpose
30338fd1498Szrj    we set AUX pointer of processed symbols in the boundary to constant 2.  */
30438fd1498Szrj 
30538fd1498Szrj bool
remove_unreachable_nodes(FILE * file)30638fd1498Szrj symbol_table::remove_unreachable_nodes (FILE *file)
30738fd1498Szrj {
30838fd1498Szrj   symtab_node *first = (symtab_node *) (void *) 1;
30938fd1498Szrj   struct cgraph_node *node, *next;
31038fd1498Szrj   varpool_node *vnode, *vnext;
31138fd1498Szrj   bool changed = false;
31238fd1498Szrj   hash_set<symtab_node *> reachable;
31338fd1498Szrj   hash_set<tree> body_needed_for_clonning;
31438fd1498Szrj   hash_set<void *> reachable_call_targets;
31538fd1498Szrj   bool before_inlining_p = symtab->state < (!optimize && !in_lto_p ? IPA_SSA
31638fd1498Szrj 					    : IPA_SSA_AFTER_INLINING);
31738fd1498Szrj 
31838fd1498Szrj   timevar_push (TV_IPA_UNREACHABLE);
31938fd1498Szrj   build_type_inheritance_graph ();
32038fd1498Szrj   if (file)
32138fd1498Szrj     fprintf (file, "\nReclaiming functions:");
32238fd1498Szrj   if (flag_checking)
32338fd1498Szrj     {
32438fd1498Szrj       FOR_EACH_FUNCTION (node)
32538fd1498Szrj 	gcc_assert (!node->aux);
32638fd1498Szrj       FOR_EACH_VARIABLE (vnode)
32738fd1498Szrj 	gcc_assert (!vnode->aux);
32838fd1498Szrj     }
32938fd1498Szrj   /* Mark functions whose bodies are obviously needed.
33038fd1498Szrj      This is mostly when they can be referenced externally.  Inline clones
33138fd1498Szrj      are special since their declarations are shared with master clone and thus
33238fd1498Szrj      cgraph_can_remove_if_no_direct_calls_and_refs_p should not be called on them.  */
33338fd1498Szrj   FOR_EACH_FUNCTION (node)
33438fd1498Szrj     {
33538fd1498Szrj       node->used_as_abstract_origin = false;
33638fd1498Szrj       node->indirect_call_target = false;
33738fd1498Szrj       if (node->definition
33838fd1498Szrj 	  && !node->global.inlined_to
33938fd1498Szrj 	  && !node->in_other_partition
34038fd1498Szrj 	  && !node->can_remove_if_no_direct_calls_and_refs_p ())
34138fd1498Szrj 	{
34238fd1498Szrj 	  gcc_assert (!node->global.inlined_to);
34338fd1498Szrj 	  reachable.add (node);
34438fd1498Szrj 	  enqueue_node (node, &first, &reachable);
34538fd1498Szrj 	}
34638fd1498Szrj       else
34738fd1498Szrj 	gcc_assert (!node->aux);
34838fd1498Szrj      }
34938fd1498Szrj 
35038fd1498Szrj   /* Mark variables that are obviously needed.  */
35138fd1498Szrj   FOR_EACH_DEFINED_VARIABLE (vnode)
35238fd1498Szrj     if (!vnode->can_remove_if_no_refs_p()
35338fd1498Szrj 	&& !vnode->in_other_partition)
35438fd1498Szrj       {
35538fd1498Szrj 	reachable.add (vnode);
35638fd1498Szrj 	enqueue_node (vnode, &first, &reachable);
35738fd1498Szrj       }
35838fd1498Szrj 
35938fd1498Szrj   /* Perform reachability analysis.  */
36038fd1498Szrj   while (first != (symtab_node *) (void *) 1)
36138fd1498Szrj     {
36238fd1498Szrj       bool in_boundary_p = !reachable.contains (first);
36338fd1498Szrj       symtab_node *node = first;
36438fd1498Szrj 
36538fd1498Szrj       first = (symtab_node *)first->aux;
36638fd1498Szrj 
36738fd1498Szrj       /* If we are processing symbol in boundary, mark its AUX pointer for
36838fd1498Szrj 	 possible later re-processing in enqueue_node.  */
36938fd1498Szrj       if (in_boundary_p)
37038fd1498Szrj 	{
37138fd1498Szrj 	  node->aux = (void *)2;
37238fd1498Szrj 	  if (node->alias && node->analyzed)
37338fd1498Szrj 	    enqueue_node (node->get_alias_target (), &first, &reachable);
37438fd1498Szrj 	}
37538fd1498Szrj       else
37638fd1498Szrj 	{
37738fd1498Szrj 	  if (TREE_CODE (node->decl) == FUNCTION_DECL
37838fd1498Szrj 	      && DECL_ABSTRACT_ORIGIN (node->decl))
37938fd1498Szrj 	    {
38038fd1498Szrj 	      struct cgraph_node *origin_node
38138fd1498Szrj 	      = cgraph_node::get (DECL_ABSTRACT_ORIGIN (node->decl));
38238fd1498Szrj 	      if (origin_node && !origin_node->used_as_abstract_origin)
38338fd1498Szrj 		{
38438fd1498Szrj 	          origin_node->used_as_abstract_origin = true;
38538fd1498Szrj 		  gcc_assert (!origin_node->prev_sibling_clone);
38638fd1498Szrj 		  gcc_assert (!origin_node->next_sibling_clone);
38738fd1498Szrj 		  for (cgraph_node *n = origin_node->clones; n;
38838fd1498Szrj 		       n = n->next_sibling_clone)
38938fd1498Szrj 		    if (n->decl == DECL_ABSTRACT_ORIGIN (node->decl))
39038fd1498Szrj 		      n->used_as_abstract_origin = true;
39138fd1498Szrj 		}
39238fd1498Szrj 	    }
39338fd1498Szrj 	  /* If any symbol in a comdat group is reachable, force
39438fd1498Szrj 	     all externally visible symbols in the same comdat
39538fd1498Szrj 	     group to be reachable as well.  Comdat-local symbols
39638fd1498Szrj 	     can be discarded if all uses were inlined.  */
39738fd1498Szrj 	  if (node->same_comdat_group)
39838fd1498Szrj 	    {
39938fd1498Szrj 	      symtab_node *next;
40038fd1498Szrj 	      for (next = node->same_comdat_group;
40138fd1498Szrj 		   next != node;
40238fd1498Szrj 		   next = next->same_comdat_group)
40338fd1498Szrj 		if (!next->comdat_local_p ()
40438fd1498Szrj 		    && !reachable.add (next))
40538fd1498Szrj 		  enqueue_node (next, &first, &reachable);
40638fd1498Szrj 	    }
40738fd1498Szrj 	  /* Mark references as reachable.  */
40838fd1498Szrj 	  process_references (node, &first, before_inlining_p, &reachable);
40938fd1498Szrj 	}
41038fd1498Szrj 
41138fd1498Szrj       if (cgraph_node *cnode = dyn_cast <cgraph_node *> (node))
41238fd1498Szrj 	{
41338fd1498Szrj 	  /* Mark the callees reachable unless they are direct calls to extern
41438fd1498Szrj  	     inline functions we decided to not inline.  */
41538fd1498Szrj 	  if (!in_boundary_p)
41638fd1498Szrj 	    {
41738fd1498Szrj 	      struct cgraph_edge *e;
41838fd1498Szrj 	      /* Keep alive possible targets for devirtualization.  */
41938fd1498Szrj 	      if (opt_for_fn (cnode->decl, optimize)
42038fd1498Szrj 		  && opt_for_fn (cnode->decl, flag_devirtualize))
42138fd1498Szrj 		{
42238fd1498Szrj 		  struct cgraph_edge *next;
42338fd1498Szrj 		  for (e = cnode->indirect_calls; e; e = next)
42438fd1498Szrj 		    {
42538fd1498Szrj 		      next = e->next_callee;
42638fd1498Szrj 		      if (e->indirect_info->polymorphic)
42738fd1498Szrj 			walk_polymorphic_call_targets (&reachable_call_targets,
42838fd1498Szrj 						       e, &first, &reachable,
42938fd1498Szrj 						       before_inlining_p);
43038fd1498Szrj 		    }
43138fd1498Szrj 		}
43238fd1498Szrj 	      for (e = cnode->callees; e; e = e->next_callee)
43338fd1498Szrj 		{
43438fd1498Szrj 	          symtab_node *body = e->callee->function_symbol ();
43538fd1498Szrj 		  if (e->callee->definition
43638fd1498Szrj 		      && !e->callee->in_other_partition
43738fd1498Szrj 		      && (!e->inline_failed
43838fd1498Szrj 			  || !DECL_EXTERNAL (e->callee->decl)
43938fd1498Szrj 			  || e->callee->alias
44038fd1498Szrj 			  || (before_inlining_p
44138fd1498Szrj 			      && (opt_for_fn (body->decl, optimize)
44238fd1498Szrj 		                  || (symtab->state < IPA_SSA
44338fd1498Szrj 		                      && lookup_attribute
44438fd1498Szrj 				          ("always_inline",
44538fd1498Szrj 				           DECL_ATTRIBUTES (body->decl)))))))
44638fd1498Szrj 		    {
44738fd1498Szrj 		      /* Be sure that we will not optimize out alias target
44838fd1498Szrj 			 body.  */
44938fd1498Szrj 		      if (DECL_EXTERNAL (e->callee->decl)
45038fd1498Szrj 			  && e->callee->alias
45138fd1498Szrj 			  && before_inlining_p)
45238fd1498Szrj 			reachable.add (body);
45338fd1498Szrj 		      reachable.add (e->callee);
45438fd1498Szrj 		    }
45538fd1498Szrj 		  enqueue_node (e->callee, &first, &reachable);
45638fd1498Szrj 		}
45738fd1498Szrj 
45838fd1498Szrj 	      /* When inline clone exists, mark body to be preserved so when removing
45938fd1498Szrj 		 offline copy of the function we don't kill it.  */
46038fd1498Szrj 	      if (cnode->global.inlined_to)
46138fd1498Szrj 	        body_needed_for_clonning.add (cnode->decl);
46238fd1498Szrj 
46338fd1498Szrj 	      /* For instrumentation clones we always need original
46438fd1498Szrj 		 function node for proper LTO privatization.  */
46538fd1498Szrj 	      if (cnode->instrumentation_clone
46638fd1498Szrj 		  && cnode->definition)
46738fd1498Szrj 		{
46838fd1498Szrj 		  gcc_assert (cnode->instrumented_version || in_lto_p);
46938fd1498Szrj 		  if (cnode->instrumented_version)
47038fd1498Szrj 		    {
47138fd1498Szrj 		      enqueue_node (cnode->instrumented_version, &first,
47238fd1498Szrj 				    &reachable);
47338fd1498Szrj 		      reachable.add (cnode->instrumented_version);
47438fd1498Szrj 		    }
47538fd1498Szrj 		}
47638fd1498Szrj 
47738fd1498Szrj 	      /* For non-inline clones, force their origins to the boundary and ensure
47838fd1498Szrj 		 that body is not removed.  */
47938fd1498Szrj 	      while (cnode->clone_of)
48038fd1498Szrj 		{
48138fd1498Szrj 		  bool noninline = cnode->clone_of->decl != cnode->decl;
48238fd1498Szrj 		  cnode = cnode->clone_of;
48338fd1498Szrj 		  if (noninline)
48438fd1498Szrj 		    {
48538fd1498Szrj 		      body_needed_for_clonning.add (cnode->decl);
48638fd1498Szrj 		      enqueue_node (cnode, &first, &reachable);
48738fd1498Szrj 		    }
48838fd1498Szrj 		}
48938fd1498Szrj 
49038fd1498Szrj 	    }
49138fd1498Szrj 	  else if (cnode->thunk.thunk_p)
49238fd1498Szrj 	    enqueue_node (cnode->callees->callee, &first, &reachable);
49338fd1498Szrj 
49438fd1498Szrj 	  /* If any reachable function has simd clones, mark them as
49538fd1498Szrj 	     reachable as well.  */
49638fd1498Szrj 	  if (cnode->simd_clones)
49738fd1498Szrj 	    {
49838fd1498Szrj 	      cgraph_node *next;
49938fd1498Szrj 	      for (next = cnode->simd_clones;
50038fd1498Szrj 		   next;
50138fd1498Szrj 		   next = next->simdclone->next_clone)
50238fd1498Szrj 		if (in_boundary_p
50338fd1498Szrj 		    || !reachable.add (next))
50438fd1498Szrj 		  enqueue_node (next, &first, &reachable);
50538fd1498Szrj 	    }
50638fd1498Szrj 	}
50738fd1498Szrj       /* When we see constructor of external variable, keep referred nodes in the
50838fd1498Szrj 	boundary.  This will also hold initializers of the external vars NODE
50938fd1498Szrj 	refers to.  */
51038fd1498Szrj       varpool_node *vnode = dyn_cast <varpool_node *> (node);
51138fd1498Szrj       if (vnode
51238fd1498Szrj 	  && DECL_EXTERNAL (node->decl)
51338fd1498Szrj 	  && !vnode->alias
51438fd1498Szrj 	  && in_boundary_p)
51538fd1498Szrj 	{
51638fd1498Szrj 	  struct ipa_ref *ref = NULL;
51738fd1498Szrj 	  for (int i = 0; node->iterate_reference (i, ref); i++)
51838fd1498Szrj 	    enqueue_node (ref->referred, &first, &reachable);
51938fd1498Szrj 	}
52038fd1498Szrj     }
52138fd1498Szrj 
52238fd1498Szrj   /* Remove unreachable functions.   */
52338fd1498Szrj   for (node = first_function (); node; node = next)
52438fd1498Szrj     {
52538fd1498Szrj       next = next_function (node);
52638fd1498Szrj 
52738fd1498Szrj       /* If node is not needed at all, remove it.  */
52838fd1498Szrj       if (!node->aux)
52938fd1498Szrj 	{
53038fd1498Szrj 	  if (file)
53138fd1498Szrj 	    fprintf (file, " %s", node->dump_name ());
53238fd1498Szrj 	  node->remove ();
53338fd1498Szrj 	  changed = true;
53438fd1498Szrj 	}
53538fd1498Szrj       /* If node is unreachable, remove its body.  */
53638fd1498Szrj       else if (!reachable.contains (node))
53738fd1498Szrj         {
53838fd1498Szrj 	  /* We keep definitions of thunks and aliases in the boundary so
53938fd1498Szrj 	     we can walk to the ultimate alias targets and function symbols
54038fd1498Szrj 	     reliably.  */
54138fd1498Szrj 	  if (node->alias || node->thunk.thunk_p)
54238fd1498Szrj 	    ;
54338fd1498Szrj 	  else if (!body_needed_for_clonning.contains (node->decl)
54438fd1498Szrj 	      && !node->alias && !node->thunk.thunk_p)
54538fd1498Szrj 	    node->release_body ();
54638fd1498Szrj 	  else if (!node->clone_of)
54738fd1498Szrj 	    gcc_assert (in_lto_p || DECL_RESULT (node->decl));
54838fd1498Szrj 	  if (node->definition && !node->alias && !node->thunk.thunk_p)
54938fd1498Szrj 	    {
55038fd1498Szrj 	      if (file)
55138fd1498Szrj 		fprintf (file, " %s", node->dump_name ());
55238fd1498Szrj 	      node->body_removed = true;
55338fd1498Szrj 	      node->analyzed = false;
55438fd1498Szrj 	      node->definition = false;
55538fd1498Szrj 	      node->cpp_implicit_alias = false;
55638fd1498Szrj 	      node->alias = false;
55738fd1498Szrj 	      node->transparent_alias = false;
55838fd1498Szrj 	      node->thunk.thunk_p = false;
55938fd1498Szrj 	      node->weakref = false;
56038fd1498Szrj 	      /* After early inlining we drop always_inline attributes on
56138fd1498Szrj 		 bodies of functions that are still referenced (have their
56238fd1498Szrj 		 address taken).  */
56338fd1498Szrj 	      DECL_ATTRIBUTES (node->decl)
56438fd1498Szrj 		= remove_attribute ("always_inline",
56538fd1498Szrj 				    DECL_ATTRIBUTES (node->decl));
56638fd1498Szrj 	      if (!node->in_other_partition)
56738fd1498Szrj 		node->local.local = false;
56838fd1498Szrj 	      node->remove_callees ();
56938fd1498Szrj 	      node->remove_all_references ();
57038fd1498Szrj 	      changed = true;
57138fd1498Szrj 	      if (node->thunk.thunk_p
57238fd1498Szrj 		  && node->thunk.add_pointer_bounds_args)
57338fd1498Szrj 		{
57438fd1498Szrj 		  node->thunk.thunk_p = false;
57538fd1498Szrj 		  node->thunk.add_pointer_bounds_args = false;
57638fd1498Szrj 		}
57738fd1498Szrj 	    }
57838fd1498Szrj 	}
57938fd1498Szrj       else
58038fd1498Szrj 	gcc_assert (node->clone_of || !node->has_gimple_body_p ()
58138fd1498Szrj 		    || in_lto_p || DECL_RESULT (node->decl));
58238fd1498Szrj     }
58338fd1498Szrj 
58438fd1498Szrj   /* Inline clones might be kept around so their materializing allows further
58538fd1498Szrj      cloning.  If the function the clone is inlined into is removed, we need
58638fd1498Szrj      to turn it into normal cone.  */
58738fd1498Szrj   FOR_EACH_FUNCTION (node)
58838fd1498Szrj     {
58938fd1498Szrj       if (node->global.inlined_to
59038fd1498Szrj 	  && !node->callers)
59138fd1498Szrj 	{
59238fd1498Szrj 	  gcc_assert (node->clones);
59338fd1498Szrj 	  node->global.inlined_to = NULL;
59438fd1498Szrj 	  update_inlined_to_pointer (node, node);
59538fd1498Szrj 	}
59638fd1498Szrj       node->aux = NULL;
59738fd1498Szrj     }
59838fd1498Szrj 
59938fd1498Szrj   /* Remove unreachable variables.  */
60038fd1498Szrj   if (file)
60138fd1498Szrj     fprintf (file, "\nReclaiming variables:");
60238fd1498Szrj   for (vnode = first_variable (); vnode; vnode = vnext)
60338fd1498Szrj     {
60438fd1498Szrj       vnext = next_variable (vnode);
60538fd1498Szrj       if (!vnode->aux
60638fd1498Szrj 	  /* For can_refer_decl_in_current_unit_p we want to track for
60738fd1498Szrj 	     all external variables if they are defined in other partition
60838fd1498Szrj 	     or not.  */
60938fd1498Szrj 	  && (!flag_ltrans || !DECL_EXTERNAL (vnode->decl)))
61038fd1498Szrj 	{
61138fd1498Szrj 	  struct ipa_ref *ref = NULL;
61238fd1498Szrj 
61338fd1498Szrj 	  /* First remove the aliases, so varpool::remove can possibly lookup
61438fd1498Szrj 	     the constructor and save it for future use.  */
61538fd1498Szrj 	  while (vnode->iterate_direct_aliases (0, ref))
61638fd1498Szrj 	    {
61738fd1498Szrj 	      if (file)
61838fd1498Szrj 		fprintf (file, " %s", ref->referred->dump_name ());
61938fd1498Szrj 	      ref->referring->remove ();
62038fd1498Szrj 	    }
62138fd1498Szrj 	  if (file)
62238fd1498Szrj 	    fprintf (file, " %s", vnode->dump_name ());
62338fd1498Szrj           vnext = next_variable (vnode);
62438fd1498Szrj 	  /* Signal removal to the debug machinery.  */
62538fd1498Szrj 	  if (! flag_wpa)
62638fd1498Szrj 	    {
62738fd1498Szrj 	      vnode->definition = false;
62838fd1498Szrj 	      (*debug_hooks->late_global_decl) (vnode->decl);
62938fd1498Szrj 	    }
63038fd1498Szrj 	  vnode->remove ();
63138fd1498Szrj 	  changed = true;
63238fd1498Szrj 	}
63338fd1498Szrj       else if (!reachable.contains (vnode) && !vnode->alias)
63438fd1498Szrj         {
63538fd1498Szrj 	  tree init;
63638fd1498Szrj 	  if (vnode->definition)
63738fd1498Szrj 	    {
63838fd1498Szrj 	      if (file)
63938fd1498Szrj 		fprintf (file, " %s", vnode->name ());
64038fd1498Szrj 	      changed = true;
64138fd1498Szrj 	    }
64238fd1498Szrj 	  /* Keep body if it may be useful for constant folding.  */
64338fd1498Szrj 	  if ((init = ctor_for_folding (vnode->decl)) == error_mark_node
64438fd1498Szrj 	      && !POINTER_BOUNDS_P (vnode->decl))
64538fd1498Szrj 	    vnode->remove_initializer ();
64638fd1498Szrj 	  else
64738fd1498Szrj 	    DECL_INITIAL (vnode->decl) = init;
64838fd1498Szrj 	  vnode->body_removed = true;
64938fd1498Szrj 	  vnode->definition = false;
65038fd1498Szrj 	  vnode->analyzed = false;
65138fd1498Szrj 	  vnode->aux = NULL;
65238fd1498Szrj 
65338fd1498Szrj 	  vnode->remove_from_same_comdat_group ();
65438fd1498Szrj 
65538fd1498Szrj 	  vnode->remove_all_references ();
65638fd1498Szrj 	}
65738fd1498Szrj       else
65838fd1498Szrj 	vnode->aux = NULL;
65938fd1498Szrj     }
66038fd1498Szrj 
66138fd1498Szrj   /* Now update address_taken flags and try to promote functions to be local.  */
66238fd1498Szrj   if (file)
66338fd1498Szrj     fprintf (file, "\nClearing address taken flags:");
66438fd1498Szrj   FOR_EACH_DEFINED_FUNCTION (node)
66538fd1498Szrj     if (node->address_taken
66638fd1498Szrj 	&& !node->used_from_other_partition)
66738fd1498Szrj       {
66838fd1498Szrj 	if (!node->call_for_symbol_and_aliases
66938fd1498Szrj 	    (has_addr_references_p, NULL, true)
67038fd1498Szrj 	    && (!node->instrumentation_clone
67138fd1498Szrj 		|| !node->instrumented_version
67238fd1498Szrj 		|| !node->instrumented_version->address_taken))
67338fd1498Szrj 	  {
67438fd1498Szrj 	    if (file)
67538fd1498Szrj 	      fprintf (file, " %s", node->name ());
67638fd1498Szrj 	    node->address_taken = false;
67738fd1498Szrj 	    changed = true;
67838fd1498Szrj 	    if (node->local_p ()
67938fd1498Szrj 		/* Virtual functions may be kept in cgraph just because
68038fd1498Szrj 		   of possible later devirtualization.  Do not mark them as
68138fd1498Szrj 		   local too early so we won't optimize them out before
68238fd1498Szrj 		   we are done with polymorphic call analysis.  */
68338fd1498Szrj 		&& (!before_inlining_p
68438fd1498Szrj 		    || !node->call_for_symbol_and_aliases
68538fd1498Szrj 		       (is_indirect_call_target_p, NULL, true)))
68638fd1498Szrj 	      {
68738fd1498Szrj 		node->local.local = true;
68838fd1498Szrj 		if (file)
68938fd1498Szrj 		  fprintf (file, " (local)");
69038fd1498Szrj 	      }
69138fd1498Szrj 	  }
69238fd1498Szrj       }
69338fd1498Szrj   if (file)
69438fd1498Szrj     fprintf (file, "\n");
69538fd1498Szrj 
69638fd1498Szrj   symtab_node::checking_verify_symtab_nodes ();
69738fd1498Szrj 
69838fd1498Szrj   /* If we removed something, perhaps profile could be improved.  */
69938fd1498Szrj   if (changed && (optimize || in_lto_p) && ipa_call_summaries)
70038fd1498Szrj     FOR_EACH_DEFINED_FUNCTION (node)
70138fd1498Szrj       ipa_propagate_frequency (node);
70238fd1498Szrj 
70338fd1498Szrj   timevar_pop (TV_IPA_UNREACHABLE);
70438fd1498Szrj   return changed;
70538fd1498Szrj }
70638fd1498Szrj 
70738fd1498Szrj /* Process references to VNODE and set flags WRITTEN, ADDRESS_TAKEN, READ
70838fd1498Szrj    as needed, also clear EXPLICIT_REFS if the references to given variable
70938fd1498Szrj    do not need to be explicit.  */
71038fd1498Szrj 
71138fd1498Szrj void
process_references(varpool_node * vnode,bool * written,bool * address_taken,bool * read,bool * explicit_refs)71238fd1498Szrj process_references (varpool_node *vnode,
71338fd1498Szrj 		    bool *written, bool *address_taken,
71438fd1498Szrj 		    bool *read, bool *explicit_refs)
71538fd1498Szrj {
71638fd1498Szrj   int i;
71738fd1498Szrj   struct ipa_ref *ref;
71838fd1498Szrj 
71938fd1498Szrj   if (!vnode->all_refs_explicit_p ()
72038fd1498Szrj       || TREE_THIS_VOLATILE (vnode->decl))
72138fd1498Szrj     *explicit_refs = false;
72238fd1498Szrj 
72338fd1498Szrj   for (i = 0; vnode->iterate_referring (i, ref)
72438fd1498Szrj 	      && *explicit_refs && (!*written || !*address_taken || !*read); i++)
72538fd1498Szrj     switch (ref->use)
72638fd1498Szrj       {
72738fd1498Szrj       case IPA_REF_ADDR:
72838fd1498Szrj 	*address_taken = true;
72938fd1498Szrj 	break;
73038fd1498Szrj       case IPA_REF_LOAD:
73138fd1498Szrj 	*read = true;
73238fd1498Szrj 	break;
73338fd1498Szrj       case IPA_REF_STORE:
73438fd1498Szrj 	*written = true;
73538fd1498Szrj 	break;
73638fd1498Szrj       case IPA_REF_ALIAS:
73738fd1498Szrj 	process_references (dyn_cast<varpool_node *> (ref->referring), written,
73838fd1498Szrj 			    address_taken, read, explicit_refs);
73938fd1498Szrj 	break;
74038fd1498Szrj       case IPA_REF_CHKP:
74138fd1498Szrj 	gcc_unreachable ();
74238fd1498Szrj       }
74338fd1498Szrj }
74438fd1498Szrj 
74538fd1498Szrj /* Set TREE_READONLY bit.  */
74638fd1498Szrj 
74738fd1498Szrj bool
set_readonly_bit(varpool_node * vnode,void * data ATTRIBUTE_UNUSED)74838fd1498Szrj set_readonly_bit (varpool_node *vnode, void *data ATTRIBUTE_UNUSED)
74938fd1498Szrj {
75038fd1498Szrj   TREE_READONLY (vnode->decl) = true;
75138fd1498Szrj   return false;
75238fd1498Szrj }
75338fd1498Szrj 
75438fd1498Szrj /* Set writeonly bit and clear the initalizer, since it will not be needed.  */
75538fd1498Szrj 
75638fd1498Szrj bool
set_writeonly_bit(varpool_node * vnode,void * data)75738fd1498Szrj set_writeonly_bit (varpool_node *vnode, void *data)
75838fd1498Szrj {
75938fd1498Szrj   vnode->writeonly = true;
76038fd1498Szrj   if (optimize || in_lto_p)
76138fd1498Szrj     {
76238fd1498Szrj       DECL_INITIAL (vnode->decl) = NULL;
76338fd1498Szrj       if (!vnode->alias)
76438fd1498Szrj 	{
76538fd1498Szrj 	  if (vnode->num_references ())
76638fd1498Szrj 	    *(bool *)data = true;
76738fd1498Szrj 	  vnode->remove_all_references ();
76838fd1498Szrj 	}
76938fd1498Szrj     }
77038fd1498Szrj   return false;
77138fd1498Szrj }
77238fd1498Szrj 
77338fd1498Szrj /* Clear addressale bit of VNODE.  */
77438fd1498Szrj 
77538fd1498Szrj bool
clear_addressable_bit(varpool_node * vnode,void * data ATTRIBUTE_UNUSED)77638fd1498Szrj clear_addressable_bit (varpool_node *vnode, void *data ATTRIBUTE_UNUSED)
77738fd1498Szrj {
77838fd1498Szrj   vnode->address_taken = false;
77938fd1498Szrj   TREE_ADDRESSABLE (vnode->decl) = 0;
78038fd1498Szrj   return false;
78138fd1498Szrj }
78238fd1498Szrj 
78338fd1498Szrj /* Discover variables that have no longer address taken or that are read only
78438fd1498Szrj    and update their flags.
78538fd1498Szrj 
78638fd1498Szrj    Return true when unreachable symbol removan should be done.
78738fd1498Szrj 
78838fd1498Szrj    FIXME: This can not be done in between gimplify and omp_expand since
78938fd1498Szrj    readonly flag plays role on what is shared and what is not.  Currently we do
79038fd1498Szrj    this transformation as part of whole program visibility and re-do at
79138fd1498Szrj    ipa-reference pass (to take into account clonning), but it would
79238fd1498Szrj    make sense to do it before early optimizations.  */
79338fd1498Szrj 
79438fd1498Szrj bool
ipa_discover_readonly_nonaddressable_vars(void)79538fd1498Szrj ipa_discover_readonly_nonaddressable_vars (void)
79638fd1498Szrj {
79738fd1498Szrj   bool remove_p = false;
79838fd1498Szrj   varpool_node *vnode;
79938fd1498Szrj   if (dump_file)
80038fd1498Szrj     fprintf (dump_file, "Clearing variable flags:");
80138fd1498Szrj   FOR_EACH_VARIABLE (vnode)
80238fd1498Szrj     if (!vnode->alias
80338fd1498Szrj 	&& (TREE_ADDRESSABLE (vnode->decl)
80438fd1498Szrj 	    || !vnode->writeonly
80538fd1498Szrj 	    || !TREE_READONLY (vnode->decl)))
80638fd1498Szrj       {
80738fd1498Szrj 	bool written = false;
80838fd1498Szrj 	bool address_taken = false;
80938fd1498Szrj 	bool read = false;
81038fd1498Szrj 	bool explicit_refs = true;
81138fd1498Szrj 
81238fd1498Szrj 	process_references (vnode, &written, &address_taken, &read,
81338fd1498Szrj 			    &explicit_refs);
81438fd1498Szrj 	if (!explicit_refs)
81538fd1498Szrj 	  continue;
81638fd1498Szrj 	if (!address_taken)
81738fd1498Szrj 	  {
81838fd1498Szrj 	    if (TREE_ADDRESSABLE (vnode->decl) && dump_file)
81938fd1498Szrj 	      fprintf (dump_file, " %s (non-addressable)", vnode->name ());
82038fd1498Szrj 	    vnode->call_for_symbol_and_aliases (clear_addressable_bit, NULL,
82138fd1498Szrj 					        true);
82238fd1498Szrj 	  }
82338fd1498Szrj 	if (!address_taken && !written
82438fd1498Szrj 	    /* Making variable in explicit section readonly can cause section
82538fd1498Szrj 	       type conflict.
82638fd1498Szrj 	       See e.g. gcc.c-torture/compile/pr23237.c */
82738fd1498Szrj 	    && vnode->get_section () == NULL)
82838fd1498Szrj 	  {
82938fd1498Szrj 	    if (!TREE_READONLY (vnode->decl) && dump_file)
83038fd1498Szrj 	      fprintf (dump_file, " %s (read-only)", vnode->name ());
83138fd1498Szrj 	    vnode->call_for_symbol_and_aliases (set_readonly_bit, NULL, true);
83238fd1498Szrj 	  }
83338fd1498Szrj 	if (!vnode->writeonly && !read && !address_taken && written)
83438fd1498Szrj 	  {
83538fd1498Szrj 	    if (dump_file)
83638fd1498Szrj 	      fprintf (dump_file, " %s (write-only)", vnode->name ());
83738fd1498Szrj 	    vnode->call_for_symbol_and_aliases (set_writeonly_bit, &remove_p,
83838fd1498Szrj 					        true);
83938fd1498Szrj 	  }
84038fd1498Szrj       }
84138fd1498Szrj   if (dump_file)
84238fd1498Szrj     fprintf (dump_file, "\n");
84338fd1498Szrj   return remove_p;
84438fd1498Szrj }
84538fd1498Szrj 
84638fd1498Szrj /* Generate and emit a static constructor or destructor.  WHICH must
84738fd1498Szrj    be one of 'I' (for a constructor), 'D' (for a destructor), 'P'
84838fd1498Szrj    (for chp static vars constructor) or 'B' (for chkp static bounds
84938fd1498Szrj    constructor).  BODY is a STATEMENT_LIST containing GENERIC
85038fd1498Szrj    statements.  PRIORITY is the initialization priority for this
85138fd1498Szrj    constructor or destructor.
85238fd1498Szrj 
85338fd1498Szrj    FINAL specify whether the externally visible name for collect2 should
85438fd1498Szrj    be produced. */
85538fd1498Szrj 
85638fd1498Szrj static void
cgraph_build_static_cdtor_1(char which,tree body,int priority,bool final,tree optimization,tree target)857*58e805e6Szrj cgraph_build_static_cdtor_1 (char which, tree body, int priority, bool final,
858*58e805e6Szrj 			     tree optimization,
859*58e805e6Szrj 			     tree target)
86038fd1498Szrj {
86138fd1498Szrj   static int counter = 0;
86238fd1498Szrj   char which_buf[16];
86338fd1498Szrj   tree decl, name, resdecl;
86438fd1498Szrj 
86538fd1498Szrj   /* The priority is encoded in the constructor or destructor name.
86638fd1498Szrj      collect2 will sort the names and arrange that they are called at
86738fd1498Szrj      program startup.  */
86838fd1498Szrj   if (final)
86938fd1498Szrj     sprintf (which_buf, "%c_%.5d_%d", which, priority, counter++);
87038fd1498Szrj   else
87138fd1498Szrj   /* Proudce sane name but one not recognizable by collect2, just for the
87238fd1498Szrj      case we fail to inline the function.  */
87338fd1498Szrj     sprintf (which_buf, "sub_%c_%.5d_%d", which, priority, counter++);
87438fd1498Szrj   name = get_file_function_name (which_buf);
87538fd1498Szrj 
87638fd1498Szrj   decl = build_decl (input_location, FUNCTION_DECL, name,
87738fd1498Szrj 		     build_function_type_list (void_type_node, NULL_TREE));
87838fd1498Szrj   current_function_decl = decl;
87938fd1498Szrj 
88038fd1498Szrj   resdecl = build_decl (input_location,
88138fd1498Szrj 			RESULT_DECL, NULL_TREE, void_type_node);
88238fd1498Szrj   DECL_ARTIFICIAL (resdecl) = 1;
88338fd1498Szrj   DECL_RESULT (decl) = resdecl;
88438fd1498Szrj   DECL_CONTEXT (resdecl) = decl;
88538fd1498Szrj 
88638fd1498Szrj   allocate_struct_function (decl, false);
88738fd1498Szrj 
88838fd1498Szrj   TREE_STATIC (decl) = 1;
88938fd1498Szrj   TREE_USED (decl) = 1;
890*58e805e6Szrj   DECL_FUNCTION_SPECIFIC_OPTIMIZATION (decl) = optimization;
891*58e805e6Szrj   DECL_FUNCTION_SPECIFIC_TARGET (decl) = target;
89238fd1498Szrj   DECL_ARTIFICIAL (decl) = 1;
89338fd1498Szrj   DECL_IGNORED_P (decl) = 1;
89438fd1498Szrj   DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (decl) = 1;
89538fd1498Szrj   DECL_SAVED_TREE (decl) = body;
89638fd1498Szrj   if (!targetm.have_ctors_dtors && final)
89738fd1498Szrj     {
89838fd1498Szrj       TREE_PUBLIC (decl) = 1;
89938fd1498Szrj       DECL_PRESERVE_P (decl) = 1;
90038fd1498Szrj     }
90138fd1498Szrj   DECL_UNINLINABLE (decl) = 1;
90238fd1498Szrj 
90338fd1498Szrj   DECL_INITIAL (decl) = make_node (BLOCK);
90438fd1498Szrj   BLOCK_SUPERCONTEXT (DECL_INITIAL (decl)) = decl;
90538fd1498Szrj   TREE_USED (DECL_INITIAL (decl)) = 1;
90638fd1498Szrj 
90738fd1498Szrj   DECL_SOURCE_LOCATION (decl) = input_location;
90838fd1498Szrj   cfun->function_end_locus = input_location;
90938fd1498Szrj 
91038fd1498Szrj   switch (which)
91138fd1498Szrj     {
91238fd1498Szrj     case 'I':
91338fd1498Szrj       DECL_STATIC_CONSTRUCTOR (decl) = 1;
91438fd1498Szrj       decl_init_priority_insert (decl, priority);
91538fd1498Szrj       break;
91638fd1498Szrj     case 'P':
91738fd1498Szrj       DECL_STATIC_CONSTRUCTOR (decl) = 1;
91838fd1498Szrj       DECL_ATTRIBUTES (decl) = tree_cons (get_identifier ("chkp ctor"),
91938fd1498Szrj 					  NULL,
92038fd1498Szrj 					  NULL_TREE);
92138fd1498Szrj       decl_init_priority_insert (decl, priority);
92238fd1498Szrj       break;
92338fd1498Szrj     case 'B':
92438fd1498Szrj       DECL_STATIC_CONSTRUCTOR (decl) = 1;
92538fd1498Szrj       DECL_ATTRIBUTES (decl) = tree_cons (get_identifier ("bnd_legacy"),
92638fd1498Szrj 					  NULL,
92738fd1498Szrj 					  NULL_TREE);
92838fd1498Szrj       decl_init_priority_insert (decl, priority);
92938fd1498Szrj       break;
93038fd1498Szrj     case 'D':
93138fd1498Szrj       DECL_STATIC_DESTRUCTOR (decl) = 1;
93238fd1498Szrj       decl_fini_priority_insert (decl, priority);
93338fd1498Szrj       break;
93438fd1498Szrj     default:
93538fd1498Szrj       gcc_unreachable ();
93638fd1498Szrj     }
93738fd1498Szrj 
93838fd1498Szrj   gimplify_function_tree (decl);
93938fd1498Szrj 
94038fd1498Szrj   cgraph_node::add_new_function (decl, false);
94138fd1498Szrj 
94238fd1498Szrj   set_cfun (NULL);
94338fd1498Szrj   current_function_decl = NULL;
94438fd1498Szrj }
94538fd1498Szrj 
94638fd1498Szrj /* Generate and emit a static constructor or destructor.  WHICH must
94738fd1498Szrj    be one of 'I' (for a constructor), 'D' (for a destructor), 'P'
94838fd1498Szrj    (for chkp static vars constructor) or 'B' (for chkp static bounds
94938fd1498Szrj    constructor).  BODY is a STATEMENT_LIST containing GENERIC
95038fd1498Szrj    statements.  PRIORITY is the initialization priority for this
95138fd1498Szrj    constructor or destructor.  */
95238fd1498Szrj 
95338fd1498Szrj void
cgraph_build_static_cdtor(char which,tree body,int priority)95438fd1498Szrj cgraph_build_static_cdtor (char which, tree body, int priority)
95538fd1498Szrj {
956*58e805e6Szrj   cgraph_build_static_cdtor_1 (which, body, priority, false, NULL, NULL);
95738fd1498Szrj }
95838fd1498Szrj 
95938fd1498Szrj /* When target does not have ctors and dtors, we call all constructor
96038fd1498Szrj    and destructor by special initialization/destruction function
96138fd1498Szrj    recognized by collect2.
96238fd1498Szrj 
96338fd1498Szrj    When we are going to build this function, collect all constructors and
96438fd1498Szrj    destructors and turn them into normal functions.  */
96538fd1498Szrj 
96638fd1498Szrj static void
record_cdtor_fn(struct cgraph_node * node,vec<tree> * ctors,vec<tree> * dtors)96738fd1498Szrj record_cdtor_fn (struct cgraph_node *node, vec<tree> *ctors, vec<tree> *dtors)
96838fd1498Szrj {
96938fd1498Szrj   if (DECL_STATIC_CONSTRUCTOR (node->decl))
97038fd1498Szrj     ctors->safe_push (node->decl);
97138fd1498Szrj   if (DECL_STATIC_DESTRUCTOR (node->decl))
97238fd1498Szrj     dtors->safe_push (node->decl);
97338fd1498Szrj   node = cgraph_node::get (node->decl);
97438fd1498Szrj   DECL_DISREGARD_INLINE_LIMITS (node->decl) = 1;
97538fd1498Szrj }
97638fd1498Szrj 
97738fd1498Szrj /* Define global constructors/destructor functions for the CDTORS, of
97838fd1498Szrj    which they are LEN.  The CDTORS are sorted by initialization
97938fd1498Szrj    priority.  If CTOR_P is true, these are constructors; otherwise,
98038fd1498Szrj    they are destructors.  */
98138fd1498Szrj 
98238fd1498Szrj static void
build_cdtor(bool ctor_p,const vec<tree> & cdtors)98338fd1498Szrj build_cdtor (bool ctor_p, const vec<tree> &cdtors)
98438fd1498Szrj {
98538fd1498Szrj   size_t i,j;
98638fd1498Szrj   size_t len = cdtors.length ();
98738fd1498Szrj 
98838fd1498Szrj   i = 0;
98938fd1498Szrj   while (i < len)
99038fd1498Szrj     {
99138fd1498Szrj       tree body;
99238fd1498Szrj       tree fn;
99338fd1498Szrj       priority_type priority;
99438fd1498Szrj 
99538fd1498Szrj       priority = 0;
99638fd1498Szrj       body = NULL_TREE;
99738fd1498Szrj       j = i;
99838fd1498Szrj       do
99938fd1498Szrj 	{
100038fd1498Szrj 	  priority_type p;
100138fd1498Szrj 	  fn = cdtors[j];
100238fd1498Szrj 	  p = ctor_p ? DECL_INIT_PRIORITY (fn) : DECL_FINI_PRIORITY (fn);
100338fd1498Szrj 	  if (j == i)
100438fd1498Szrj 	    priority = p;
100538fd1498Szrj 	  else if (p != priority)
100638fd1498Szrj 	    break;
100738fd1498Szrj 	  j++;
100838fd1498Szrj 	}
100938fd1498Szrj       while (j < len);
101038fd1498Szrj 
101138fd1498Szrj       /* When there is only one cdtor and target supports them, do nothing.  */
101238fd1498Szrj       if (j == i + 1
101338fd1498Szrj 	  && targetm.have_ctors_dtors)
101438fd1498Szrj 	{
101538fd1498Szrj 	  i++;
101638fd1498Szrj 	  continue;
101738fd1498Szrj 	}
101838fd1498Szrj       /* Find the next batch of constructors/destructors with the same
101938fd1498Szrj 	 initialization priority.  */
102038fd1498Szrj       for (;i < j; i++)
102138fd1498Szrj 	{
102238fd1498Szrj 	  tree call;
102338fd1498Szrj 	  fn = cdtors[i];
102438fd1498Szrj 	  call = build_call_expr (fn, 0);
102538fd1498Szrj 	  if (ctor_p)
102638fd1498Szrj 	    DECL_STATIC_CONSTRUCTOR (fn) = 0;
102738fd1498Szrj 	  else
102838fd1498Szrj 	    DECL_STATIC_DESTRUCTOR (fn) = 0;
102938fd1498Szrj 	  /* We do not want to optimize away pure/const calls here.
103038fd1498Szrj 	     When optimizing, these should be already removed, when not
103138fd1498Szrj 	     optimizing, we want user to be able to breakpoint in them.  */
103238fd1498Szrj 	  TREE_SIDE_EFFECTS (call) = 1;
103338fd1498Szrj 	  append_to_statement_list (call, &body);
103438fd1498Szrj 	}
103538fd1498Szrj       gcc_assert (body != NULL_TREE);
103638fd1498Szrj       /* Generate a function to call all the function of like
103738fd1498Szrj 	 priority.  */
1038*58e805e6Szrj       cgraph_build_static_cdtor_1 (ctor_p ? 'I' : 'D', body, priority, true,
1039*58e805e6Szrj 				   DECL_FUNCTION_SPECIFIC_OPTIMIZATION (cdtors[0]),
1040*58e805e6Szrj 				   DECL_FUNCTION_SPECIFIC_TARGET (cdtors[0]));
104138fd1498Szrj     }
104238fd1498Szrj }
104338fd1498Szrj 
104438fd1498Szrj /* Comparison function for qsort.  P1 and P2 are actually of type
104538fd1498Szrj    "tree *" and point to static constructors.  DECL_INIT_PRIORITY is
104638fd1498Szrj    used to determine the sort order.  */
104738fd1498Szrj 
104838fd1498Szrj static int
compare_ctor(const void * p1,const void * p2)104938fd1498Szrj compare_ctor (const void *p1, const void *p2)
105038fd1498Szrj {
105138fd1498Szrj   tree f1;
105238fd1498Szrj   tree f2;
105338fd1498Szrj   int priority1;
105438fd1498Szrj   int priority2;
105538fd1498Szrj 
105638fd1498Szrj   f1 = *(const tree *)p1;
105738fd1498Szrj   f2 = *(const tree *)p2;
105838fd1498Szrj   priority1 = DECL_INIT_PRIORITY (f1);
105938fd1498Szrj   priority2 = DECL_INIT_PRIORITY (f2);
106038fd1498Szrj 
106138fd1498Szrj   if (priority1 < priority2)
106238fd1498Szrj     return -1;
106338fd1498Szrj   else if (priority1 > priority2)
106438fd1498Szrj     return 1;
106538fd1498Szrj   else
106638fd1498Szrj     /* Ensure a stable sort.  Constructors are executed in backwarding
106738fd1498Szrj        order to make LTO initialize braries first.  */
106838fd1498Szrj     return DECL_UID (f2) - DECL_UID (f1);
106938fd1498Szrj }
107038fd1498Szrj 
107138fd1498Szrj /* Comparison function for qsort.  P1 and P2 are actually of type
107238fd1498Szrj    "tree *" and point to static destructors.  DECL_FINI_PRIORITY is
107338fd1498Szrj    used to determine the sort order.  */
107438fd1498Szrj 
107538fd1498Szrj static int
compare_dtor(const void * p1,const void * p2)107638fd1498Szrj compare_dtor (const void *p1, const void *p2)
107738fd1498Szrj {
107838fd1498Szrj   tree f1;
107938fd1498Szrj   tree f2;
108038fd1498Szrj   int priority1;
108138fd1498Szrj   int priority2;
108238fd1498Szrj 
108338fd1498Szrj   f1 = *(const tree *)p1;
108438fd1498Szrj   f2 = *(const tree *)p2;
108538fd1498Szrj   priority1 = DECL_FINI_PRIORITY (f1);
108638fd1498Szrj   priority2 = DECL_FINI_PRIORITY (f2);
108738fd1498Szrj 
108838fd1498Szrj   if (priority1 < priority2)
108938fd1498Szrj     return -1;
109038fd1498Szrj   else if (priority1 > priority2)
109138fd1498Szrj     return 1;
109238fd1498Szrj   else
109338fd1498Szrj     /* Ensure a stable sort.  */
109438fd1498Szrj     return DECL_UID (f1) - DECL_UID (f2);
109538fd1498Szrj }
109638fd1498Szrj 
109738fd1498Szrj /* Generate functions to call static constructors and destructors
109838fd1498Szrj    for targets that do not support .ctors/.dtors sections.  These
109938fd1498Szrj    functions have magic names which are detected by collect2.  */
110038fd1498Szrj 
110138fd1498Szrj static void
build_cdtor_fns(vec<tree> * ctors,vec<tree> * dtors)110238fd1498Szrj build_cdtor_fns (vec<tree> *ctors, vec<tree> *dtors)
110338fd1498Szrj {
110438fd1498Szrj   if (!ctors->is_empty ())
110538fd1498Szrj     {
110638fd1498Szrj       gcc_assert (!targetm.have_ctors_dtors || in_lto_p);
110738fd1498Szrj       ctors->qsort (compare_ctor);
110838fd1498Szrj       build_cdtor (/*ctor_p=*/true, *ctors);
110938fd1498Szrj     }
111038fd1498Szrj 
111138fd1498Szrj   if (!dtors->is_empty ())
111238fd1498Szrj     {
111338fd1498Szrj       gcc_assert (!targetm.have_ctors_dtors || in_lto_p);
111438fd1498Szrj       dtors->qsort (compare_dtor);
111538fd1498Szrj       build_cdtor (/*ctor_p=*/false, *dtors);
111638fd1498Szrj     }
111738fd1498Szrj }
111838fd1498Szrj 
111938fd1498Szrj /* Look for constructors and destructors and produce function calling them.
112038fd1498Szrj    This is needed for targets not supporting ctors or dtors, but we perform the
112138fd1498Szrj    transformation also at linktime to merge possibly numerous
112238fd1498Szrj    constructors/destructors into single function to improve code locality and
112338fd1498Szrj    reduce size.  */
112438fd1498Szrj 
112538fd1498Szrj static unsigned int
ipa_cdtor_merge(void)112638fd1498Szrj ipa_cdtor_merge (void)
112738fd1498Szrj {
112838fd1498Szrj   /* A vector of FUNCTION_DECLs declared as static constructors.  */
112938fd1498Szrj   auto_vec<tree, 20> ctors;
113038fd1498Szrj   /* A vector of FUNCTION_DECLs declared as static destructors.  */
113138fd1498Szrj   auto_vec<tree, 20> dtors;
113238fd1498Szrj   struct cgraph_node *node;
113338fd1498Szrj   FOR_EACH_DEFINED_FUNCTION (node)
113438fd1498Szrj     if (DECL_STATIC_CONSTRUCTOR (node->decl)
113538fd1498Szrj 	|| DECL_STATIC_DESTRUCTOR (node->decl))
113638fd1498Szrj        record_cdtor_fn (node, &ctors, &dtors);
113738fd1498Szrj   build_cdtor_fns (&ctors, &dtors);
113838fd1498Szrj   return 0;
113938fd1498Szrj }
114038fd1498Szrj 
114138fd1498Szrj namespace {
114238fd1498Szrj 
114338fd1498Szrj const pass_data pass_data_ipa_cdtor_merge =
114438fd1498Szrj {
114538fd1498Szrj   IPA_PASS, /* type */
114638fd1498Szrj   "cdtor", /* name */
114738fd1498Szrj   OPTGROUP_NONE, /* optinfo_flags */
114838fd1498Szrj   TV_CGRAPHOPT, /* tv_id */
114938fd1498Szrj   0, /* properties_required */
115038fd1498Szrj   0, /* properties_provided */
115138fd1498Szrj   0, /* properties_destroyed */
115238fd1498Szrj   0, /* todo_flags_start */
115338fd1498Szrj   0, /* todo_flags_finish */
115438fd1498Szrj };
115538fd1498Szrj 
115638fd1498Szrj class pass_ipa_cdtor_merge : public ipa_opt_pass_d
115738fd1498Szrj {
115838fd1498Szrj public:
pass_ipa_cdtor_merge(gcc::context * ctxt)115938fd1498Szrj   pass_ipa_cdtor_merge (gcc::context *ctxt)
116038fd1498Szrj     : ipa_opt_pass_d (pass_data_ipa_cdtor_merge, ctxt,
116138fd1498Szrj 		      NULL, /* generate_summary */
116238fd1498Szrj 		      NULL, /* write_summary */
116338fd1498Szrj 		      NULL, /* read_summary */
116438fd1498Szrj 		      NULL, /* write_optimization_summary */
116538fd1498Szrj 		      NULL, /* read_optimization_summary */
116638fd1498Szrj 		      NULL, /* stmt_fixup */
116738fd1498Szrj 		      0, /* function_transform_todo_flags_start */
116838fd1498Szrj 		      NULL, /* function_transform */
116938fd1498Szrj 		      NULL) /* variable_transform */
117038fd1498Szrj   {}
117138fd1498Szrj 
117238fd1498Szrj   /* opt_pass methods: */
117338fd1498Szrj   virtual bool gate (function *);
execute(function *)117438fd1498Szrj   virtual unsigned int execute (function *) { return ipa_cdtor_merge (); }
117538fd1498Szrj 
117638fd1498Szrj }; // class pass_ipa_cdtor_merge
117738fd1498Szrj 
117838fd1498Szrj bool
gate(function *)117938fd1498Szrj pass_ipa_cdtor_merge::gate (function *)
118038fd1498Szrj {
118138fd1498Szrj   /* Perform the pass when we have no ctors/dtors support
118238fd1498Szrj      or at LTO time to merge multiple constructors into single
118338fd1498Szrj      function.  */
118438fd1498Szrj   return !targetm.have_ctors_dtors || in_lto_p;
118538fd1498Szrj }
118638fd1498Szrj 
118738fd1498Szrj } // anon namespace
118838fd1498Szrj 
118938fd1498Szrj ipa_opt_pass_d *
make_pass_ipa_cdtor_merge(gcc::context * ctxt)119038fd1498Szrj make_pass_ipa_cdtor_merge (gcc::context *ctxt)
119138fd1498Szrj {
119238fd1498Szrj   return new pass_ipa_cdtor_merge (ctxt);
119338fd1498Szrj }
119438fd1498Szrj 
119538fd1498Szrj /* Invalid pointer representing BOTTOM for single user dataflow.  */
119638fd1498Szrj #define BOTTOM ((cgraph_node *)(size_t) 2)
119738fd1498Szrj 
119838fd1498Szrj /* Meet operation for single user dataflow.
119938fd1498Szrj    Here we want to associate variables with sigle function that may access it.
120038fd1498Szrj 
120138fd1498Szrj    FUNCTION is current single user of a variable, VAR is variable that uses it.
120238fd1498Szrj    Latttice is stored in SINGLE_USER_MAP.
120338fd1498Szrj 
120438fd1498Szrj    We represent:
120538fd1498Szrj     - TOP by no entry in SIGNLE_USER_MAP
120638fd1498Szrj     - BOTTOM by BOTTOM in AUX pointer (to save lookups)
120738fd1498Szrj     - known single user by cgraph pointer in SINGLE_USER_MAP.  */
120838fd1498Szrj 
120938fd1498Szrj cgraph_node *
meet(cgraph_node * function,varpool_node * var,hash_map<varpool_node *,cgraph_node * > & single_user_map)121038fd1498Szrj meet (cgraph_node *function, varpool_node *var,
121138fd1498Szrj        hash_map<varpool_node *, cgraph_node *> &single_user_map)
121238fd1498Szrj {
121338fd1498Szrj   struct cgraph_node *user, **f;
121438fd1498Szrj 
121538fd1498Szrj   if (var->aux == BOTTOM)
121638fd1498Szrj     return BOTTOM;
121738fd1498Szrj 
121838fd1498Szrj   f = single_user_map.get (var);
121938fd1498Szrj   if (!f)
122038fd1498Szrj     return function;
122138fd1498Szrj   user = *f;
122238fd1498Szrj   if (!function)
122338fd1498Szrj     return user;
122438fd1498Szrj   else if (function != user)
122538fd1498Szrj     return BOTTOM;
122638fd1498Szrj   else
122738fd1498Szrj     return function;
122838fd1498Szrj }
122938fd1498Szrj 
123038fd1498Szrj /* Propagation step of single-use dataflow.
123138fd1498Szrj 
123238fd1498Szrj    Check all uses of VNODE and see if they are used by single function FUNCTION.
123338fd1498Szrj    SINGLE_USER_MAP represents the dataflow lattice.  */
123438fd1498Szrj 
123538fd1498Szrj cgraph_node *
propagate_single_user(varpool_node * vnode,cgraph_node * function,hash_map<varpool_node *,cgraph_node * > & single_user_map)123638fd1498Szrj propagate_single_user (varpool_node *vnode, cgraph_node *function,
123738fd1498Szrj 		       hash_map<varpool_node *, cgraph_node *> &single_user_map)
123838fd1498Szrj {
123938fd1498Szrj   int i;
124038fd1498Szrj   struct ipa_ref *ref;
124138fd1498Szrj 
124238fd1498Szrj   gcc_assert (!vnode->externally_visible);
124338fd1498Szrj 
124438fd1498Szrj   /* If node is an alias, first meet with its target.  */
124538fd1498Szrj   if (vnode->alias)
124638fd1498Szrj     function = meet (function, vnode->get_alias_target (), single_user_map);
124738fd1498Szrj 
124838fd1498Szrj   /* Check all users and see if they correspond to a single function.  */
124938fd1498Szrj   for (i = 0; vnode->iterate_referring (i, ref) && function != BOTTOM; i++)
125038fd1498Szrj     {
125138fd1498Szrj       struct cgraph_node *cnode = dyn_cast <cgraph_node *> (ref->referring);
125238fd1498Szrj       if (cnode)
125338fd1498Szrj 	{
125438fd1498Szrj 	  if (cnode->global.inlined_to)
125538fd1498Szrj 	    cnode = cnode->global.inlined_to;
125638fd1498Szrj 	  if (!function)
125738fd1498Szrj 	    function = cnode;
125838fd1498Szrj 	  else if (function != cnode)
125938fd1498Szrj 	    function = BOTTOM;
126038fd1498Szrj 	}
126138fd1498Szrj       else
126238fd1498Szrj 	function = meet (function, dyn_cast <varpool_node *> (ref->referring),
126338fd1498Szrj 			 single_user_map);
126438fd1498Szrj     }
126538fd1498Szrj   return function;
126638fd1498Szrj }
126738fd1498Szrj 
126838fd1498Szrj /* Pass setting used_by_single_function flag.
126938fd1498Szrj    This flag is set on variable when there is only one function that may
127038fd1498Szrj    possibly referr to it.  */
127138fd1498Szrj 
127238fd1498Szrj static unsigned int
ipa_single_use(void)127338fd1498Szrj ipa_single_use (void)
127438fd1498Szrj {
127538fd1498Szrj   varpool_node *first = (varpool_node *) (void *) 1;
127638fd1498Szrj   varpool_node *var;
127738fd1498Szrj   hash_map<varpool_node *, cgraph_node *> single_user_map;
127838fd1498Szrj 
127938fd1498Szrj   FOR_EACH_DEFINED_VARIABLE (var)
128038fd1498Szrj     if (!var->all_refs_explicit_p ())
128138fd1498Szrj       var->aux = BOTTOM;
128238fd1498Szrj     else
128338fd1498Szrj       {
128438fd1498Szrj 	/* Enqueue symbol for dataflow.  */
128538fd1498Szrj         var->aux = first;
128638fd1498Szrj 	first = var;
128738fd1498Szrj       }
128838fd1498Szrj 
128938fd1498Szrj   /* The actual dataflow.  */
129038fd1498Szrj 
129138fd1498Szrj   while (first != (void *) 1)
129238fd1498Szrj     {
129338fd1498Szrj       cgraph_node *user, *orig_user, **f;
129438fd1498Szrj 
129538fd1498Szrj       var = first;
129638fd1498Szrj       first = (varpool_node *)first->aux;
129738fd1498Szrj 
129838fd1498Szrj       f = single_user_map.get (var);
129938fd1498Szrj       if (f)
130038fd1498Szrj 	orig_user = *f;
130138fd1498Szrj       else
130238fd1498Szrj 	orig_user = NULL;
130338fd1498Szrj       user = propagate_single_user (var, orig_user, single_user_map);
130438fd1498Szrj 
130538fd1498Szrj       gcc_checking_assert (var->aux != BOTTOM);
130638fd1498Szrj 
130738fd1498Szrj       /* If user differs, enqueue all references.  */
130838fd1498Szrj       if (user != orig_user)
130938fd1498Szrj 	{
131038fd1498Szrj 	  unsigned int i;
131138fd1498Szrj 	  ipa_ref *ref;
131238fd1498Szrj 
131338fd1498Szrj 	  single_user_map.put (var, user);
131438fd1498Szrj 
131538fd1498Szrj 	  /* Enqueue all aliases for re-processing.  */
131638fd1498Szrj 	  for (i = 0; var->iterate_direct_aliases (i, ref); i++)
131738fd1498Szrj 	    if (!ref->referring->aux)
131838fd1498Szrj 	      {
131938fd1498Szrj 		ref->referring->aux = first;
132038fd1498Szrj 		first = dyn_cast <varpool_node *> (ref->referring);
132138fd1498Szrj 	      }
132238fd1498Szrj 	  /* Enqueue all users for re-processing.  */
132338fd1498Szrj 	  for (i = 0; var->iterate_reference (i, ref); i++)
132438fd1498Szrj 	    if (!ref->referred->aux
132538fd1498Szrj 	        && ref->referred->definition
132638fd1498Szrj 		&& is_a <varpool_node *> (ref->referred))
132738fd1498Szrj 	      {
132838fd1498Szrj 		ref->referred->aux = first;
132938fd1498Szrj 		first = dyn_cast <varpool_node *> (ref->referred);
133038fd1498Szrj 	      }
133138fd1498Szrj 
133238fd1498Szrj 	  /* If user is BOTTOM, just punt on this var.  */
133338fd1498Szrj 	  if (user == BOTTOM)
133438fd1498Szrj 	    var->aux = BOTTOM;
133538fd1498Szrj 	  else
133638fd1498Szrj 	    var->aux = NULL;
133738fd1498Szrj 	}
133838fd1498Szrj       else
133938fd1498Szrj 	var->aux = NULL;
134038fd1498Szrj     }
134138fd1498Szrj 
134238fd1498Szrj   FOR_EACH_DEFINED_VARIABLE (var)
134338fd1498Szrj     {
134438fd1498Szrj       if (var->aux != BOTTOM)
134538fd1498Szrj 	{
134638fd1498Szrj 	  /* Not having the single user known means that the VAR is
134738fd1498Szrj 	     unreachable.  Either someone forgot to remove unreachable
134838fd1498Szrj 	     variables or the reachability here is wrong.  */
134938fd1498Szrj 
135038fd1498Szrj 	  gcc_checking_assert (single_user_map.get (var));
135138fd1498Szrj 
135238fd1498Szrj 	  if (dump_file)
135338fd1498Szrj 	    {
135438fd1498Szrj 	      fprintf (dump_file, "Variable %s is used by single function\n",
135538fd1498Szrj 		       var->dump_name ());
135638fd1498Szrj 	    }
135738fd1498Szrj 	  var->used_by_single_function = true;
135838fd1498Szrj 	}
135938fd1498Szrj       var->aux = NULL;
136038fd1498Szrj     }
136138fd1498Szrj   return 0;
136238fd1498Szrj }
136338fd1498Szrj 
136438fd1498Szrj namespace {
136538fd1498Szrj 
136638fd1498Szrj const pass_data pass_data_ipa_single_use =
136738fd1498Szrj {
136838fd1498Szrj   IPA_PASS, /* type */
136938fd1498Szrj   "single-use", /* name */
137038fd1498Szrj   OPTGROUP_NONE, /* optinfo_flags */
137138fd1498Szrj   TV_CGRAPHOPT, /* tv_id */
137238fd1498Szrj   0, /* properties_required */
137338fd1498Szrj   0, /* properties_provided */
137438fd1498Szrj   0, /* properties_destroyed */
137538fd1498Szrj   0, /* todo_flags_start */
137638fd1498Szrj   0, /* todo_flags_finish */
137738fd1498Szrj };
137838fd1498Szrj 
137938fd1498Szrj class pass_ipa_single_use : public ipa_opt_pass_d
138038fd1498Szrj {
138138fd1498Szrj public:
pass_ipa_single_use(gcc::context * ctxt)138238fd1498Szrj   pass_ipa_single_use (gcc::context *ctxt)
138338fd1498Szrj     : ipa_opt_pass_d (pass_data_ipa_single_use, ctxt,
138438fd1498Szrj 		      NULL, /* generate_summary */
138538fd1498Szrj 		      NULL, /* write_summary */
138638fd1498Szrj 		      NULL, /* read_summary */
138738fd1498Szrj 		      NULL, /* write_optimization_summary */
138838fd1498Szrj 		      NULL, /* read_optimization_summary */
138938fd1498Szrj 		      NULL, /* stmt_fixup */
139038fd1498Szrj 		      0, /* function_transform_todo_flags_start */
139138fd1498Szrj 		      NULL, /* function_transform */
139238fd1498Szrj 		      NULL) /* variable_transform */
139338fd1498Szrj   {}
139438fd1498Szrj 
139538fd1498Szrj   /* opt_pass methods: */
execute(function *)139638fd1498Szrj   virtual unsigned int execute (function *) { return ipa_single_use (); }
139738fd1498Szrj 
139838fd1498Szrj }; // class pass_ipa_single_use
139938fd1498Szrj 
140038fd1498Szrj } // anon namespace
140138fd1498Szrj 
140238fd1498Szrj ipa_opt_pass_d *
make_pass_ipa_single_use(gcc::context * ctxt)140338fd1498Szrj make_pass_ipa_single_use (gcc::context *ctxt)
140438fd1498Szrj {
140538fd1498Szrj   return new pass_ipa_single_use (ctxt);
140638fd1498Szrj }
140738fd1498Szrj 
140838fd1498Szrj /* Materialize all clones.  */
140938fd1498Szrj 
141038fd1498Szrj namespace {
141138fd1498Szrj 
141238fd1498Szrj const pass_data pass_data_materialize_all_clones =
141338fd1498Szrj {
141438fd1498Szrj   SIMPLE_IPA_PASS, /* type */
141538fd1498Szrj   "materialize-all-clones", /* name */
141638fd1498Szrj   OPTGROUP_NONE, /* optinfo_flags */
141738fd1498Szrj   TV_IPA_OPT, /* tv_id */
141838fd1498Szrj   0, /* properties_required */
141938fd1498Szrj   0, /* properties_provided */
142038fd1498Szrj   0, /* properties_destroyed */
142138fd1498Szrj   0, /* todo_flags_start */
142238fd1498Szrj   0, /* todo_flags_finish */
142338fd1498Szrj };
142438fd1498Szrj 
142538fd1498Szrj class pass_materialize_all_clones : public simple_ipa_opt_pass
142638fd1498Szrj {
142738fd1498Szrj public:
pass_materialize_all_clones(gcc::context * ctxt)142838fd1498Szrj   pass_materialize_all_clones (gcc::context *ctxt)
142938fd1498Szrj     : simple_ipa_opt_pass (pass_data_materialize_all_clones, ctxt)
143038fd1498Szrj   {}
143138fd1498Szrj 
143238fd1498Szrj   /* opt_pass methods: */
execute(function *)143338fd1498Szrj   virtual unsigned int execute (function *)
143438fd1498Szrj     {
143538fd1498Szrj       symtab->materialize_all_clones ();
143638fd1498Szrj       return 0;
143738fd1498Szrj     }
143838fd1498Szrj 
143938fd1498Szrj }; // class pass_materialize_all_clones
144038fd1498Szrj 
144138fd1498Szrj } // anon namespace
144238fd1498Szrj 
144338fd1498Szrj simple_ipa_opt_pass *
make_pass_materialize_all_clones(gcc::context * ctxt)144438fd1498Szrj make_pass_materialize_all_clones (gcc::context *ctxt)
144538fd1498Szrj {
144638fd1498Szrj   return new pass_materialize_all_clones (ctxt);
144738fd1498Szrj }
1448