mirrored from git://gcc.gnu.org/git/gcc.git
-
Notifications
You must be signed in to change notification settings - Fork 4.3k
/
cgraphunit.c
2633 lines (2246 loc) · 78.4 KB
/
cgraphunit.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/* Driver of optimization process
Copyright (C) 2003-2016 Free Software Foundation, Inc.
Contributed by Jan Hubicka
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/>. */
/* This module implements main driver of compilation process.
The main scope of this file is to act as an interface in between
tree based frontends and the backend.
The front-end is supposed to use following functionality:
- finalize_function
This function is called once front-end has parsed whole body of function
and it is certain that the function body nor the declaration will change.
(There is one exception needed for implementing GCC extern inline
function.)
- varpool_finalize_decl
This function has same behavior as the above but is used for static
variables.
- add_asm_node
Insert new toplevel ASM statement
- finalize_compilation_unit
This function is called once (source level) compilation unit is finalized
and it will no longer change.
The symbol table is constructed starting from the trivially needed
symbols finalized by the frontend. Functions are lowered into
GIMPLE representation and callgraph/reference lists are constructed.
Those are used to discover other necessary functions and variables.
At the end the bodies of unreachable functions are removed.
The function can be called multiple times when multiple source level
compilation units are combined.
- compile
This passes control to the back-end. Optimizations are performed and
final assembler is generated. This is done in the following way. Note
that with link time optimization the process is split into three
stages (compile time, linktime analysis and parallel linktime as
indicated bellow).
Compile time:
1) Inter-procedural optimization.
(ipa_passes)
This part is further split into:
a) early optimizations. These are local passes executed in
the topological order on the callgraph.
The purpose of early optimiations is to optimize away simple
things that may otherwise confuse IP analysis. Very simple
propagation across the callgraph is done i.e. to discover
functions without side effects and simple inlining is performed.
b) early small interprocedural passes.
Those are interprocedural passes executed only at compilation
time. These include, for example, transational memory lowering,
unreachable code removal and other simple transformations.
c) IP analysis stage. All interprocedural passes do their
analysis.
Interprocedural passes differ from small interprocedural
passes by their ability to operate across whole program
at linktime. Their analysis stage is performed early to
both reduce linking times and linktime memory usage by
not having to represent whole program in memory.
d) LTO sreaming. When doing LTO, everything important gets
streamed into the object file.
Compile time and or linktime analysis stage (WPA):
At linktime units gets streamed back and symbol table is
merged. Function bodies are not streamed in and not
available.
e) IP propagation stage. All IP passes execute their
IP propagation. This is done based on the earlier analysis
without having function bodies at hand.
f) Ltrans streaming. When doing WHOPR LTO, the program
is partitioned and streamed into multple object files.
Compile time and/or parallel linktime stage (ltrans)
Each of the object files is streamed back and compiled
separately. Now the function bodies becomes available
again.
2) Virtual clone materialization
(cgraph_materialize_clone)
IP passes can produce copies of existing functoins (such
as versioned clones or inline clones) without actually
manipulating their bodies by creating virtual clones in
the callgraph. At this time the virtual clones are
turned into real functions
3) IP transformation
All IP passes transform function bodies based on earlier
decision of the IP propagation.
4) late small IP passes
Simple IP passes working within single program partition.
5) Expansion
(expand_all_functions)
At this stage functions that needs to be output into
assembler are identified and compiled in topological order
6) Output of variables and aliases
Now it is known what variable references was not optimized
out and thus all variables are output to the file.
Note that with -fno-toplevel-reorder passes 5 and 6
are combined together in cgraph_output_in_order.
Finally there are functions to manipulate the callgraph from
backend.
- cgraph_add_new_function is used to add backend produced
functions introduced after the unit is finalized.
The functions are enqueue for later processing and inserted
into callgraph with cgraph_process_new_functions.
- cgraph_function_versioning
produces a copy of function into new one (a version)
and apply simple transformations
*/
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "backend.h"
#include "target.h"
#include "rtl.h"
#include "tree.h"
#include "gimple.h"
#include "cfghooks.h"
#include "regset.h" /* FIXME: For reg_obstack. */
#include "alloc-pool.h"
#include "tree-pass.h"
#include "stringpool.h"
#include "gimple-ssa.h"
#include "cgraph.h"
#include "coverage.h"
#include "lto-streamer.h"
#include "fold-const.h"
#include "varasm.h"
#include "stor-layout.h"
#include "output.h"
#include "cfgcleanup.h"
#include "gimple-fold.h"
#include "gimplify.h"
#include "gimple-iterator.h"
#include "gimplify-me.h"
#include "tree-cfg.h"
#include "tree-into-ssa.h"
#include "tree-ssa.h"
#include "langhooks.h"
#include "toplev.h"
#include "debug.h"
#include "symbol-summary.h"
#include "tree-vrp.h"
#include "ipa-prop.h"
#include "gimple-pretty-print.h"
#include "plugin.h"
#include "ipa-inline.h"
#include "ipa-utils.h"
#include "except.h"
#include "cfgloop.h"
#include "context.h"
#include "pass_manager.h"
#include "tree-nested.h"
#include "dbgcnt.h"
#include "tree-chkp.h"
#include "lto-section-names.h"
/* Queue of cgraph nodes scheduled to be added into cgraph. This is a
secondary queue used during optimization to accommodate passes that
may generate new functions that need to be optimized and expanded. */
vec<cgraph_node *> cgraph_new_nodes;
static void expand_all_functions (void);
static void mark_functions_to_output (void);
static void handle_alias_pairs (void);
/* Used for vtable lookup in thunk adjusting. */
static GTY (()) tree vtable_entry_type;
/* Determine if symbol declaration is needed. That is, visible to something
either outside this translation unit, something magic in the system
configury */
bool
symtab_node::needed_p (void)
{
/* Double check that no one output the function into assembly file
early. */
gcc_checking_assert (!DECL_ASSEMBLER_NAME_SET_P (decl)
|| !TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl)));
if (!definition)
return false;
if (DECL_EXTERNAL (decl))
return false;
/* If the user told us it is used, then it must be so. */
if (force_output)
return true;
/* ABI forced symbols are needed when they are external. */
if (forced_by_abi && TREE_PUBLIC (decl))
return true;
/* Keep constructors, destructors and virtual functions. */
if (TREE_CODE (decl) == FUNCTION_DECL
&& (DECL_STATIC_CONSTRUCTOR (decl) || DECL_STATIC_DESTRUCTOR (decl)))
return true;
/* Externally visible variables must be output. The exception is
COMDAT variables that must be output only when they are needed. */
if (TREE_PUBLIC (decl) && !DECL_COMDAT (decl))
return true;
return false;
}
/* Head and terminator of the queue of nodes to be processed while building
callgraph. */
static symtab_node symtab_terminator;
static symtab_node *queued_nodes = &symtab_terminator;
/* Add NODE to queue starting at QUEUED_NODES.
The queue is linked via AUX pointers and terminated by pointer to 1. */
static void
enqueue_node (symtab_node *node)
{
if (node->aux)
return;
gcc_checking_assert (queued_nodes);
node->aux = queued_nodes;
queued_nodes = node;
}
/* Process CGRAPH_NEW_FUNCTIONS and perform actions necessary to add these
functions into callgraph in a way so they look like ordinary reachable
functions inserted into callgraph already at construction time. */
void
symbol_table::process_new_functions (void)
{
tree fndecl;
if (!cgraph_new_nodes.exists ())
return;
handle_alias_pairs ();
/* Note that this queue may grow as its being processed, as the new
functions may generate new ones. */
for (unsigned i = 0; i < cgraph_new_nodes.length (); i++)
{
cgraph_node *node = cgraph_new_nodes[i];
fndecl = node->decl;
switch (state)
{
case CONSTRUCTION:
/* At construction time we just need to finalize function and move
it into reachable functions list. */
cgraph_node::finalize_function (fndecl, false);
call_cgraph_insertion_hooks (node);
enqueue_node (node);
break;
case IPA:
case IPA_SSA:
case IPA_SSA_AFTER_INLINING:
/* When IPA optimization already started, do all essential
transformations that has been already performed on the whole
cgraph but not on this function. */
gimple_register_cfg_hooks ();
if (!node->analyzed)
node->analyze ();
push_cfun (DECL_STRUCT_FUNCTION (fndecl));
if ((state == IPA_SSA || state == IPA_SSA_AFTER_INLINING)
&& !gimple_in_ssa_p (DECL_STRUCT_FUNCTION (fndecl)))
g->get_passes ()->execute_early_local_passes ();
else if (inline_summaries != NULL)
compute_inline_parameters (node, true);
free_dominance_info (CDI_POST_DOMINATORS);
free_dominance_info (CDI_DOMINATORS);
pop_cfun ();
call_cgraph_insertion_hooks (node);
break;
case EXPANSION:
/* Functions created during expansion shall be compiled
directly. */
node->process = 0;
call_cgraph_insertion_hooks (node);
node->expand ();
break;
default:
gcc_unreachable ();
break;
}
}
cgraph_new_nodes.release ();
}
/* As an GCC extension we allow redefinition of the function. The
semantics when both copies of bodies differ is not well defined.
We replace the old body with new body so in unit at a time mode
we always use new body, while in normal mode we may end up with
old body inlined into some functions and new body expanded and
inlined in others.
??? It may make more sense to use one body for inlining and other
body for expanding the function but this is difficult to do. */
void
cgraph_node::reset (void)
{
/* If process is set, then we have already begun whole-unit analysis.
This is *not* testing for whether we've already emitted the function.
That case can be sort-of legitimately seen with real function redefinition
errors. I would argue that the front end should never present us with
such a case, but don't enforce that for now. */
gcc_assert (!process);
/* Reset our data structures so we can analyze the function again. */
memset (&local, 0, sizeof (local));
memset (&global, 0, sizeof (global));
memset (&rtl, 0, sizeof (rtl));
analyzed = false;
definition = false;
alias = false;
transparent_alias = false;
weakref = false;
cpp_implicit_alias = false;
remove_callees ();
remove_all_references ();
}
/* Return true when there are references to the node. INCLUDE_SELF is
true if a self reference counts as a reference. */
bool
symtab_node::referred_to_p (bool include_self)
{
ipa_ref *ref = NULL;
/* See if there are any references at all. */
if (iterate_referring (0, ref))
return true;
/* For functions check also calls. */
cgraph_node *cn = dyn_cast <cgraph_node *> (this);
if (cn && cn->callers)
{
if (include_self)
return true;
for (cgraph_edge *e = cn->callers; e; e = e->next_caller)
if (e->caller != this)
return true;
}
return false;
}
/* DECL has been parsed. Take it, queue it, compile it at the whim of the
logic in effect. If NO_COLLECT is true, then our caller cannot stand to have
the garbage collector run at the moment. We would need to either create
a new GC context, or just not compile right now. */
void
cgraph_node::finalize_function (tree decl, bool no_collect)
{
cgraph_node *node = cgraph_node::get_create (decl);
if (node->definition)
{
/* Nested functions should only be defined once. */
gcc_assert (!DECL_CONTEXT (decl)
|| TREE_CODE (DECL_CONTEXT (decl)) != FUNCTION_DECL);
node->reset ();
node->local.redefined_extern_inline = true;
}
/* Set definition first before calling notice_global_symbol so that
it is available to notice_global_symbol. */
node->definition = true;
notice_global_symbol (decl);
node->lowered = DECL_STRUCT_FUNCTION (decl)->cfg != NULL;
/* With -fkeep-inline-functions we are keeping all inline functions except
for extern inline ones. */
if (flag_keep_inline_functions
&& DECL_DECLARED_INLINE_P (decl)
&& !DECL_EXTERNAL (decl)
&& !DECL_DISREGARD_INLINE_LIMITS (decl))
node->force_output = 1;
/* When not optimizing, also output the static functions. (see
PR24561), but don't do so for always_inline functions, functions
declared inline and nested functions. These were optimized out
in the original implementation and it is unclear whether we want
to change the behavior here. */
if (((!opt_for_fn (decl, optimize) || flag_keep_static_functions)
&& !node->cpp_implicit_alias
&& !DECL_DISREGARD_INLINE_LIMITS (decl)
&& !DECL_DECLARED_INLINE_P (decl)
&& !(DECL_CONTEXT (decl)
&& TREE_CODE (DECL_CONTEXT (decl)) == FUNCTION_DECL))
&& !DECL_COMDAT (decl) && !DECL_EXTERNAL (decl))
node->force_output = 1;
/* If we've not yet emitted decl, tell the debug info about it. */
if (!TREE_ASM_WRITTEN (decl))
(*debug_hooks->deferred_inline_function) (decl);
if (!no_collect)
ggc_collect ();
if (symtab->state == CONSTRUCTION
&& (node->needed_p () || node->referred_to_p ()))
enqueue_node (node);
}
/* Add the function FNDECL to the call graph.
Unlike finalize_function, this function is intended to be used
by middle end and allows insertion of new function at arbitrary point
of compilation. The function can be either in high, low or SSA form
GIMPLE.
The function is assumed to be reachable and have address taken (so no
API breaking optimizations are performed on it).
Main work done by this function is to enqueue the function for later
processing to avoid need the passes to be re-entrant. */
void
cgraph_node::add_new_function (tree fndecl, bool lowered)
{
gcc::pass_manager *passes = g->get_passes ();
cgraph_node *node;
if (dump_file)
{
struct function *fn = DECL_STRUCT_FUNCTION (fndecl);
const char *function_type = ((gimple_has_body_p (fndecl))
? (lowered
? (gimple_in_ssa_p (fn)
? "ssa gimple"
: "low gimple")
: "high gimple")
: "to-be-gimplified");
fprintf (dump_file,
"Added new %s function %s to callgraph\n",
function_type,
fndecl_name (fndecl));
}
switch (symtab->state)
{
case PARSING:
cgraph_node::finalize_function (fndecl, false);
break;
case CONSTRUCTION:
/* Just enqueue function to be processed at nearest occurrence. */
node = cgraph_node::get_create (fndecl);
if (lowered)
node->lowered = true;
cgraph_new_nodes.safe_push (node);
break;
case IPA:
case IPA_SSA:
case IPA_SSA_AFTER_INLINING:
case EXPANSION:
/* Bring the function into finalized state and enqueue for later
analyzing and compilation. */
node = cgraph_node::get_create (fndecl);
node->local.local = false;
node->definition = true;
node->force_output = true;
if (!lowered && symtab->state == EXPANSION)
{
push_cfun (DECL_STRUCT_FUNCTION (fndecl));
gimple_register_cfg_hooks ();
bitmap_obstack_initialize (NULL);
execute_pass_list (cfun, passes->all_lowering_passes);
passes->execute_early_local_passes ();
bitmap_obstack_release (NULL);
pop_cfun ();
lowered = true;
}
if (lowered)
node->lowered = true;
cgraph_new_nodes.safe_push (node);
break;
case FINISHED:
/* At the very end of compilation we have to do all the work up
to expansion. */
node = cgraph_node::create (fndecl);
if (lowered)
node->lowered = true;
node->definition = true;
node->analyze ();
push_cfun (DECL_STRUCT_FUNCTION (fndecl));
gimple_register_cfg_hooks ();
bitmap_obstack_initialize (NULL);
if (!gimple_in_ssa_p (DECL_STRUCT_FUNCTION (fndecl)))
g->get_passes ()->execute_early_local_passes ();
bitmap_obstack_release (NULL);
pop_cfun ();
node->expand ();
break;
default:
gcc_unreachable ();
}
/* Set a personality if required and we already passed EH lowering. */
if (lowered
&& (function_needs_eh_personality (DECL_STRUCT_FUNCTION (fndecl))
== eh_personality_lang))
DECL_FUNCTION_PERSONALITY (fndecl) = lang_hooks.eh_personality ();
}
/* Analyze the function scheduled to be output. */
void
cgraph_node::analyze (void)
{
tree decl = this->decl;
location_t saved_loc = input_location;
input_location = DECL_SOURCE_LOCATION (decl);
if (thunk.thunk_p)
{
cgraph_node *t = cgraph_node::get (thunk.alias);
create_edge (t, NULL, 0, CGRAPH_FREQ_BASE);
callees->can_throw_external = !TREE_NOTHROW (t->decl);
/* Target code in expand_thunk may need the thunk's target
to be analyzed, so recurse here. */
if (!t->analyzed)
t->analyze ();
if (t->alias)
{
t = t->get_alias_target ();
if (!t->analyzed)
t->analyze ();
}
if (!expand_thunk (false, false))
{
thunk.alias = NULL;
return;
}
thunk.alias = NULL;
}
if (alias)
resolve_alias (cgraph_node::get (alias_target), transparent_alias);
else if (dispatcher_function)
{
/* Generate the dispatcher body of multi-versioned functions. */
cgraph_function_version_info *dispatcher_version_info
= function_version ();
if (dispatcher_version_info != NULL
&& (dispatcher_version_info->dispatcher_resolver
== NULL_TREE))
{
tree resolver = NULL_TREE;
gcc_assert (targetm.generate_version_dispatcher_body);
resolver = targetm.generate_version_dispatcher_body (this);
gcc_assert (resolver != NULL_TREE);
}
}
else
{
push_cfun (DECL_STRUCT_FUNCTION (decl));
assign_assembler_name_if_neeeded (decl);
/* Make sure to gimplify bodies only once. During analyzing a
function we lower it, which will require gimplified nested
functions, so we can end up here with an already gimplified
body. */
if (!gimple_has_body_p (decl))
gimplify_function_tree (decl);
/* Lower the function. */
if (!lowered)
{
if (nested)
lower_nested_functions (decl);
gcc_assert (!nested);
gimple_register_cfg_hooks ();
bitmap_obstack_initialize (NULL);
execute_pass_list (cfun, g->get_passes ()->all_lowering_passes);
free_dominance_info (CDI_POST_DOMINATORS);
free_dominance_info (CDI_DOMINATORS);
compact_blocks ();
bitmap_obstack_release (NULL);
lowered = true;
}
pop_cfun ();
}
analyzed = true;
input_location = saved_loc;
}
/* C++ frontend produce same body aliases all over the place, even before PCH
gets streamed out. It relies on us linking the aliases with their function
in order to do the fixups, but ipa-ref is not PCH safe. Consequentely we
first produce aliases without links, but once C++ FE is sure he won't sream
PCH we build the links via this function. */
void
symbol_table::process_same_body_aliases (void)
{
symtab_node *node;
FOR_EACH_SYMBOL (node)
if (node->cpp_implicit_alias && !node->analyzed)
node->resolve_alias
(TREE_CODE (node->alias_target) == VAR_DECL
? (symtab_node *)varpool_node::get_create (node->alias_target)
: (symtab_node *)cgraph_node::get_create (node->alias_target));
cpp_implicit_aliases_done = true;
}
/* Process attributes common for vars and functions. */
static void
process_common_attributes (symtab_node *node, tree decl)
{
tree weakref = lookup_attribute ("weakref", DECL_ATTRIBUTES (decl));
if (weakref && !lookup_attribute ("alias", DECL_ATTRIBUTES (decl)))
{
warning_at (DECL_SOURCE_LOCATION (decl), OPT_Wattributes,
"%<weakref%> attribute should be accompanied with"
" an %<alias%> attribute");
DECL_WEAK (decl) = 0;
DECL_ATTRIBUTES (decl) = remove_attribute ("weakref",
DECL_ATTRIBUTES (decl));
}
if (lookup_attribute ("no_reorder", DECL_ATTRIBUTES (decl)))
node->no_reorder = 1;
}
/* Look for externally_visible and used attributes and mark cgraph nodes
accordingly.
We cannot mark the nodes at the point the attributes are processed (in
handle_*_attribute) because the copy of the declarations available at that
point may not be canonical. For example, in:
void f();
void f() __attribute__((used));
the declaration we see in handle_used_attribute will be the second
declaration -- but the front end will subsequently merge that declaration
with the original declaration and discard the second declaration.
Furthermore, we can't mark these nodes in finalize_function because:
void f() {}
void f() __attribute__((externally_visible));
is valid.
So, we walk the nodes at the end of the translation unit, applying the
attributes at that point. */
static void
process_function_and_variable_attributes (cgraph_node *first,
varpool_node *first_var)
{
cgraph_node *node;
varpool_node *vnode;
for (node = symtab->first_function (); node != first;
node = symtab->next_function (node))
{
tree decl = node->decl;
if (DECL_PRESERVE_P (decl))
node->mark_force_output ();
else if (lookup_attribute ("externally_visible", DECL_ATTRIBUTES (decl)))
{
if (! TREE_PUBLIC (node->decl))
warning_at (DECL_SOURCE_LOCATION (node->decl), OPT_Wattributes,
"%<externally_visible%>"
" attribute have effect only on public objects");
}
if (lookup_attribute ("weakref", DECL_ATTRIBUTES (decl))
&& (node->definition && !node->alias))
{
warning_at (DECL_SOURCE_LOCATION (node->decl), OPT_Wattributes,
"%<weakref%> attribute ignored"
" because function is defined");
DECL_WEAK (decl) = 0;
DECL_ATTRIBUTES (decl) = remove_attribute ("weakref",
DECL_ATTRIBUTES (decl));
}
if (lookup_attribute ("always_inline", DECL_ATTRIBUTES (decl))
&& !DECL_DECLARED_INLINE_P (decl)
/* redefining extern inline function makes it DECL_UNINLINABLE. */
&& !DECL_UNINLINABLE (decl))
warning_at (DECL_SOURCE_LOCATION (decl), OPT_Wattributes,
"always_inline function might not be inlinable");
process_common_attributes (node, decl);
}
for (vnode = symtab->first_variable (); vnode != first_var;
vnode = symtab->next_variable (vnode))
{
tree decl = vnode->decl;
if (DECL_EXTERNAL (decl)
&& DECL_INITIAL (decl))
varpool_node::finalize_decl (decl);
if (DECL_PRESERVE_P (decl))
vnode->force_output = true;
else if (lookup_attribute ("externally_visible", DECL_ATTRIBUTES (decl)))
{
if (! TREE_PUBLIC (vnode->decl))
warning_at (DECL_SOURCE_LOCATION (vnode->decl), OPT_Wattributes,
"%<externally_visible%>"
" attribute have effect only on public objects");
}
if (lookup_attribute ("weakref", DECL_ATTRIBUTES (decl))
&& vnode->definition
&& DECL_INITIAL (decl))
{
warning_at (DECL_SOURCE_LOCATION (vnode->decl), OPT_Wattributes,
"%<weakref%> attribute ignored"
" because variable is initialized");
DECL_WEAK (decl) = 0;
DECL_ATTRIBUTES (decl) = remove_attribute ("weakref",
DECL_ATTRIBUTES (decl));
}
process_common_attributes (vnode, decl);
}
}
/* Mark DECL as finalized. By finalizing the declaration, frontend instruct the
middle end to output the variable to asm file, if needed or externally
visible. */
void
varpool_node::finalize_decl (tree decl)
{
varpool_node *node = varpool_node::get_create (decl);
gcc_assert (TREE_STATIC (decl) || DECL_EXTERNAL (decl));
if (node->definition)
return;
/* Set definition first before calling notice_global_symbol so that
it is available to notice_global_symbol. */
node->definition = true;
notice_global_symbol (decl);
if (TREE_THIS_VOLATILE (decl) || DECL_PRESERVE_P (decl)
/* Traditionally we do not eliminate static variables when not
optimizing and when not doing toplevel reoder. */
|| node->no_reorder
|| ((!flag_toplevel_reorder
&& !DECL_COMDAT (node->decl)
&& !DECL_ARTIFICIAL (node->decl))))
node->force_output = true;
if (symtab->state == CONSTRUCTION
&& (node->needed_p () || node->referred_to_p ()))
enqueue_node (node);
if (symtab->state >= IPA_SSA)
node->analyze ();
/* Some frontends produce various interface variables after compilation
finished. */
if (symtab->state == FINISHED
|| (!flag_toplevel_reorder
&& symtab->state == EXPANSION))
node->assemble_decl ();
if (DECL_INITIAL (decl))
chkp_register_var_initializer (decl);
}
/* EDGE is an polymorphic call. Mark all possible targets as reachable
and if there is only one target, perform trivial devirtualization.
REACHABLE_CALL_TARGETS collects target lists we already walked to
avoid udplicate work. */
static void
walk_polymorphic_call_targets (hash_set<void *> *reachable_call_targets,
cgraph_edge *edge)
{
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))
{
if (symtab->dump_file)
dump_possible_polymorphic_call_targets
(symtab->dump_file, edge);
for (i = 0; i < targets.length (); 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 (targets[i]->definition
&& TREE_CODE
(TREE_TYPE (targets[i]->decl))
== METHOD_TYPE
&& !type_in_anonymous_namespace_p
(TYPE_METHOD_BASETYPE (TREE_TYPE (targets[i]->decl))))
enqueue_node (targets[i]);
}
}
/* 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;
if (targets.length () == 1)
target = targets[0];
else
target = cgraph_node::create
(builtin_decl_implicit (BUILT_IN_UNREACHABLE));
if (symtab->dump_file)
{
fprintf (symtab->dump_file,
"Devirtualizing call: ");
print_gimple_stmt (symtab->dump_file,
edge->call_stmt, 0,
TDF_SLIM);
}
if (dump_enabled_p ())
{
location_t locus = gimple_location_safe (edge->call_stmt);
dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, locus,
"devirtualizing call in %s to %s\n",
edge->caller->name (), target->name ());
}
edge->make_direct (target);
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);
if (symtab->dump_file)
{
fprintf (symtab->dump_file,
"Devirtualized as: ");
print_gimple_stmt (symtab->dump_file,
edge->call_stmt, 0,
TDF_SLIM);
}
}
}
}
/* Issue appropriate warnings for the global declaration DECL. */
static void
check_global_declaration (symtab_node *snode)
{
const char *decl_file;
tree decl = snode->decl;
/* Warn about any function declared static but not defined. We don't
warn about variables, because many programs have static variables
that exist only to get some text into the object file. */
if (TREE_CODE (decl) == FUNCTION_DECL
&& DECL_INITIAL (decl) == 0
&& DECL_EXTERNAL (decl)
&& ! DECL_ARTIFICIAL (decl)
&& ! TREE_NO_WARNING (decl)
&& ! TREE_PUBLIC (decl)
&& (warn_unused_function
|| snode->referred_to_p (/*include_self=*/false)))
{
if (snode->referred_to_p (/*include_self=*/false))
pedwarn (input_location, 0, "%q+F used but never defined", decl);
else
warning (OPT_Wunused_function, "%q+F declared %<static%> but never defined", decl);
/* This symbol is effectively an "extern" declaration now. */
TREE_PUBLIC (decl) = 1;
}
/* Warn about static fns or vars defined but not used. */
if (((warn_unused_function && TREE_CODE (decl) == FUNCTION_DECL)
|| (((warn_unused_variable && ! TREE_READONLY (decl))
|| (warn_unused_const_variable > 0 && TREE_READONLY (decl)
&& (warn_unused_const_variable == 2
|| (main_input_filename != NULL
&& (decl_file = DECL_SOURCE_FILE (decl)) != NULL
&& filename_cmp (main_input_filename,
decl_file) == 0))))
&& TREE_CODE (decl) == VAR_DECL))
&& ! DECL_IN_SYSTEM_HEADER (decl)
&& ! snode->referred_to_p (/*include_self=*/false)
/* This TREE_USED check is needed in addition to referred_to_p
above, because the `__unused__' attribute is not being
considered for referred_to_p. */
&& ! TREE_USED (decl)
/* The TREE_USED bit for file-scope decls is kept in the identifier,
to handle multiple external decls in different scopes. */
&& ! (DECL_NAME (decl) && TREE_USED (DECL_NAME (decl)))
&& ! DECL_EXTERNAL (decl)
&& ! DECL_ARTIFICIAL (decl)
&& ! DECL_ABSTRACT_ORIGIN (decl)
&& ! TREE_PUBLIC (decl)
/* A volatile variable might be used in some non-obvious way. */
&& (! VAR_P (decl) || ! TREE_THIS_VOLATILE (decl))
/* Global register variables must be declared to reserve them. */
&& ! (TREE_CODE (decl) == VAR_DECL && DECL_REGISTER (decl))
/* Global ctors and dtors are called by the runtime. */
&& (TREE_CODE (decl) != FUNCTION_DECL
|| (!DECL_STATIC_CONSTRUCTOR (decl)
&& !DECL_STATIC_DESTRUCTOR (decl)))
/* Otherwise, ask the language. */
&& lang_hooks.decls.warn_unused_global (decl))
warning_at (DECL_SOURCE_LOCATION (decl),
(TREE_CODE (decl) == FUNCTION_DECL)
? OPT_Wunused_function
: (TREE_READONLY (decl)
? OPT_Wunused_const_variable_
: OPT_Wunused_variable),
"%qD defined but not used", decl);
}
/* Discover all functions and variables that are trivially needed, analyze
them as well as all functions and variables referred by them */
static cgraph_node *first_analyzed;
static varpool_node *first_analyzed_var;
/* FIRST_TIME is set to TRUE for the first time we are called for a
translation unit from finalize_compilation_unit() or false
otherwise. */
static void
analyze_functions (bool first_time)
{
/* Keep track of already processed nodes when called multiple times for
intermodule optimization. */
cgraph_node *first_handled = first_analyzed;