diff options
author | dnovillo <dnovillo@138bc75d-0d04-0410-961f-82ee72b054a4> | 2009-10-03 21:10:11 +0000 |
---|---|---|
committer | dnovillo <dnovillo@138bc75d-0d04-0410-961f-82ee72b054a4> | 2009-10-03 21:10:11 +0000 |
commit | 7bfefa9d2c82e804ef4e59772f4060ac325bf99a (patch) | |
tree | 3a9882bd235e5026410e5397a5e46a97ece50b48 /gcc/lto/lto.c | |
parent | 7271d48ec9cd1a9aa3893d1d95e1d4a1c5882c37 (diff) | |
download | gcc-7bfefa9d2c82e804ef4e59772f4060ac325bf99a.tar.gz |
Merge lto branch into trunk.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@152434 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/lto/lto.c')
-rw-r--r-- | gcc/lto/lto.c | 2067 |
1 files changed, 2067 insertions, 0 deletions
diff --git a/gcc/lto/lto.c b/gcc/lto/lto.c new file mode 100644 index 00000000000..056d249a161 --- /dev/null +++ b/gcc/lto/lto.c @@ -0,0 +1,2067 @@ +/* Top-level LTO routines. + Copyright 2009 Free Software Foundation, Inc. + Contributed by CodeSourcery, 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 "opts.h" +#include "toplev.h" +#include "tree.h" +#include "diagnostic.h" +#include "tm.h" +#include "libiberty.h" +#include "cgraph.h" +#include "ggc.h" +#include "tree-ssa-operands.h" +#include "tree-pass.h" +#include "langhooks.h" +#include "vec.h" +#include "bitmap.h" +#include "pointer-set.h" +#include "ipa-prop.h" +#include "common.h" +#include "timevar.h" +#include "gimple.h" +#include "lto.h" +#include "lto-tree.h" +#include "lto-streamer.h" + +/* This needs to be included after config.h. Otherwise, _GNU_SOURCE will not + be defined in time to set __USE_GNU in the system headers, and strsignal + will not be declared. */ +#include <sys/mman.h> + +DEF_VEC_P(bitmap); +DEF_VEC_ALLOC_P(bitmap,heap); + +/* Read the constructors and inits. */ + +static void +lto_materialize_constructors_and_inits (struct lto_file_decl_data * file_data) +{ + size_t len; + const char *data = lto_get_section_data (file_data, + LTO_section_static_initializer, + NULL, &len); + lto_input_constructors_and_inits (file_data, data); + lto_free_section_data (file_data, LTO_section_static_initializer, NULL, + data, len); +} + +/* Read the function body for the function associated with NODE if possible. */ + +static void +lto_materialize_function (struct cgraph_node *node) +{ + tree decl; + struct lto_file_decl_data *file_data; + const char *data, *name; + size_t len; + tree step; + + /* Ignore clone nodes. Read the body only from the original one. + We may find clone nodes during LTRANS after WPA has made inlining + decisions. */ + if (node->clone_of) + return; + + decl = node->decl; + file_data = node->local.lto_file_data; + name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)); + + /* We may have renamed the declaration, e.g., a static function. */ + name = lto_get_decl_name_mapping (file_data, name); + + data = lto_get_section_data (file_data, LTO_section_function_body, + name, &len); + if (data) + { + struct function *fn; + + gcc_assert (!DECL_IS_BUILTIN (decl)); + + /* This function has a definition. */ + TREE_STATIC (decl) = 1; + + gcc_assert (DECL_STRUCT_FUNCTION (decl) == NULL); + allocate_struct_function (decl, false); + + /* Load the function body only if not operating in WPA mode. In + WPA mode, the body of the function is not needed. */ + if (!flag_wpa) + { + lto_input_function_body (file_data, decl, data); + lto_stats.num_function_bodies++; + } + + fn = DECL_STRUCT_FUNCTION (decl); + lto_free_section_data (file_data, LTO_section_function_body, name, + data, len); + + /* Look for initializers of constant variables and private + statics. */ + for (step = fn->local_decls; step; step = TREE_CHAIN (step)) + { + tree decl = TREE_VALUE (step); + if (TREE_CODE (decl) == VAR_DECL + && (TREE_STATIC (decl) && !DECL_EXTERNAL (decl)) + && flag_unit_at_a_time) + varpool_finalize_decl (decl); + } + } + else + DECL_EXTERNAL (decl) = 1; + + /* Let the middle end know about the function. */ + rest_of_decl_compilation (decl, 1, 0); + if (cgraph_node (decl)->needed) + cgraph_mark_reachable_node (cgraph_node (decl)); +} + + +/* Decode the content of memory pointed to by DATA in the the + in decl state object STATE. DATA_IN points to a data_in structure for + decoding. Return the address after the decoded object in the input. */ + +static const uint32_t * +lto_read_in_decl_state (struct data_in *data_in, const uint32_t *data, + struct lto_in_decl_state *state) +{ + uint32_t ix; + tree decl; + uint32_t i, j; + + ix = *data++; + decl = lto_streamer_cache_get (data_in->reader_cache, (int) ix); + if (TREE_CODE (decl) != FUNCTION_DECL) + { + gcc_assert (decl == void_type_node); + decl = NULL_TREE; + } + state->fn_decl = decl; + + for (i = 0; i < LTO_N_DECL_STREAMS; i++) + { + uint32_t size = *data++; + tree *decls = (tree *) xcalloc (size, sizeof (tree)); + + for (j = 0; j < size; j++) + { + decls[j] = lto_streamer_cache_get (data_in->reader_cache, data[j]); + + /* Register every type in the global type table. If the + type existed already, use the existing type. */ + if (TYPE_P (decls[j])) + decls[j] = gimple_register_type (decls[j]); + } + + state->streams[i].size = size; + state->streams[i].trees = decls; + data += size; + } + + return data; +} + + +/* Read all the symbols from buffer DATA, using descriptors in DECL_DATA. + RESOLUTIONS is the set of symbols picked by the linker (read from the + resolution file when the linker plugin is being used). */ + +static void +lto_read_decls (struct lto_file_decl_data *decl_data, const void *data, + VEC(ld_plugin_symbol_resolution_t,heap) *resolutions) +{ + const struct lto_decl_header *header = (const struct lto_decl_header *) data; + const int32_t decl_offset = sizeof (struct lto_decl_header); + const int32_t main_offset = decl_offset + header->decl_state_size; + const int32_t string_offset = main_offset + header->main_size; + struct lto_input_block ib_main; + struct data_in *data_in; + unsigned int i; + const uint32_t *data_ptr, *data_end; + uint32_t num_decl_states; + + LTO_INIT_INPUT_BLOCK (ib_main, (const char *) data + main_offset, 0, + header->main_size); + + data_in = lto_data_in_create (decl_data, (const char *) data + string_offset, + header->string_size, resolutions); + + /* Read the global declarations and types. */ + while (ib_main.p < ib_main.len) + { + tree t = lto_input_tree (&ib_main, data_in); + gcc_assert (t && ib_main.p <= ib_main.len); + } + + /* Read in lto_in_decl_state objects. */ + data_ptr = (const uint32_t *) ((const char*) data + decl_offset); + data_end = + (const uint32_t *) ((const char*) data_ptr + header->decl_state_size); + num_decl_states = *data_ptr++; + + gcc_assert (num_decl_states > 0); + decl_data->global_decl_state = lto_new_in_decl_state (); + data_ptr = lto_read_in_decl_state (data_in, data_ptr, + decl_data->global_decl_state); + + /* Read in per-function decl states and enter them in hash table. */ + decl_data->function_decl_states = + htab_create (37, lto_hash_in_decl_state, lto_eq_in_decl_state, free); + + for (i = 1; i < num_decl_states; i++) + { + struct lto_in_decl_state *state = lto_new_in_decl_state (); + void **slot; + + data_ptr = lto_read_in_decl_state (data_in, data_ptr, state); + slot = htab_find_slot (decl_data->function_decl_states, state, INSERT); + gcc_assert (*slot == NULL); + *slot = state; + } + + if (data_ptr != data_end) + internal_error ("bytecode stream: garbage at the end of symbols section"); + + /* Set the current decl state to be the global state. */ + decl_data->current_decl_state = decl_data->global_decl_state; + + /* After each CU is read register and possibly merge global + symbols and their types. */ + lto_register_deferred_decls_in_symtab (data_in); + + lto_data_in_delete (data_in); +} + +/* Read resolution for file named FILE_NAME. The resolution is read from + RESOLUTION. An array with the symbol resolution is returned. The array + size is written to SIZE. */ + +static VEC(ld_plugin_symbol_resolution_t,heap) * +lto_resolution_read (FILE *resolution, const char *file_name) +{ + /* We require that objects in the resolution file are in the same + order as the lto1 command line. */ + unsigned int name_len; + char *obj_name; + unsigned int num_symbols; + unsigned int i; + VEC(ld_plugin_symbol_resolution_t,heap) *ret = NULL; + unsigned max_index = 0; + + if (!resolution) + return NULL; + + name_len = strlen (file_name); + obj_name = XNEWVEC (char, name_len + 1); + fscanf (resolution, " "); /* Read white space. */ + + fread (obj_name, sizeof (char), name_len, resolution); + obj_name[name_len] = '\0'; + if (strcmp (obj_name, file_name) != 0) + internal_error ("unexpected file name %s in linker resolution file. " + "Expected %s", obj_name, file_name); + + free (obj_name); + + fscanf (resolution, "%u", &num_symbols); + + for (i = 0; i < num_symbols; i++) + { + unsigned index; + char r_str[27]; + enum ld_plugin_symbol_resolution r; + unsigned int j; + unsigned int lto_resolution_str_len = + sizeof (lto_resolution_str) / sizeof (char *); + + fscanf (resolution, "%u %26s", &index, r_str); + if (index > max_index) + max_index = index; + + for (j = 0; j < lto_resolution_str_len; j++) + { + if (strcmp (lto_resolution_str[j], r_str) == 0) + { + r = (enum ld_plugin_symbol_resolution) j; + break; + } + } + if (j >= lto_resolution_str_len) + internal_error ("tried to read past the end of the linker resolution " + "file"); + + VEC_safe_grow_cleared (ld_plugin_symbol_resolution_t, heap, ret, + index + 1); + VEC_replace (ld_plugin_symbol_resolution_t, ret, index, r); + } + + return ret; +} + +/* Generate a TREE representation for all types and external decls + entities in FILE. + + Read all of the globals out of the file. Then read the cgraph + and process the .o index into the cgraph nodes so that it can open + the .o file to load the functions and ipa information. */ + +static struct lto_file_decl_data * +lto_file_read (lto_file *file, FILE *resolution_file) +{ + struct lto_file_decl_data *file_data; + const char *data; + size_t len; + VEC(ld_plugin_symbol_resolution_t,heap) *resolutions; + + resolutions = lto_resolution_read (resolution_file, file->filename); + + file_data = XCNEW (struct lto_file_decl_data); + file_data->file_name = file->filename; + file_data->fd = -1; + file_data->section_hash_table = lto_elf_build_section_table (file); + file_data->renaming_hash_table = lto_create_renaming_table (); + + data = lto_get_section_data (file_data, LTO_section_decls, NULL, &len); + lto_read_decls (file_data, data, resolutions); + lto_free_section_data (file_data, LTO_section_decls, NULL, data, len); + + return file_data; +} + +#if HAVE_MMAP_FILE && HAVE_SYSCONF && defined _SC_PAGE_SIZE +#define LTO_MMAP_IO 1 +#endif + +#if LTO_MMAP_IO +/* Page size of machine is used for mmap and munmap calls. */ +static size_t page_mask; +#endif + +/* Get the section data of length LEN from FILENAME starting at + OFFSET. The data segment must be freed by the caller when the + caller is finished. Returns NULL if all was not well. */ + +static char * +lto_read_section_data (struct lto_file_decl_data *file_data, + intptr_t offset, size_t len) +{ + char *result; +#if LTO_MMAP_IO + intptr_t computed_len; + intptr_t computed_offset; + intptr_t diff; +#endif + + if (file_data->fd == -1) + file_data->fd = open (file_data->file_name, O_RDONLY); + + if (file_data->fd == -1) + return NULL; + +#if LTO_MMAP_IO + if (!page_mask) + { + size_t page_size = sysconf (_SC_PAGE_SIZE); + page_mask = ~(page_size - 1); + } + + computed_offset = offset & page_mask; + diff = offset - computed_offset; + computed_len = len + diff; + + result = (char *) mmap (NULL, computed_len, PROT_READ, MAP_PRIVATE, + file_data->fd, computed_offset); + if (result == MAP_FAILED) + { + close (file_data->fd); + return NULL; + } + + return result + diff; +#else + result = (char *) xmalloc (len); + if (result == NULL) + { + close (file_data->fd); + return NULL; + } + if (lseek (file_data->fd, offset, SEEK_SET) != offset + || read (file_data->fd, result, len) != (ssize_t) len) + { + free (result); + close (file_data->fd); + return NULL; + } + + return result; +#endif +} + + +/* Get the section data from FILE_DATA of SECTION_TYPE with NAME. + NAME will be NULL unless the section type is for a function + body. */ + +static const char * +get_section_data (struct lto_file_decl_data *file_data, + enum lto_section_type section_type, + const char *name, + size_t *len) +{ + htab_t section_hash_table = file_data->section_hash_table; + struct lto_section_slot *f_slot; + struct lto_section_slot s_slot; + const char *section_name = lto_get_section_name (section_type, name); + char *data = NULL; + + *len = 0; + s_slot.name = section_name; + f_slot = (struct lto_section_slot *) htab_find (section_hash_table, &s_slot); + if (f_slot) + { + data = lto_read_section_data (file_data, f_slot->start, f_slot->len); + *len = f_slot->len; + } + + free (CONST_CAST (char *, section_name)); + return data; +} + + +/* Free the section data from FILE_DATA of SECTION_TYPE with NAME that + starts at OFFSET and has LEN bytes. */ + +static void +free_section_data (struct lto_file_decl_data *file_data, + enum lto_section_type section_type ATTRIBUTE_UNUSED, + const char *name ATTRIBUTE_UNUSED, + const char *offset, size_t len ATTRIBUTE_UNUSED) +{ +#if LTO_MMAP_IO + intptr_t computed_len; + intptr_t computed_offset; + intptr_t diff; +#endif + + if (file_data->fd == -1) + return; + +#if LTO_MMAP_IO + computed_offset = ((intptr_t) offset) & page_mask; + diff = (intptr_t) offset - computed_offset; + computed_len = len + diff; + + munmap ((caddr_t) computed_offset, computed_len); +#else + free (CONST_CAST(char *, offset)); +#endif +} + +/* Vector of all cgraph node sets. */ +static GTY (()) VEC(cgraph_node_set, gc) *lto_cgraph_node_sets; + + +/* Group cgrah nodes by input files. This is used mainly for testing + right now. */ + +static void +lto_1_to_1_map (void) +{ + struct cgraph_node *node; + struct lto_file_decl_data *file_data; + struct pointer_map_t *pmap; + cgraph_node_set set; + void **slot; + + timevar_push (TV_WHOPR_WPA); + + lto_cgraph_node_sets = VEC_alloc (cgraph_node_set, gc, 1); + + /* If the cgraph is empty, create one cgraph node set so that there is still + an output file for any variables that need to be exported in a DSO. */ + if (!cgraph_nodes) + { + set = cgraph_node_set_new (); + VEC_safe_push (cgraph_node_set, gc, lto_cgraph_node_sets, set); + goto finish; + } + + pmap = pointer_map_create (); + + for (node = cgraph_nodes; node; node = node->next) + { + /* We only need to partition the nodes that we read from the + gimple bytecode files. */ + file_data = node->local.lto_file_data; + if (file_data == NULL) + continue; + + slot = pointer_map_contains (pmap, file_data); + if (slot) + set = (cgraph_node_set) *slot; + else + { + set = cgraph_node_set_new (); + slot = pointer_map_insert (pmap, file_data); + *slot = set; + VEC_safe_push (cgraph_node_set, gc, lto_cgraph_node_sets, set); + } + + cgraph_node_set_add (set, node); + } + + pointer_map_destroy (pmap); + +finish: + timevar_pop (TV_WHOPR_WPA); + + lto_stats.num_cgraph_partitions += VEC_length (cgraph_node_set, + lto_cgraph_node_sets); +} + + +/* Add inlined clone NODE and its master clone to SET, if NODE itself has + inlined callees, recursively add the callees. */ + +static void +lto_add_inline_clones (cgraph_node_set set, struct cgraph_node *node, + bitmap original_decls, bitmap inlined_decls) +{ + struct cgraph_node *callee; + struct cgraph_edge *edge; + + cgraph_node_set_add (set, node); + + if (!bitmap_bit_p (original_decls, DECL_UID (node->decl))) + bitmap_set_bit (inlined_decls, DECL_UID (node->decl)); + + /* Check to see if NODE has any inlined callee. */ + for (edge = node->callees; edge != NULL; edge = edge->next_callee) + { + callee = edge->callee; + if (callee->global.inlined_to != NULL) + lto_add_inline_clones (set, callee, original_decls, inlined_decls); + } +} + +/* Compute the transitive closure of inlining of SET based on the + information in the callgraph. Returns a bitmap of decls that have + been inlined into SET indexed by UID. */ + +static bitmap +lto_add_all_inlinees (cgraph_node_set set) +{ + cgraph_node_set_iterator csi; + struct cgraph_node *node; + bitmap original_nodes = lto_bitmap_alloc (); + bitmap original_decls = lto_bitmap_alloc (); + bitmap inlined_decls = lto_bitmap_alloc (); + bool changed; + + /* We are going to iterate SET while adding to it, mark all original + nodes so that we only add node inlined to original nodes. */ + for (csi = csi_start (set); !csi_end_p (csi); csi_next (&csi)) + { + bitmap_set_bit (original_nodes, csi_node (csi)->uid); + bitmap_set_bit (original_decls, DECL_UID (csi_node (csi)->decl)); + } + + /* Some of the original nodes might not be needed anymore. + Remove them. */ + do + { + changed = false; + for (csi = csi_start (set); !csi_end_p (csi); csi_next (&csi)) + { + struct cgraph_node *inlined_to; + node = csi_node (csi); + + /* NODE was not inlined. We still need it. */ + if (!node->global.inlined_to) + continue; + + inlined_to = node->global.inlined_to; + + /* NODE should have only one caller. */ + gcc_assert (!node->callers->next_caller); + + if (!bitmap_bit_p (original_nodes, inlined_to->uid)) + { + bitmap_clear_bit (original_nodes, node->uid); + cgraph_node_set_remove (set, node); + changed = true; + } + } + } + while (changed); + + /* Transitively add to SET all the inline clones for every node that + has been inlined. */ + for (csi = csi_start (set); !csi_end_p (csi); csi_next (&csi)) + { + node = csi_node (csi); + if (bitmap_bit_p (original_nodes, node->uid)) + lto_add_inline_clones (set, node, original_decls, inlined_decls); + } + + lto_bitmap_free (original_nodes); + lto_bitmap_free (original_decls); + + return inlined_decls; +} + +/* Owing to inlining, we may need to promote a file-scope variable + to a global variable. Consider this case: + + a.c: + static int var; + + void + foo (void) + { + var++; + } + + b.c: + + extern void foo (void); + + void + bar (void) + { + foo (); + } + + If WPA inlines FOO inside BAR, then the static variable VAR needs to + be promoted to global because BAR and VAR may be in different LTRANS + files. */ + +/* This struct keeps track of states used in globalization. */ + +typedef struct +{ + /* Current cgraph node set. */ + cgraph_node_set set; + + /* Function DECLs of cgraph nodes seen. */ + bitmap seen_node_decls; + + /* Use in walk_tree to avoid multiple visits of a node. */ + struct pointer_set_t *visited; + + /* static vars in this set. */ + bitmap static_vars_in_set; + + /* static vars in all previous set. */ + bitmap all_static_vars; + + /* all vars in all previous set. */ + bitmap all_vars; +} globalize_context_t; + +/* Callback for walk_tree. Examine the tree pointer to by TP and see if + if its a file-scope static variable of function that need to be turned + into a global. */ + +static tree +globalize_cross_file_statics (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED, + void *data) +{ + globalize_context_t *context = (globalize_context_t *) data; + tree t = *tp; + + if (t == NULL_TREE) + return NULL; + + /* The logic for globalization of VAR_DECLs and FUNCTION_DECLs are + different. For functions, we can simply look at the cgraph node sets + to tell if there are references to static functions outside the set. + The cgraph node sets do not keep track of vars, we need to traverse + the trees to determine what vars need to be globalized. */ + if (TREE_CODE (t) == VAR_DECL) + { + if (!TREE_PUBLIC (t)) + { + /* This file-scope static variable is reachable from more + that one set. Make it global but with hidden visibility + so that we do not export it in dynamic linking. */ + if (bitmap_bit_p (context->all_static_vars, DECL_UID (t))) + { + TREE_PUBLIC (t) = 1; + DECL_VISIBILITY (t) = VISIBILITY_HIDDEN; + } + bitmap_set_bit (context->static_vars_in_set, DECL_UID (t)); + } + bitmap_set_bit (context->all_vars, DECL_UID (t)); + walk_tree (&DECL_INITIAL (t), globalize_cross_file_statics, context, + context->visited); + } + else if (TREE_CODE (t) == FUNCTION_DECL && !TREE_PUBLIC (t)) + { + if (!cgraph_node_in_set_p (cgraph_node (t), context->set)) + { + /* This file-scope static function is reachable from a set + which does not contain the function DECL. Make it global + but with hidden visibility. */ + TREE_PUBLIC (t) = 1; + DECL_VISIBILITY (t) = VISIBILITY_HIDDEN; + } + } + + return NULL; +} + +/* Helper of lto_scan_statics_in_cgraph_node below. Scan TABLE for + static decls that may be used in more than one LTRANS file. + CONTEXT is a globalize_context_t for storing scanning states. */ + +static void +lto_scan_statics_in_ref_table (struct lto_tree_ref_table *table, + globalize_context_t *context) +{ + unsigned i; + + for (i = 0; i < table->size; i++) + walk_tree (&table->trees[i], globalize_cross_file_statics, context, + context->visited); +} + +/* Promote file-scope decl reachable from NODE if necessary to global. + CONTEXT is a globalize_context_t storing scanning states. */ + +static void +lto_scan_statics_in_cgraph_node (struct cgraph_node *node, + globalize_context_t *context) +{ + struct lto_in_decl_state *state; + + /* Do nothing if NODE has no function body. */ + if (!node->analyzed) + return; + + /* Return if the DECL of nodes has been visited before. */ + if (bitmap_bit_p (context->seen_node_decls, DECL_UID (node->decl))) + return; + + bitmap_set_bit (context->seen_node_decls, DECL_UID (node->decl)); + + state = lto_get_function_in_decl_state (node->local.lto_file_data, + node->decl); + gcc_assert (state); + + lto_scan_statics_in_ref_table (&state->streams[LTO_DECL_STREAM_VAR_DECL], + context); + lto_scan_statics_in_ref_table (&state->streams[LTO_DECL_STREAM_FN_DECL], + context); +} + +/* Scan all global variables that we have not yet seen so far. CONTEXT + is a globalize_context_t storing scanning states. */ + +static void +lto_scan_statics_in_remaining_global_vars (globalize_context_t *context) +{ + tree var, var_context; + struct varpool_node *vnode; + + FOR_EACH_STATIC_VARIABLE (vnode) + { + var = vnode->decl; + var_context = DECL_CONTEXT (var); + if (TREE_STATIC (var) + && TREE_PUBLIC (var) + && (!var_context || TREE_CODE (var_context) != FUNCTION_DECL) + && !bitmap_bit_p (context->all_vars, DECL_UID (var))) + walk_tree (&var, globalize_cross_file_statics, context, + context->visited); + } +} + +/* Find out all static decls that need to be promoted to global because + of cross file sharing. This function must be run in the WPA mode after + all inlinees are added. */ + +static void +lto_promote_cross_file_statics (void) +{ + unsigned i, n_sets; + cgraph_node_set set; + cgraph_node_set_iterator csi; + globalize_context_t context; + + memset (&context, 0, sizeof (context)); + context.all_vars = lto_bitmap_alloc (); + context.all_static_vars = lto_bitmap_alloc (); + + n_sets = VEC_length (cgraph_node_set, lto_cgraph_node_sets); + for (i = 0; i < n_sets; i++) + { + set = VEC_index (cgraph_node_set, lto_cgraph_node_sets, i); + context.set = set; + context.visited = pointer_set_create (); + context.static_vars_in_set = lto_bitmap_alloc (); + context.seen_node_decls = lto_bitmap_alloc (); + + for (csi = csi_start (set); !csi_end_p (csi); csi_next (&csi)) + lto_scan_statics_in_cgraph_node (csi_node (csi), &context); + + if (i == n_sets - 1) + lto_scan_statics_in_remaining_global_vars (&context); + + bitmap_ior_into (context.all_static_vars, context.static_vars_in_set); + + pointer_set_destroy (context.visited); + lto_bitmap_free (context.static_vars_in_set); + lto_bitmap_free (context.seen_node_decls); + } + + lto_bitmap_free (context.all_vars); + lto_bitmap_free (context.all_static_vars); +} + + +/* Given a file name FNAME, return a string with FNAME prefixed with '*'. */ + +static char * +prefix_name_with_star (const char *fname) +{ + char *star_fname; + size_t len; + + len = strlen (fname) + 1 + 1; + star_fname = XNEWVEC (char, len); + snprintf (star_fname, len, "*%s", fname); + + return star_fname; +} + + +/* Return a copy of FNAME without the .o extension. */ + +static char * +strip_extension (const char *fname) +{ + char *s = XNEWVEC (char, strlen (fname) - 2 + 1); + gcc_assert (strstr (fname, ".o")); + snprintf (s, strlen (fname) - 2 + 1, "%s", fname); + + return s; +} + + +/* Return a file name associated with cgraph node set SET. This may + be a new temporary file name if SET needs to be processed by + LTRANS, or the original file name if all the nodes in SET belong to + the same input file. */ + +static char * +get_filename_for_set (cgraph_node_set set) +{ + char *fname = NULL; + static const size_t max_fname_len = 100; + + if (cgraph_node_set_needs_ltrans_p (set)) + { + /* Create a new temporary file to store SET. To facilitate + debugging, use file names from SET as part of the new + temporary file name. */ + cgraph_node_set_iterator si; + struct pointer_set_t *pset = pointer_set_create (); + for (si = csi_start (set); !csi_end_p (si); csi_next (&si)) + { + struct cgraph_node *n = csi_node (si); + const char *node_fname; + char *f; + + /* Don't use the same file name more than once. */ + if (pointer_set_insert (pset, n->local.lto_file_data)) + continue; + + /* The first file name found in SET determines the output + directory. For the remaining files, we use their + base names. */ + node_fname = n->local.lto_file_data->file_name; + if (fname == NULL) + { + fname = strip_extension (node_fname); + continue; + } + + f = strip_extension (lbasename (node_fname)); + + /* If the new name causes an excessively long file name, + make the last component "___" to indicate overflow. */ + if (strlen (fname) + strlen (f) > max_fname_len - 3) + { + fname = reconcat (fname, fname, "___", NULL); + break; + } + else + { + fname = reconcat (fname, fname, "_", f, NULL); + free (f); + } + } + + pointer_set_destroy (pset); + + /* Add the extension .wpa.o to indicate that this file has been + produced by WPA. */ + fname = reconcat (fname, fname, ".wpa.o", NULL); + gcc_assert (fname); + } + else + { + /* Since SET does not need to be processed by LTRANS, use + the original file name and mark it with a '*' prefix so that + lto_execute_ltrans knows not to process it. */ + cgraph_node_set_iterator si = csi_start (set); + struct cgraph_node *first = csi_node (si); + fname = prefix_name_with_star (first->local.lto_file_data->file_name); + } + + return fname; +} + +static lto_file *current_lto_file; + + +/* Write all output files in WPA mode. Returns a NULL-terminated array of + output file names. */ + +static char ** +lto_wpa_write_files (void) +{ + char **output_files; + unsigned i, n_sets, last_out_file_ix, num_out_files; + lto_file *file; + cgraph_node_set set; + bitmap decls; + VEC(bitmap,heap) *inlined_decls = NULL; + + timevar_push (TV_WHOPR_WPA); + + /* Include all inlined functions and determine what sets need to be + compiled by LTRANS. After this loop, only those sets that + contain callgraph nodes from more than one file will need to be + compiled by LTRANS. */ + for (i = 0; VEC_iterate (cgraph_node_set, lto_cgraph_node_sets, i, set); i++) + { + decls = lto_add_all_inlinees (set); + VEC_safe_push (bitmap, heap, inlined_decls, decls); + lto_stats.num_output_cgraph_nodes += VEC_length (cgraph_node_ptr, + set->nodes); + } + + /* After adding all inlinees, find out statics that need to be promoted + to globals because of cross-file inlining. */ + lto_promote_cross_file_statics (); + + timevar_pop (TV_WHOPR_WPA); + + timevar_push (TV_WHOPR_WPA_IO); + + /* The number of output files depends on the number of input files + and how many callgraph node sets we create. Reserve enough space + for the maximum of these two. */ + num_out_files = MAX (VEC_length (cgraph_node_set, lto_cgraph_node_sets), + num_in_fnames); + output_files = XNEWVEC (char *, num_out_files + 1); + + n_sets = VEC_length (cgraph_node_set, lto_cgraph_node_sets); + for (i = 0; i < n_sets; i++) + { + char *temp_filename; + + set = VEC_index (cgraph_node_set, lto_cgraph_node_sets, i); + temp_filename = get_filename_for_set (set); + output_files[i] = temp_filename; + + if (cgraph_node_set_needs_ltrans_p (set)) + { + /* Write all the nodes in SET to TEMP_FILENAME. */ + file = lto_elf_file_open (temp_filename, true); + if (!file) + fatal_error ("lto_elf_file_open() failed"); + + lto_set_current_out_file (file); + lto_new_extern_inline_states (); + + decls = VEC_index (bitmap, inlined_decls, i); + lto_force_functions_extern_inline (decls); + + ipa_write_summaries_of_cgraph_node_set (set); + lto_delete_extern_inline_states (); + + lto_set_current_out_file (NULL); + lto_elf_file_close (file); + } + } + + last_out_file_ix = n_sets; + + lto_stats.num_output_files += n_sets; + + output_files[last_out_file_ix] = NULL; + + for (i = 0; VEC_iterate (bitmap, inlined_decls, i, decls); i++) + lto_bitmap_free (decls); + VEC_free (bitmap, heap, inlined_decls); + + timevar_pop (TV_WHOPR_WPA_IO); + + return output_files; +} + + +/* Perform local transformations (LTRANS) on the files in the NULL-terminated + FILES array. These should have been written previously by + lto_wpa_write_files (). Transformations are performed via executing + COLLECT_GCC for reach file. */ + +static void +lto_execute_ltrans (char *const *files) +{ + struct pex_obj *pex; + const char *collect_gcc_options, *collect_gcc; + struct obstack env_obstack; + const char **argv; + const char **argv_ptr; + const char *errmsg; + size_t i, j; + int err; + int status; + FILE *ltrans_output_list_stream = NULL; + + timevar_push (TV_WHOPR_WPA_LTRANS_EXEC); + + /* Get the driver and options. */ + collect_gcc = getenv ("COLLECT_GCC"); + if (!collect_gcc) + fatal_error ("environment variable COLLECT_GCC must be set"); + + /* Set the CFLAGS environment variable. */ + collect_gcc_options = getenv ("COLLECT_GCC_OPTIONS"); + if (!collect_gcc_options) + fatal_error ("environment variable COLLECT_GCC_OPTIONS must be set"); + + /* Count arguments. */ + i = 0; + for (j = 0; collect_gcc_options[j] != '\0'; ++j) + if (collect_gcc_options[j] == '\'') + ++i; + + if (i % 2 != 0) + fatal_error ("malformed COLLECT_GCC_OPTIONS"); + + /* Initalize the arguments for the LTRANS driver. */ + argv = XNEWVEC (const char *, 8 + i / 2); + argv_ptr = argv; + *argv_ptr++ = collect_gcc; + *argv_ptr++ = "-xlto"; + for (j = 0; collect_gcc_options[j] != '\0'; ++j) + if (collect_gcc_options[j] == '\'') + { + char *option; + + ++j; + i = j; + while (collect_gcc_options[j] != '\'') + ++j; + obstack_init (&env_obstack); + obstack_grow (&env_obstack, &collect_gcc_options[i], j - i); + obstack_1grow (&env_obstack, 0); + option = XOBFINISH (&env_obstack, char *); + + /* LTRANS does not need -fwpa nor -fltrans-*. */ + if (strncmp (option, "-fwpa", 5) != 0 + && strncmp (option, "-fltrans-", 9) != 0) + *argv_ptr++ = option; + } + *argv_ptr++ = "-fltrans"; + + /* Open the LTRANS output list. */ + if (ltrans_output_list) + { + ltrans_output_list_stream = fopen (ltrans_output_list, "w"); + if (ltrans_output_list_stream == NULL) + error ("opening LTRANS output list %s: %m", ltrans_output_list); + } + + for (i = 0; files[i]; ++i) + { + size_t len; + + /* If the file is prefixed with a '*', it means that we do not + need to re-compile it with LTRANS because it has not been + modified by WPA. Skip it from the command line to + lto_execute_ltrans, but add it to ltrans_output_list_stream + so it is linked after we are done. */ + if (files[i][0] == '*') + { + size_t len = strlen (files[i]) - 1; + if (ltrans_output_list_stream) + if (fwrite (&files[i][1], 1, len, ltrans_output_list_stream) < len + || fwrite ("\n", 1, 1, ltrans_output_list_stream) < 1) + error ("writing to LTRANS output list %s: %m", + ltrans_output_list); + } + else + { + char *output_name; + + /* Otherwise, add FILES[I] to lto_execute_ltrans command line + and add the resulting file to LTRANS output list. */ + + /* Replace the .o suffix with a .ltrans.o suffix and write + the resulting name to the LTRANS output list. */ + obstack_init (&env_obstack); + obstack_grow (&env_obstack, files[i], strlen (files[i]) - 2); + obstack_grow (&env_obstack, ".ltrans.o", sizeof (".ltrans.o")); + output_name = XOBFINISH (&env_obstack, char *); + if (ltrans_output_list_stream) + { + len = strlen (output_name); + + if (fwrite (output_name, 1, len, ltrans_output_list_stream) < len + || fwrite ("\n", 1, 1, ltrans_output_list_stream) < 1) + error ("writing to LTRANS output list %s: %m", + ltrans_output_list); + } + + argv_ptr[0] = "-o"; + argv_ptr[1] = output_name; + argv_ptr[2] = files[i]; + argv_ptr[3] = NULL; + + /* Execute the driver. */ + pex = pex_init (0, "lto1", NULL); + if (pex == NULL) + fatal_error ("pex_init failed: %s", xstrerror (errno)); + + errmsg = pex_run (pex, PEX_LAST | PEX_SEARCH, argv[0], + CONST_CAST (char **, argv), NULL, NULL, &err); + if (errmsg) + fatal_error ("%s: %s", errmsg, xstrerror (err)); + + if (!pex_get_status (pex, 1, &status)) + fatal_error ("can't get program status: %s", xstrerror (errno)); + + if (status) + { + if (WIFSIGNALED (status)) + { + int sig = WTERMSIG (status); + fatal_error ("%s terminated with signal %d [%s]%s", + argv[0], sig, strsignal (sig), + WCOREDUMP (status) ? ", core dumped" : ""); + } + else + fatal_error ("%s terminated with status %d", argv[0], status); + } + + pex_free (pex); + } + } + + /* Close the LTRANS output list. */ + if (ltrans_output_list_stream && fclose (ltrans_output_list_stream)) + error ("closing LTRANS output list %s: %m", ltrans_output_list); + + obstack_free (&env_obstack, NULL); + free (argv); + + timevar_pop (TV_WHOPR_WPA_LTRANS_EXEC); +} + + +typedef struct { + struct pointer_set_t *free_list; + struct pointer_set_t *seen; +} lto_fixup_data_t; + +#define LTO_FIXUP_SUBTREE(t) \ + do \ + walk_tree (&(t), lto_fixup_tree, data, NULL); \ + while (0) + +#define LTO_REGISTER_TYPE_AND_FIXUP_SUBTREE(t) \ + do \ + { \ + if (t) \ + (t) = gimple_register_type (t); \ + walk_tree (&(t), lto_fixup_tree, data, NULL); \ + } \ + while (0) + +static tree lto_fixup_tree (tree *, int *, void *); + +/* Return true if T does not need to be fixed up recursively. */ + +static inline bool +no_fixup_p (tree t) +{ + return (t == NULL + || CONSTANT_CLASS_P (t) + || TREE_CODE (t) == IDENTIFIER_NODE); +} + +/* Fix up fields of a tree_common T. DATA points to fix-up states. */ + +static void +lto_fixup_common (tree t, void *data) +{ + /* The following re-creates the TYPE_REFERENCE_TO and TYPE_POINTER_TO + lists. We do not stream TYPE_REFERENCE_TO, TYPE_POINTER_TO or + TYPE_NEXT_PTR_TO and TYPE_NEXT_REF_TO. + First remove us from any pointer list we are on. */ + if (TREE_CODE (t) == POINTER_TYPE) + { + if (TYPE_POINTER_TO (TREE_TYPE (t)) == t) + TYPE_POINTER_TO (TREE_TYPE (t)) = TYPE_NEXT_PTR_TO (t); + else + { + tree tem = TYPE_POINTER_TO (TREE_TYPE (t)); + while (tem && TYPE_NEXT_PTR_TO (tem) != t) + tem = TYPE_NEXT_PTR_TO (tem); + if (tem) + TYPE_NEXT_PTR_TO (tem) = TYPE_NEXT_PTR_TO (t); + } + TYPE_NEXT_PTR_TO (t) = NULL_TREE; + } + else if (TREE_CODE (t) == REFERENCE_TYPE) + { + if (TYPE_REFERENCE_TO (TREE_TYPE (t)) == t) + TYPE_REFERENCE_TO (TREE_TYPE (t)) = TYPE_NEXT_REF_TO (t); + else + { + tree tem = TYPE_REFERENCE_TO (TREE_TYPE (t)); + while (tem && TYPE_NEXT_REF_TO (tem) != t) + tem = TYPE_NEXT_REF_TO (tem); + if (tem) + TYPE_NEXT_REF_TO (tem) = TYPE_NEXT_REF_TO (t); + } + TYPE_NEXT_REF_TO (t) = NULL_TREE; + } + + /* Fixup our type. */ + LTO_REGISTER_TYPE_AND_FIXUP_SUBTREE (TREE_TYPE (t)); + + /* Second put us on the list of pointers of the new pointed-to type + if we are a main variant. This is done in lto_fixup_type after + fixing up our main variant. */ + + /* This is not very efficient because we cannot do tail-recursion with + a long chain of trees. */ + LTO_FIXUP_SUBTREE (TREE_CHAIN (t)); +} + +/* Fix up fields of a decl_minimal T. DATA points to fix-up states. */ + +static void +lto_fixup_decl_minimal (tree t, void *data) +{ + lto_fixup_common (t, data); + LTO_FIXUP_SUBTREE (DECL_NAME (t)); + LTO_FIXUP_SUBTREE (DECL_CONTEXT (t)); +} + +/* Fix up fields of a decl_common T. DATA points to fix-up states. */ + +static void +lto_fixup_decl_common (tree t, void *data) +{ + lto_fixup_decl_minimal (t, data); + LTO_FIXUP_SUBTREE (DECL_SIZE (t)); + LTO_FIXUP_SUBTREE (DECL_SIZE_UNIT (t)); + LTO_FIXUP_SUBTREE (DECL_INITIAL (t)); + LTO_FIXUP_SUBTREE (DECL_ATTRIBUTES (t)); + LTO_FIXUP_SUBTREE (DECL_ABSTRACT_ORIGIN (t)); +} + +/* Fix up fields of a decl_with_vis T. DATA points to fix-up states. */ + +static void +lto_fixup_decl_with_vis (tree t, void *data) +{ + lto_fixup_decl_common (t, data); + + /* Accessor macro has side-effects, use field-name here. */ + LTO_FIXUP_SUBTREE (t->decl_with_vis.assembler_name); + + gcc_assert (no_fixup_p (DECL_SECTION_NAME (t))); +} + +/* Fix up fields of a decl_non_common T. DATA points to fix-up states. */ + +static void +lto_fixup_decl_non_common (tree t, void *data) +{ + lto_fixup_decl_with_vis (t, data); + LTO_FIXUP_SUBTREE (DECL_ARGUMENT_FLD (t)); + LTO_FIXUP_SUBTREE (DECL_RESULT_FLD (t)); + LTO_FIXUP_SUBTREE (DECL_VINDEX (t)); + + /* SAVED_TREE should not cleared by now. Also no accessor for base type. */ + gcc_assert (no_fixup_p (t->decl_non_common.saved_tree)); +} + +/* Fix up fields of a decl_non_common T. DATA points to fix-up states. */ + +static void +lto_fixup_function (tree t, void *data) +{ + lto_fixup_decl_non_common (t, data); + LTO_FIXUP_SUBTREE (DECL_FUNCTION_PERSONALITY (t)); +} + +/* Fix up fields of a field_decl T. DATA points to fix-up states. */ + +static void +lto_fixup_field_decl (tree t, void *data) +{ + lto_fixup_decl_common (t, data); + gcc_assert (no_fixup_p (DECL_FIELD_OFFSET (t))); + LTO_FIXUP_SUBTREE (DECL_BIT_FIELD_TYPE (t)); + LTO_FIXUP_SUBTREE (DECL_QUALIFIER (t)); + gcc_assert (no_fixup_p (DECL_FIELD_BIT_OFFSET (t))); + LTO_FIXUP_SUBTREE (DECL_FCONTEXT (t)); +} + +/* Fix up fields of a type T. DATA points to fix-up states. */ + +static void +lto_fixup_type (tree t, void *data) +{ + tree tem, mv; + + lto_fixup_common (t, data); + LTO_FIXUP_SUBTREE (TYPE_CACHED_VALUES (t)); + LTO_FIXUP_SUBTREE (TYPE_SIZE (t)); + LTO_FIXUP_SUBTREE (TYPE_SIZE_UNIT (t)); + LTO_FIXUP_SUBTREE (TYPE_ATTRIBUTES (t)); + LTO_FIXUP_SUBTREE (TYPE_NAME (t)); + + /* Accessors are for derived node types only. */ + if (!POINTER_TYPE_P (t)) + LTO_FIXUP_SUBTREE (t->type.minval); + LTO_FIXUP_SUBTREE (t->type.maxval); + + /* Accessor is for derived node types only. */ + LTO_FIXUP_SUBTREE (t->type.binfo); + + LTO_REGISTER_TYPE_AND_FIXUP_SUBTREE (TYPE_CONTEXT (t)); + LTO_REGISTER_TYPE_AND_FIXUP_SUBTREE (TYPE_CANONICAL (t)); + + /* The following re-creates proper variant lists while fixing up + the variant leaders. We do not stream TYPE_NEXT_VARIANT so the + variant list state before fixup is broken. */ + + /* Remove us from our main variant list if we are not the variant leader. */ + if (TYPE_MAIN_VARIANT (t) != t) + { + tem = TYPE_MAIN_VARIANT (t); + while (tem && TYPE_NEXT_VARIANT (tem) != t) + tem = TYPE_NEXT_VARIANT (tem); + if (tem) + TYPE_NEXT_VARIANT (tem) = TYPE_NEXT_VARIANT (t); + TYPE_NEXT_VARIANT (t) = NULL_TREE; + } + + /* Query our new main variant. */ + mv = gimple_register_type (TYPE_MAIN_VARIANT (t)); + + /* If we were the variant leader and we get replaced ourselves drop + all variants from our list. */ + if (TYPE_MAIN_VARIANT (t) == t + && mv != t) + { + tem = t; + while (tem) + { + tree tem2 = TYPE_NEXT_VARIANT (tem); + TYPE_NEXT_VARIANT (tem) = NULL_TREE; + tem = tem2; + } + } + + /* If we are not our own variant leader link us into our new leaders + variant list. */ + if (mv != t) + { + TYPE_NEXT_VARIANT (t) = TYPE_NEXT_VARIANT (mv); + TYPE_NEXT_VARIANT (mv) = t; + } + + /* Finally adjust our main variant and fix it up. */ + TYPE_MAIN_VARIANT (t) = mv; + LTO_FIXUP_SUBTREE (TYPE_MAIN_VARIANT (t)); + + /* As the second step of reconstructing the pointer chains put us + on the list of pointers of the new pointed-to type + if we are a main variant. See lto_fixup_common for the first step. */ + if (TREE_CODE (t) == POINTER_TYPE + && TYPE_MAIN_VARIANT (t) == t) + { + TYPE_NEXT_PTR_TO (t) = TYPE_POINTER_TO (TREE_TYPE (t)); + TYPE_POINTER_TO (TREE_TYPE (t)) = t; + } + else if (TREE_CODE (t) == REFERENCE_TYPE + && TYPE_MAIN_VARIANT (t) == t) + { + TYPE_NEXT_REF_TO (t) = TYPE_REFERENCE_TO (TREE_TYPE (t)); + TYPE_REFERENCE_TO (TREE_TYPE (t)) = t; + } +} + +/* Fix up fields of a BINFO T. DATA points to fix-up states. */ + +static void +lto_fixup_binfo (tree t, void *data) +{ + unsigned HOST_WIDE_INT i, n; + tree base, saved_base; + + lto_fixup_common (t, data); + gcc_assert (no_fixup_p (BINFO_OFFSET (t))); + LTO_FIXUP_SUBTREE (BINFO_VTABLE (t)); + LTO_FIXUP_SUBTREE (BINFO_VIRTUALS (t)); + LTO_FIXUP_SUBTREE (BINFO_VPTR_FIELD (t)); + n = VEC_length (tree, BINFO_BASE_ACCESSES (t)); + for (i = 0; i < n; i++) + { + saved_base = base = BINFO_BASE_ACCESS (t, i); + LTO_FIXUP_SUBTREE (base); + if (base != saved_base) + VEC_replace (tree, BINFO_BASE_ACCESSES (t), i, base); + } + LTO_FIXUP_SUBTREE (BINFO_INHERITANCE_CHAIN (t)); + LTO_FIXUP_SUBTREE (BINFO_SUBVTT_INDEX (t)); + LTO_FIXUP_SUBTREE (BINFO_VPTR_INDEX (t)); + n = BINFO_N_BASE_BINFOS (t); + for (i = 0; i < n; i++) + { + saved_base = base = BINFO_BASE_BINFO (t, i); + LTO_FIXUP_SUBTREE (base); + if (base != saved_base) + VEC_replace (tree, BINFO_BASE_BINFOS (t), i, base); + } +} + +/* Fix up fields of a CONSTRUCTOR T. DATA points to fix-up states. */ + +static void +lto_fixup_constructor (tree t, void *data) +{ + unsigned HOST_WIDE_INT idx; + constructor_elt *ce; + + LTO_REGISTER_TYPE_AND_FIXUP_SUBTREE (TREE_TYPE (t)); + + for (idx = 0; + VEC_iterate(constructor_elt, CONSTRUCTOR_ELTS (t), idx, ce); + idx++) + { + LTO_FIXUP_SUBTREE (ce->index); + LTO_FIXUP_SUBTREE (ce->value); + } +} + +/* A walk_tree callback used by lto_fixup_state. TP is the pointer to the + current tree. WALK_SUBTREES indicates if the subtrees will be walked. + DATA is a pointer set to record visited nodes. */ + +static tree +lto_fixup_tree (tree *tp, int *walk_subtrees, void *data) +{ + tree t; + lto_fixup_data_t *fixup_data = (lto_fixup_data_t *) data; + tree prevailing; + + t = *tp; + *walk_subtrees = 0; + if (pointer_set_contains (fixup_data->seen, t)) + return NULL; + + if (TREE_CODE (t) == VAR_DECL || TREE_CODE (t) == FUNCTION_DECL) + { + prevailing = lto_symtab_prevailing_decl (t); + + if (t != prevailing) + { + if (TREE_CODE (t) == FUNCTION_DECL + && TREE_NOTHROW (prevailing) != TREE_NOTHROW (t)) + { + /* If the prevailing definition does not throw but the + declaration (T) was considered throwing, then we + simply add PREVAILING to the list of throwing + functions. However, if the opposite is true, then + the call to PREVAILING was generated assuming that + the function didn't throw, which means that CFG + cleanup may have removed surrounding try/catch + regions. + + Note that we currently accept these cases even when + they occur within a single file. It's certainly a + user error, but we silently allow the compiler to + remove surrounding try/catch regions. Perhaps we + could emit a warning here, instead of silently + accepting the conflicting declaration. */ + if (TREE_NOTHROW (prevailing)) + lto_mark_nothrow_fndecl (prevailing); + } + + pointer_set_insert (fixup_data->free_list, t); + + /* Also replace t with prevailing defintion. We don't want to + insert the other defintion in the seen set as we want to + replace all instances of it. */ + *tp = prevailing; + t = prevailing; + } + } + else if (TYPE_P (t)) + { + /* Replace t with the prevailing type. We don't want to insert the + other type in the seen set as we want to replace all instances of it. */ + t = gimple_register_type (t); + *tp = t; + } + + if (pointer_set_insert (fixup_data->seen, t)) + return NULL; + + /* walk_tree does not visit all reachable nodes that need to be fixed up. + Hence we do special processing here for those kind of nodes. */ + switch (TREE_CODE (t)) + { + case FIELD_DECL: + lto_fixup_field_decl (t, data); + break; + + case LABEL_DECL: + case CONST_DECL: + case PARM_DECL: + case RESULT_DECL: + case IMPORTED_DECL: + lto_fixup_decl_common (t, data); + break; + + case VAR_DECL: + lto_fixup_decl_with_vis (t, data); + break; + + case TYPE_DECL: + lto_fixup_decl_non_common (t, data); + break; + + case FUNCTION_DECL: + lto_fixup_function (t, data); + break; + + case TREE_BINFO: + lto_fixup_binfo (t, data); + break; + + default: + if (TYPE_P (t)) + lto_fixup_type (t, data); + else if (TREE_CODE (t) == CONSTRUCTOR) + lto_fixup_constructor (t, data); + else if (CONSTANT_CLASS_P (t)) + LTO_REGISTER_TYPE_AND_FIXUP_SUBTREE (TREE_TYPE (t)); + else if (EXPR_P (t)) + { + /* walk_tree only handles TREE_OPERANDs. Do the rest here. */ + lto_fixup_common (t, data); + LTO_FIXUP_SUBTREE (t->exp.block); + *walk_subtrees = 1; + } + else + { + /* Let walk_tree handle sub-trees. */ + *walk_subtrees = 1; + } + } + + return NULL; +} + +/* Helper function of lto_fixup_decls. Walks the var and fn streams in STATE, + replaces var and function decls with the corresponding prevailing def and + records the old decl in the free-list in DATA. We also record visted nodes + in the seen-set in DATA to avoid multiple visit for nodes that need not + to be replaced. */ + +static void +lto_fixup_state (struct lto_in_decl_state *state, lto_fixup_data_t *data) +{ + unsigned i, si; + struct lto_tree_ref_table *table; + + /* Although we only want to replace FUNCTION_DECLs and VAR_DECLs, + we still need to walk from all DECLs to find the reachable + FUNCTION_DECLs and VAR_DECLs. */ + for (si = 0; si < LTO_N_DECL_STREAMS; si++) + { + table = &state->streams[si]; + for (i = 0; i < table->size; i++) + walk_tree (table->trees + i, lto_fixup_tree, data, NULL); + } +} + +/* A callback of htab_traverse. Just extract a state from SLOT and the + lto_fixup_data_t object from AUX and calls lto_fixup_state. */ + +static int +lto_fixup_state_aux (void **slot, void *aux) +{ + struct lto_in_decl_state *state = (struct lto_in_decl_state *) *slot; + lto_fixup_state (state, (lto_fixup_data_t *) aux); + return 1; +} + +/* A callback to pointer_set_traverse. Frees the tree pointed by p. Removes + from it from the UID -> DECL mapping. */ + +static bool +free_decl (const void *p, void *data ATTRIBUTE_UNUSED) +{ + const_tree ct = (const_tree) p; + tree t = CONST_CAST_TREE (ct); + + lto_symtab_clear_resolution (t); + + return true; +} + +/* Fix the decls from all FILES. Replaces each decl with the corresponding + prevailing one. */ + +static void +lto_fixup_decls (struct lto_file_decl_data **files) +{ + unsigned int i; + tree decl; + struct pointer_set_t *free_list = pointer_set_create (); + struct pointer_set_t *seen = pointer_set_create (); + lto_fixup_data_t data; + + data.free_list = free_list; + data.seen = seen; + for (i = 0; files[i]; i++) + { + struct lto_file_decl_data *file = files[i]; + struct lto_in_decl_state *state = file->global_decl_state; + lto_fixup_state (state, &data); + + htab_traverse (file->function_decl_states, lto_fixup_state_aux, &data); + } + + for (i = 0; VEC_iterate (tree, lto_global_var_decls, i, decl); i++) + { + tree saved_decl = decl; + walk_tree (&decl, lto_fixup_tree, &data, NULL); + if (decl != saved_decl) + VEC_replace (tree, lto_global_var_decls, i, decl); + } + + pointer_set_traverse (free_list, free_decl, NULL); + pointer_set_destroy (free_list); + pointer_set_destroy (seen); +} + +/* Unlink a temporary LTRANS file unless requested otherwise. */ + +static void +lto_maybe_unlink (const char *file) +{ + if (!getenv ("WPA_SAVE_LTRANS")) + { + if (unlink_if_ordinary (file)) + error ("deleting LTRANS input file %s: %m", file); + } + else + fprintf (stderr, "[Leaving LTRANS input file %s]\n", file); +} + +/* Read the options saved from each file in the command line. Called + from lang_hooks.post_options which is called by process_options + right before all the options are used to initialize the compiler. + This assumes that decode_options has already run, so the + num_in_fnames and in_fnames are properly set. + + Note that this assumes that all the files had been compiled with + the same options, which is not a good assumption. In general, + options ought to be read from all the files in the set and merged. + However, it is still unclear what the merge rules should be. */ + +void +lto_read_all_file_options (void) +{ + size_t i; + + /* Clear any file options currently saved. */ + lto_clear_file_options (); + + /* Set the hooks to read ELF sections. */ + lto_set_in_hooks (NULL, get_section_data, free_section_data); + + for (i = 0; i < num_in_fnames; i++) + { + struct lto_file_decl_data *file_data; + lto_file *file = lto_elf_file_open (in_fnames[i], false); + if (!file) + break; + + file_data = XCNEW (struct lto_file_decl_data); + file_data->file_name = file->filename; + file_data->fd = -1; + file_data->section_hash_table = lto_elf_build_section_table (file); + + lto_read_file_options (file_data); + + lto_elf_file_close (file); + htab_delete (file_data->section_hash_table); + if (file_data->fd != -1) + close (file_data->fd); + free (file_data); + } + + /* Apply globally the options read from all the files. */ + lto_reissue_options (); +} + + +/* Read all the symbols from the input files FNAMES. NFILES is the + number of files requested in the command line. Instantiate a + global call graph by aggregating all the sub-graphs found in each + file. */ + +static void +read_cgraph_and_symbols (unsigned nfiles, const char **fnames) +{ + unsigned int i, last_file_ix; + struct lto_file_decl_data **all_file_decl_data; + FILE *resolution; + + lto_stats.num_input_files = nfiles; + + timevar_push (TV_IPA_LTO_DECL_IO); + + /* Set the hooks so that all of the ipa passes can read in their data. */ + all_file_decl_data = XNEWVEC (struct lto_file_decl_data *, nfiles + 1); + lto_set_in_hooks (all_file_decl_data, get_section_data, free_section_data); + + /* Read the resolution file. */ + resolution = NULL; + if (resolution_file_name) + { + int t; + unsigned num_objects; + + resolution = fopen (resolution_file_name, "r"); + gcc_assert (resolution != NULL); + t = fscanf (resolution, "%u", &num_objects); + gcc_assert (t == 1); + + /* True, since the plugin splits the archives. */ + gcc_assert (num_objects == nfiles); + } + + /* Read all of the object files specified on the command line. */ + for (i = 0, last_file_ix = 0; i < nfiles; ++i) + { + struct lto_file_decl_data *file_data = NULL; + + current_lto_file = lto_elf_file_open (fnames[i], false); + if (!current_lto_file) + break; + + file_data = lto_file_read (current_lto_file, resolution); + if (!file_data) + break; + + all_file_decl_data[last_file_ix++] = file_data; + + lto_elf_file_close (current_lto_file); + current_lto_file = NULL; + } + + if (resolution_file_name) + fclose (resolution); + + all_file_decl_data[last_file_ix] = NULL; + + /* Set the hooks so that all of the ipa passes can read in their data. */ + lto_set_in_hooks (all_file_decl_data, get_section_data, free_section_data); + + /* Each pass will set the appropriate timer. */ + timevar_pop (TV_IPA_LTO_DECL_IO); + + /* Read the callgraph. */ + input_cgraph (); + + ipa_read_summaries (); + + timevar_push (TV_IPA_LTO_DECL_IO); + + lto_fixup_decls (all_file_decl_data); + + /* See if we have multiple decls for a symbol and choose the largest + one to generate the common. */ + for (i = 0; i < VEC_length (tree, lto_global_var_decls); ++i) + { + tree decl = VEC_index (tree, lto_global_var_decls, i); + tree prev_decl = NULL_TREE; + tree size; + + if (TREE_CODE (decl) != VAR_DECL + || !DECL_LANG_SPECIFIC (decl)) + continue; + + /* Find the preceeding decl of the largest one. */ + size = DECL_SIZE (decl); + do + { + tree next = (tree) DECL_LANG_SPECIFIC (decl); + if (tree_int_cst_lt (size, DECL_SIZE (next))) + { + size = DECL_SIZE (next); + prev_decl = decl; + } + decl = next; + } + while (DECL_LANG_SPECIFIC (decl)); + + /* If necessary move the largest decl to the front of the + chain. */ + if (prev_decl != NULL_TREE) + { + decl = (tree) DECL_LANG_SPECIFIC (prev_decl); + DECL_LANG_SPECIFIC (prev_decl) = DECL_LANG_SPECIFIC (decl); + DECL_LANG_SPECIFIC (decl) + = (struct lang_decl *) VEC_index (tree, lto_global_var_decls, i); + VEC_replace (tree, lto_global_var_decls, i, decl); + } + + /* Mark everything apart from the first var as written out and + unlink the chain. */ + decl = VEC_index (tree, lto_global_var_decls, i); + while (DECL_LANG_SPECIFIC (decl)) + { + tree next = (tree) DECL_LANG_SPECIFIC (decl); + DECL_LANG_SPECIFIC (decl) = NULL; + decl = next; + TREE_ASM_WRITTEN (decl) = true; + } + } + + /* FIXME lto. This loop needs to be changed to use the pass manager to + call the ipa passes directly. */ + if (!errorcount) + for (i = 0; i < last_file_ix; i++) + { + struct lto_file_decl_data *file_data = all_file_decl_data [i]; + lto_materialize_constructors_and_inits (file_data); + } + + /* Indicate that the cgraph is built and ready. */ + cgraph_function_flags_ready = true; + + timevar_pop (TV_IPA_LTO_DECL_IO); +} + + +/* Materialize all the bodies for all the nodes in the callgraph. */ + +static void +materialize_cgraph (void) +{ + tree decl; + struct cgraph_node *node; + unsigned i; + timevar_id_t lto_timer; + + /* Now that we have input the cgraph, we need to clear all of the aux + nodes and read the functions if we are not running in WPA mode. */ + timevar_push (TV_IPA_LTO_GIMPLE_IO); + + for (node = cgraph_nodes; node; node = node->next) + { + /* Some cgraph nodes get created on the fly, and they don't need + to be materialized. For instance, nodes for nested functions + where the parent function was not streamed out or builtin + functions. Additionally, builtin functions should not be + materialized and may, in fact, cause confusion because there + may be a regular function in the file whose assembler name + matches that of the function. + See gcc.c-torture/execute/20030125-1.c and + gcc.c-torture/execute/921215-1.c. */ + if (node->local.lto_file_data + && !DECL_IS_BUILTIN (node->decl)) + { + lto_materialize_function (node); + lto_stats.num_input_cgraph_nodes++; + } + } + + timevar_pop (TV_IPA_LTO_GIMPLE_IO); + + /* Start the appropriate timer depending on the mode that we are + operating in. */ + lto_timer = (flag_wpa) ? TV_WHOPR_WPA + : (flag_ltrans) ? TV_WHOPR_LTRANS + : TV_LTO; + timevar_push (lto_timer); + + current_function_decl = NULL; + set_cfun (NULL); + + /* Inform the middle end about the global variables we have seen. */ + for (i = 0; VEC_iterate (tree, lto_global_var_decls, i, decl); i++) + rest_of_decl_compilation (decl, 1, 0); + + /* Fix up any calls to DECLs that have become not exception throwing. */ + lto_fixup_nothrow_decls (); + + timevar_pop (lto_timer); +} + + +/* Perform whole program analysis (WPA) on the callgraph and write out the + optimization plan. */ + +static void +do_whole_program_analysis (void) +{ + char **output_files; + size_t i; + struct cgraph_node *node; + + lto_1_to_1_map (); + + /* Note that since we are in WPA mode, materialize_cgraph will not + actually read in all the function bodies. It only materializes + the decls and cgraph nodes so that analysis can be performed. */ + materialize_cgraph (); + + /* Reading in the cgraph uses different timers, start timing WPA now. */ + timevar_push (TV_WHOPR_WPA); + + /* FIXME lto. Hack. We should use the IPA passes. There are a + number of issues with this now. 1. There is no convenient way to + do this. 2. Some passes may depend on properties that requires + the function bodies to compute. */ + cgraph_function_flags_ready = true; + bitmap_obstack_initialize (NULL); + ipa_register_cgraph_hooks (); + + /* Reset inlining information before running IPA inliner. */ + for (node = cgraph_nodes; node; node = node->next) + reset_inline_failed (node); + + /* FIXME lto. We should not call this function directly. */ + pass_ipa_inline.pass.execute (); + + verify_cgraph (); + bitmap_obstack_release (NULL); + + /* We are about to launch the final LTRANS phase, stop the WPA timer. */ + timevar_pop (TV_WHOPR_WPA); + + output_files = lto_wpa_write_files (); + + /* Show the LTO report before launching LTRANS. */ + if (flag_lto_report) + print_lto_report (); + + lto_execute_ltrans (output_files); + + for (i = 0; output_files[i]; ++i) + { + if (output_files[i][0] != '*') + lto_maybe_unlink (output_files[i]); + + free (output_files[i]); + } + + XDELETEVEC (output_files); +} + + +/* Main entry point for the GIMPLE front end. This front end has + three main personalities: + + - LTO (-flto). All the object files on the command line are + loaded in memory and processed as a single translation unit. + This is the traditional link-time optimization behavior. + + - WPA (-fwpa). Only the callgraph and summary information for + files in the command file are loaded. A single callgraph + (without function bodies) is instantiated for the whole set of + files. IPA passes are only allowed to analyze the call graph + and make transformation decisions. The callgraph is + partitioned, each partition is written to a new object file + together with the transformation decisions. + + - LTRANS (-fltrans). Similar to -flto but it prevents the IPA + summary files from running again. Since WPA computed summary + information and decided what transformations to apply, LTRANS + simply applies them. */ + +void +lto_main (int debug_p ATTRIBUTE_UNUSED) +{ + lto_init_reader (); + + /* Read all the symbols and call graph from all the files in the + command line. */ + read_cgraph_and_symbols (num_in_fnames, in_fnames); + + if (!errorcount) + { + /* If WPA is enabled analyze the whole call graph and create an + optimization plan. Otherwise, read in all the function + bodies and continue with optimization. */ + if (flag_wpa) + do_whole_program_analysis (); + else + { + materialize_cgraph (); + + /* Let the middle end know that we have read and merged all of + the input files. */ + cgraph_optimize (); + + /* FIXME lto, if the processes spawned by WPA fail, we miss + the chance to print WPA's report, so WPA will call + print_lto_report before launching LTRANS. If LTRANS was + launched directly by the driver we would not need to do + this. */ + if (flag_lto_report) + print_lto_report (); + } + } +} + +#include "gt-lto-lto.h" |