void fixup_same_cpp_alias_visibility (symtab_node node, symtab_node target) { if (is_a <cgraph_node> (node)) { DECL_DECLARED_INLINE_P (node->symbol.decl) = DECL_DECLARED_INLINE_P (target->symbol.decl); DECL_DISREGARD_INLINE_LIMITS (node->symbol.decl) = DECL_DISREGARD_INLINE_LIMITS (target->symbol.decl); } /* FIXME: It is not really clear why those flags should not be copied for functions, too. */ else { DECL_WEAK (node->symbol.decl) = DECL_WEAK (target->symbol.decl); DECL_EXTERNAL (node->symbol.decl) = DECL_EXTERNAL (target->symbol.decl); DECL_VISIBILITY (node->symbol.decl) = DECL_VISIBILITY (target->symbol.decl); } DECL_VIRTUAL_P (node->symbol.decl) = DECL_VIRTUAL_P (target->symbol.decl); if (TREE_PUBLIC (node->symbol.decl)) { DECL_EXTERNAL (node->symbol.decl) = DECL_EXTERNAL (target->symbol.decl); DECL_COMDAT (node->symbol.decl) = DECL_COMDAT (target->symbol.decl); DECL_COMDAT_GROUP (node->symbol.decl) = DECL_COMDAT_GROUP (target->symbol.decl); if (DECL_ONE_ONLY (target->symbol.decl) && !node->symbol.same_comdat_group) symtab_add_to_same_comdat_group ((symtab_node)node, (symtab_node)target); } node->symbol.externally_visible = target->symbol.externally_visible; }
void symtab_node::fixup_same_cpp_alias_visibility (symtab_node *target) { if (is_a <cgraph_node *> (this)) { DECL_DECLARED_INLINE_P (decl) = DECL_DECLARED_INLINE_P (target->decl); DECL_DISREGARD_INLINE_LIMITS (decl) = DECL_DISREGARD_INLINE_LIMITS (target->decl); } /* FIXME: It is not really clear why those flags should not be copied for functions, too. */ else { DECL_WEAK (decl) = DECL_WEAK (target->decl); DECL_EXTERNAL (decl) = DECL_EXTERNAL (target->decl); DECL_VISIBILITY (decl) = DECL_VISIBILITY (target->decl); } DECL_VIRTUAL_P (decl) = DECL_VIRTUAL_P (target->decl); if (TREE_PUBLIC (decl)) { tree group; DECL_EXTERNAL (decl) = DECL_EXTERNAL (target->decl); DECL_COMDAT (decl) = DECL_COMDAT (target->decl); group = target->get_comdat_group (); set_comdat_group (group); if (group && !same_comdat_group) add_to_same_comdat_group (target); } externally_visible = target->externally_visible; }
/* Output one variable, if necessary. Return whether we output it. */ bool varpool_assemble_decl (struct varpool_node *node) { tree decl = node->decl; if (!TREE_ASM_WRITTEN (decl) && !node->alias && !DECL_EXTERNAL (decl) && (TREE_CODE (decl) != VAR_DECL || !DECL_HAS_VALUE_EXPR_P (decl))) { assemble_variable (decl, 0, 1, 0); if (TREE_ASM_WRITTEN (decl)) { struct varpool_node *alias; node->next_needed = varpool_assembled_nodes_queue; varpool_assembled_nodes_queue = node; node->finalized = 1; /* Also emit any extra name aliases. */ for (alias = node->extra_name; alias; alias = alias->next) { /* Update linkage fields in case they've changed. */ DECL_WEAK (alias->decl) = DECL_WEAK (decl); TREE_PUBLIC (alias->decl) = TREE_PUBLIC (decl); DECL_VISIBILITY (alias->decl) = DECL_VISIBILITY (decl); assemble_alias (alias->decl, DECL_ASSEMBLER_NAME (decl)); } return true; } } return false; }
static void pack_ts_decl_with_vis_value_fields (struct bitpack_d *bp, tree expr) { bp_pack_value (bp, DECL_COMMON (expr), 1); bp_pack_value (bp, DECL_DLLIMPORT_P (expr), 1); bp_pack_value (bp, DECL_WEAK (expr), 1); bp_pack_value (bp, DECL_SEEN_IN_BIND_EXPR_P (expr), 1); bp_pack_value (bp, DECL_COMDAT (expr), 1); bp_pack_value (bp, DECL_VISIBILITY (expr), 2); bp_pack_value (bp, DECL_VISIBILITY_SPECIFIED (expr), 1); if (TREE_CODE (expr) == VAR_DECL) { bp_pack_value (bp, DECL_HARD_REGISTER (expr), 1); /* DECL_IN_TEXT_SECTION is set during final asm output only. */ bp_pack_value (bp, DECL_IN_CONSTANT_POOL (expr), 1); } if (TREE_CODE (expr) == FUNCTION_DECL) { bp_pack_value (bp, DECL_FINAL_P (expr), 1); bp_pack_value (bp, DECL_CXX_CONSTRUCTOR_P (expr), 1); bp_pack_value (bp, DECL_CXX_DESTRUCTOR_P (expr), 1); } }
static void unpack_ts_decl_with_vis_value_fields (struct bitpack_d *bp, tree expr) { DECL_DEFER_OUTPUT (expr) = (unsigned) bp_unpack_value (bp, 1); DECL_COMMON (expr) = (unsigned) bp_unpack_value (bp, 1); DECL_DLLIMPORT_P (expr) = (unsigned) bp_unpack_value (bp, 1); DECL_WEAK (expr) = (unsigned) bp_unpack_value (bp, 1); DECL_SEEN_IN_BIND_EXPR_P (expr) = (unsigned) bp_unpack_value (bp, 1); DECL_COMDAT (expr) = (unsigned) bp_unpack_value (bp, 1); DECL_VISIBILITY (expr) = (enum symbol_visibility) bp_unpack_value (bp, 2); DECL_VISIBILITY_SPECIFIED (expr) = (unsigned) bp_unpack_value (bp, 1); if (TREE_CODE (expr) == VAR_DECL) { DECL_HARD_REGISTER (expr) = (unsigned) bp_unpack_value (bp, 1); DECL_IN_TEXT_SECTION (expr) = (unsigned) bp_unpack_value (bp, 1); DECL_IN_CONSTANT_POOL (expr) = (unsigned) bp_unpack_value (bp, 1); DECL_TLS_MODEL (expr) = (enum tls_model) bp_unpack_value (bp, 3); } if (VAR_OR_FUNCTION_DECL_P (expr)) { priority_type p; p = (priority_type) bp_unpack_var_len_unsigned (bp); SET_DECL_INIT_PRIORITY (expr, p); } }
void symtab_node::make_decl_local (void) { rtx rtl, symbol; /* Avoid clearing comdat_groups on comdat-local decls. */ if (TREE_PUBLIC (decl) == 0) return; if (TREE_CODE (decl) == VAR_DECL) DECL_COMMON (decl) = 0; else gcc_assert (TREE_CODE (decl) == FUNCTION_DECL); DECL_COMDAT (decl) = 0; DECL_WEAK (decl) = 0; DECL_EXTERNAL (decl) = 0; DECL_VISIBILITY_SPECIFIED (decl) = 0; DECL_VISIBILITY (decl) = VISIBILITY_DEFAULT; TREE_PUBLIC (decl) = 0; if (!DECL_RTL_SET_P (decl)) return; /* Update rtl flags. */ make_decl_rtl (decl); rtl = DECL_RTL (decl); if (!MEM_P (rtl)) return; symbol = XEXP (rtl, 0); if (GET_CODE (symbol) != SYMBOL_REF) return; SYMBOL_REF_WEAK (symbol) = DECL_WEAK (decl); }
void varpool_analyze_node (struct varpool_node *node) { tree decl = node->symbol.decl; /* 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 (!node->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_for_decl (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->symbol.decl); node->alias = false; continue; } if (!vec_safe_length (node->symbol.ref_list.references)) ipa_record_reference ((symtab_node)node, (symtab_node)tgt, IPA_REF_ALIAS, NULL); if (node->extra_name_alias) { DECL_WEAK (node->symbol.decl) = DECL_WEAK (node->alias_of); DECL_EXTERNAL (node->symbol.decl) = DECL_EXTERNAL (node->alias_of); DECL_VISIBILITY (node->symbol.decl) = DECL_VISIBILITY (node->alias_of); fixup_same_cpp_alias_visibility ((symtab_node) node, (symtab_node) tgt, node->alias_of); } } else if (DECL_INITIAL (decl)) record_references_in_initializer (decl, node->analyzed); node->analyzed = true; }
void symtab_make_decl_local (tree decl) { rtx rtl, symbol; /* Avoid clearing DECL_COMDAT_GROUP on comdat-local decls. */ if (TREE_PUBLIC (decl) == 0) return; if (TREE_CODE (decl) == VAR_DECL) DECL_COMMON (decl) = 0; else gcc_assert (TREE_CODE (decl) == FUNCTION_DECL); if (DECL_ONE_ONLY (decl) || DECL_COMDAT (decl)) { DECL_SECTION_NAME (decl) = 0; DECL_COMDAT (decl) = 0; } DECL_COMDAT_GROUP (decl) = 0; DECL_WEAK (decl) = 0; DECL_EXTERNAL (decl) = 0; DECL_VISIBILITY_SPECIFIED (decl) = 0; DECL_VISIBILITY (decl) = VISIBILITY_DEFAULT; TREE_PUBLIC (decl) = 0; DECL_DLLIMPORT_P (decl) = 0; if (!DECL_RTL_SET_P (decl)) return; /* Update rtl flags. */ make_decl_rtl (decl); rtl = DECL_RTL (decl); if (!MEM_P (rtl)) return; symbol = XEXP (rtl, 0); if (GET_CODE (symbol) != SYMBOL_REF) return; SYMBOL_REF_WEAK (symbol) = DECL_WEAK (decl); }
static void pack_ts_decl_with_vis_value_fields (struct bitpack_d *bp, tree expr) { bp_pack_value (bp, DECL_DEFER_OUTPUT (expr), 1); bp_pack_value (bp, DECL_COMMON (expr), 1); bp_pack_value (bp, DECL_DLLIMPORT_P (expr), 1); bp_pack_value (bp, DECL_WEAK (expr), 1); bp_pack_value (bp, DECL_SEEN_IN_BIND_EXPR_P (expr), 1); bp_pack_value (bp, DECL_COMDAT (expr), 1); bp_pack_value (bp, DECL_VISIBILITY (expr), 2); bp_pack_value (bp, DECL_VISIBILITY_SPECIFIED (expr), 1); if (TREE_CODE (expr) == VAR_DECL) { bp_pack_value (bp, DECL_HARD_REGISTER (expr), 1); bp_pack_value (bp, DECL_IN_TEXT_SECTION (expr), 1); bp_pack_value (bp, DECL_IN_CONSTANT_POOL (expr), 1); bp_pack_value (bp, DECL_TLS_MODEL (expr), 3); } if (VAR_OR_FUNCTION_DECL_P (expr)) bp_pack_var_len_unsigned (bp, DECL_INIT_PRIORITY (expr)); }
static void unpack_ts_decl_with_vis_value_fields (struct bitpack_d *bp, tree expr) { DECL_COMMON (expr) = (unsigned) bp_unpack_value (bp, 1); DECL_DLLIMPORT_P (expr) = (unsigned) bp_unpack_value (bp, 1); DECL_WEAK (expr) = (unsigned) bp_unpack_value (bp, 1); DECL_SEEN_IN_BIND_EXPR_P (expr) = (unsigned) bp_unpack_value (bp, 1); DECL_COMDAT (expr) = (unsigned) bp_unpack_value (bp, 1); DECL_VISIBILITY (expr) = (enum symbol_visibility) bp_unpack_value (bp, 2); DECL_VISIBILITY_SPECIFIED (expr) = (unsigned) bp_unpack_value (bp, 1); if (TREE_CODE (expr) == VAR_DECL) { DECL_HARD_REGISTER (expr) = (unsigned) bp_unpack_value (bp, 1); DECL_IN_CONSTANT_POOL (expr) = (unsigned) bp_unpack_value (bp, 1); } if (TREE_CODE (expr) == FUNCTION_DECL) { DECL_FINAL_P (expr) = (unsigned) bp_unpack_value (bp, 1); DECL_CXX_CONSTRUCTOR_P (expr) = (unsigned) bp_unpack_value (bp, 1); DECL_CXX_DESTRUCTOR_P (expr) = (unsigned) bp_unpack_value (bp, 1); } }
void dump_symtab_base (FILE *f, symtab_node node) { static const char * const visibility_types[] = { "default", "protected", "hidden", "internal" }; fprintf (f, "%s/%i (%s)", symtab_node_asm_name (node), node->symbol.order, symtab_node_name (node)); dump_addr (f, " @", (void *)node); fprintf (f, "\n Type: %s\n", symtab_type_names[node->symbol.type]); fprintf (f, " Visibility:"); if (node->symbol.in_other_partition) fprintf (f, " in_other_partition"); if (node->symbol.used_from_other_partition) fprintf (f, " used_from_other_partition"); if (node->symbol.force_output) fprintf (f, " force_output"); if (node->symbol.resolution != LDPR_UNKNOWN) fprintf (f, " %s", ld_plugin_symbol_resolution_names[(int)node->symbol.resolution]); if (TREE_ASM_WRITTEN (node->symbol.decl)) fprintf (f, " asm_written"); if (DECL_EXTERNAL (node->symbol.decl)) fprintf (f, " external"); if (TREE_PUBLIC (node->symbol.decl)) fprintf (f, " public"); if (DECL_COMMON (node->symbol.decl)) fprintf (f, " common"); if (DECL_WEAK (node->symbol.decl)) fprintf (f, " weak"); if (DECL_DLLIMPORT_P (node->symbol.decl)) fprintf (f, " dll_import"); if (DECL_COMDAT (node->symbol.decl)) fprintf (f, " comdat"); if (DECL_COMDAT_GROUP (node->symbol.decl)) fprintf (f, " comdat_group:%s", IDENTIFIER_POINTER (DECL_COMDAT_GROUP (node->symbol.decl))); if (DECL_ONE_ONLY (node->symbol.decl)) fprintf (f, " one_only"); if (DECL_SECTION_NAME (node->symbol.decl)) fprintf (f, " section_name:%s", TREE_STRING_POINTER (DECL_SECTION_NAME (node->symbol.decl))); if (DECL_VISIBILITY_SPECIFIED (node->symbol.decl)) fprintf (f, " visibility_specified"); if (DECL_VISIBILITY (node->symbol.decl)) fprintf (f, " visibility:%s", visibility_types [DECL_VISIBILITY (node->symbol.decl)]); if (DECL_VIRTUAL_P (node->symbol.decl)) fprintf (f, " virtual"); if (DECL_ARTIFICIAL (node->symbol.decl)) fprintf (f, " artificial"); if (TREE_CODE (node->symbol.decl) == FUNCTION_DECL) { if (DECL_STATIC_CONSTRUCTOR (node->symbol.decl)) fprintf (f, " constructor"); if (DECL_STATIC_DESTRUCTOR (node->symbol.decl)) fprintf (f, " destructor"); } fprintf (f, "\n"); if (node->symbol.same_comdat_group) fprintf (f, " Same comdat group as: %s/%i\n", symtab_node_asm_name (node->symbol.same_comdat_group), node->symbol.same_comdat_group->symbol.order); if (node->symbol.next_sharing_asm_name) fprintf (f, " next sharing asm name: %i\n", node->symbol.next_sharing_asm_name->symbol.order); if (node->symbol.previous_sharing_asm_name) fprintf (f, " previous sharing asm name: %i\n", node->symbol.previous_sharing_asm_name->symbol.order); if (node->symbol.address_taken) fprintf (f, " Address is taken.\n"); if (node->symbol.aux) { fprintf (f, " Aux:"); dump_addr (f, " @", (void *)node->symbol.aux); } fprintf (f, " References: "); ipa_dump_references (f, &node->symbol.ref_list); fprintf (f, " Referring: "); ipa_dump_referring (f, &node->symbol.ref_list); }
/* Add code: __thread gcov* __gcov_indirect_call_counters; // pointer to actual counter __thread void* __gcov_indirect_call_callee; // actual callee address __thread int __gcov_function_counter; // time profiler function counter */ static void init_ic_make_global_vars (void) { tree gcov_type_ptr; ptr_void = build_pointer_type (void_type_node); /* Workaround for binutils bug 14342. Once it is fixed, remove lto path. */ if (flag_lto) { ic_void_ptr_var = build_decl (UNKNOWN_LOCATION, VAR_DECL, get_identifier ("__gcov_indirect_call_callee_ltopriv"), ptr_void); TREE_PUBLIC (ic_void_ptr_var) = 1; DECL_COMMON (ic_void_ptr_var) = 1; DECL_VISIBILITY (ic_void_ptr_var) = VISIBILITY_HIDDEN; DECL_VISIBILITY_SPECIFIED (ic_void_ptr_var) = true; } else { ic_void_ptr_var = build_decl (UNKNOWN_LOCATION, VAR_DECL, get_identifier ( (PARAM_VALUE (PARAM_INDIR_CALL_TOPN_PROFILE) ? "__gcov_indirect_call_topn_callee" : "__gcov_indirect_call_callee")), ptr_void); TREE_PUBLIC (ic_void_ptr_var) = 1; DECL_EXTERNAL (ic_void_ptr_var) = 1; } TREE_STATIC (ic_void_ptr_var) = 1; DECL_ARTIFICIAL (ic_void_ptr_var) = 1; DECL_INITIAL (ic_void_ptr_var) = NULL; if (targetm.have_tls) set_decl_tls_model (ic_void_ptr_var, decl_default_tls_model (ic_void_ptr_var)); varpool_node::finalize_decl (ic_void_ptr_var); gcov_type_ptr = build_pointer_type (get_gcov_type ()); /* Workaround for binutils bug 14342. Once it is fixed, remove lto path. */ if (flag_lto) { ic_gcov_type_ptr_var = build_decl (UNKNOWN_LOCATION, VAR_DECL, get_identifier ("__gcov_indirect_call_counters_ltopriv"), gcov_type_ptr); TREE_PUBLIC (ic_gcov_type_ptr_var) = 1; DECL_COMMON (ic_gcov_type_ptr_var) = 1; DECL_VISIBILITY (ic_gcov_type_ptr_var) = VISIBILITY_HIDDEN; DECL_VISIBILITY_SPECIFIED (ic_gcov_type_ptr_var) = true; } else { ic_gcov_type_ptr_var = build_decl (UNKNOWN_LOCATION, VAR_DECL, get_identifier ( (PARAM_VALUE (PARAM_INDIR_CALL_TOPN_PROFILE) ? "__gcov_indirect_call_topn_counters" : "__gcov_indirect_call_counters")), gcov_type_ptr); TREE_PUBLIC (ic_gcov_type_ptr_var) = 1; DECL_EXTERNAL (ic_gcov_type_ptr_var) = 1; } TREE_STATIC (ic_gcov_type_ptr_var) = 1; DECL_ARTIFICIAL (ic_gcov_type_ptr_var) = 1; DECL_INITIAL (ic_gcov_type_ptr_var) = NULL; if (targetm.have_tls) set_decl_tls_model (ic_gcov_type_ptr_var, decl_default_tls_model (ic_gcov_type_ptr_var)); varpool_node::finalize_decl (ic_gcov_type_ptr_var); }
void symtab_node::dump_base (FILE *f) { static const char * const visibility_types[] = { "default", "protected", "hidden", "internal" }; fprintf (f, "%s/%i (%s)", asm_name (), order, name ()); dump_addr (f, " @", (void *)this); fprintf (f, "\n Type: %s", symtab_type_names[type]); if (definition) fprintf (f, " definition"); if (analyzed) fprintf (f, " analyzed"); if (alias) fprintf (f, " alias"); if (weakref) fprintf (f, " weakref"); if (cpp_implicit_alias) fprintf (f, " cpp_implicit_alias"); if (alias_target) fprintf (f, " target:%s", DECL_P (alias_target) ? IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (alias_target)) : IDENTIFIER_POINTER (alias_target)); if (body_removed) fprintf (f, "\n Body removed by symtab_remove_unreachable_nodes"); fprintf (f, "\n Visibility:"); if (in_other_partition) fprintf (f, " in_other_partition"); if (used_from_other_partition) fprintf (f, " used_from_other_partition"); if (force_output) fprintf (f, " force_output"); if (forced_by_abi) fprintf (f, " forced_by_abi"); if (externally_visible) fprintf (f, " externally_visible"); if (resolution != LDPR_UNKNOWN) fprintf (f, " %s", ld_plugin_symbol_resolution_names[(int)resolution]); if (TREE_ASM_WRITTEN (decl)) fprintf (f, " asm_written"); if (DECL_EXTERNAL (decl)) fprintf (f, " external"); if (TREE_PUBLIC (decl)) fprintf (f, " public"); if (DECL_COMMON (decl)) fprintf (f, " common"); if (DECL_WEAK (decl)) fprintf (f, " weak"); if (DECL_DLLIMPORT_P (decl)) fprintf (f, " dll_import"); if (DECL_COMDAT (decl)) fprintf (f, " comdat"); if (get_comdat_group ()) fprintf (f, " comdat_group:%s", IDENTIFIER_POINTER (get_comdat_group_id ())); if (DECL_ONE_ONLY (decl)) fprintf (f, " one_only"); if (get_section ()) fprintf (f, " section:%s", get_section ()); if (implicit_section) fprintf (f," (implicit_section)"); if (DECL_VISIBILITY_SPECIFIED (decl)) fprintf (f, " visibility_specified"); if (DECL_VISIBILITY (decl)) fprintf (f, " visibility:%s", visibility_types [DECL_VISIBILITY (decl)]); if (DECL_VIRTUAL_P (decl)) fprintf (f, " virtual"); if (DECL_ARTIFICIAL (decl)) fprintf (f, " artificial"); if (TREE_CODE (decl) == FUNCTION_DECL) { if (DECL_STATIC_CONSTRUCTOR (decl)) fprintf (f, " constructor"); if (DECL_STATIC_DESTRUCTOR (decl)) fprintf (f, " destructor"); } fprintf (f, "\n"); if (same_comdat_group) fprintf (f, " Same comdat group as: %s/%i\n", same_comdat_group->asm_name (), same_comdat_group->order); if (next_sharing_asm_name) fprintf (f, " next sharing asm name: %i\n", next_sharing_asm_name->order); if (previous_sharing_asm_name) fprintf (f, " previous sharing asm name: %i\n", previous_sharing_asm_name->order); if (address_taken) fprintf (f, " Address is taken.\n"); if (aux) { fprintf (f, " Aux:"); dump_addr (f, " @", (void *)aux); } fprintf (f, " References: "); dump_references (f); fprintf (f, " Referring: "); dump_referring (f); if (lto_file_data) fprintf (f, " Read from file: %s\n", lto_file_data->file_name); }
// make_decl_llvm - Create the DECL_RTL for a VAR_DECL or FUNCTION_DECL. DECL // should have static storage duration. In other words, it should not be an // automatic variable, including PARM_DECLs. // // There is, however, one exception: this function handles variables explicitly // placed in a particular register by the user. // // This function corresponds to make_decl_rtl in varasm.c, and is implicitly // called by DECL_LLVM if a decl doesn't have an LLVM set. // void make_decl_llvm(tree decl) { #ifdef ENABLE_CHECKING // Check that we are not being given an automatic variable. // A weak alias has TREE_PUBLIC set but not the other bits. if (TREE_CODE(decl) == PARM_DECL || TREE_CODE(decl) == RESULT_DECL || (TREE_CODE(decl) == VAR_DECL && !TREE_STATIC(decl) && !TREE_PUBLIC(decl) && !DECL_EXTERNAL(decl) && !DECL_REGISTER(decl))) abort(); // And that we were not given a type or a label. */ else if (TREE_CODE(decl) == TYPE_DECL || TREE_CODE(decl) == LABEL_DECL) abort (); #endif // For a duplicate declaration, we can be called twice on the // same DECL node. Don't discard the LLVM already made. if (DECL_LLVM_SET_P(decl)) return; if (errorcount || sorrycount) return; // Do not process broken code. // Global register variable with asm name, e.g.: // register unsigned long esp __asm__("ebp"); if (TREE_CODE(decl) != FUNCTION_DECL && DECL_REGISTER(decl)) { // This just verifies that the variable is ok. The actual "load/store" // code paths handle accesses to the variable. ValidateRegisterVariable(decl); return; } timevar_push(TV_LLVM_GLOBALS); const char *Name = ""; if (DECL_NAME(decl)) if (tree AssemblerName = DECL_ASSEMBLER_NAME(decl)) Name = IDENTIFIER_POINTER(AssemblerName); // Now handle ordinary static variables and functions (in memory). // Also handle vars declared register invalidly. if (Name[0] == 1) { #ifdef REGISTER_PREFIX if (strlen (REGISTER_PREFIX) != 0) { int reg_number = decode_reg_name(Name); if (reg_number >= 0 || reg_number == -3) error("%Jregister name given for non-register variable %qD", decl, decl); } #endif } // Specifying a section attribute on a variable forces it into a // non-.bss section, and thus it cannot be common. if (TREE_CODE(decl) == VAR_DECL && DECL_SECTION_NAME(decl) != NULL_TREE && DECL_INITIAL(decl) == NULL_TREE && DECL_COMMON(decl)) DECL_COMMON(decl) = 0; // Variables can't be both common and weak. if (TREE_CODE(decl) == VAR_DECL && DECL_WEAK(decl)) DECL_COMMON(decl) = 0; // Okay, now we need to create an LLVM global variable or function for this // object. Note that this is quite possibly a forward reference to the // object, so its type may change later. if (TREE_CODE(decl) == FUNCTION_DECL) { assert(Name[0] && "Function with empty name!"); // If this function has already been created, reuse the decl. This happens // when we have something like __builtin_memset and memset in the same file. Function *FnEntry = TheModule->getFunction(Name); if (FnEntry == 0) { unsigned CC; const FunctionType *Ty = TheTypeConverter->ConvertFunctionType(TREE_TYPE(decl), decl, NULL, CC); FnEntry = new Function(Ty, Function::ExternalLinkage, Name, TheModule); FnEntry->setCallingConv(CC); // Check for external weak linkage if (DECL_EXTERNAL(decl) && DECL_WEAK(decl)) FnEntry->setLinkage(Function::ExternalWeakLinkage); #ifdef TARGET_ADJUST_LLVM_LINKAGE TARGET_ADJUST_LLVM_LINKAGE(FnEntry,decl); #endif /* TARGET_ADJUST_LLVM_LINKAGE */ // Handle visibility style if (TREE_PUBLIC(decl)) { if (DECL_VISIBILITY(decl) == VISIBILITY_HIDDEN) FnEntry->setVisibility(GlobalValue::HiddenVisibility); else if (DECL_VISIBILITY(decl) == VISIBILITY_PROTECTED) FnEntry->setVisibility(GlobalValue::ProtectedVisibility); } assert(FnEntry->getName() == Name &&"Preexisting fn with the same name!"); } SET_DECL_LLVM(decl, FnEntry); } else { assert((TREE_CODE(decl) == VAR_DECL || TREE_CODE(decl) == CONST_DECL) && "Not a function or var decl?"); const Type *Ty = ConvertType(TREE_TYPE(decl)); GlobalVariable *GV ; // If we have "extern void foo", make the global have type {} instead of // type void. if (Ty == Type::VoidTy) Ty = StructType::get(std::vector<const Type*>(), false); if (Name[0] == 0) { // Global has no name. GV = new GlobalVariable(Ty, false, GlobalValue::ExternalLinkage, 0, "", TheModule); // Check for external weak linkage if (DECL_EXTERNAL(decl) && DECL_WEAK(decl)) GV->setLinkage(GlobalValue::ExternalWeakLinkage); #ifdef TARGET_ADJUST_LLVM_LINKAGE TARGET_ADJUST_LLVM_LINKAGE(GV,decl); #endif /* TARGET_ADJUST_LLVM_LINKAGE */ // Handle visibility style if (TREE_PUBLIC(decl)) { if (DECL_VISIBILITY(decl) == VISIBILITY_HIDDEN) GV->setVisibility(GlobalValue::HiddenVisibility); else if (DECL_VISIBILITY(decl) == VISIBILITY_PROTECTED) GV->setVisibility(GlobalValue::ProtectedVisibility); } } else { // If the global has a name, prevent multiple vars with the same name from // being created. GlobalVariable *GVE = TheModule->getGlobalVariable(Name); if (GVE == 0) { GV = new GlobalVariable(Ty, false, GlobalValue::ExternalLinkage,0, Name, TheModule); // Check for external weak linkage if (DECL_EXTERNAL(decl) && DECL_WEAK(decl)) GV->setLinkage(GlobalValue::ExternalWeakLinkage); #ifdef TARGET_ADJUST_LLVM_LINKAGE TARGET_ADJUST_LLVM_LINKAGE(GV,decl); #endif /* TARGET_ADJUST_LLVM_LINKAGE */ // Handle visibility style if (TREE_PUBLIC(decl)) { if (DECL_VISIBILITY(decl) == VISIBILITY_HIDDEN) GV->setVisibility(GlobalValue::HiddenVisibility); else if (DECL_VISIBILITY(decl) == VISIBILITY_PROTECTED) GV->setVisibility(GlobalValue::ProtectedVisibility); } // If GV got renamed, then there is already an object with this name in // the symbol table. If this happens, the old one must be a forward // decl, just replace it with a cast of the new one. if (GV->getName() != Name) { Function *F = TheModule->getFunction(Name); assert(F && F->isDeclaration() && "A function turned into a global?"); // Replace any uses of "F" with uses of GV. Value *FInNewType = ConstantExpr::getBitCast(GV, F->getType()); F->replaceAllUsesWith(FInNewType); // Update the decl that points to F. changeLLVMValue(F, FInNewType); // Now we can give GV the proper name. GV->takeName(F); // F is now dead, nuke it. F->eraseFromParent(); } } else { GV = GVE; // Global already created, reuse it. } } if ((TREE_READONLY(decl) && !TREE_SIDE_EFFECTS(decl)) || TREE_CODE(decl) == CONST_DECL) { if (DECL_EXTERNAL(decl)) { // Mark external globals constant even though they could be marked // non-constant in the defining translation unit. The definition of the // global determines whether the global is ultimately constant or not, // marking this constant will allow us to do some extra (legal) // optimizations that we would otherwise not be able to do. (In C++, // any global that is 'C++ const' may not be readonly: it could have a // dynamic initializer. // GV->setConstant(true); } else { // Mark readonly globals with constant initializers constant. if (DECL_INITIAL(decl) != error_mark_node && // uninitialized? DECL_INITIAL(decl) && (TREE_CONSTANT(DECL_INITIAL(decl)) || TREE_CODE(DECL_INITIAL(decl)) == STRING_CST)) GV->setConstant(true); } } // Set thread local (TLS) if (TREE_CODE(decl) == VAR_DECL && DECL_THREAD_LOCAL(decl)) GV->setThreadLocal(true); SET_DECL_LLVM(decl, GV); } timevar_pop(TV_LLVM_GLOBALS); }
/// emit_global_to_llvm - Emit the specified VAR_DECL or aggregate CONST_DECL to /// LLVM as a global variable. This function implements the end of /// assemble_variable. void emit_global_to_llvm(tree decl) { if (errorcount || sorrycount) return; // FIXME: Support alignment on globals: DECL_ALIGN. // FIXME: DECL_PRESERVE_P indicates the var is marked with attribute 'used'. // Global register variables don't turn into LLVM GlobalVariables. if (TREE_CODE(decl) == VAR_DECL && DECL_REGISTER(decl)) return; timevar_push(TV_LLVM_GLOBALS); // Get or create the global variable now. GlobalVariable *GV = cast<GlobalVariable>(DECL_LLVM(decl)); // Convert the initializer over. Constant *Init; if (DECL_INITIAL(decl) == 0 || DECL_INITIAL(decl) == error_mark_node) { // This global should be zero initialized. Reconvert the type in case the // forward def of the global and the real def differ in type (e.g. declared // as 'int A[]', and defined as 'int A[100]'). Init = Constant::getNullValue(ConvertType(TREE_TYPE(decl))); } else { assert((TREE_CONSTANT(DECL_INITIAL(decl)) || TREE_CODE(DECL_INITIAL(decl)) == STRING_CST) && "Global initializer should be constant!"); // Temporarily set an initializer for the global, so we don't infinitely // recurse. If we don't do this, we can hit cases where we see "oh a global // with an initializer hasn't been initialized yet, call emit_global_to_llvm // on it". When constructing the initializer it might refer to itself. // this can happen for things like void *G = &G; // GV->setInitializer(UndefValue::get(GV->getType()->getElementType())); Init = TreeConstantToLLVM::Convert(DECL_INITIAL(decl)); } // If we had a forward definition that has a type that disagrees with our // initializer, insert a cast now. This sort of thing occurs when we have a // global union, and the LLVM type followed a union initializer that is // different from the union element used for the type. if (GV->getType()->getElementType() != Init->getType()) { GV->removeFromParent(); GlobalVariable *NGV = new GlobalVariable(Init->getType(), GV->isConstant(), GlobalValue::ExternalLinkage, 0, GV->getName(), TheModule); GV->replaceAllUsesWith(ConstantExpr::getBitCast(NGV, GV->getType())); delete GV; SET_DECL_LLVM(decl, NGV); GV = NGV; } // Set the initializer. GV->setInitializer(Init); // Set thread local (TLS) if (TREE_CODE(decl) == VAR_DECL && DECL_THREAD_LOCAL(decl)) GV->setThreadLocal(true); // Set the linkage. if (!TREE_PUBLIC(decl)) { GV->setLinkage(GlobalValue::InternalLinkage); } else if (DECL_WEAK(decl) || DECL_ONE_ONLY(decl) || (DECL_COMMON(decl) && // DECL_COMMON is only meaningful if no init (!DECL_INITIAL(decl) || DECL_INITIAL(decl) == error_mark_node))) { // llvm-gcc also includes DECL_VIRTUAL_P here. GV->setLinkage(GlobalValue::WeakLinkage); } else if (DECL_COMDAT(decl)) { GV->setLinkage(GlobalValue::LinkOnceLinkage); } #ifdef TARGET_ADJUST_LLVM_LINKAGE TARGET_ADJUST_LLVM_LINKAGE(GV,decl); #endif /* TARGET_ADJUST_LLVM_LINKAGE */ // Handle visibility style if (TREE_PUBLIC(decl)) { if (DECL_VISIBILITY(decl) == VISIBILITY_HIDDEN) GV->setVisibility(GlobalValue::HiddenVisibility); else if (DECL_VISIBILITY(decl) == VISIBILITY_PROTECTED) GV->setVisibility(GlobalValue::ProtectedVisibility); } // Set the section for the global. if (TREE_CODE(decl) == VAR_DECL || TREE_CODE(decl) == CONST_DECL) { if (DECL_SECTION_NAME(decl)) { GV->setSection(TREE_STRING_POINTER(DECL_SECTION_NAME(decl))); #ifdef LLVM_IMPLICIT_TARGET_GLOBAL_VAR_SECTION } else if (const char *Section = LLVM_IMPLICIT_TARGET_GLOBAL_VAR_SECTION(decl)) { GV->setSection(Section); #endif } // Set the alignment for the global if one of the following condition is met // 1) DECL_ALIGN_UNIT does not match alignment as per ABI specification // 2) DECL_ALIGN is set by user. if (DECL_ALIGN_UNIT(decl)) { unsigned TargetAlign = getTargetData().getABITypeAlignment(GV->getType()->getElementType()); if (DECL_USER_ALIGN(decl) || TargetAlign != DECL_ALIGN_UNIT(decl)) GV->setAlignment(DECL_ALIGN_UNIT(decl)); } // Handle used decls if (DECL_PRESERVE_P (decl)) { const Type *SBP= PointerType::get(Type::Int8Ty); AttributeUsedGlobals.push_back(ConstantExpr::getBitCast(GV, SBP)); } // Add annotate attributes for globals if (DECL_ATTRIBUTES(decl)) AddAnnotateAttrsToGlobal(GV, decl); } if (TheDebugInfo) TheDebugInfo->EmitGlobalVariable(GV, decl); timevar_pop(TV_LLVM_GLOBALS); }
// emit_alias_to_llvm - Given decl and target emit alias to target. void emit_alias_to_llvm(tree decl, tree target, tree target_decl) { if (errorcount || sorrycount) return; timevar_push(TV_LLVM_GLOBALS); // Get or create LLVM global for our alias. GlobalValue *V = cast<GlobalValue>(DECL_LLVM(decl)); GlobalValue *Aliasee = NULL; if (target_decl) Aliasee = cast<GlobalValue>(DECL_LLVM(target_decl)); else { // This is something insane. Probably only LTHUNKs can be here // Try to grab decl from IDENTIFIER_NODE // Query SymTab for aliasee const char* AliaseeName = IDENTIFIER_POINTER(target); Aliasee = dyn_cast_or_null<GlobalValue>(TheModule-> getValueSymbolTable().lookup(AliaseeName)); // Last resort. Query for name set via __asm__ if (!Aliasee) { std::string starred = std::string("\001") + AliaseeName; Aliasee = dyn_cast_or_null<GlobalValue>(TheModule-> getValueSymbolTable().lookup(starred)); } if (!Aliasee) { error ("%J%qD aliased to undefined symbol %qE", decl, decl, target); timevar_pop(TV_LLVM_GLOBALS); return; } } GlobalValue::LinkageTypes Linkage; // Check for external weak linkage if (DECL_EXTERNAL(decl) && DECL_WEAK(decl)) Linkage = GlobalValue::WeakLinkage; else if (!TREE_PUBLIC(decl)) Linkage = GlobalValue::InternalLinkage; else Linkage = GlobalValue::ExternalLinkage; GlobalAlias* GA = new GlobalAlias(Aliasee->getType(), Linkage, "", Aliasee, TheModule); // Handle visibility style if (TREE_PUBLIC(decl)) { if (DECL_VISIBILITY(decl) == VISIBILITY_HIDDEN) GA->setVisibility(GlobalValue::HiddenVisibility); else if (DECL_VISIBILITY(decl) == VISIBILITY_PROTECTED) GA->setVisibility(GlobalValue::ProtectedVisibility); } if (V->getType() == GA->getType()) V->replaceAllUsesWith(GA); else if (!V->use_empty()) { error ("%J Alias %qD used with invalid type!", decl, decl); timevar_pop(TV_LLVM_GLOBALS); return; } changeLLVMValue(V, GA); GA->takeName(V); if (GlobalVariable *GV = dyn_cast<GlobalVariable>(V)) GV->eraseFromParent(); else if (GlobalAlias *GA = dyn_cast<GlobalAlias>(V)) GA->eraseFromParent(); else if (Function *F = dyn_cast<Function>(V)) F->eraseFromParent(); else assert(0 && "Unsuported global value"); TREE_ASM_WRITTEN(decl) = 1; timevar_pop(TV_LLVM_GLOBALS); return; }
/* 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; }
bool maybe_clone_body (tree fn) { tree clone; bool first = true; /* APPLE LOCAL begin ARM structor thunks */ tree clone_to_call; struct clone_info info; /* APPLE LOCAL end ARM structor thunks */ /* We only clone constructors and destructors. */ if (!DECL_MAYBE_IN_CHARGE_CONSTRUCTOR_P (fn) && !DECL_MAYBE_IN_CHARGE_DESTRUCTOR_P (fn)) return 0; /* Emit the DWARF1 abstract instance. */ (*debug_hooks->deferred_inline_function) (fn); /* APPLE LOCAL begin ARM structor thunks */ /* Figure out whether we can use the 'thunk' implementation, and if so on which clones. */ info.next_clone = 0; info.which_thunks_ok = compute_use_thunks (fn); /* APPLE LOCAL end ARM structor thunks */ /* We know that any clones immediately follow FN in the TYPE_METHODS list. */ push_to_top_level (); FOR_EACH_CLONE (clone, fn) { tree parm; tree clone_parm; int parmno; splay_tree decl_map; /* Update CLONE's source position information to match FN's. */ DECL_SOURCE_LOCATION (clone) = DECL_SOURCE_LOCATION (fn); DECL_INLINE (clone) = DECL_INLINE (fn); DECL_DECLARED_INLINE_P (clone) = DECL_DECLARED_INLINE_P (fn); /* LLVM LOCAL begin inlinehint attribute */ DECL_EXPLICIT_INLINE_P (clone) = DECL_EXPLICIT_INLINE_P (fn); /* LLVM LOCAL end inlinehint attribute */ DECL_COMDAT (clone) = DECL_COMDAT (fn); DECL_WEAK (clone) = DECL_WEAK (fn); DECL_ONE_ONLY (clone) = DECL_ONE_ONLY (fn); DECL_SECTION_NAME (clone) = DECL_SECTION_NAME (fn); DECL_USE_TEMPLATE (clone) = DECL_USE_TEMPLATE (fn); DECL_EXTERNAL (clone) = DECL_EXTERNAL (fn); DECL_INTERFACE_KNOWN (clone) = DECL_INTERFACE_KNOWN (fn); DECL_NOT_REALLY_EXTERN (clone) = DECL_NOT_REALLY_EXTERN (fn); TREE_PUBLIC (clone) = TREE_PUBLIC (fn); DECL_VISIBILITY (clone) = DECL_VISIBILITY (fn); DECL_VISIBILITY_SPECIFIED (clone) = DECL_VISIBILITY_SPECIFIED (fn); /* Adjust the parameter names and locations. */ parm = DECL_ARGUMENTS (fn); clone_parm = DECL_ARGUMENTS (clone); /* Update the `this' parameter, which is always first. */ update_cloned_parm (parm, clone_parm, first); parm = TREE_CHAIN (parm); clone_parm = TREE_CHAIN (clone_parm); if (DECL_HAS_IN_CHARGE_PARM_P (fn)) parm = TREE_CHAIN (parm); if (DECL_HAS_VTT_PARM_P (fn)) parm = TREE_CHAIN (parm); if (DECL_HAS_VTT_PARM_P (clone)) clone_parm = TREE_CHAIN (clone_parm); for (; parm; parm = TREE_CHAIN (parm), clone_parm = TREE_CHAIN (clone_parm)) /* Update this parameter. */ update_cloned_parm (parm, clone_parm, first); /* Start processing the function. */ start_preparsed_function (clone, NULL_TREE, SF_PRE_PARSED); /* Remap the parameters. */ decl_map = splay_tree_new (splay_tree_compare_pointers, NULL, NULL); for (parmno = 0, parm = DECL_ARGUMENTS (fn), clone_parm = DECL_ARGUMENTS (clone); parm; ++parmno, parm = TREE_CHAIN (parm)) { /* Map the in-charge parameter to an appropriate constant. */ if (DECL_HAS_IN_CHARGE_PARM_P (fn) && parmno == 1) { tree in_charge; in_charge = in_charge_arg_for_name (DECL_NAME (clone)); splay_tree_insert (decl_map, (splay_tree_key) parm, (splay_tree_value) in_charge); /* APPLE LOCAL ARM structor thunks */ info.in_charge_value [info.next_clone] = in_charge; } else if (DECL_ARTIFICIAL (parm) && DECL_NAME (parm) == vtt_parm_identifier) { /* For a subobject constructor or destructor, the next argument is the VTT parameter. Remap the VTT_PARM from the CLONE to this parameter. */ if (DECL_HAS_VTT_PARM_P (clone)) { DECL_ABSTRACT_ORIGIN (clone_parm) = parm; splay_tree_insert (decl_map, (splay_tree_key) parm, (splay_tree_value) clone_parm); clone_parm = TREE_CHAIN (clone_parm); } /* Otherwise, map the VTT parameter to `NULL'. */ else { splay_tree_insert (decl_map, (splay_tree_key) parm, (splay_tree_value) null_pointer_node); } } /* Map other parameters to their equivalents in the cloned function. */ else { splay_tree_insert (decl_map, (splay_tree_key) parm, (splay_tree_value) clone_parm); clone_parm = TREE_CHAIN (clone_parm); } } if (targetm.cxx.cdtor_returns_this ()) { parm = DECL_RESULT (fn); clone_parm = DECL_RESULT (clone); splay_tree_insert (decl_map, (splay_tree_key) parm, (splay_tree_value) clone_parm); } /* APPLE LOCAL begin ARM structor thunks */ clone_to_call = find_earlier_clone (&info); if (clone_to_call) /* Bodies are identical; replace later one with call to an earlier one. */ thunk_body (clone, fn, clone_to_call); else /* Clone the body. */ clone_body (clone, fn, decl_map); /* APPLE LOCAL end ARM structor thunks */ /* The clone can throw iff the original function can throw. */ cp_function_chain->can_throw = !TREE_NOTHROW (fn); /* Now, expand this function into RTL, if appropriate. */ finish_function (0); BLOCK_ABSTRACT_ORIGIN (DECL_INITIAL (clone)) = DECL_INITIAL (fn); expand_or_defer_fn (clone); first = false; /* APPLE LOCAL begin ARM structor thunks */ info.clones [info.next_clone] = clone; info.next_clone++; /* APPLE LOCAL end ARM structor thunks */ }
void use_thunk (tree thunk_fndecl, bool emit_p) { tree a, t, function, alias; tree virtual_offset; HOST_WIDE_INT fixed_offset, virtual_value; bool this_adjusting = DECL_THIS_THUNK_P (thunk_fndecl); /* We should have called finish_thunk to give it a name. */ gcc_assert (DECL_NAME (thunk_fndecl)); /* We should never be using an alias, always refer to the aliased thunk. */ gcc_assert (!THUNK_ALIAS (thunk_fndecl)); if (TREE_ASM_WRITTEN (thunk_fndecl)) return; function = THUNK_TARGET (thunk_fndecl); if (DECL_RESULT (thunk_fndecl)) /* We already turned this thunk into an ordinary function. There's no need to process this thunk again. */ return; if (DECL_THUNK_P (function)) /* The target is itself a thunk, process it now. */ use_thunk (function, emit_p); /* Thunks are always addressable; they only appear in vtables. */ TREE_ADDRESSABLE (thunk_fndecl) = 1; /* Figure out what function is being thunked to. It's referenced in this translation unit. */ TREE_ADDRESSABLE (function) = 1; mark_used (function); if (!emit_p) return; if (TARGET_USE_LOCAL_THUNK_ALIAS_P (function)) alias = make_alias_for_thunk (function); else alias = function; fixed_offset = THUNK_FIXED_OFFSET (thunk_fndecl); virtual_offset = THUNK_VIRTUAL_OFFSET (thunk_fndecl); if (virtual_offset) { if (!this_adjusting) virtual_offset = BINFO_VPTR_FIELD (virtual_offset); virtual_value = tree_low_cst (virtual_offset, /*pos=*/0); gcc_assert (virtual_value); } else virtual_value = 0; /* And, if we need to emit the thunk, it's used. */ mark_used (thunk_fndecl); /* This thunk is actually defined. */ DECL_EXTERNAL (thunk_fndecl) = 0; /* The linkage of the function may have changed. FIXME in linkage rewrite. */ TREE_PUBLIC (thunk_fndecl) = TREE_PUBLIC (function); DECL_VISIBILITY (thunk_fndecl) = DECL_VISIBILITY (function); DECL_VISIBILITY_SPECIFIED (thunk_fndecl) = DECL_VISIBILITY_SPECIFIED (function); if (DECL_ONE_ONLY (function)) make_decl_one_only (thunk_fndecl); if (flag_syntax_only) { TREE_ASM_WRITTEN (thunk_fndecl) = 1; return; } push_to_top_level (); if (TARGET_USE_LOCAL_THUNK_ALIAS_P (function) && targetm.have_named_sections) { resolve_unique_section (function, 0, flag_function_sections); if (DECL_SECTION_NAME (function) != NULL && DECL_ONE_ONLY (function)) { resolve_unique_section (thunk_fndecl, 0, flag_function_sections); /* Output the thunk into the same section as function. */ DECL_SECTION_NAME (thunk_fndecl) = DECL_SECTION_NAME (function); } } /* The back-end expects DECL_INITIAL to contain a BLOCK, so we create one. */ DECL_INITIAL (thunk_fndecl) = make_node (BLOCK); /* Set up cloned argument trees for the thunk. */ t = NULL_TREE; for (a = DECL_ARGUMENTS (function); a; a = TREE_CHAIN (a)) { tree x = copy_node (a); TREE_CHAIN (x) = t; DECL_CONTEXT (x) = thunk_fndecl; SET_DECL_RTL (x, NULL_RTX); DECL_HAS_VALUE_EXPR_P (x) = 0; t = x; } a = nreverse (t); DECL_ARGUMENTS (thunk_fndecl) = a; BLOCK_VARS (DECL_INITIAL (thunk_fndecl)) = a; if (this_adjusting && targetm.asm_out.can_output_mi_thunk (thunk_fndecl, fixed_offset, virtual_value, alias)) { const char *fnname; current_function_decl = thunk_fndecl; DECL_RESULT (thunk_fndecl) = build_decl (RESULT_DECL, 0, integer_type_node); fnname = XSTR (XEXP (DECL_RTL (thunk_fndecl), 0), 0); init_function_start (thunk_fndecl); current_function_is_thunk = 1; assemble_start_function (thunk_fndecl, fnname); targetm.asm_out.output_mi_thunk (asm_out_file, thunk_fndecl, fixed_offset, virtual_value, alias); assemble_end_function (thunk_fndecl, fnname); init_insn_lengths (); current_function_decl = 0; cfun = 0; TREE_ASM_WRITTEN (thunk_fndecl) = 1; } else { /* If this is a covariant thunk, or we don't have the necessary code for efficient thunks, generate a thunk function that just makes a call to the real function. Unfortunately, this doesn't work for varargs. */ if (varargs_function_p (function)) error ("generic thunk code fails for method %q#D which uses %<...%>", function); DECL_RESULT (thunk_fndecl) = NULL_TREE; start_preparsed_function (thunk_fndecl, NULL_TREE, SF_PRE_PARSED); /* We don't bother with a body block for thunks. */ /* There's no need to check accessibility inside the thunk body. */ push_deferring_access_checks (dk_no_check); t = a; if (this_adjusting) t = thunk_adjust (t, /*this_adjusting=*/1, fixed_offset, virtual_offset); /* Build up the call to the real function. */ t = tree_cons (NULL_TREE, t, NULL_TREE); for (a = TREE_CHAIN (a); a; a = TREE_CHAIN (a)) t = tree_cons (NULL_TREE, a, t); t = nreverse (t); t = build_call (alias, t); CALL_FROM_THUNK_P (t) = 1; if (VOID_TYPE_P (TREE_TYPE (t))) finish_expr_stmt (t); else { if (!this_adjusting) { tree cond = NULL_TREE; if (TREE_CODE (TREE_TYPE (t)) == POINTER_TYPE) { /* If the return type is a pointer, we need to protect against NULL. We know there will be an adjustment, because that's why we're emitting a thunk. */ t = save_expr (t); cond = cp_convert (boolean_type_node, t); } t = thunk_adjust (t, /*this_adjusting=*/0, fixed_offset, virtual_offset); if (cond) t = build3 (COND_EXPR, TREE_TYPE (t), cond, t, cp_convert (TREE_TYPE (t), integer_zero_node)); } if (IS_AGGR_TYPE (TREE_TYPE (t))) t = build_cplus_new (TREE_TYPE (t), t); finish_return_stmt (t); } /* Since we want to emit the thunk, we explicitly mark its name as referenced. */ mark_decl_referenced (thunk_fndecl); /* But we don't want debugging information about it. */ DECL_IGNORED_P (thunk_fndecl) = 1; /* Re-enable access control. */ pop_deferring_access_checks (); thunk_fndecl = finish_function (0); tree_lowering_passes (thunk_fndecl); expand_body (thunk_fndecl); } pop_from_top_level (); }
void use_thunk (tree thunk_fndecl, bool emit_p) { tree a, t, function, alias; tree virtual_offset; HOST_WIDE_INT fixed_offset, virtual_value; bool this_adjusting = DECL_THIS_THUNK_P (thunk_fndecl); /* We should have called finish_thunk to give it a name. */ gcc_assert (DECL_NAME (thunk_fndecl)); /* We should never be using an alias, always refer to the aliased thunk. */ gcc_assert (!THUNK_ALIAS (thunk_fndecl)); if (TREE_ASM_WRITTEN (thunk_fndecl)) return; function = THUNK_TARGET (thunk_fndecl); if (DECL_RESULT (thunk_fndecl)) /* We already turned this thunk into an ordinary function. There's no need to process this thunk again. */ return; if (DECL_THUNK_P (function)) /* The target is itself a thunk, process it now. */ use_thunk (function, emit_p); /* Thunks are always addressable; they only appear in vtables. */ TREE_ADDRESSABLE (thunk_fndecl) = 1; /* Figure out what function is being thunked to. It's referenced in this translation unit. */ TREE_ADDRESSABLE (function) = 1; mark_used (function); if (!emit_p) return; if (0 && TARGET_USE_LOCAL_THUNK_ALIAS_P (function)) alias = make_alias_for_thunk (function); else alias = function; fixed_offset = THUNK_FIXED_OFFSET (thunk_fndecl); virtual_offset = THUNK_VIRTUAL_OFFSET (thunk_fndecl); if (virtual_offset) { if (!this_adjusting) virtual_offset = BINFO_VPTR_FIELD (virtual_offset); virtual_value = tree_low_cst (virtual_offset, /*pos=*/0); gcc_assert (virtual_value); } else virtual_value = 0; /* And, if we need to emit the thunk, it's used. */ mark_used (thunk_fndecl); /* This thunk is actually defined. */ DECL_EXTERNAL (thunk_fndecl) = 0; /* The linkage of the function may have changed. FIXME in linkage rewrite. */ TREE_PUBLIC (thunk_fndecl) = TREE_PUBLIC (function); DECL_VISIBILITY (thunk_fndecl) = DECL_VISIBILITY (function); DECL_VISIBILITY_SPECIFIED (thunk_fndecl) = DECL_VISIBILITY_SPECIFIED (function); if (DECL_ONE_ONLY (function) || DECL_WEAK (function)) make_decl_one_only (thunk_fndecl, cxx_comdat_group (thunk_fndecl)); if (flag_syntax_only) { TREE_ASM_WRITTEN (thunk_fndecl) = 1; return; } push_to_top_level (); if (TARGET_USE_LOCAL_THUNK_ALIAS_P (function) && targetm.have_named_sections) { resolve_unique_section (function, 0, flag_function_sections); if (DECL_SECTION_NAME (function) != NULL && DECL_ONE_ONLY (function)) { resolve_unique_section (thunk_fndecl, 0, flag_function_sections); /* Output the thunk into the same section as function. */ DECL_SECTION_NAME (thunk_fndecl) = DECL_SECTION_NAME (function); } } /* Set up cloned argument trees for the thunk. */ t = NULL_TREE; for (a = DECL_ARGUMENTS (function); a; a = TREE_CHAIN (a)) { tree x = copy_node (a); TREE_CHAIN (x) = t; DECL_CONTEXT (x) = thunk_fndecl; SET_DECL_RTL (x, NULL_RTX); DECL_HAS_VALUE_EXPR_P (x) = 0; t = x; } a = nreverse (t); DECL_ARGUMENTS (thunk_fndecl) = a; TREE_ASM_WRITTEN (thunk_fndecl) = 1; cgraph_add_thunk (thunk_fndecl, function, this_adjusting, fixed_offset, virtual_value, virtual_offset, alias); if (!this_adjusting || !targetm.asm_out.can_output_mi_thunk (thunk_fndecl, fixed_offset, virtual_value, alias)) { /* If this is a covariant thunk, or we don't have the necessary code for efficient thunks, generate a thunk function that just makes a call to the real function. Unfortunately, this doesn't work for varargs. */ if (varargs_function_p (function)) error ("generic thunk code fails for method %q#D which uses %<...%>", function); } pop_from_top_level (); }