]>
Commit | Line | Data |
---|---|---|
ca29da43 NS |
1 | /* Read and write coverage files, and associated functionality. |
2 | Copyright (C) 1990, 1991, 1992, 1993, 1994, 1996, 1997, 1998, 1999, | |
5366b186 | 3 | 2000, 2001, 2003, 2004, 2005, 2007, 2008, 2009, 2010, 2011 |
1da2ed5f | 4 | Free Software Foundation, Inc. |
ca29da43 NS |
5 | Contributed by James E. Wilson, UC Berkeley/Cygnus Support; |
6 | based on some ideas from Dain Samples of UC Berkeley. | |
7 | Further mangling by Bob Manson, Cygnus Support. | |
8 | Further mangled by Nathan Sidwell, CodeSourcery | |
9 | ||
10 | This file is part of GCC. | |
11 | ||
12 | GCC is free software; you can redistribute it and/or modify it under | |
13 | the terms of the GNU General Public License as published by the Free | |
9dcd6f09 | 14 | Software Foundation; either version 3, or (at your option) any later |
ca29da43 NS |
15 | version. |
16 | ||
17 | GCC is distributed in the hope that it will be useful, but WITHOUT ANY | |
18 | WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
19 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
20 | for more details. | |
21 | ||
22 | You should have received a copy of the GNU General Public License | |
9dcd6f09 NC |
23 | along with GCC; see the file COPYING3. If not see |
24 | <http://www.gnu.org/licenses/>. */ | |
ca29da43 NS |
25 | |
26 | ||
27 | #define GCOV_LINKAGE | |
28 | ||
29 | #include "config.h" | |
30 | #include "system.h" | |
31 | #include "coretypes.h" | |
32 | #include "tm.h" | |
33 | #include "rtl.h" | |
34 | #include "tree.h" | |
35 | #include "flags.h" | |
36 | #include "output.h" | |
37 | #include "regs.h" | |
38 | #include "expr.h" | |
39 | #include "function.h" | |
2d1a4cc1 | 40 | #include "basic-block.h" |
ca29da43 | 41 | #include "toplev.h" |
5773a50f | 42 | #include "tm_p.h" |
ca29da43 | 43 | #include "ggc.h" |
ca29da43 | 44 | #include "coverage.h" |
ca29da43 NS |
45 | #include "langhooks.h" |
46 | #include "hashtab.h" | |
c9b9aa64 RH |
47 | #include "tree-iterator.h" |
48 | #include "cgraph.h" | |
27624b9e | 49 | #include "tree-pass.h" |
1da2ed5f | 50 | #include "diagnostic-core.h" |
650cfcab | 51 | #include "intl.h" |
ba78087b | 52 | #include "filenames.h" |
ca29da43 | 53 | |
10adac51 | 54 | #include "gcov-io.h" |
ca29da43 NS |
55 | #include "gcov-io.c" |
56 | ||
b724567e | 57 | struct GTY((chain_next ("%h.next"))) coverage_data |
ca29da43 | 58 | { |
b724567e | 59 | struct coverage_data *next; /* next function */ |
159b3be1 | 60 | unsigned ident; /* function ident */ |
10adac51 XDL |
61 | unsigned lineno_checksum; /* function lineno checksum */ |
62 | unsigned cfg_checksum; /* function cfg checksum */ | |
5366b186 NS |
63 | tree fn_decl; /* the function decl */ |
64 | tree ctr_vars[GCOV_COUNTERS]; /* counter variables. */ | |
ca29da43 NS |
65 | }; |
66 | ||
67 | /* Counts information for a function. */ | |
68 | typedef struct counts_entry | |
69 | { | |
70 | /* We hash by */ | |
796621e8 | 71 | unsigned ident; |
cdb23767 | 72 | unsigned ctr; |
159b3be1 | 73 | |
ca29da43 | 74 | /* Store */ |
10adac51 XDL |
75 | unsigned lineno_checksum; |
76 | unsigned cfg_checksum; | |
ca29da43 | 77 | gcov_type *counts; |
cdb23767 | 78 | struct gcov_ctr_summary summary; |
ca29da43 NS |
79 | } counts_entry_t; |
80 | ||
b724567e NS |
81 | static GTY(()) struct coverage_data *functions_head = 0; |
82 | static struct coverage_data **functions_tail = &functions_head; | |
6d70e6be | 83 | static unsigned no_coverage = 0; |
ca29da43 | 84 | |
cdb23767 NS |
85 | /* Cumulative counter information for whole program. */ |
86 | static unsigned prg_ctr_mask; /* Mask of counter types generated. */ | |
ca29da43 | 87 | |
cdb23767 | 88 | /* Counter information for current function. */ |
71c0e7fc | 89 | static unsigned fn_ctr_mask; /* Mask of counters used. */ |
5366b186 | 90 | static GTY(()) tree fn_v_ctrs[GCOV_COUNTERS]; /* counter variables. */ |
6d70e6be NS |
91 | static unsigned fn_n_ctrs[GCOV_COUNTERS]; /* Counters allocated. */ |
92 | static unsigned fn_b_ctrs[GCOV_COUNTERS]; /* Allocation base. */ | |
ca29da43 | 93 | |
b724567e NS |
94 | /* Coverage info VAR_DECL and function info type nodes. */ |
95 | static GTY(()) tree gcov_info_var; | |
96 | static GTY(()) tree gcov_fn_info_type; | |
97 | static GTY(()) tree gcov_fn_info_ptr_type; | |
98 | ||
99 | /* Name of the output file for coverage output file. If this is NULL | |
100 | we're not writing to the notes file. */ | |
ca29da43 | 101 | static char *bbg_file_name; |
ca29da43 NS |
102 | |
103 | /* Name of the count data file. */ | |
104 | static char *da_file_name; | |
105 | ||
106 | /* Hash table of count data. */ | |
107 | static htab_t counts_hash = NULL; | |
108 | ||
09780dfb | 109 | /* The names of merge functions for counters. */ |
9b514d25 NS |
110 | static const char *const ctr_merge_functions[GCOV_COUNTERS] = GCOV_MERGE_FUNCTIONS; |
111 | static const char *const ctr_names[GCOV_COUNTERS] = GCOV_COUNTER_NAMES; | |
09780dfb | 112 | |
ca29da43 | 113 | /* Forward declarations. */ |
159b3be1 AJ |
114 | static hashval_t htab_counts_entry_hash (const void *); |
115 | static int htab_counts_entry_eq (const void *, const void *); | |
116 | static void htab_counts_entry_del (void *); | |
117 | static void read_counts_file (void); | |
5366b186 NS |
118 | static tree build_var (tree, tree, int); |
119 | static void build_fn_info_type (tree, unsigned, tree); | |
b724567e NS |
120 | static void build_info_type (tree, tree); |
121 | static tree build_fn_info (const struct coverage_data *, tree, tree); | |
122 | static tree build_info (tree, tree); | |
123 | static bool coverage_obj_init (void); | |
124 | static VEC(constructor_elt,gc) *coverage_obj_fn | |
125 | (VEC(constructor_elt,gc) *, tree, struct coverage_data const *); | |
126 | static void coverage_obj_finish (VEC(constructor_elt,gc) *); | |
251e2ff2 RS |
127 | \f |
128 | /* Return the type node for gcov_type. */ | |
129 | ||
130 | tree | |
131 | get_gcov_type (void) | |
132 | { | |
133 | return lang_hooks.types.type_for_size (GCOV_TYPE_SIZE, false); | |
134 | } | |
135 | ||
136 | /* Return the type node for gcov_unsigned_t. */ | |
ca29da43 | 137 | |
251e2ff2 RS |
138 | static tree |
139 | get_gcov_unsigned_t (void) | |
140 | { | |
141 | return lang_hooks.types.type_for_size (32, true); | |
142 | } | |
ca29da43 NS |
143 | \f |
144 | static hashval_t | |
159b3be1 | 145 | htab_counts_entry_hash (const void *of) |
ca29da43 | 146 | { |
f883e0a7 | 147 | const counts_entry_t *const entry = (const counts_entry_t *) of; |
ca29da43 | 148 | |
796621e8 | 149 | return entry->ident * GCOV_COUNTERS + entry->ctr; |
ca29da43 NS |
150 | } |
151 | ||
152 | static int | |
159b3be1 | 153 | htab_counts_entry_eq (const void *of1, const void *of2) |
ca29da43 | 154 | { |
f883e0a7 KG |
155 | const counts_entry_t *const entry1 = (const counts_entry_t *) of1; |
156 | const counts_entry_t *const entry2 = (const counts_entry_t *) of2; | |
ca29da43 | 157 | |
796621e8 | 158 | return entry1->ident == entry2->ident && entry1->ctr == entry2->ctr; |
ca29da43 NS |
159 | } |
160 | ||
161 | static void | |
159b3be1 | 162 | htab_counts_entry_del (void *of) |
ca29da43 | 163 | { |
f883e0a7 | 164 | counts_entry_t *const entry = (counts_entry_t *) of; |
ca29da43 | 165 | |
ca29da43 NS |
166 | free (entry->counts); |
167 | free (entry); | |
168 | } | |
169 | ||
170 | /* Read in the counts file, if available. */ | |
171 | ||
172 | static void | |
159b3be1 | 173 | read_counts_file (void) |
ca29da43 | 174 | { |
9b514d25 | 175 | gcov_unsigned_t fn_ident = 0; |
5366b186 NS |
176 | struct gcov_summary summary; |
177 | unsigned new_summary = 1; | |
7d63a2fa | 178 | gcov_unsigned_t tag; |
24a4a033 | 179 | int is_error = 0; |
10adac51 XDL |
180 | unsigned lineno_checksum = 0; |
181 | unsigned cfg_checksum = 0; | |
7d63a2fa | 182 | |
ca29da43 NS |
183 | if (!gcov_open (da_file_name, 1)) |
184 | return; | |
159b3be1 | 185 | |
160e2e4f | 186 | if (!gcov_magic (gcov_read_unsigned (), GCOV_DATA_MAGIC)) |
ca29da43 | 187 | { |
d4ee4d25 | 188 | warning (0, "%qs is not a gcov data file", da_file_name); |
ca29da43 NS |
189 | gcov_close (); |
190 | return; | |
191 | } | |
160e2e4f | 192 | else if ((tag = gcov_read_unsigned ()) != GCOV_VERSION) |
ca29da43 | 193 | { |
330d2e2a NS |
194 | char v[4], e[4]; |
195 | ||
196 | GCOV_UNSIGNED2STRING (v, tag); | |
197 | GCOV_UNSIGNED2STRING (e, GCOV_VERSION); | |
159b3be1 | 198 | |
d4ee4d25 | 199 | warning (0, "%qs is version %q.*s, expected version %q.*s", |
cb83302c | 200 | da_file_name, 4, v, 4, e); |
ca29da43 NS |
201 | gcov_close (); |
202 | return; | |
203 | } | |
159b3be1 | 204 | |
4ed43216 | 205 | /* Read and discard the stamp. */ |
dd486eb2 | 206 | gcov_read_unsigned (); |
b8698a0f | 207 | |
ca29da43 NS |
208 | counts_hash = htab_create (10, |
209 | htab_counts_entry_hash, htab_counts_entry_eq, | |
210 | htab_counts_entry_del); | |
7d63a2fa | 211 | while ((tag = gcov_read_unsigned ())) |
ca29da43 | 212 | { |
7d63a2fa | 213 | gcov_unsigned_t length; |
9b514d25 | 214 | gcov_position_t offset; |
159b3be1 | 215 | |
ca29da43 NS |
216 | length = gcov_read_unsigned (); |
217 | offset = gcov_position (); | |
218 | if (tag == GCOV_TAG_FUNCTION) | |
219 | { | |
5366b186 | 220 | if (length) |
ca29da43 | 221 | { |
5366b186 NS |
222 | fn_ident = gcov_read_unsigned (); |
223 | lineno_checksum = gcov_read_unsigned (); | |
224 | cfg_checksum = gcov_read_unsigned (); | |
ca29da43 | 225 | } |
5366b186 NS |
226 | else |
227 | fn_ident = lineno_checksum = cfg_checksum = 0; | |
228 | new_summary = 1; | |
ca29da43 NS |
229 | } |
230 | else if (tag == GCOV_TAG_PROGRAM_SUMMARY) | |
231 | { | |
5366b186 NS |
232 | struct gcov_summary sum; |
233 | unsigned ix; | |
159b3be1 | 234 | |
5366b186 NS |
235 | if (new_summary) |
236 | memset (&summary, 0, sizeof (summary)); | |
159b3be1 | 237 | |
5366b186 NS |
238 | gcov_read_summary (&sum); |
239 | for (ix = 0; ix != GCOV_COUNTERS_SUMMABLE; ix++) | |
240 | { | |
241 | summary.ctrs[ix].runs += sum.ctrs[ix].runs; | |
242 | summary.ctrs[ix].sum_all += sum.ctrs[ix].sum_all; | |
243 | if (summary.ctrs[ix].run_max < sum.ctrs[ix].run_max) | |
244 | summary.ctrs[ix].run_max = sum.ctrs[ix].run_max; | |
245 | summary.ctrs[ix].sum_max += sum.ctrs[ix].sum_max; | |
ca29da43 | 246 | } |
5366b186 | 247 | new_summary = 0; |
ca29da43 | 248 | } |
796621e8 | 249 | else if (GCOV_TAG_IS_COUNTER (tag) && fn_ident) |
ca29da43 NS |
250 | { |
251 | counts_entry_t **slot, *entry, elt; | |
330d2e2a | 252 | unsigned n_counts = GCOV_TAG_COUNTER_NUM (length); |
ca29da43 NS |
253 | unsigned ix; |
254 | ||
796621e8 | 255 | elt.ident = fn_ident; |
cdb23767 | 256 | elt.ctr = GCOV_COUNTER_FOR_TAG (tag); |
ca29da43 NS |
257 | |
258 | slot = (counts_entry_t **) htab_find_slot | |
259 | (counts_hash, &elt, INSERT); | |
260 | entry = *slot; | |
261 | if (!entry) | |
262 | { | |
5ed6ace5 | 263 | *slot = entry = XCNEW (counts_entry_t); |
10adac51 | 264 | entry->ident = fn_ident; |
cdb23767 | 265 | entry->ctr = elt.ctr; |
10adac51 XDL |
266 | entry->lineno_checksum = lineno_checksum; |
267 | entry->cfg_checksum = cfg_checksum; | |
5366b186 | 268 | entry->summary = summary.ctrs[elt.ctr]; |
cdb23767 | 269 | entry->summary.num = n_counts; |
5ed6ace5 | 270 | entry->counts = XCNEWVEC (gcov_type, n_counts); |
ca29da43 | 271 | } |
10adac51 XDL |
272 | else if (entry->lineno_checksum != lineno_checksum |
273 | || entry->cfg_checksum != cfg_checksum) | |
ca29da43 | 274 | { |
10adac51 XDL |
275 | error ("Profile data for function %u is corrupted", fn_ident); |
276 | error ("checksum is (%x,%x) instead of (%x,%x)", | |
277 | entry->lineno_checksum, entry->cfg_checksum, | |
278 | lineno_checksum, cfg_checksum); | |
24a4a033 JH |
279 | htab_delete (counts_hash); |
280 | break; | |
281 | } | |
282 | else if (entry->summary.num != n_counts) | |
283 | { | |
10adac51 | 284 | error ("Profile data for function %u is corrupted", fn_ident); |
24a4a033 | 285 | error ("number of counters is %d instead of %d", entry->summary.num, n_counts); |
ca29da43 NS |
286 | htab_delete (counts_hash); |
287 | break; | |
288 | } | |
9b514d25 NS |
289 | else if (elt.ctr >= GCOV_COUNTERS_SUMMABLE) |
290 | { | |
24a4a033 JH |
291 | error ("cannot merge separate %s counters for function %u", |
292 | ctr_names[elt.ctr], fn_ident); | |
9b514d25 NS |
293 | goto skip_merge; |
294 | } | |
5366b186 | 295 | else |
ca29da43 | 296 | { |
5366b186 NS |
297 | entry->summary.runs += summary.ctrs[elt.ctr].runs; |
298 | entry->summary.sum_all += summary.ctrs[elt.ctr].sum_all; | |
299 | if (entry->summary.run_max < summary.ctrs[elt.ctr].run_max) | |
300 | entry->summary.run_max = summary.ctrs[elt.ctr].run_max; | |
301 | entry->summary.sum_max += summary.ctrs[elt.ctr].sum_max; | |
ca29da43 NS |
302 | } |
303 | for (ix = 0; ix != n_counts; ix++) | |
304 | entry->counts[ix] += gcov_read_counter (); | |
9b514d25 | 305 | skip_merge:; |
ca29da43 | 306 | } |
474f141e | 307 | gcov_sync (offset, length); |
24a4a033 | 308 | if ((is_error = gcov_is_error ())) |
00cf2913 | 309 | { |
971801ff | 310 | error (is_error < 0 ? "%qs has overflowed" : "%qs is corrupted", |
00cf2913 NS |
311 | da_file_name); |
312 | htab_delete (counts_hash); | |
313 | break; | |
314 | } | |
7d63a2fa | 315 | } |
159b3be1 | 316 | |
ca29da43 NS |
317 | gcov_close (); |
318 | } | |
319 | ||
320 | /* Returns the counters for a particular tag. */ | |
321 | ||
322 | gcov_type * | |
cdb23767 | 323 | get_coverage_counts (unsigned counter, unsigned expected, |
10adac51 | 324 | unsigned cfg_checksum, unsigned lineno_checksum, |
cdb23767 | 325 | const struct gcov_ctr_summary **summary) |
ca29da43 NS |
326 | { |
327 | counts_entry_t *entry, elt; | |
328 | ||
71c0e7fc | 329 | /* No hash table, no counts. */ |
ca29da43 NS |
330 | if (!counts_hash) |
331 | { | |
332 | static int warned = 0; | |
333 | ||
334 | if (!warned++) | |
1f5b3869 | 335 | inform (input_location, (flag_guess_branch_prob |
02307675 R |
336 | ? "file %s not found, execution counts estimated" |
337 | : "file %s not found, execution counts assumed to be zero"), | |
bbfff586 | 338 | da_file_name); |
ca29da43 NS |
339 | return NULL; |
340 | } | |
341 | ||
6d70e6be | 342 | elt.ident = current_function_funcdef_no + 1; |
cdb23767 | 343 | elt.ctr = counter; |
f883e0a7 | 344 | entry = (counts_entry_t *) htab_find (counts_hash, &elt); |
5366b186 NS |
345 | if (!entry || !entry->summary.num) |
346 | /* The function was not emitted, or is weak and not chosen in the | |
347 | final executable. Silently fail, because there's nothing we | |
348 | can do about it. */ | |
349 | return NULL; | |
350 | ||
10adac51 | 351 | if (entry->cfg_checksum != cfg_checksum |
16c1c158 | 352 | || entry->summary.num != expected) |
24a4a033 | 353 | { |
16c1c158 | 354 | static int warned = 0; |
650cfcab | 355 | bool warning_printed = false; |
4f1e4960 | 356 | tree id = DECL_ASSEMBLER_NAME (current_function_decl); |
16c1c158 | 357 | |
10adac51 XDL |
358 | warning_printed = |
359 | warning_at (input_location, OPT_Wcoverage_mismatch, | |
5366b186 | 360 | "the control flow of function %qE does not match " |
10adac51 | 361 | "its profile data (counter %qs)", id, ctr_names[counter]); |
650cfcab | 362 | if (warning_printed) |
16c1c158 | 363 | { |
5366b186 | 364 | inform (input_location, "use -Wno-error=coverage-mismatch to tolerate " |
10adac51 | 365 | "the mismatch but performance may drop if the function is hot"); |
650cfcab | 366 | |
1da2ed5f | 367 | if (!seen_error () |
650cfcab NV |
368 | && !warned++) |
369 | { | |
370 | inform (input_location, "coverage mismatch ignored"); | |
371 | inform (input_location, flag_guess_branch_prob | |
372 | ? G_("execution counts estimated") | |
373 | : G_("execution counts assumed to be zero")); | |
374 | if (!flag_guess_branch_prob) | |
375 | inform (input_location, | |
376 | "this can result in poorly optimized code"); | |
377 | } | |
16c1c158 RG |
378 | } |
379 | ||
380 | return NULL; | |
ca29da43 | 381 | } |
5366b186 NS |
382 | else if (entry->lineno_checksum != lineno_checksum) |
383 | { | |
b724567e | 384 | warning (0, "source locations for function %qE have changed," |
5366b186 NS |
385 | " the profile data may be out of date", |
386 | DECL_ASSEMBLER_NAME (current_function_decl)); | |
387 | } | |
159b3be1 | 388 | |
cdb23767 NS |
389 | if (summary) |
390 | *summary = &entry->summary; | |
ca29da43 NS |
391 | |
392 | return entry->counts; | |
393 | } | |
cdb23767 | 394 | |
6356f892 | 395 | /* Allocate NUM counters of type COUNTER. Returns nonzero if the |
6d70e6be | 396 | allocation succeeded. */ |
cdb23767 | 397 | |
6d70e6be NS |
398 | int |
399 | coverage_counter_alloc (unsigned counter, unsigned num) | |
cdb23767 | 400 | { |
6d70e6be NS |
401 | if (no_coverage) |
402 | return 0; | |
159b3be1 | 403 | |
6d70e6be NS |
404 | if (!num) |
405 | return 1; | |
159b3be1 | 406 | |
5366b186 | 407 | if (!fn_v_ctrs[counter]) |
cdb23767 | 408 | { |
5366b186 NS |
409 | tree array_type = build_array_type (get_gcov_type (), NULL_TREE); |
410 | ||
411 | fn_v_ctrs[counter] | |
412 | = build_var (current_function_decl, array_type, counter); | |
cdb23767 | 413 | } |
5366b186 | 414 | |
6d70e6be NS |
415 | fn_b_ctrs[counter] = fn_n_ctrs[counter]; |
416 | fn_n_ctrs[counter] += num; | |
5366b186 | 417 | |
6d70e6be NS |
418 | fn_ctr_mask |= 1 << counter; |
419 | return 1; | |
420 | } | |
cdb23767 | 421 | |
6de9cd9a DN |
422 | /* Generate a tree to access COUNTER NO. */ |
423 | ||
424 | tree | |
425 | tree_coverage_counter_ref (unsigned counter, unsigned no) | |
426 | { | |
070e3969 | 427 | tree gcov_type_node = get_gcov_type (); |
6de9cd9a | 428 | |
341c100f | 429 | gcc_assert (no < fn_n_ctrs[counter] - fn_b_ctrs[counter]); |
6de9cd9a | 430 | |
5366b186 NS |
431 | no += fn_b_ctrs[counter]; |
432 | ||
6de9cd9a | 433 | /* "no" here is an array index, scaled to bytes later. */ |
5366b186 | 434 | return build4 (ARRAY_REF, gcov_type_node, fn_v_ctrs[counter], |
f81b1a3d | 435 | build_int_cst (integer_type_node, no), NULL, NULL); |
6de9cd9a | 436 | } |
fc9161c1 RG |
437 | |
438 | /* Generate a tree to access the address of COUNTER NO. */ | |
439 | ||
440 | tree | |
441 | tree_coverage_counter_addr (unsigned counter, unsigned no) | |
442 | { | |
443 | tree gcov_type_node = get_gcov_type (); | |
444 | ||
445 | gcc_assert (no < fn_n_ctrs[counter] - fn_b_ctrs[counter]); | |
5366b186 | 446 | no += fn_b_ctrs[counter]; |
628c189e | 447 | |
fc9161c1 RG |
448 | /* "no" here is an array index, scaled to bytes later. */ |
449 | return build_fold_addr_expr (build4 (ARRAY_REF, gcov_type_node, | |
5366b186 | 450 | fn_v_ctrs[counter], |
f81b1a3d | 451 | build_int_cst (integer_type_node, no), |
fc9161c1 RG |
452 | NULL, NULL)); |
453 | } | |
ca29da43 | 454 | \f |
10adac51 | 455 | |
ca29da43 | 456 | /* Generate a checksum for a string. CHKSUM is the current |
71c0e7fc | 457 | checksum. */ |
ca29da43 NS |
458 | |
459 | static unsigned | |
20c361f3 | 460 | coverage_checksum_string (unsigned chksum, const char *string) |
ca29da43 | 461 | { |
20c361f3 JH |
462 | int i; |
463 | char *dup = NULL; | |
464 | ||
465 | /* Look for everything that looks if it were produced by | |
5880f14f | 466 | get_file_function_name and zero out the second part |
20c361f3 JH |
467 | that may result from flag_random_seed. This is not critical |
468 | as the checksums are used only for sanity checking. */ | |
469 | for (i = 0; string[i]; i++) | |
ca29da43 | 470 | { |
2b557972 JH |
471 | int offset = 0; |
472 | if (!strncmp (string + i, "_GLOBAL__N_", 11)) | |
473 | offset = 11; | |
20c361f3 | 474 | if (!strncmp (string + i, "_GLOBAL__", 9)) |
2b557972 JH |
475 | offset = 9; |
476 | ||
477 | /* C++ namespaces do have scheme: | |
478 | _GLOBAL__N_<filename>_<wrongmagicnumber>_<magicnumber>functionname | |
479 | since filename might contain extra underscores there seems | |
480 | to be no better chance then walk all possible offsets looking | |
fa10beec | 481 | for magicnumber. */ |
2b557972 | 482 | if (offset) |
1f651229 JH |
483 | { |
484 | for (i = i + offset; string[i]; i++) | |
485 | if (string[i]=='_') | |
486 | { | |
487 | int y; | |
488 | ||
489 | for (y = 1; y < 9; y++) | |
490 | if (!(string[i + y] >= '0' && string[i + y] <= '9') | |
491 | && !(string[i + y] >= 'A' && string[i + y] <= 'F')) | |
492 | break; | |
493 | if (y != 9 || string[i + 9] != '_') | |
494 | continue; | |
495 | for (y = 10; y < 18; y++) | |
496 | if (!(string[i + y] >= '0' && string[i + y] <= '9') | |
497 | && !(string[i + y] >= 'A' && string[i + y] <= 'F')) | |
498 | break; | |
499 | if (y != 18) | |
500 | continue; | |
501 | if (!dup) | |
502 | string = dup = xstrdup (string); | |
503 | for (y = 10; y < 18; y++) | |
504 | dup[i + y] = '0'; | |
505 | } | |
506 | break; | |
507 | } | |
ca29da43 | 508 | } |
20c361f3 JH |
509 | |
510 | chksum = crc32_string (chksum, string); | |
04695783 | 511 | free (dup); |
159b3be1 | 512 | |
ca29da43 NS |
513 | return chksum; |
514 | } | |
515 | ||
516 | /* Compute checksum for the current function. We generate a CRC32. */ | |
517 | ||
10adac51 XDL |
518 | unsigned |
519 | coverage_compute_lineno_checksum (void) | |
ca29da43 | 520 | { |
a281759f PB |
521 | expanded_location xloc |
522 | = expand_location (DECL_SOURCE_LOCATION (current_function_decl)); | |
523 | unsigned chksum = xloc.line; | |
ca29da43 | 524 | |
a281759f | 525 | chksum = coverage_checksum_string (chksum, xloc.file); |
20c361f3 | 526 | chksum = coverage_checksum_string |
ca29da43 NS |
527 | (chksum, IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (current_function_decl))); |
528 | ||
529 | return chksum; | |
530 | } | |
10adac51 XDL |
531 | |
532 | /* Compute cfg checksum for the current function. | |
533 | The checksum is calculated carefully so that | |
534 | source code changes that doesn't affect the control flow graph | |
535 | won't change the checksum. | |
536 | This is to make the profile data useable across source code change. | |
537 | The downside of this is that the compiler may use potentially | |
538 | wrong profile data - that the source code change has non-trivial impact | |
539 | on the validity of profile data (e.g. the reversed condition) | |
540 | but the compiler won't detect the change and use the wrong profile data. */ | |
541 | ||
542 | unsigned | |
543 | coverage_compute_cfg_checksum (void) | |
544 | { | |
545 | basic_block bb; | |
546 | unsigned chksum = n_basic_blocks; | |
547 | ||
548 | FOR_EACH_BB (bb) | |
549 | { | |
550 | edge e; | |
551 | edge_iterator ei; | |
552 | chksum = crc32_byte (chksum, bb->index); | |
553 | FOR_EACH_EDGE (e, ei, bb->succs) | |
554 | { | |
555 | chksum = crc32_byte (chksum, e->dest->index); | |
556 | } | |
557 | } | |
558 | ||
559 | return chksum; | |
560 | } | |
ca29da43 NS |
561 | \f |
562 | /* Begin output to the graph file for the current function. | |
b724567e | 563 | Writes the function header. Returns nonzero if data should be output. */ |
ca29da43 NS |
564 | |
565 | int | |
b724567e | 566 | coverage_begin_function (unsigned lineno_checksum, unsigned cfg_checksum) |
ca29da43 | 567 | { |
b724567e NS |
568 | expanded_location xloc; |
569 | unsigned long offset; | |
570 | ||
2f908293 SP |
571 | /* We don't need to output .gcno file unless we're under -ftest-coverage |
572 | (e.g. -fprofile-arcs/generate/use don't need .gcno to work). */ | |
b724567e | 573 | if (no_coverage || !bbg_file_name) |
6d70e6be | 574 | return 0; |
159b3be1 | 575 | |
b724567e | 576 | xloc = expand_location (DECL_SOURCE_LOCATION (current_function_decl)); |
10adac51 | 577 | |
b724567e NS |
578 | /* Announce function */ |
579 | offset = gcov_write_tag (GCOV_TAG_FUNCTION); | |
580 | gcov_write_unsigned (current_function_funcdef_no + 1); | |
581 | gcov_write_unsigned (lineno_checksum); | |
582 | gcov_write_unsigned (cfg_checksum); | |
583 | gcov_write_string (IDENTIFIER_POINTER | |
584 | (DECL_ASSEMBLER_NAME (current_function_decl))); | |
585 | gcov_write_string (xloc.file); | |
586 | gcov_write_unsigned (xloc.line); | |
587 | gcov_write_length (offset); | |
ca29da43 | 588 | |
ca29da43 NS |
589 | return !gcov_is_error (); |
590 | } | |
591 | ||
592 | /* Finish coverage data for the current function. Verify no output | |
593 | error has occurred. Save function coverage counts. */ | |
594 | ||
595 | void | |
10adac51 | 596 | coverage_end_function (unsigned lineno_checksum, unsigned cfg_checksum) |
ca29da43 NS |
597 | { |
598 | unsigned i; | |
159b3be1 | 599 | |
b724567e | 600 | if (bbg_file_name && gcov_is_error ()) |
159b3be1 | 601 | { |
d4ee4d25 | 602 | warning (0, "error writing %qs", bbg_file_name); |
b724567e NS |
603 | unlink (bbg_file_name); |
604 | bbg_file_name = NULL; | |
ca29da43 | 605 | } |
cdb23767 | 606 | |
2ac69a0c | 607 | if (fn_ctr_mask) |
cdb23767 | 608 | { |
2ac69a0c NS |
609 | struct coverage_data *item = 0; |
610 | ||
611 | /* If the function is extern (i.e. extern inline), then we won't | |
612 | be outputting it, so don't chain it onto the function | |
613 | list. */ | |
614 | if (!DECL_EXTERNAL (current_function_decl)) | |
615 | { | |
616 | item = ggc_alloc_coverage_data (); | |
617 | ||
618 | item->ident = current_function_funcdef_no + 1; | |
619 | item->lineno_checksum = lineno_checksum; | |
620 | item->cfg_checksum = cfg_checksum; | |
621 | ||
622 | item->fn_decl = current_function_decl; | |
623 | item->next = 0; | |
624 | *functions_tail = item; | |
625 | functions_tail = &item->next; | |
626 | } | |
10adac51 | 627 | |
cdb23767 NS |
628 | for (i = 0; i != GCOV_COUNTERS; i++) |
629 | { | |
5366b186 | 630 | tree var = fn_v_ctrs[i]; |
2ac69a0c NS |
631 | |
632 | if (item) | |
633 | item->ctr_vars[i] = var; | |
5366b186 NS |
634 | if (var) |
635 | { | |
636 | tree array_type = build_index_type (size_int (fn_n_ctrs[i] - 1)); | |
637 | array_type = build_array_type (get_gcov_type (), array_type); | |
638 | TREE_TYPE (var) = array_type; | |
639 | DECL_SIZE (var) = TYPE_SIZE (array_type); | |
640 | DECL_SIZE_UNIT (var) = TYPE_SIZE_UNIT (array_type); | |
641 | varpool_finalize_decl (var); | |
642 | } | |
2ac69a0c | 643 | |
5366b186 NS |
644 | fn_b_ctrs[i] = fn_n_ctrs[i] = 0; |
645 | fn_v_ctrs[i] = NULL_TREE; | |
cdb23767 NS |
646 | } |
647 | prg_ctr_mask |= fn_ctr_mask; | |
648 | fn_ctr_mask = 0; | |
649 | } | |
ca29da43 NS |
650 | } |
651 | ||
5366b186 | 652 | /* Build a coverage variable of TYPE for function FN_DECL. If COUNTER |
0e485da0 | 653 | >= 0 it is a counter array, otherwise it is the function structure. */ |
ca29da43 | 654 | |
ca29da43 | 655 | static tree |
5366b186 NS |
656 | build_var (tree fn_decl, tree type, int counter) |
657 | { | |
658 | tree var = build_decl (BUILTINS_LOCATION, VAR_DECL, NULL_TREE, type); | |
659 | tree fn_name = DECL_ASSEMBLER_NAME (fn_decl); | |
660 | char *buf = (char *)alloca (IDENTIFIER_LENGTH (fn_name) + 10); | |
661 | ||
5366b186 NS |
662 | if (counter < 0) |
663 | sprintf (buf, "__gcov__%s", IDENTIFIER_POINTER (fn_name)); | |
664 | else | |
665 | sprintf (buf, "__gcov%u_%s", counter, IDENTIFIER_POINTER (fn_name)); | |
666 | DECL_NAME (var) = get_identifier (buf); | |
89b0c303 NS |
667 | TREE_STATIC (var) = 1; |
668 | TREE_ADDRESSABLE (var) = 1; | |
669 | DECL_ALIGN (var) = TYPE_ALIGN (type); | |
5366b186 NS |
670 | |
671 | return var; | |
672 | } | |
673 | ||
674 | /* Creates the gcov_fn_info RECORD_TYPE. */ | |
675 | ||
676 | static void | |
677 | build_fn_info_type (tree type, unsigned counters, tree gcov_info_type) | |
ca29da43 | 678 | { |
5366b186 | 679 | tree ctr_info = lang_hooks.types.make_type (RECORD_TYPE); |
ca29da43 | 680 | tree field, fields; |
cdb23767 | 681 | tree array_type; |
159b3be1 | 682 | |
5366b186 NS |
683 | gcc_assert (counters); |
684 | ||
685 | /* ctr_info::num */ | |
686 | field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE, | |
687 | get_gcov_unsigned_t ()); | |
688 | fields = field; | |
689 | ||
690 | /* ctr_info::values */ | |
691 | field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE, | |
692 | build_pointer_type (get_gcov_type ())); | |
693 | DECL_CHAIN (field) = fields; | |
694 | fields = field; | |
695 | ||
696 | finish_builtin_struct (ctr_info, "__gcov_ctr_info", fields, NULL_TREE); | |
697 | ||
698 | /* key */ | |
699 | field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE, | |
700 | build_pointer_type (build_qualified_type | |
701 | (gcov_info_type, TYPE_QUAL_CONST))); | |
702 | fields = field; | |
703 | ||
796621e8 | 704 | /* ident */ |
5366b186 NS |
705 | field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE, |
706 | get_gcov_unsigned_t ()); | |
707 | DECL_CHAIN (field) = fields; | |
708 | fields = field; | |
709 | ||
10adac51 | 710 | /* lineno_checksum */ |
5366b186 NS |
711 | field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE, |
712 | get_gcov_unsigned_t ()); | |
910ad8de | 713 | DECL_CHAIN (field) = fields; |
ca29da43 NS |
714 | fields = field; |
715 | ||
10adac51 | 716 | /* cfg checksum */ |
5366b186 NS |
717 | field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE, |
718 | get_gcov_unsigned_t ()); | |
10adac51 XDL |
719 | DECL_CHAIN (field) = fields; |
720 | fields = field; | |
721 | ||
f81b1a3d | 722 | array_type = build_index_type (size_int (counters - 1)); |
5366b186 | 723 | array_type = build_array_type (ctr_info, array_type); |
159b3be1 | 724 | |
ca29da43 | 725 | /* counters */ |
5366b186 | 726 | field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE, array_type); |
910ad8de | 727 | DECL_CHAIN (field) = fields; |
ca29da43 NS |
728 | fields = field; |
729 | ||
cdb23767 | 730 | finish_builtin_struct (type, "__gcov_fn_info", fields, NULL_TREE); |
ca29da43 NS |
731 | } |
732 | ||
b724567e NS |
733 | /* Returns a CONSTRUCTOR for a gcov_fn_info. DATA is |
734 | the coverage data for the function and TYPE is the gcov_fn_info | |
735 | RECORD_TYPE. KEY is the object file key. */ | |
cdb23767 | 736 | |
ca29da43 | 737 | static tree |
b724567e | 738 | build_fn_info (const struct coverage_data *data, tree type, tree key) |
ca29da43 | 739 | { |
cdb23767 | 740 | tree fields = TYPE_FIELDS (type); |
5366b186 | 741 | tree ctr_type; |
cdb23767 | 742 | unsigned ix; |
f9b36bb3 KH |
743 | VEC(constructor_elt,gc) *v1 = NULL; |
744 | VEC(constructor_elt,gc) *v2 = NULL; | |
159b3be1 | 745 | |
5366b186 NS |
746 | /* key */ |
747 | CONSTRUCTOR_APPEND_ELT (v1, fields, | |
748 | build1 (ADDR_EXPR, TREE_TYPE (fields), key)); | |
749 | fields = DECL_CHAIN (fields); | |
750 | ||
796621e8 | 751 | /* ident */ |
f9b36bb3 KH |
752 | CONSTRUCTOR_APPEND_ELT (v1, fields, |
753 | build_int_cstu (get_gcov_unsigned_t (), | |
b724567e | 754 | data->ident)); |
910ad8de | 755 | fields = DECL_CHAIN (fields); |
159b3be1 | 756 | |
10adac51 XDL |
757 | /* lineno_checksum */ |
758 | CONSTRUCTOR_APPEND_ELT (v1, fields, | |
759 | build_int_cstu (get_gcov_unsigned_t (), | |
b724567e | 760 | data->lineno_checksum)); |
10adac51 XDL |
761 | fields = DECL_CHAIN (fields); |
762 | ||
763 | /* cfg_checksum */ | |
f9b36bb3 KH |
764 | CONSTRUCTOR_APPEND_ELT (v1, fields, |
765 | build_int_cstu (get_gcov_unsigned_t (), | |
b724567e | 766 | data->cfg_checksum)); |
910ad8de | 767 | fields = DECL_CHAIN (fields); |
159b3be1 | 768 | |
ca29da43 | 769 | /* counters */ |
5366b186 | 770 | ctr_type = TREE_TYPE (TREE_TYPE (fields)); |
cdb23767 NS |
771 | for (ix = 0; ix != GCOV_COUNTERS; ix++) |
772 | if (prg_ctr_mask & (1 << ix)) | |
5366b186 NS |
773 | { |
774 | VEC(constructor_elt,gc) *ctr = NULL; | |
b724567e | 775 | tree var = data->ctr_vars[ix]; |
5366b186 NS |
776 | unsigned count = 0; |
777 | ||
778 | if (var) | |
779 | count | |
780 | = tree_low_cst (TYPE_MAX_VALUE (TYPE_DOMAIN (TREE_TYPE (var))), 0) | |
781 | + 1; | |
782 | ||
783 | CONSTRUCTOR_APPEND_ELT (ctr, TYPE_FIELDS (ctr_type), | |
784 | build_int_cstu (get_gcov_unsigned_t (), | |
785 | count)); | |
786 | ||
787 | if (var) | |
788 | CONSTRUCTOR_APPEND_ELT (ctr, DECL_CHAIN (TYPE_FIELDS (ctr_type)), | |
789 | build_fold_addr_expr (var)); | |
790 | ||
791 | CONSTRUCTOR_APPEND_ELT (v2, NULL, build_constructor (ctr_type, ctr)); | |
792 | } | |
793 | ||
f9b36bb3 KH |
794 | CONSTRUCTOR_APPEND_ELT (v1, fields, |
795 | build_constructor (TREE_TYPE (fields), v2)); | |
159b3be1 | 796 | |
f9b36bb3 | 797 | return build_constructor (type, v1); |
ca29da43 NS |
798 | } |
799 | ||
b724567e NS |
800 | /* Create gcov_info struct. TYPE is the incomplete RECORD_TYPE to be |
801 | completed, and FN_INFO_PTR_TYPE is a pointer to the function info type. */ | |
cdb23767 | 802 | |
5366b186 | 803 | static void |
b724567e | 804 | build_info_type (tree type, tree fn_info_ptr_type) |
ca29da43 | 805 | { |
cdb23767 | 806 | tree field, fields = NULL_TREE; |
b724567e | 807 | tree merge_fn_type; |
9b514d25 | 808 | |
5366b186 NS |
809 | /* Version ident */ |
810 | field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE, | |
811 | get_gcov_unsigned_t ()); | |
910ad8de | 812 | DECL_CHAIN (field) = fields; |
ca29da43 NS |
813 | fields = field; |
814 | ||
5366b186 NS |
815 | /* next pointer */ |
816 | field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE, | |
817 | build_pointer_type (build_qualified_type | |
818 | (type, TYPE_QUAL_CONST))); | |
910ad8de | 819 | DECL_CHAIN (field) = fields; |
ca29da43 NS |
820 | fields = field; |
821 | ||
5366b186 NS |
822 | /* stamp */ |
823 | field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE, | |
824 | get_gcov_unsigned_t ()); | |
910ad8de | 825 | DECL_CHAIN (field) = fields; |
09780dfb ZD |
826 | fields = field; |
827 | ||
5366b186 NS |
828 | /* Filename */ |
829 | field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE, | |
830 | build_pointer_type (build_qualified_type | |
831 | (char_type_node, TYPE_QUAL_CONST))); | |
832 | DECL_CHAIN (field) = fields; | |
833 | fields = field; | |
ca29da43 | 834 | |
5366b186 NS |
835 | /* merge fn array */ |
836 | merge_fn_type | |
837 | = build_function_type_list (void_type_node, | |
838 | build_pointer_type (get_gcov_type ()), | |
839 | get_gcov_unsigned_t (), NULL_TREE); | |
840 | merge_fn_type | |
841 | = build_array_type (build_pointer_type (merge_fn_type), | |
842 | build_index_type (size_int (GCOV_COUNTERS - 1))); | |
843 | field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE, | |
844 | merge_fn_type); | |
845 | DECL_CHAIN (field) = fields; | |
846 | fields = field; | |
847 | ||
848 | /* n_functions */ | |
849 | field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE, | |
850 | get_gcov_unsigned_t ()); | |
851 | DECL_CHAIN (field) = fields; | |
852 | fields = field; | |
853 | ||
b724567e NS |
854 | /* function_info pointer pointer */ |
855 | fn_info_ptr_type = build_pointer_type | |
856 | (build_qualified_type (fn_info_ptr_type, TYPE_QUAL_CONST)); | |
5366b186 | 857 | field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE, |
b724567e | 858 | fn_info_ptr_type); |
5366b186 NS |
859 | DECL_CHAIN (field) = fields; |
860 | fields = field; | |
09780dfb | 861 | |
5366b186 | 862 | finish_builtin_struct (type, "__gcov_info", fields, NULL_TREE); |
ca29da43 NS |
863 | } |
864 | ||
b724567e NS |
865 | /* Returns a CONSTRUCTOR for the gcov_info object. INFO_TYPE is the |
866 | gcov_info structure type, FN_ARY is the array of pointers to | |
867 | function info objects. */ | |
cdb23767 | 868 | |
ca29da43 | 869 | static tree |
b724567e | 870 | build_info (tree info_type, tree fn_ary) |
ca29da43 | 871 | { |
5366b186 | 872 | tree info_fields = TYPE_FIELDS (info_type); |
b724567e | 873 | tree merge_fn_type, n_funcs; |
5366b186 | 874 | unsigned ix; |
cdb23767 | 875 | tree filename_string; |
2f908293 | 876 | int da_file_name_len; |
f9b36bb3 KH |
877 | VEC(constructor_elt,gc) *v1 = NULL; |
878 | VEC(constructor_elt,gc) *v2 = NULL; | |
159b3be1 | 879 | |
cdb23767 | 880 | /* Version ident */ |
5366b186 NS |
881 | CONSTRUCTOR_APPEND_ELT (v1, info_fields, |
882 | build_int_cstu (TREE_TYPE (info_fields), | |
883 | GCOV_VERSION)); | |
884 | info_fields = DECL_CHAIN (info_fields); | |
159b3be1 | 885 | |
ca29da43 | 886 | /* next -- NULL */ |
5366b186 NS |
887 | CONSTRUCTOR_APPEND_ELT (v1, info_fields, null_pointer_node); |
888 | info_fields = DECL_CHAIN (info_fields); | |
889 | ||
dd486eb2 | 890 | /* stamp */ |
5366b186 NS |
891 | CONSTRUCTOR_APPEND_ELT (v1, info_fields, |
892 | build_int_cstu (TREE_TYPE (info_fields), | |
893 | local_tick)); | |
894 | info_fields = DECL_CHAIN (info_fields); | |
dd486eb2 | 895 | |
ca29da43 | 896 | /* Filename */ |
2f908293 SP |
897 | da_file_name_len = strlen (da_file_name); |
898 | filename_string = build_string (da_file_name_len + 1, da_file_name); | |
4a90aeeb | 899 | TREE_TYPE (filename_string) = build_array_type |
f81b1a3d | 900 | (char_type_node, build_index_type (size_int (da_file_name_len))); |
5366b186 NS |
901 | CONSTRUCTOR_APPEND_ELT (v1, info_fields, |
902 | build1 (ADDR_EXPR, TREE_TYPE (info_fields), | |
903 | filename_string)); | |
904 | info_fields = DECL_CHAIN (info_fields); | |
159b3be1 | 905 | |
5366b186 NS |
906 | /* merge fn array -- NULL slots indicate unmeasured counters */ |
907 | merge_fn_type = TREE_TYPE (TREE_TYPE (info_fields)); | |
908 | for (ix = 0; ix != GCOV_COUNTERS; ix++) | |
ca29da43 | 909 | { |
5366b186 | 910 | tree ptr = null_pointer_node; |
159b3be1 | 911 | |
5366b186 NS |
912 | if ((1u << ix) & prg_ctr_mask) |
913 | { | |
914 | tree merge_fn = build_decl (BUILTINS_LOCATION, | |
915 | FUNCTION_DECL, | |
916 | get_identifier (ctr_merge_functions[ix]), | |
917 | TREE_TYPE (merge_fn_type)); | |
918 | DECL_EXTERNAL (merge_fn) = 1; | |
919 | TREE_PUBLIC (merge_fn) = 1; | |
920 | DECL_ARTIFICIAL (merge_fn) = 1; | |
921 | TREE_NOTHROW (merge_fn) = 1; | |
922 | /* Initialize assembler name so we can stream out. */ | |
923 | DECL_ASSEMBLER_NAME (merge_fn); | |
924 | ptr = build1 (ADDR_EXPR, merge_fn_type, merge_fn); | |
925 | } | |
926 | CONSTRUCTOR_APPEND_ELT (v2, NULL, ptr); | |
ca29da43 | 927 | } |
5366b186 NS |
928 | CONSTRUCTOR_APPEND_ELT (v1, info_fields, |
929 | build_constructor (TREE_TYPE (info_fields), v2)); | |
930 | info_fields = DECL_CHAIN (info_fields); | |
931 | ||
932 | /* n_functions */ | |
b724567e NS |
933 | n_funcs = TYPE_MAX_VALUE (TYPE_DOMAIN (TREE_TYPE (fn_ary))); |
934 | n_funcs = fold_build2 (PLUS_EXPR, TREE_TYPE (info_fields), | |
935 | n_funcs, size_one_node); | |
936 | CONSTRUCTOR_APPEND_ELT (v1, info_fields, n_funcs); | |
5366b186 | 937 | info_fields = DECL_CHAIN (info_fields); |
5366b186 | 938 | |
b724567e | 939 | /* functions */ |
5366b186 | 940 | CONSTRUCTOR_APPEND_ELT (v1, info_fields, |
b724567e NS |
941 | build1 (ADDR_EXPR, TREE_TYPE (info_fields), fn_ary)); |
942 | info_fields = DECL_CHAIN (info_fields); | |
943 | ||
944 | gcc_assert (!info_fields); | |
5366b186 | 945 | return build_constructor (info_type, v1); |
ca29da43 NS |
946 | } |
947 | ||
b724567e NS |
948 | /* Create the gcov_info types and object. Generate the constructor |
949 | function to call __gcov_init. Does not generate the initializer | |
950 | for the object. Returns TRUE if coverage data is being emitted. */ | |
ca29da43 | 951 | |
b724567e NS |
952 | static bool |
953 | coverage_obj_init (void) | |
ca29da43 | 954 | { |
b724567e NS |
955 | tree gcov_info_type, ctor, stmt, init_fn; |
956 | unsigned n_counters = 0; | |
5366b186 | 957 | unsigned ix; |
b724567e NS |
958 | struct coverage_data *fn; |
959 | struct coverage_data **fn_prev; | |
c9b9aa64 | 960 | char name_buf[32]; |
ca29da43 | 961 | |
6d70e6be | 962 | no_coverage = 1; /* Disable any further coverage. */ |
159b3be1 | 963 | |
cdb23767 | 964 | if (!prg_ctr_mask) |
b724567e | 965 | return false; |
159b3be1 | 966 | |
5366b186 NS |
967 | if (cgraph_dump_file) |
968 | fprintf (cgraph_dump_file, "Using data file %s\n", da_file_name); | |
ca29da43 | 969 | |
b724567e | 970 | /* Prune functions. */ |
5366b186 NS |
971 | for (fn_prev = &functions_head; (fn = *fn_prev);) |
972 | if (DECL_STRUCT_FUNCTION (fn->fn_decl)) | |
b724567e | 973 | fn_prev = &fn->next; |
5366b186 NS |
974 | else |
975 | /* The function is not being emitted, remove from list. */ | |
976 | *fn_prev = fn->next; | |
b724567e NS |
977 | |
978 | for (ix = 0; ix != GCOV_COUNTERS; ix++) | |
979 | if ((1u << ix) & prg_ctr_mask) | |
980 | n_counters++; | |
5366b186 NS |
981 | |
982 | /* Build the info and fn_info types. These are mutually recursive. */ | |
983 | gcov_info_type = lang_hooks.types.make_type (RECORD_TYPE); | |
b724567e NS |
984 | gcov_fn_info_type = lang_hooks.types.make_type (RECORD_TYPE); |
985 | gcov_fn_info_ptr_type = build_pointer_type | |
986 | (build_qualified_type (gcov_fn_info_type, TYPE_QUAL_CONST)); | |
987 | build_fn_info_type (gcov_fn_info_type, n_counters, gcov_info_type); | |
988 | build_info_type (gcov_info_type, gcov_fn_info_ptr_type); | |
5366b186 NS |
989 | |
990 | /* Build the gcov info var, this is referred to in its own | |
991 | initializer. */ | |
b724567e NS |
992 | gcov_info_var = build_decl (BUILTINS_LOCATION, |
993 | VAR_DECL, NULL_TREE, gcov_info_type); | |
994 | TREE_STATIC (gcov_info_var) = 1; | |
c9b9aa64 | 995 | ASM_GENERATE_INTERNAL_LABEL (name_buf, "LPBX", 0); |
b724567e | 996 | DECL_NAME (gcov_info_var) = get_identifier (name_buf); |
ca29da43 | 997 | |
c9b9aa64 | 998 | /* Build a decl for __gcov_init. */ |
b724567e NS |
999 | init_fn = build_pointer_type (gcov_info_type); |
1000 | init_fn = build_function_type_list (void_type_node, init_fn, NULL); | |
1001 | init_fn = build_decl (BUILTINS_LOCATION, FUNCTION_DECL, | |
1002 | get_identifier ("__gcov_init"), init_fn); | |
1003 | TREE_PUBLIC (init_fn) = 1; | |
1004 | DECL_EXTERNAL (init_fn) = 1; | |
1005 | DECL_ASSEMBLER_NAME (init_fn); | |
c9b9aa64 RH |
1006 | |
1007 | /* Generate a call to __gcov_init(&gcov_info). */ | |
b724567e NS |
1008 | ctor = NULL; |
1009 | stmt = build_fold_addr_expr (gcov_info_var); | |
1010 | stmt = build_call_expr (init_fn, 1, stmt); | |
1011 | append_to_statement_list (stmt, &ctor); | |
c9b9aa64 RH |
1012 | |
1013 | /* Generate a constructor to run it. */ | |
b724567e NS |
1014 | cgraph_build_static_cdtor ('I', ctor, DEFAULT_INIT_PRIORITY); |
1015 | ||
1016 | return true; | |
ca29da43 | 1017 | } |
b724567e NS |
1018 | |
1019 | /* Generate the coverage function info for FN and DATA. Append a | |
1020 | pointer to that object to CTOR and return the appended CTOR. */ | |
1021 | ||
1022 | static VEC(constructor_elt,gc) * | |
1023 | coverage_obj_fn (VEC(constructor_elt,gc) *ctor, tree fn, | |
1024 | struct coverage_data const *data) | |
1025 | { | |
1026 | tree init = build_fn_info (data, gcov_fn_info_type, gcov_info_var); | |
1027 | tree var = build_var (fn, gcov_fn_info_type, -1); | |
1028 | ||
1029 | DECL_INITIAL (var) = init; | |
1030 | varpool_finalize_decl (var); | |
1031 | ||
1032 | CONSTRUCTOR_APPEND_ELT (ctor, NULL, | |
1033 | build1 (ADDR_EXPR, gcov_fn_info_ptr_type, var)); | |
1034 | return ctor; | |
1035 | } | |
1036 | ||
1037 | /* Finalize the coverage data. Generates the array of pointers to | |
1038 | function objects from CTOR. Generate the gcov_info initializer. */ | |
1039 | ||
1040 | static void | |
1041 | coverage_obj_finish (VEC(constructor_elt,gc) *ctor) | |
1042 | { | |
1043 | unsigned n_functions = VEC_length(constructor_elt, ctor); | |
1044 | tree fn_info_ary_type = build_array_type | |
1045 | (build_qualified_type (gcov_fn_info_ptr_type, TYPE_QUAL_CONST), | |
1046 | build_index_type (size_int (n_functions - 1))); | |
1047 | tree fn_info_ary = build_decl (BUILTINS_LOCATION, VAR_DECL, NULL_TREE, | |
1048 | fn_info_ary_type); | |
1049 | char name_buf[32]; | |
1050 | ||
1051 | TREE_STATIC (fn_info_ary) = 1; | |
1052 | ASM_GENERATE_INTERNAL_LABEL (name_buf, "LPBX", 1); | |
1053 | DECL_NAME (fn_info_ary) = get_identifier (name_buf); | |
1054 | DECL_INITIAL (fn_info_ary) = build_constructor (fn_info_ary_type, ctor); | |
1055 | varpool_finalize_decl (fn_info_ary); | |
1056 | ||
1057 | DECL_INITIAL (gcov_info_var) | |
1058 | = build_info (TREE_TYPE (gcov_info_var), fn_info_ary); | |
1059 | varpool_finalize_decl (gcov_info_var); | |
1060 | } | |
1061 | ||
ca29da43 | 1062 | /* Perform file-level initialization. Read in data file, generate name |
71c0e7fc | 1063 | of graph file. */ |
ca29da43 NS |
1064 | |
1065 | void | |
159b3be1 | 1066 | coverage_init (const char *filename) |
ca29da43 NS |
1067 | { |
1068 | int len = strlen (filename); | |
b724567e | 1069 | int prefix_len = 0; |
b8698a0f | 1070 | |
b724567e | 1071 | if (!profile_data_prefix && !IS_ABSOLUTE_PATH (filename)) |
2f908293 SP |
1072 | profile_data_prefix = getpwd (); |
1073 | ||
b724567e NS |
1074 | if (profile_data_prefix) |
1075 | prefix_len = strlen (profile_data_prefix); | |
ca29da43 | 1076 | |
796621e8 | 1077 | /* Name of da file. */ |
b8698a0f | 1078 | da_file_name = XNEWVEC (char, len + strlen (GCOV_DATA_SUFFIX) |
b724567e | 1079 | + prefix_len + 2); |
2f908293 SP |
1080 | |
1081 | if (profile_data_prefix) | |
1082 | { | |
b724567e NS |
1083 | memcpy (da_file_name, profile_data_prefix, prefix_len); |
1084 | da_file_name[prefix_len++] = '/'; | |
2f908293 | 1085 | } |
b724567e NS |
1086 | memcpy (da_file_name + prefix_len, filename, len); |
1087 | strcpy (da_file_name + prefix_len + len, GCOV_DATA_SUFFIX); | |
159b3be1 | 1088 | |
796621e8 | 1089 | /* Name of bbg file. */ |
b724567e NS |
1090 | if (flag_test_coverage && !flag_compare_debug) |
1091 | { | |
1092 | bbg_file_name = XNEWVEC (char, len + strlen (GCOV_NOTE_SUFFIX) + 1); | |
1093 | memcpy (bbg_file_name, filename, len); | |
1094 | strcpy (bbg_file_name + len, GCOV_NOTE_SUFFIX); | |
1095 | ||
1096 | if (!gcov_open (bbg_file_name, -1)) | |
1097 | { | |
1098 | error ("cannot open %s", bbg_file_name); | |
1099 | bbg_file_name = NULL; | |
1100 | } | |
1101 | else | |
1102 | { | |
1103 | gcov_write_unsigned (GCOV_NOTE_MAGIC); | |
1104 | gcov_write_unsigned (GCOV_VERSION); | |
1105 | gcov_write_unsigned (local_tick); | |
1106 | } | |
1107 | } | |
796621e8 | 1108 | |
cb89a171 | 1109 | if (flag_branch_probabilities) |
2f908293 | 1110 | read_counts_file (); |
ca29da43 NS |
1111 | } |
1112 | ||
1113 | /* Performs file-level cleanup. Close graph file, generate coverage | |
1114 | variables and constructor. */ | |
1115 | ||
1116 | void | |
159b3be1 | 1117 | coverage_finish (void) |
ca29da43 | 1118 | { |
b724567e NS |
1119 | if (bbg_file_name && gcov_close ()) |
1120 | unlink (bbg_file_name); | |
1121 | ||
bdbdc4e1 | 1122 | if (!local_tick || local_tick == (unsigned)-1) |
b724567e NS |
1123 | /* Only remove the da file, if we cannot stamp it. If we can |
1124 | stamp it, libgcov will DTRT. */ | |
1125 | unlink (da_file_name); | |
1126 | ||
1127 | if (coverage_obj_init ()) | |
ca29da43 | 1128 | { |
b724567e NS |
1129 | VEC(constructor_elt,gc) *fn_ctor = NULL; |
1130 | struct coverage_data *fn; | |
1131 | ||
1132 | for (fn = functions_head; fn; fn = fn->next) | |
1133 | fn_ctor = coverage_obj_fn (fn_ctor, fn->fn_decl, fn); | |
1134 | coverage_obj_finish (fn_ctor); | |
ca29da43 NS |
1135 | } |
1136 | } | |
1137 | ||
ca29da43 | 1138 | #include "gt-coverage.h" |