/* Add code: static gcov* __gcov_indirect_call_counters; // pointer to actual counter static void* __gcov_indirect_call_callee; // actual callee address */ static void init_ic_make_global_vars (void) { tree gcov_type_ptr; ptr_void = build_pointer_type (void_type_node); ic_void_ptr_var = build_decl (UNKNOWN_LOCATION, VAR_DECL, get_identifier ("__gcov_indirect_call_callee"), ptr_void); TREE_STATIC (ic_void_ptr_var) = 1; TREE_PUBLIC (ic_void_ptr_var) = 0; DECL_ARTIFICIAL (ic_void_ptr_var) = 1; DECL_INITIAL (ic_void_ptr_var) = NULL; varpool_finalize_decl (ic_void_ptr_var); varpool_mark_needed_node (varpool_node (ic_void_ptr_var)); gcov_type_ptr = build_pointer_type (get_gcov_type ()); ic_gcov_type_ptr_var = build_decl (UNKNOWN_LOCATION, VAR_DECL, get_identifier ("__gcov_indirect_call_counters"), gcov_type_ptr); TREE_STATIC (ic_gcov_type_ptr_var) = 1; TREE_PUBLIC (ic_gcov_type_ptr_var) = 0; DECL_ARTIFICIAL (ic_gcov_type_ptr_var) = 1; DECL_INITIAL (ic_gcov_type_ptr_var) = NULL; varpool_finalize_decl (ic_gcov_type_ptr_var); varpool_mark_needed_node (varpool_node (ic_gcov_type_ptr_var)); }
static bool mark_load (gimple stmt, tree t, void *data) { t = get_base_address (t); if (t && TREE_CODE (t) == FUNCTION_DECL) { /* ??? This can happen on platforms with descriptors when these are directly manipulated in the code. Pretend that it's an address. */ struct cgraph_node *node = cgraph_get_create_node (t); cgraph_mark_address_taken_node (node); ipa_record_reference ((struct cgraph_node *)data, NULL, node, NULL, IPA_REF_ADDR, stmt); } else if (t && TREE_CODE (t) == VAR_DECL && (TREE_STATIC (t) || DECL_EXTERNAL (t))) { struct varpool_node *vnode = varpool_node (t); int walk_subtrees; if (lang_hooks.callgraph.analyze_expr) lang_hooks.callgraph.analyze_expr (&t, &walk_subtrees); varpool_mark_needed_node (vnode); ipa_record_reference ((struct cgraph_node *)data, NULL, NULL, vnode, IPA_REF_LOAD, stmt); } return false; }
static bool mark_address (gimple stmt, tree addr, void *data) { addr = get_base_address (addr); if (TREE_CODE (addr) == FUNCTION_DECL) { struct cgraph_node *node = cgraph_get_create_node (addr); cgraph_mark_address_taken_node (node); ipa_record_reference ((struct cgraph_node *)data, NULL, node, NULL, IPA_REF_ADDR, stmt); } else if (addr && TREE_CODE (addr) == VAR_DECL && (TREE_STATIC (addr) || DECL_EXTERNAL (addr))) { struct varpool_node *vnode = varpool_node (addr); int walk_subtrees; if (lang_hooks.callgraph.analyze_expr) lang_hooks.callgraph.analyze_expr (&addr, &walk_subtrees); varpool_mark_needed_node (vnode); ipa_record_reference ((struct cgraph_node *)data, NULL, NULL, vnode, IPA_REF_ADDR, stmt); } return false; }
struct varpool_node * varpool_extra_name_alias (tree alias, tree decl) { struct varpool_node key, *alias_node, *decl_node, **slot; #ifndef ASM_OUTPUT_DEF /* If aliases aren't supported by the assembler, fail. */ return false; #endif gcc_assert (TREE_CODE (decl) == VAR_DECL); gcc_assert (TREE_CODE (alias) == VAR_DECL); /* Make sure the hash table has been created. */ decl_node = varpool_node (decl); key.decl = alias; slot = (struct varpool_node **) htab_find_slot (varpool_hash, &key, INSERT); /* If the varpool_node has been already created, fail. */ if (*slot) return NULL; alias_node = ggc_alloc_cleared_varpool_node (); alias_node->decl = alias; alias_node->alias = 1; alias_node->extra_name = decl_node; alias_node->next = decl_node->extra_name; ipa_empty_ref_list (&alias_node->ref_list); if (decl_node->extra_name) decl_node->extra_name->prev = alias_node; decl_node->extra_name = alias_node; *slot = alias_node; return alias_node; }
bool varpool_extra_name_alias (tree alias, tree decl) { struct varpool_node key, *alias_node, *decl_node, **slot; #ifndef ASM_OUTPUT_DEF /* If aliases aren't supported by the assembler, fail. */ return false; #endif gcc_assert (TREE_CODE (decl) == VAR_DECL); gcc_assert (TREE_CODE (alias) == VAR_DECL); /* Make sure the hash table has been created. */ decl_node = varpool_node (decl); key.decl = alias; slot = (struct varpool_node **) htab_find_slot (varpool_hash, &key, INSERT); /* If the varpool_node has been already created, fail. */ if (*slot) return false; alias_node = GGC_CNEW (struct varpool_node); alias_node->decl = alias; alias_node->alias = 1; alias_node->extra_name = decl_node; alias_node->next = decl_node->extra_name; decl_node->extra_name = alias_node; *slot = alias_node; return true; }
/* Mark DECL as finalized. By finalizing the declaration, frontend instruct the middle end to output the variable to asm file, if needed or externally visible. */ void varpool_finalize_decl (tree decl) { struct varpool_node *node = varpool_node (decl); gcc_assert (TREE_STATIC (decl)); /* The first declaration of a variable that comes through this function decides whether it is global (in C, has external linkage) or local (in C, has internal linkage). So do nothing more if this function has already run. */ if (node->finalized) { if (cgraph_global_info_ready) varpool_assemble_pending_decls (); return; } if (node->needed) varpool_enqueue_needed_node (node); node->finalized = true; if (TREE_THIS_VOLATILE (decl) || DECL_PRESERVE_P (decl) /* Traditionally we do not eliminate static variables when not optimizing and when not doing toplevel reoder. */ || (!flag_toplevel_reorder && !DECL_COMDAT (node->symbol.decl) && !DECL_ARTIFICIAL (node->symbol.decl))) node->symbol.force_output = true; if (decide_is_variable_needed (node, decl)) varpool_mark_needed_node (node); if (cgraph_global_info_ready) varpool_assemble_pending_decls (); }
/* Mark DECL as finalized. By finalizing the declaration, frontend instruct the middle end to output the variable to asm file, if needed or externally visible. */ void varpool_finalize_decl (tree decl) { struct varpool_node *node = varpool_node (decl); gcc_assert (TREE_STATIC (decl)); /* The first declaration of a variable that comes through this function decides whether it is global (in C, has external linkage) or local (in C, has internal linkage). So do nothing more if this function has already run. */ if (node->finalized) { if (cgraph_global_info_ready) varpool_assemble_pending_decls (); return; } if (node->needed) varpool_enqueue_needed_node (node); node->finalized = true; if (TREE_THIS_VOLATILE (decl) || DECL_PRESERVE_P (decl)) node->force_output = true; if (decide_is_variable_needed (node, decl)) varpool_mark_needed_node (node); if (cgraph_global_info_ready) varpool_assemble_pending_decls (); }
/* Mark DECL as finalized. By finalizing the declaration, frontend instruct the middle end to output the variable to asm file, if needed or externally visible. */ void varpool_finalize_decl (tree decl) { struct varpool_node *node = varpool_node (decl); /* The first declaration of a variable that comes through this function decides whether it is global (in C, has external linkage) or local (in C, has internal linkage). So do nothing more if this function has already run. */ if (node->finalized) { if (cgraph_global_info_ready) varpool_assemble_pending_decls (); return; } if (node->needed) varpool_enqueue_needed_node (node); node->finalized = true; if (decide_is_variable_needed (node, decl)) varpool_mark_needed_node (node); /* Since we reclaim unreachable nodes at the end of every language level unit, we need to be conservative about possible entry points there. */ else if (TREE_PUBLIC (decl) && !DECL_COMDAT (decl) && !DECL_EXTERNAL (decl)) varpool_mark_needed_node (node); if (cgraph_global_info_ready) varpool_assemble_pending_decls (); }
/* Add the variable DECL to the varpool. Unlike varpool_finalize_decl function is intended to be used by middle end and allows insertion of new variable at arbitrary point of compilation. */ void varpool_add_new_variable (tree decl) { struct varpool_node *node; varpool_finalize_decl (decl); node = varpool_node (decl); if (varpool_externally_visible_p (node, false)) node->externally_visible = true; }
void record_references_in_initializer (tree decl, bool only_vars) { struct pointer_set_t *visited_nodes = pointer_set_create (); struct varpool_node *node = varpool_node (decl); struct record_reference_ctx ctx = {false, NULL}; ctx.varpool_node = node; ctx.only_vars = only_vars; walk_tree (&DECL_INITIAL (decl), record_reference, &ctx, visited_nodes); pointer_set_destroy (visited_nodes); }
/* Determine if variable DECL is needed. That is, visible to something either outside this translation unit, something magic in the system configury */ bool decide_is_variable_needed (struct varpool_node *node, tree decl) { /* If the user told us it is used, then it must be so. */ if ((node->externally_visible && !DECL_COMDAT (decl)) || node->force_output) return true; /* ??? If the assembler name is set by hand, it is possible to assemble the name later after finalizing the function and the fact is noticed in assemble_name then. This is arguably a bug. */ if (DECL_ASSEMBLER_NAME_SET_P (decl) && TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl))) return true; /* Externally visible variables must be output. The exception is COMDAT variables that must be output only when they are needed. */ if (TREE_PUBLIC (decl) && !flag_whole_program && !flag_lto && !flag_whopr && !DECL_COMDAT (decl) && !DECL_EXTERNAL (decl)) return true; /* When emulating tls, we actually see references to the control variable, rather than the user-level variable. */ if (!targetm.have_tls && TREE_CODE (decl) == VAR_DECL && DECL_THREAD_LOCAL_P (decl)) { tree control = emutls_decl (decl); if (decide_is_variable_needed (varpool_node (control), control)) return true; } /* When not reordering top level variables, we have to assume that we are going to keep everything. */ if (flag_toplevel_reorder) return false; /* We want to emit COMDAT variables only when absolutely necessary. */ if (DECL_COMDAT (decl)) return false; return true; }
struct varpool_node * varpool_create_variable_alias (tree alias, tree decl) { struct varpool_node *alias_node; gcc_assert (TREE_CODE (decl) == VAR_DECL); gcc_assert (TREE_CODE (alias) == VAR_DECL); alias_node = varpool_node (alias); alias_node->alias = 1; alias_node->finalized = 1; alias_node->alias_of = decl; if ((!DECL_EXTERNAL (alias) && decide_is_variable_needed (alias_node, alias)) || alias_node->needed) varpool_mark_needed_node (alias_node); return alias_node; }
static bool mark_store (gimple stmt, tree t, void *data) { t = get_base_address (t); if (t && TREE_CODE (t) == VAR_DECL && (TREE_STATIC (t) || DECL_EXTERNAL (t))) { struct varpool_node *vnode = varpool_node (t); int walk_subtrees; if (lang_hooks.callgraph.analyze_expr) lang_hooks.callgraph.analyze_expr (&t, &walk_subtrees); varpool_mark_needed_node (vnode); ipa_record_reference ((struct cgraph_node *)data, NULL, NULL, vnode, IPA_REF_STORE, stmt); } return false; }
static void build_one_array (gimple swtch, int num, tree arr_index_type, gimple phi, tree tidx) { tree array_type, ctor, decl, value_type, name, fetch; gimple load; gimple_stmt_iterator gsi; gcc_assert (info.default_values[num]); value_type = TREE_TYPE (info.default_values[num]); array_type = build_array_type (value_type, arr_index_type); ctor = build_constructor (array_type, info.constructors[num]); TREE_CONSTANT (ctor) = true; decl = build_decl (VAR_DECL, NULL_TREE, array_type); TREE_STATIC (decl) = 1; DECL_INITIAL (decl) = ctor; DECL_NAME (decl) = create_tmp_var_name ("CSWTCH"); DECL_ARTIFICIAL (decl) = 1; TREE_CONSTANT (decl) = 1; add_referenced_var (decl); varpool_mark_needed_node (varpool_node (decl)); varpool_finalize_decl (decl); mark_sym_for_renaming (decl); name = make_ssa_name (SSA_NAME_VAR (PHI_RESULT (phi)), NULL); info.target_inbound_names[num] = name; fetch = build4 (ARRAY_REF, value_type, decl, tidx, NULL_TREE, NULL_TREE); load = gimple_build_assign (name, fetch); SSA_NAME_DEF_STMT (name) = load; gsi = gsi_for_stmt (swtch); gsi_insert_before (&gsi, load, GSI_SAME_STMT); mark_symbols_for_renaming (load); info.arr_ref_last = load; }
/* Mark DECL as finalized. By finalizing the declaration, frontend instruct the middle end to output the variable to asm file, if needed or externally visible. */ void varpool_finalize_decl (tree decl) { struct varpool_node *node = varpool_node (decl); /* FIXME: We don't really stream varpool datastructure and instead rebuild it by varpool_finalize_decl. This is not quite correct since this way we can't attach any info to varpool. Eventually we will want to stream varpool nodes and the flags. For the moment just prevent analysis of varpool nodes to happen again, so we will re-try to compute "address_taken" flag of varpool that breaks in presence of clones. */ if (in_lto_p) node->analyzed = true; /* The first declaration of a variable that comes through this function decides whether it is global (in C, has external linkage) or local (in C, has internal linkage). So do nothing more if this function has already run. */ if (node->finalized) { if (cgraph_global_info_ready) varpool_assemble_pending_decls (); return; } if (node->needed) varpool_enqueue_needed_node (node); node->finalized = true; if (decide_is_variable_needed (node, decl)) varpool_mark_needed_node (node); /* Since we reclaim unreachable nodes at the end of every language level unit, we need to be conservative about possible entry points there. */ else if (TREE_PUBLIC (decl) && !DECL_COMDAT (decl) && !DECL_EXTERNAL (decl)) varpool_mark_needed_node (node); if (cgraph_global_info_ready) varpool_assemble_pending_decls (); }
/* Create a new global variable of type TYPE. */ tree add_new_static_var (tree type) { tree new_decl; struct varpool_node *new_node; new_decl = create_tmp_var (type, NULL); DECL_NAME (new_decl) = create_tmp_var_name (NULL); TREE_READONLY (new_decl) = 0; TREE_STATIC (new_decl) = 1; TREE_USED (new_decl) = 1; DECL_CONTEXT (new_decl) = NULL_TREE; DECL_ABSTRACT (new_decl) = 0; lang_hooks.dup_lang_specific_decl (new_decl); create_var_ann (new_decl); new_node = varpool_node (new_decl); varpool_mark_needed_node (new_node); add_referenced_var (new_decl); varpool_finalize_decl (new_decl); return new_node->decl; }
static void record_type_list (struct cgraph_node *node, tree list) { for (; list; list = TREE_CHAIN (list)) { tree type = TREE_VALUE (list); if (TYPE_P (type)) type = lookup_type_for_runtime (type); STRIP_NOPS (type); if (TREE_CODE (type) == ADDR_EXPR) { type = TREE_OPERAND (type, 0); if (TREE_CODE (type) == VAR_DECL) { struct varpool_node *vnode = varpool_node (type); varpool_mark_needed_node (vnode); ipa_record_reference (node, NULL, NULL, vnode, IPA_REF_ADDR, NULL); } } } }
void rest_of_decl_compilation (tree decl, int top_level, int at_end) { /* We deferred calling assemble_alias so that we could collect other attributes such as visibility. Emit the alias now. */ { tree alias; alias = lookup_attribute ("alias", DECL_ATTRIBUTES (decl)); if (alias) { alias = TREE_VALUE (TREE_VALUE (alias)); alias = get_identifier (TREE_STRING_POINTER (alias)); assemble_alias (decl, alias); } } /* Can't defer this, because it needs to happen before any later function definitions are processed. */ if (DECL_ASSEMBLER_NAME_SET_P (decl) && DECL_REGISTER (decl)) make_decl_rtl (decl); /* Forward declarations for nested functions are not "external", but we need to treat them as if they were. */ if (TREE_STATIC (decl) || DECL_EXTERNAL (decl) || TREE_CODE (decl) == FUNCTION_DECL) { timevar_push (TV_VARCONST); /* Don't output anything when a tentative file-scope definition is seen. But at end of compilation, do output code for them. We do output all variables when unit-at-a-time is active and rely on callgraph code to defer them except for forward declarations (see gcc.c-torture/compile/920624-1.c) */ if ((at_end || !DECL_DEFER_OUTPUT (decl) || DECL_INITIAL (decl)) && !DECL_EXTERNAL (decl)) { if (TREE_CODE (decl) != FUNCTION_DECL) varpool_finalize_decl (decl); else assemble_variable (decl, top_level, at_end, 0); } #ifdef ASM_FINISH_DECLARE_OBJECT if (decl == last_assemble_variable_decl) { ASM_FINISH_DECLARE_OBJECT (asm_out_file, decl, top_level, at_end); } #endif timevar_pop (TV_VARCONST); } else if (TREE_CODE (decl) == TYPE_DECL /* Like in rest_of_type_compilation, avoid confusing the debug information machinery when there are errors. */ && !(sorrycount || errorcount)) { timevar_push (TV_SYMOUT); debug_hooks->type_decl (decl, !top_level); timevar_pop (TV_SYMOUT); } /* Let cgraph know about the existence of variables. */ if (TREE_CODE (decl) == VAR_DECL && !DECL_EXTERNAL (decl)) varpool_node (decl); }
/* Walk the decls we marked as necessary and see if they reference new variables or functions and add them into the worklists. */ bool varpool_analyze_pending_decls (void) { bool changed = false; timevar_push (TV_VARPOOL); while (varpool_first_unanalyzed_node) { struct varpool_node *node = varpool_first_unanalyzed_node, *next; tree decl = node->decl; bool analyzed = node->analyzed; varpool_first_unanalyzed_node->analyzed = true; varpool_first_unanalyzed_node = varpool_first_unanalyzed_node->next_needed; /* When reading back varpool at LTO time, we re-construct the queue in order to have "needed" list right by inserting all needed nodes into varpool. We however don't want to re-analyze already analyzed nodes. */ if (!analyzed) { gcc_assert (!in_lto_p || cgraph_function_flags_ready); /* Compute the alignment early so function body expanders are already informed about increased alignment. */ align_variable (decl, 0); } if (node->alias && node->alias_of) { struct varpool_node *tgt = varpool_node (node->alias_of); struct varpool_node *n; for (n = tgt; n && n->alias; n = n->analyzed ? varpool_alias_aliased_node (n) : NULL) if (n == node) { error ("variable %q+D part of alias cycle", node->decl); node->alias = false; continue; } if (!VEC_length (ipa_ref_t, node->ref_list.references)) ipa_record_reference (NULL, node, NULL, tgt, IPA_REF_ALIAS, NULL); /* C++ FE sometimes change linkage flags after producing same body aliases. */ if (node->extra_name_alias) { DECL_WEAK (node->decl) = DECL_WEAK (node->alias_of); TREE_PUBLIC (node->decl) = TREE_PUBLIC (node->alias_of); DECL_EXTERNAL (node->decl) = DECL_EXTERNAL (node->alias_of); DECL_VISIBILITY (node->decl) = DECL_VISIBILITY (node->alias_of); if (TREE_PUBLIC (node->decl)) { DECL_COMDAT (node->decl) = DECL_COMDAT (node->alias_of); DECL_COMDAT_GROUP (node->decl) = DECL_COMDAT_GROUP (node->alias_of); if (DECL_ONE_ONLY (node->alias_of) && !node->same_comdat_group) { node->same_comdat_group = tgt; if (!tgt->same_comdat_group) tgt->same_comdat_group = node; else { struct varpool_node *n; for (n = tgt->same_comdat_group; n->same_comdat_group != tgt; n = n->same_comdat_group) ; n->same_comdat_group = node; } } } } } else if (DECL_INITIAL (decl)) record_references_in_initializer (decl, analyzed); if (node->same_comdat_group) { for (next = node->same_comdat_group; next != node; next = next->same_comdat_group) varpool_mark_needed_node (next); } changed = true; } timevar_pop (TV_VARPOOL); return changed; }
static tree record_reference (tree *tp, int *walk_subtrees, void *data) { tree t = *tp; tree decl; struct record_reference_ctx *ctx = (struct record_reference_ctx *)data; t = canonicalize_constructor_val (t); if (!t) t = *tp; else if (t != *tp) *tp = t; switch (TREE_CODE (t)) { case VAR_DECL: case FUNCTION_DECL: gcc_unreachable (); break; case FDESC_EXPR: case ADDR_EXPR: /* Record dereferences to the functions. This makes the functions reachable unconditionally. */ decl = get_base_var (*tp); if (TREE_CODE (decl) == FUNCTION_DECL) { struct cgraph_node *node = cgraph_get_create_node (decl); if (!ctx->only_vars) cgraph_mark_address_taken_node (node); ipa_record_reference (NULL, ctx->varpool_node, node, NULL, IPA_REF_ADDR, NULL); } if (TREE_CODE (decl) == VAR_DECL) { struct varpool_node *vnode = varpool_node (decl); if (lang_hooks.callgraph.analyze_expr) lang_hooks.callgraph.analyze_expr (&decl, walk_subtrees); varpool_mark_needed_node (vnode); ipa_record_reference (NULL, ctx->varpool_node, NULL, vnode, IPA_REF_ADDR, NULL); } *walk_subtrees = 0; break; default: /* Save some cycles by not walking types and declaration as we won't find anything useful there anyway. */ if (IS_TYPE_OR_DECL_P (*tp)) { *walk_subtrees = 0; break; } if ((unsigned int) TREE_CODE (t) >= LAST_AND_UNUSED_TREE_CODE) return lang_hooks.callgraph.analyze_expr (tp, walk_subtrees); break; } return NULL_TREE; }