1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459 |
- /* Basic IPA optimizations and utilities.
- Copyright (C) 2003-2015 Free Software Foundation, Inc.
- This file is part of GCC.
- GCC is free software; you can redistribute it and/or modify it under
- the terms of the GNU General Public License as published by the Free
- Software Foundation; either version 3, or (at your option) any later
- version.
- GCC is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- for more details.
- You should have received a copy of the GNU General Public License
- along with GCC; see the file COPYING3. If not see
- <http://www.gnu.org/licenses/>. */
- #include "config.h"
- #include "system.h"
- #include "coretypes.h"
- #include "tm.h"
- #include "hash-set.h"
- #include "machmode.h"
- #include "vec.h"
- #include "double-int.h"
- #include "input.h"
- #include "alias.h"
- #include "symtab.h"
- #include "options.h"
- #include "wide-int.h"
- #include "inchash.h"
- #include "tree.h"
- #include "fold-const.h"
- #include "calls.h"
- #include "stringpool.h"
- #include "predict.h"
- #include "basic-block.h"
- #include "hash-map.h"
- #include "is-a.h"
- #include "plugin-api.h"
- #include "hard-reg-set.h"
- #include "input.h"
- #include "function.h"
- #include "ipa-ref.h"
- #include "cgraph.h"
- #include "tree-pass.h"
- #include "gimple-expr.h"
- #include "gimplify.h"
- #include "flags.h"
- #include "target.h"
- #include "tree-iterator.h"
- #include "ipa-utils.h"
- #include "alloc-pool.h"
- #include "symbol-summary.h"
- #include "ipa-prop.h"
- #include "ipa-inline.h"
- #include "tree-inline.h"
- #include "profile.h"
- #include "params.h"
- #include "internal-fn.h"
- #include "tree-ssa-alias.h"
- #include "gimple.h"
- #include "dbgcnt.h"
- /* Return true when NODE has ADDR reference. */
- static bool
- has_addr_references_p (struct cgraph_node *node,
- void *data ATTRIBUTE_UNUSED)
- {
- int i;
- struct ipa_ref *ref = NULL;
- for (i = 0; node->iterate_referring (i, ref); i++)
- if (ref->use == IPA_REF_ADDR)
- return true;
- return false;
- }
- /* Look for all functions inlined to NODE and update their inlined_to pointers
- to INLINED_TO. */
- static void
- update_inlined_to_pointer (struct cgraph_node *node, struct cgraph_node *inlined_to)
- {
- struct cgraph_edge *e;
- for (e = node->callees; e; e = e->next_callee)
- if (e->callee->global.inlined_to)
- {
- e->callee->global.inlined_to = inlined_to;
- update_inlined_to_pointer (e->callee, inlined_to);
- }
- }
- /* Add symtab NODE to queue starting at FIRST.
- The queue is linked via AUX pointers and terminated by pointer to 1.
- We enqueue nodes at two occasions: when we find them reachable or when we find
- their bodies needed for further clonning. In the second case we mark them
- by pointer to 2 after processing so they are re-queue when they become
- reachable. */
- static void
- enqueue_node (symtab_node *node, symtab_node **first,
- hash_set<symtab_node *> *reachable)
- {
- /* Node is still in queue; do nothing. */
- if (node->aux && node->aux != (void *) 2)
- return;
- /* Node was already processed as unreachable, re-enqueue
- only if it became reachable now. */
- if (node->aux == (void *)2 && !reachable->contains (node))
- return;
- node->aux = *first;
- *first = node;
- }
- /* Process references. */
- static void
- process_references (symtab_node *snode,
- symtab_node **first,
- bool before_inlining_p,
- hash_set<symtab_node *> *reachable)
- {
- int i;
- struct ipa_ref *ref = NULL;
- for (i = 0; snode->iterate_reference (i, ref); i++)
- {
- symtab_node *node = ref->referred;
- symtab_node *body = node->ultimate_alias_target ();
- if (node->definition && !node->in_other_partition
- && ((!DECL_EXTERNAL (node->decl) || node->alias)
- || (((before_inlining_p
- && ((TREE_CODE (node->decl) != FUNCTION_DECL
- && optimize)
- || (TREE_CODE (node->decl) == FUNCTION_DECL
- && opt_for_fn (body->decl, optimize))
- || (symtab->state < IPA_SSA
- && lookup_attribute
- ("always_inline",
- DECL_ATTRIBUTES (body->decl))))))
- /* We use variable constructors during late compilation for
- constant folding. Keep references alive so partitioning
- knows about potential references. */
- || (TREE_CODE (node->decl) == VAR_DECL
- && flag_wpa
- && ctor_for_folding (node->decl)
- != error_mark_node))))
- {
- /* Be sure that we will not optimize out alias target
- body. */
- if (DECL_EXTERNAL (node->decl)
- && node->alias
- && before_inlining_p)
- reachable->add (body);
- reachable->add (node);
- }
- enqueue_node (node, first, reachable);
- }
- }
- /* EDGE is an polymorphic call. If BEFORE_INLINING_P is set, mark
- all its potential targets as reachable to permit later inlining if
- devirtualization happens. After inlining still keep their declarations
- around, so we can devirtualize to a direct call.
- Also try to make trivial devirutalization when no or only one target is
- possible. */
- 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)
- {
- unsigned int i;
- void *cache_token;
- bool final;
- vec <cgraph_node *>targets
- = possible_polymorphic_call_targets
- (edge, &final, &cache_token);
- if (!reachable_call_targets->add (cache_token))
- {
- for (i = 0; i < targets.length (); i++)
- {
- struct cgraph_node *n = targets[i];
- /* Do not bother to mark virtual methods in anonymous namespace;
- either we will find use of virtual table defining it, or it is
- unused. */
- if (TREE_CODE (TREE_TYPE (n->decl)) == METHOD_TYPE
- && type_in_anonymous_namespace_p
- (method_class_type (TREE_TYPE (n->decl))))
- continue;
- symtab_node *body = n->function_symbol ();
- /* Prior inlining, keep alive bodies of possible targets for
- devirtualization. */
- if (n->definition
- && (before_inlining_p
- && opt_for_fn (body->decl, optimize)
- && opt_for_fn (body->decl, flag_devirtualize)))
- {
- /* Be sure that we will not optimize out alias target
- body. */
- if (DECL_EXTERNAL (n->decl)
- && n->alias
- && before_inlining_p)
- reachable->add (body);
- reachable->add (n);
- }
- /* Even after inlining we want to keep the possible targets in the
- boundary, so late passes can still produce direct call even if
- the chance for inlining is lost. */
- enqueue_node (n, first, reachable);
- }
- }
- /* Very trivial devirtualization; when the type is
- final or anonymous (so we know all its derivation)
- and there is only one possible virtual call target,
- make the edge direct. */
- if (final)
- {
- if (targets.length () <= 1 && dbg_cnt (devirt))
- {
- cgraph_node *target, *node = edge->caller;
- if (targets.length () == 1)
- target = targets[0];
- else
- target = cgraph_node::get_create
- (builtin_decl_implicit (BUILT_IN_UNREACHABLE));
- if (dump_enabled_p ())
- {
- location_t locus;
- if (edge->call_stmt)
- locus = gimple_location (edge->call_stmt);
- else
- locus = UNKNOWN_LOCATION;
- dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, locus,
- "devirtualizing call in %s/%i to %s/%i\n",
- edge->caller->name (), edge->caller->order,
- target->name (),
- target->order);
- }
- edge = edge->make_direct (target);
- if (inline_summaries)
- inline_update_overall_summary (node);
- else if (edge->call_stmt)
- {
- edge->redirect_call_stmt_to_callee ();
- /* Call to __builtin_unreachable shouldn't be instrumented. */
- if (!targets.length ())
- gimple_call_set_with_bounds (edge->call_stmt, false);
- }
- }
- }
- }
- /* Perform reachability analysis and reclaim all unreachable nodes.
- The algorithm is basically mark&sweep but with some extra refinements:
- - reachable extern inline functions needs special handling; the bodies needs
- to stay in memory until inlining in hope that they will be inlined.
- After inlining we release their bodies and turn them into unanalyzed
- nodes even when they are reachable.
- - virtual functions are kept in callgraph even if they seem unreachable in
- hope calls to them will be devirtualized.
- Again we remove them after inlining. In late optimization some
- devirtualization may happen, but it is not important since we won't inline
- the call. In theory early opts and IPA should work out all important cases.
- - virtual clones needs bodies of their origins for later materialization;
- this means that we want to keep the body even if the origin is unreachable
- otherwise. To avoid origin from sitting in the callgraph and being
- walked by IPA passes, we turn them into unanalyzed nodes with body
- defined.
- We maintain set of function declaration where body needs to stay in
- body_needed_for_clonning
- Inline clones represent special case: their declaration match the
- declaration of origin and cgraph_remove_node already knows how to
- reshape callgraph and preserve body when offline copy of function or
- inline clone is being removed.
- - C++ virtual tables keyed to other unit are represented as DECL_EXTERNAL
- variables with DECL_INITIAL set. We finalize these and keep reachable
- ones around for constant folding purposes. After inlining we however
- stop walking their references to let everything static referneced by them
- to be removed when it is otherwise unreachable.
- We maintain queue of both reachable symbols (i.e. defined symbols that needs
- to stay) and symbols that are in boundary (i.e. external symbols referenced
- by reachable symbols or origins of clones). The queue is represented
- as linked list by AUX pointer terminated by 1.
- At the end we keep all reachable symbols. For symbols in boundary we always
- turn definition into a declaration, but we may keep function body around
- based on body_needed_for_clonning
- All symbols that enter the queue have AUX pointer non-zero and are in the
- boundary. Pointer set REACHABLE is used to track reachable symbols.
- Every symbol can be visited twice - once as part of boundary and once
- as real reachable symbol. enqueue_node needs to decide whether the
- node needs to be re-queued for second processing. For this purpose
- we set AUX pointer of processed symbols in the boundary to constant 2. */
- bool
- symbol_table::remove_unreachable_nodes (FILE *file)
- {
- symtab_node *first = (symtab_node *) (void *) 1;
- struct cgraph_node *node, *next;
- varpool_node *vnode, *vnext;
- bool changed = false;
- hash_set<symtab_node *> reachable;
- hash_set<tree> body_needed_for_clonning;
- hash_set<void *> reachable_call_targets;
- bool before_inlining_p = symtab->state < (!optimize ? IPA_SSA
- : IPA_SSA_AFTER_INLINING);
- timevar_push (TV_IPA_UNREACHABLE);
- build_type_inheritance_graph ();
- if (file)
- fprintf (file, "\nReclaiming functions:");
- #ifdef ENABLE_CHECKING
- FOR_EACH_FUNCTION (node)
- gcc_assert (!node->aux);
- FOR_EACH_VARIABLE (vnode)
- gcc_assert (!vnode->aux);
- #endif
- /* Mark functions whose bodies are obviously needed.
- This is mostly when they can be referenced externally. Inline clones
- are special since their declarations are shared with master clone and thus
- cgraph_can_remove_if_no_direct_calls_and_refs_p should not be called on them. */
- FOR_EACH_FUNCTION (node)
- {
- node->used_as_abstract_origin = false;
- if (node->definition
- && !node->global.inlined_to
- && !node->in_other_partition
- && !node->can_remove_if_no_direct_calls_and_refs_p ())
- {
- gcc_assert (!node->global.inlined_to);
- reachable.add (node);
- enqueue_node (node, &first, &reachable);
- }
- else
- gcc_assert (!node->aux);
- }
- /* Mark variables that are obviously needed. */
- FOR_EACH_DEFINED_VARIABLE (vnode)
- if (!vnode->can_remove_if_no_refs_p()
- && !vnode->in_other_partition)
- {
- reachable.add (vnode);
- enqueue_node (vnode, &first, &reachable);
- }
- /* Perform reachability analysis. */
- while (first != (symtab_node *) (void *) 1)
- {
- bool in_boundary_p = !reachable.contains (first);
- symtab_node *node = first;
- first = (symtab_node *)first->aux;
- /* If we are processing symbol in boundary, mark its AUX pointer for
- possible later re-processing in enqueue_node. */
- if (in_boundary_p)
- {
- node->aux = (void *)2;
- if (node->alias && node->analyzed)
- enqueue_node (node->get_alias_target (), &first, &reachable);
- }
- else
- {
- if (TREE_CODE (node->decl) == FUNCTION_DECL
- && DECL_ABSTRACT_ORIGIN (node->decl))
- {
- struct cgraph_node *origin_node
- = cgraph_node::get (DECL_ABSTRACT_ORIGIN (node->decl));
- if (origin_node && !origin_node->used_as_abstract_origin)
- {
- origin_node->used_as_abstract_origin = true;
- gcc_assert (!origin_node->prev_sibling_clone);
- gcc_assert (!origin_node->next_sibling_clone);
- for (cgraph_node *n = origin_node->clones; n;
- n = n->next_sibling_clone)
- if (n->decl == DECL_ABSTRACT_ORIGIN (node->decl))
- n->used_as_abstract_origin = true;
- }
- }
- /* If any symbol in a comdat group is reachable, force
- all externally visible symbols in the same comdat
- group to be reachable as well. Comdat-local symbols
- can be discarded if all uses were inlined. */
- if (node->same_comdat_group)
- {
- symtab_node *next;
- for (next = node->same_comdat_group;
- next != node;
- next = next->same_comdat_group)
- if (!next->comdat_local_p ()
- && !reachable.add (next))
- enqueue_node (next, &first, &reachable);
- }
- /* Mark references as reachable. */
- process_references (node, &first, before_inlining_p, &reachable);
- }
- if (cgraph_node *cnode = dyn_cast <cgraph_node *> (node))
- {
- /* Mark the callees reachable unless they are direct calls to extern
- inline functions we decided to not inline. */
- if (!in_boundary_p)
- {
- struct cgraph_edge *e;
- /* Keep alive possible targets for devirtualization. */
- if (opt_for_fn (cnode->decl, optimize)
- && opt_for_fn (cnode->decl, flag_devirtualize))
- {
- struct cgraph_edge *next;
- for (e = cnode->indirect_calls; e; e = next)
- {
- next = e->next_callee;
- if (e->indirect_info->polymorphic)
- walk_polymorphic_call_targets (&reachable_call_targets,
- e, &first, &reachable,
- before_inlining_p);
- }
- }
- for (e = cnode->callees; e; e = e->next_callee)
- {
- symtab_node *body = e->callee->function_symbol ();
- if (e->callee->definition
- && !e->callee->in_other_partition
- && (!e->inline_failed
- || !DECL_EXTERNAL (e->callee->decl)
- || e->callee->alias
- || (before_inlining_p
- && (opt_for_fn (body->decl, optimize)
- || (symtab->state < IPA_SSA
- && lookup_attribute
- ("always_inline",
- DECL_ATTRIBUTES (body->decl)))))))
- {
- /* Be sure that we will not optimize out alias target
- body. */
- if (DECL_EXTERNAL (e->callee->decl)
- && e->callee->alias
- && before_inlining_p)
- reachable.add (body);
- reachable.add (e->callee);
- }
- enqueue_node (e->callee, &first, &reachable);
- }
- /* When inline clone exists, mark body to be preserved so when removing
- offline copy of the function we don't kill it. */
- if (cnode->global.inlined_to)
- body_needed_for_clonning.add (cnode->decl);
- /* For instrumentation clones we always need original
- function node for proper LTO privatization. */
- if (cnode->instrumentation_clone
- && cnode->definition)
- {
- gcc_assert (cnode->instrumented_version || in_lto_p);
- if (cnode->instrumented_version)
- {
- enqueue_node (cnode->instrumented_version, &first,
- &reachable);
- reachable.add (cnode->instrumented_version);
- }
- }
- /* For non-inline clones, force their origins to the boundary and ensure
- that body is not removed. */
- while (cnode->clone_of)
- {
- bool noninline = cnode->clone_of->decl != cnode->decl;
- cnode = cnode->clone_of;
- if (noninline)
- {
- body_needed_for_clonning.add (cnode->decl);
- enqueue_node (cnode, &first, &reachable);
- }
- }
- }
- else if (cnode->thunk.thunk_p)
- enqueue_node (cnode->callees->callee, &first, &reachable);
- /* If any reachable function has simd clones, mark them as
- reachable as well. */
- if (cnode->simd_clones)
- {
- cgraph_node *next;
- for (next = cnode->simd_clones;
- next;
- next = next->simdclone->next_clone)
- if (in_boundary_p
- || !reachable.add (next))
- enqueue_node (next, &first, &reachable);
- }
- }
- /* When we see constructor of external variable, keep referred nodes in the
- boundary. This will also hold initializers of the external vars NODE
- refers to. */
- varpool_node *vnode = dyn_cast <varpool_node *> (node);
- if (vnode
- && DECL_EXTERNAL (node->decl)
- && !vnode->alias
- && in_boundary_p)
- {
- struct ipa_ref *ref = NULL;
- for (int i = 0; node->iterate_reference (i, ref); i++)
- enqueue_node (ref->referred, &first, &reachable);
- }
- }
- /* Remove unreachable functions. */
- for (node = first_function (); node; node = next)
- {
- next = next_function (node);
- /* If node is not needed at all, remove it. */
- if (!node->aux)
- {
- if (file)
- fprintf (file, " %s/%i", node->name (), node->order);
- node->remove ();
- changed = true;
- }
- /* If node is unreachable, remove its body. */
- else if (!reachable.contains (node))
- {
- /* We keep definitions of thunks and aliases in the boundary so
- we can walk to the ultimate alias targets and function symbols
- reliably. */
- if (node->alias || node->thunk.thunk_p)
- ;
- else if (!body_needed_for_clonning.contains (node->decl)
- && !node->alias && !node->thunk.thunk_p)
- node->release_body ();
- else if (!node->clone_of)
- gcc_assert (in_lto_p || DECL_RESULT (node->decl));
- if (node->definition && !node->alias && !node->thunk.thunk_p)
- {
- if (file)
- fprintf (file, " %s/%i", node->name (), node->order);
- node->body_removed = true;
- node->analyzed = false;
- node->definition = false;
- node->cpp_implicit_alias = false;
- node->alias = false;
- node->thunk.thunk_p = false;
- node->weakref = false;
- /* After early inlining we drop always_inline attributes on
- bodies of functions that are still referenced (have their
- address taken). */
- DECL_ATTRIBUTES (node->decl)
- = remove_attribute ("always_inline",
- DECL_ATTRIBUTES (node->decl));
- if (!node->in_other_partition)
- node->local.local = false;
- node->remove_callees ();
- node->remove_all_references ();
- changed = true;
- if (node->thunk.thunk_p
- && node->thunk.add_pointer_bounds_args)
- {
- node->thunk.thunk_p = false;
- node->thunk.add_pointer_bounds_args = false;
- }
- }
- }
- else
- gcc_assert (node->clone_of || !node->has_gimple_body_p ()
- || in_lto_p || DECL_RESULT (node->decl));
- }
- /* Inline clones might be kept around so their materializing allows further
- cloning. If the function the clone is inlined into is removed, we need
- to turn it into normal cone. */
- FOR_EACH_FUNCTION (node)
- {
- if (node->global.inlined_to
- && !node->callers)
- {
- gcc_assert (node->clones);
- node->global.inlined_to = NULL;
- update_inlined_to_pointer (node, node);
- }
- node->aux = NULL;
- }
- /* Remove unreachable variables. */
- if (file)
- fprintf (file, "\nReclaiming variables:");
- for (vnode = first_variable (); vnode; vnode = vnext)
- {
- vnext = next_variable (vnode);
- if (!vnode->aux
- /* For can_refer_decl_in_current_unit_p we want to track for
- all external variables if they are defined in other partition
- or not. */
- && (!flag_ltrans || !DECL_EXTERNAL (vnode->decl)))
- {
- struct ipa_ref *ref = NULL;
- /* First remove the aliases, so varpool::remove can possibly lookup
- the constructor and save it for future use. */
- while (vnode->iterate_direct_aliases (0, ref))
- {
- if (file)
- fprintf (file, " %s/%i", ref->referred->name (),
- ref->referred->order);
- ref->referring->remove ();
- }
- if (file)
- fprintf (file, " %s/%i", vnode->name (), vnode->order);
- vnext = next_variable (vnode);
- vnode->remove ();
- changed = true;
- }
- else if (!reachable.contains (vnode) && !vnode->alias)
- {
- tree init;
- if (vnode->definition)
- {
- if (file)
- fprintf (file, " %s", vnode->name ());
- changed = true;
- }
- /* Keep body if it may be useful for constant folding. */
- if ((init = ctor_for_folding (vnode->decl)) == error_mark_node
- && !POINTER_BOUNDS_P (vnode->decl))
- vnode->remove_initializer ();
- else
- DECL_INITIAL (vnode->decl) = init;
- vnode->body_removed = true;
- vnode->definition = false;
- vnode->analyzed = false;
- vnode->aux = NULL;
- vnode->remove_from_same_comdat_group ();
- vnode->remove_all_references ();
- }
- else
- vnode->aux = NULL;
- }
- /* Now update address_taken flags and try to promote functions to be local. */
- if (file)
- fprintf (file, "\nClearing address taken flags:");
- FOR_EACH_DEFINED_FUNCTION (node)
- if (node->address_taken
- && !node->used_from_other_partition)
- {
- if (!node->call_for_symbol_and_aliases
- (has_addr_references_p, NULL, true)
- && (!node->instrumentation_clone
- || !node->instrumented_version
- || !node->instrumented_version->address_taken))
- {
- if (file)
- fprintf (file, " %s", node->name ());
- node->address_taken = false;
- changed = true;
- if (node->local_p ())
- {
- node->local.local = true;
- if (file)
- fprintf (file, " (local)");
- }
- }
- }
- if (file)
- fprintf (file, "\n");
- #ifdef ENABLE_CHECKING
- symtab_node::verify_symtab_nodes ();
- #endif
- /* If we removed something, perhaps profile could be improved. */
- if (changed && optimize && inline_edge_summary_vec.exists ())
- FOR_EACH_DEFINED_FUNCTION (node)
- ipa_propagate_frequency (node);
- timevar_pop (TV_IPA_UNREACHABLE);
- return changed;
- }
- /* Process references to VNODE and set flags WRITTEN, ADDRESS_TAKEN, READ
- as needed, also clear EXPLICIT_REFS if the references to given variable
- do not need to be explicit. */
- void
- process_references (varpool_node *vnode,
- bool *written, bool *address_taken,
- bool *read, bool *explicit_refs)
- {
- int i;
- struct ipa_ref *ref;
- if (!vnode->all_refs_explicit_p ()
- || TREE_THIS_VOLATILE (vnode->decl))
- *explicit_refs = false;
- for (i = 0; vnode->iterate_referring (i, ref)
- && *explicit_refs && (!*written || !*address_taken || !*read); i++)
- switch (ref->use)
- {
- case IPA_REF_ADDR:
- *address_taken = true;
- break;
- case IPA_REF_LOAD:
- *read = true;
- break;
- case IPA_REF_STORE:
- *written = true;
- break;
- case IPA_REF_ALIAS:
- process_references (dyn_cast<varpool_node *> (ref->referring), written,
- address_taken, read, explicit_refs);
- break;
- case IPA_REF_CHKP:
- gcc_unreachable ();
- }
- }
- /* Set TREE_READONLY bit. */
- bool
- set_readonly_bit (varpool_node *vnode, void *data ATTRIBUTE_UNUSED)
- {
- TREE_READONLY (vnode->decl) = true;
- return false;
- }
- /* Set writeonly bit and clear the initalizer, since it will not be needed. */
- bool
- set_writeonly_bit (varpool_node *vnode, void *data)
- {
- vnode->writeonly = true;
- if (optimize)
- {
- DECL_INITIAL (vnode->decl) = NULL;
- if (!vnode->alias)
- {
- if (vnode->num_references ())
- *(bool *)data = true;
- vnode->remove_all_references ();
- }
- }
- return false;
- }
- /* Clear addressale bit of VNODE. */
- bool
- clear_addressable_bit (varpool_node *vnode, void *data ATTRIBUTE_UNUSED)
- {
- vnode->address_taken = false;
- TREE_ADDRESSABLE (vnode->decl) = 0;
- return false;
- }
- /* Discover variables that have no longer address taken or that are read only
- and update their flags.
- Return true when unreachable symbol removan should be done.
- FIXME: This can not be done in between gimplify and omp_expand since
- readonly flag plays role on what is shared and what is not. Currently we do
- this transformation as part of whole program visibility and re-do at
- ipa-reference pass (to take into account clonning), but it would
- make sense to do it before early optimizations. */
- bool
- ipa_discover_readonly_nonaddressable_vars (void)
- {
- bool remove_p = false;
- varpool_node *vnode;
- if (dump_file)
- fprintf (dump_file, "Clearing variable flags:");
- FOR_EACH_VARIABLE (vnode)
- if (!vnode->alias
- && (TREE_ADDRESSABLE (vnode->decl)
- || !vnode->writeonly
- || !TREE_READONLY (vnode->decl)))
- {
- bool written = false;
- bool address_taken = false;
- bool read = false;
- bool explicit_refs = true;
- process_references (vnode, &written, &address_taken, &read,
- &explicit_refs);
- if (!explicit_refs)
- continue;
- if (!address_taken)
- {
- if (TREE_ADDRESSABLE (vnode->decl) && dump_file)
- fprintf (dump_file, " %s (non-addressable)", vnode->name ());
- vnode->call_for_symbol_and_aliases (clear_addressable_bit, NULL,
- true);
- }
- if (!address_taken && !written
- /* Making variable in explicit section readonly can cause section
- type conflict.
- See e.g. gcc.c-torture/compile/pr23237.c */
- && vnode->get_section () == NULL)
- {
- if (!TREE_READONLY (vnode->decl) && dump_file)
- fprintf (dump_file, " %s (read-only)", vnode->name ());
- vnode->call_for_symbol_and_aliases (set_readonly_bit, NULL, true);
- }
- if (!vnode->writeonly && !read && !address_taken && written)
- {
- if (dump_file)
- fprintf (dump_file, " %s (write-only)", vnode->name ());
- vnode->call_for_symbol_and_aliases (set_writeonly_bit, &remove_p,
- true);
- }
- }
- if (dump_file)
- fprintf (dump_file, "\n");
- return remove_p;
- }
- /* Free inline summary. */
- namespace {
- const pass_data pass_data_ipa_free_inline_summary =
- {
- SIMPLE_IPA_PASS, /* type */
- "free-inline-summary", /* name */
- OPTGROUP_NONE, /* optinfo_flags */
- TV_IPA_FREE_INLINE_SUMMARY, /* tv_id */
- 0, /* properties_required */
- 0, /* properties_provided */
- 0, /* properties_destroyed */
- 0, /* todo_flags_start */
- /* Early optimizations may make function unreachable. We can not
- remove unreachable functions as part of the ealry opts pass because
- TODOs are run before subpasses. Do it here. */
- ( TODO_remove_functions | TODO_dump_symtab ), /* todo_flags_finish */
- };
- class pass_ipa_free_inline_summary : public simple_ipa_opt_pass
- {
- public:
- pass_ipa_free_inline_summary (gcc::context *ctxt)
- : simple_ipa_opt_pass (pass_data_ipa_free_inline_summary, ctxt)
- {}
- /* opt_pass methods: */
- virtual unsigned int execute (function *)
- {
- inline_free_summary ();
- return 0;
- }
- }; // class pass_ipa_free_inline_summary
- } // anon namespace
- simple_ipa_opt_pass *
- make_pass_ipa_free_inline_summary (gcc::context *ctxt)
- {
- return new pass_ipa_free_inline_summary (ctxt);
- }
- /* Generate and emit a static constructor or destructor. WHICH must
- be one of 'I' (for a constructor), 'D' (for a destructor), 'P'
- (for chp static vars constructor) or 'B' (for chkp static bounds
- constructor). BODY is a STATEMENT_LIST containing GENERIC
- statements. PRIORITY is the initialization priority for this
- constructor or destructor.
- FINAL specify whether the externally visible name for collect2 should
- be produced. */
- static void
- cgraph_build_static_cdtor_1 (char which, tree body, int priority, bool final)
- {
- static int counter = 0;
- char which_buf[16];
- tree decl, name, resdecl;
- /* The priority is encoded in the constructor or destructor name.
- collect2 will sort the names and arrange that they are called at
- program startup. */
- if (final)
- sprintf (which_buf, "%c_%.5d_%d", which, priority, counter++);
- else
- /* Proudce sane name but one not recognizable by collect2, just for the
- case we fail to inline the function. */
- sprintf (which_buf, "sub_%c_%.5d_%d", which, priority, counter++);
- name = get_file_function_name (which_buf);
- decl = build_decl (input_location, FUNCTION_DECL, name,
- build_function_type_list (void_type_node, NULL_TREE));
- current_function_decl = decl;
- resdecl = build_decl (input_location,
- RESULT_DECL, NULL_TREE, void_type_node);
- DECL_ARTIFICIAL (resdecl) = 1;
- DECL_RESULT (decl) = resdecl;
- DECL_CONTEXT (resdecl) = decl;
- allocate_struct_function (decl, false);
- TREE_STATIC (decl) = 1;
- TREE_USED (decl) = 1;
- DECL_ARTIFICIAL (decl) = 1;
- DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (decl) = 1;
- DECL_SAVED_TREE (decl) = body;
- if (!targetm.have_ctors_dtors && final)
- {
- TREE_PUBLIC (decl) = 1;
- DECL_PRESERVE_P (decl) = 1;
- }
- DECL_UNINLINABLE (decl) = 1;
- DECL_INITIAL (decl) = make_node (BLOCK);
- TREE_USED (DECL_INITIAL (decl)) = 1;
- DECL_SOURCE_LOCATION (decl) = input_location;
- cfun->function_end_locus = input_location;
- switch (which)
- {
- case 'I':
- DECL_STATIC_CONSTRUCTOR (decl) = 1;
- decl_init_priority_insert (decl, priority);
- break;
- case 'P':
- DECL_STATIC_CONSTRUCTOR (decl) = 1;
- DECL_ATTRIBUTES (decl) = tree_cons (get_identifier ("chkp ctor"),
- NULL,
- NULL_TREE);
- decl_init_priority_insert (decl, priority);
- break;
- case 'B':
- DECL_STATIC_CONSTRUCTOR (decl) = 1;
- DECL_ATTRIBUTES (decl) = tree_cons (get_identifier ("bnd_legacy"),
- NULL,
- NULL_TREE);
- decl_init_priority_insert (decl, priority);
- break;
- case 'D':
- DECL_STATIC_DESTRUCTOR (decl) = 1;
- decl_fini_priority_insert (decl, priority);
- break;
- default:
- gcc_unreachable ();
- }
- gimplify_function_tree (decl);
- cgraph_node::add_new_function (decl, false);
- set_cfun (NULL);
- current_function_decl = NULL;
- }
- /* Generate and emit a static constructor or destructor. WHICH must
- be one of 'I' (for a constructor), 'D' (for a destructor), 'P'
- (for chkp static vars constructor) or 'B' (for chkp static bounds
- constructor). BODY is a STATEMENT_LIST containing GENERIC
- statements. PRIORITY is the initialization priority for this
- constructor or destructor. */
- void
- cgraph_build_static_cdtor (char which, tree body, int priority)
- {
- cgraph_build_static_cdtor_1 (which, body, priority, false);
- }
- /* A vector of FUNCTION_DECLs declared as static constructors. */
- static vec<tree> static_ctors;
- /* A vector of FUNCTION_DECLs declared as static destructors. */
- static vec<tree> static_dtors;
- /* When target does not have ctors and dtors, we call all constructor
- and destructor by special initialization/destruction function
- recognized by collect2.
- When we are going to build this function, collect all constructors and
- destructors and turn them into normal functions. */
- static void
- record_cdtor_fn (struct cgraph_node *node)
- {
- if (DECL_STATIC_CONSTRUCTOR (node->decl))
- static_ctors.safe_push (node->decl);
- if (DECL_STATIC_DESTRUCTOR (node->decl))
- static_dtors.safe_push (node->decl);
- node = cgraph_node::get (node->decl);
- DECL_DISREGARD_INLINE_LIMITS (node->decl) = 1;
- }
- /* Define global constructors/destructor functions for the CDTORS, of
- which they are LEN. The CDTORS are sorted by initialization
- priority. If CTOR_P is true, these are constructors; otherwise,
- they are destructors. */
- static void
- build_cdtor (bool ctor_p, vec<tree> cdtors)
- {
- size_t i,j;
- size_t len = cdtors.length ();
- i = 0;
- while (i < len)
- {
- tree body;
- tree fn;
- priority_type priority;
- priority = 0;
- body = NULL_TREE;
- j = i;
- do
- {
- priority_type p;
- fn = cdtors[j];
- p = ctor_p ? DECL_INIT_PRIORITY (fn) : DECL_FINI_PRIORITY (fn);
- if (j == i)
- priority = p;
- else if (p != priority)
- break;
- j++;
- }
- while (j < len);
- /* When there is only one cdtor and target supports them, do nothing. */
- if (j == i + 1
- && targetm.have_ctors_dtors)
- {
- i++;
- continue;
- }
- /* Find the next batch of constructors/destructors with the same
- initialization priority. */
- for (;i < j; i++)
- {
- tree call;
- fn = cdtors[i];
- call = build_call_expr (fn, 0);
- if (ctor_p)
- DECL_STATIC_CONSTRUCTOR (fn) = 0;
- else
- DECL_STATIC_DESTRUCTOR (fn) = 0;
- /* We do not want to optimize away pure/const calls here.
- When optimizing, these should be already removed, when not
- optimizing, we want user to be able to breakpoint in them. */
- TREE_SIDE_EFFECTS (call) = 1;
- append_to_statement_list (call, &body);
- }
- gcc_assert (body != NULL_TREE);
- /* Generate a function to call all the function of like
- priority. */
- cgraph_build_static_cdtor_1 (ctor_p ? 'I' : 'D', body, priority, true);
- }
- }
- /* Comparison function for qsort. P1 and P2 are actually of type
- "tree *" and point to static constructors. DECL_INIT_PRIORITY is
- used to determine the sort order. */
- static int
- compare_ctor (const void *p1, const void *p2)
- {
- tree f1;
- tree f2;
- int priority1;
- int priority2;
- f1 = *(const tree *)p1;
- f2 = *(const tree *)p2;
- priority1 = DECL_INIT_PRIORITY (f1);
- priority2 = DECL_INIT_PRIORITY (f2);
- if (priority1 < priority2)
- return -1;
- else if (priority1 > priority2)
- return 1;
- else
- /* Ensure a stable sort. Constructors are executed in backwarding
- order to make LTO initialize braries first. */
- return DECL_UID (f2) - DECL_UID (f1);
- }
- /* Comparison function for qsort. P1 and P2 are actually of type
- "tree *" and point to static destructors. DECL_FINI_PRIORITY is
- used to determine the sort order. */
- static int
- compare_dtor (const void *p1, const void *p2)
- {
- tree f1;
- tree f2;
- int priority1;
- int priority2;
- f1 = *(const tree *)p1;
- f2 = *(const tree *)p2;
- priority1 = DECL_FINI_PRIORITY (f1);
- priority2 = DECL_FINI_PRIORITY (f2);
- if (priority1 < priority2)
- return -1;
- else if (priority1 > priority2)
- return 1;
- else
- /* Ensure a stable sort. */
- return DECL_UID (f1) - DECL_UID (f2);
- }
- /* Generate functions to call static constructors and destructors
- for targets that do not support .ctors/.dtors sections. These
- functions have magic names which are detected by collect2. */
- static void
- build_cdtor_fns (void)
- {
- if (!static_ctors.is_empty ())
- {
- gcc_assert (!targetm.have_ctors_dtors || in_lto_p);
- static_ctors.qsort (compare_ctor);
- build_cdtor (/*ctor_p=*/true, static_ctors);
- }
- if (!static_dtors.is_empty ())
- {
- gcc_assert (!targetm.have_ctors_dtors || in_lto_p);
- static_dtors.qsort (compare_dtor);
- build_cdtor (/*ctor_p=*/false, static_dtors);
- }
- }
- /* Look for constructors and destructors and produce function calling them.
- This is needed for targets not supporting ctors or dtors, but we perform the
- transformation also at linktime to merge possibly numerous
- constructors/destructors into single function to improve code locality and
- reduce size. */
- static unsigned int
- ipa_cdtor_merge (void)
- {
- struct cgraph_node *node;
- FOR_EACH_DEFINED_FUNCTION (node)
- if (DECL_STATIC_CONSTRUCTOR (node->decl)
- || DECL_STATIC_DESTRUCTOR (node->decl))
- record_cdtor_fn (node);
- build_cdtor_fns ();
- static_ctors.release ();
- static_dtors.release ();
- return 0;
- }
- namespace {
- const pass_data pass_data_ipa_cdtor_merge =
- {
- IPA_PASS, /* type */
- "cdtor", /* name */
- OPTGROUP_NONE, /* optinfo_flags */
- TV_CGRAPHOPT, /* tv_id */
- 0, /* properties_required */
- 0, /* properties_provided */
- 0, /* properties_destroyed */
- 0, /* todo_flags_start */
- 0, /* todo_flags_finish */
- };
- class pass_ipa_cdtor_merge : public ipa_opt_pass_d
- {
- public:
- pass_ipa_cdtor_merge (gcc::context *ctxt)
- : ipa_opt_pass_d (pass_data_ipa_cdtor_merge, ctxt,
- NULL, /* generate_summary */
- NULL, /* write_summary */
- NULL, /* read_summary */
- NULL, /* write_optimization_summary */
- NULL, /* read_optimization_summary */
- NULL, /* stmt_fixup */
- 0, /* function_transform_todo_flags_start */
- NULL, /* function_transform */
- NULL) /* variable_transform */
- {}
- /* opt_pass methods: */
- virtual bool gate (function *);
- virtual unsigned int execute (function *) { return ipa_cdtor_merge (); }
- }; // class pass_ipa_cdtor_merge
- bool
- pass_ipa_cdtor_merge::gate (function *)
- {
- /* Perform the pass when we have no ctors/dtors support
- or at LTO time to merge multiple constructors into single
- function. */
- return !targetm.have_ctors_dtors || (optimize && in_lto_p);
- }
- } // anon namespace
- ipa_opt_pass_d *
- make_pass_ipa_cdtor_merge (gcc::context *ctxt)
- {
- return new pass_ipa_cdtor_merge (ctxt);
- }
- /* Invalid pointer representing BOTTOM for single user dataflow. */
- #define BOTTOM ((cgraph_node *)(size_t) 2)
- /* Meet operation for single user dataflow.
- Here we want to associate variables with sigle function that may access it.
- FUNCTION is current single user of a variable, VAR is variable that uses it.
- Latttice is stored in SINGLE_USER_MAP.
- We represent:
- - TOP by no entry in SIGNLE_USER_MAP
- - BOTTOM by BOTTOM in AUX pointer (to save lookups)
- - known single user by cgraph pointer in SINGLE_USER_MAP. */
- cgraph_node *
- meet (cgraph_node *function, varpool_node *var,
- hash_map<varpool_node *, cgraph_node *> &single_user_map)
- {
- struct cgraph_node *user, **f;
- if (var->aux == BOTTOM)
- return BOTTOM;
- f = single_user_map.get (var);
- if (!f)
- return function;
- user = *f;
- if (!function)
- return user;
- else if (function != user)
- return BOTTOM;
- else
- return function;
- }
- /* Propagation step of single-use dataflow.
- Check all uses of VNODE and see if they are used by single function FUNCTION.
- SINGLE_USER_MAP represents the dataflow lattice. */
- cgraph_node *
- propagate_single_user (varpool_node *vnode, cgraph_node *function,
- hash_map<varpool_node *, cgraph_node *> &single_user_map)
- {
- int i;
- struct ipa_ref *ref;
- gcc_assert (!vnode->externally_visible);
- /* If node is an alias, first meet with its target. */
- if (vnode->alias)
- function = meet (function, vnode->get_alias_target (), single_user_map);
- /* Check all users and see if they correspond to a single function. */
- for (i = 0; vnode->iterate_referring (i, ref) && function != BOTTOM; i++)
- {
- struct cgraph_node *cnode = dyn_cast <cgraph_node *> (ref->referring);
- if (cnode)
- {
- if (cnode->global.inlined_to)
- cnode = cnode->global.inlined_to;
- if (!function)
- function = cnode;
- else if (function != cnode)
- function = BOTTOM;
- }
- else
- function = meet (function, dyn_cast <varpool_node *> (ref->referring),
- single_user_map);
- }
- return function;
- }
- /* Pass setting used_by_single_function flag.
- This flag is set on variable when there is only one function that may
- possibly referr to it. */
- static unsigned int
- ipa_single_use (void)
- {
- varpool_node *first = (varpool_node *) (void *) 1;
- varpool_node *var;
- hash_map<varpool_node *, cgraph_node *> single_user_map;
- FOR_EACH_DEFINED_VARIABLE (var)
- if (!var->all_refs_explicit_p ())
- var->aux = BOTTOM;
- else
- {
- /* Enqueue symbol for dataflow. */
- var->aux = first;
- first = var;
- }
- /* The actual dataflow. */
- while (first != (void *) 1)
- {
- cgraph_node *user, *orig_user, **f;
- var = first;
- first = (varpool_node *)first->aux;
- f = single_user_map.get (var);
- if (f)
- orig_user = *f;
- else
- orig_user = NULL;
- user = propagate_single_user (var, orig_user, single_user_map);
- gcc_checking_assert (var->aux != BOTTOM);
- /* If user differs, enqueue all references. */
- if (user != orig_user)
- {
- unsigned int i;
- ipa_ref *ref;
- single_user_map.put (var, user);
- /* Enqueue all aliases for re-processing. */
- for (i = 0; var->iterate_direct_aliases (i, ref); i++)
- if (!ref->referring->aux)
- {
- ref->referring->aux = first;
- first = dyn_cast <varpool_node *> (ref->referring);
- }
- /* Enqueue all users for re-processing. */
- for (i = 0; var->iterate_reference (i, ref); i++)
- if (!ref->referred->aux
- && ref->referred->definition
- && is_a <varpool_node *> (ref->referred))
- {
- ref->referred->aux = first;
- first = dyn_cast <varpool_node *> (ref->referred);
- }
- /* If user is BOTTOM, just punt on this var. */
- if (user == BOTTOM)
- var->aux = BOTTOM;
- else
- var->aux = NULL;
- }
- else
- var->aux = NULL;
- }
- FOR_EACH_DEFINED_VARIABLE (var)
- {
- if (var->aux != BOTTOM)
- {
- #ifdef ENABLE_CHECKING
- /* Not having the single user known means that the VAR is
- unreachable. Either someone forgot to remove unreachable
- variables or the reachability here is wrong. */
- gcc_assert (single_user_map.get (var));
- #endif
- if (dump_file)
- {
- fprintf (dump_file, "Variable %s/%i is used by single function\n",
- var->name (), var->order);
- }
- var->used_by_single_function = true;
- }
- var->aux = NULL;
- }
- return 0;
- }
- namespace {
- const pass_data pass_data_ipa_single_use =
- {
- IPA_PASS, /* type */
- "single-use", /* name */
- OPTGROUP_NONE, /* optinfo_flags */
- TV_CGRAPHOPT, /* tv_id */
- 0, /* properties_required */
- 0, /* properties_provided */
- 0, /* properties_destroyed */
- 0, /* todo_flags_start */
- 0, /* todo_flags_finish */
- };
- class pass_ipa_single_use : public ipa_opt_pass_d
- {
- public:
- pass_ipa_single_use (gcc::context *ctxt)
- : ipa_opt_pass_d (pass_data_ipa_single_use, ctxt,
- NULL, /* generate_summary */
- NULL, /* write_summary */
- NULL, /* read_summary */
- NULL, /* write_optimization_summary */
- NULL, /* read_optimization_summary */
- NULL, /* stmt_fixup */
- 0, /* function_transform_todo_flags_start */
- NULL, /* function_transform */
- NULL) /* variable_transform */
- {}
- /* opt_pass methods: */
- virtual bool gate (function *);
- virtual unsigned int execute (function *) { return ipa_single_use (); }
- }; // class pass_ipa_single_use
- bool
- pass_ipa_single_use::gate (function *)
- {
- return optimize;
- }
- } // anon namespace
- ipa_opt_pass_d *
- make_pass_ipa_single_use (gcc::context *ctxt)
- {
- return new pass_ipa_single_use (ctxt);
- }
|