| /* Output variables, constants and external declarations, for GNU compiler. |
| Copyright (C) 1987-2024 Free Software Foundation, 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/>. */ |
| |
| |
| /* This file handles generation of all the assembler code |
| *except* the instructions of a function. |
| This includes declarations of variables and their initial values. |
| |
| We also output the assembler code for constants stored in memory |
| and are responsible for combining constants with the same value. */ |
| |
| #include "config.h" |
| #include "system.h" |
| #include "coretypes.h" |
| #include "backend.h" |
| #include "target.h" |
| #include "rtl.h" |
| #include "tree.h" |
| #include "predict.h" |
| #include "memmodel.h" |
| #include "tm_p.h" |
| #include "stringpool.h" |
| #include "regs.h" |
| #include "emit-rtl.h" |
| #include "cgraph.h" |
| #include "diagnostic-core.h" |
| #include "fold-const.h" |
| #include "stor-layout.h" |
| #include "varasm.h" |
| #include "version.h" |
| #include "flags.h" |
| #include "stmt.h" |
| #include "expr.h" |
| #include "expmed.h" |
| #include "optabs.h" |
| #include "output.h" |
| #include "langhooks.h" |
| #include "debug.h" |
| #include "common/common-target.h" |
| #include "stringpool.h" |
| #include "attribs.h" |
| #include "asan.h" |
| #include "rtl-iter.h" |
| #include "file-prefix-map.h" /* remap_debug_filename() */ |
| #include "alloc-pool.h" |
| #include "toplev.h" |
| #include "opts.h" |
| #include "asan.h" |
| |
| /* The (assembler) name of the first globally-visible object output. */ |
| extern GTY(()) const char *first_global_object_name; |
| extern GTY(()) const char *weak_global_object_name; |
| |
| const char *first_global_object_name; |
| const char *weak_global_object_name; |
| |
| class addr_const; |
| class constant_descriptor_rtx; |
| struct rtx_constant_pool; |
| |
| #define n_deferred_constants (crtl->varasm.deferred_constants) |
| |
| /* Number for making the label on the next |
| constant that is stored in memory. */ |
| |
| static GTY(()) int const_labelno; |
| |
| /* Carry information from ASM_DECLARE_OBJECT_NAME |
| to ASM_FINISH_DECLARE_OBJECT. */ |
| |
| int size_directive_output; |
| |
| /* The last decl for which assemble_variable was called, |
| if it did ASM_DECLARE_OBJECT_NAME. |
| If the last call to assemble_variable didn't do that, |
| this holds 0. */ |
| |
| tree last_assemble_variable_decl; |
| |
| /* The following global variable indicates if the first basic block |
| in a function belongs to the cold partition or not. */ |
| |
| bool first_function_block_is_cold; |
| |
| /* Whether we saw any functions with no_split_stack. */ |
| |
| static bool saw_no_split_stack; |
| |
| static const char *strip_reg_name (const char *); |
| static bool contains_pointers_p (tree); |
| #ifdef ASM_OUTPUT_EXTERNAL |
| static bool incorporeal_function_p (tree); |
| #endif |
| static void decode_addr_const (tree, class addr_const *); |
| static hashval_t const_hash_1 (const tree); |
| static bool compare_constant (const tree, const tree); |
| static void output_constant_def_contents (rtx); |
| static void output_addressed_constants (tree, int); |
| static unsigned HOST_WIDE_INT output_constant (tree, unsigned HOST_WIDE_INT, |
| unsigned int, bool, bool); |
| static void globalize_decl (tree); |
| static bool decl_readonly_section_1 (enum section_category); |
| #ifdef BSS_SECTION_ASM_OP |
| #ifdef ASM_OUTPUT_ALIGNED_BSS |
| static void asm_output_aligned_bss (FILE *, tree, const char *, |
| unsigned HOST_WIDE_INT, int) |
| ATTRIBUTE_UNUSED; |
| #endif |
| #endif /* BSS_SECTION_ASM_OP */ |
| static void mark_weak (tree); |
| static void output_constant_pool (const char *, tree); |
| static void handle_vtv_comdat_section (section *, const_tree); |
| |
| /* Well-known sections, each one associated with some sort of *_ASM_OP. */ |
| section *text_section; |
| section *data_section; |
| section *readonly_data_section; |
| section *sdata_section; |
| section *ctors_section; |
| section *dtors_section; |
| section *bss_section; |
| section *sbss_section; |
| |
| /* Various forms of common section. All are guaranteed to be nonnull. */ |
| section *tls_comm_section; |
| section *comm_section; |
| section *lcomm_section; |
| |
| /* A SECTION_NOSWITCH section used for declaring global BSS variables. |
| May be null. */ |
| section *bss_noswitch_section; |
| |
| /* The section that holds the main exception table, when known. The section |
| is set either by the target's init_sections hook or by the first call to |
| switch_to_exception_section. */ |
| section *exception_section; |
| |
| /* The section that holds the DWARF2 frame unwind information, when known. |
| The section is set either by the target's init_sections hook or by the |
| first call to switch_to_eh_frame_section. */ |
| section *eh_frame_section; |
| |
| /* asm_out_file's current section. This is NULL if no section has yet |
| been selected or if we lose track of what the current section is. */ |
| section *in_section; |
| |
| /* True if code for the current function is currently being directed |
| at the cold section. */ |
| bool in_cold_section_p; |
| |
| /* The following global holds the "function name" for the code in the |
| cold section of a function, if hot/cold function splitting is enabled |
| and there was actually code that went into the cold section. A |
| pseudo function name is needed for the cold section of code for some |
| debugging tools that perform symbolization. */ |
| tree cold_function_name = NULL_TREE; |
| |
| /* A linked list of all the unnamed sections. */ |
| static GTY(()) section *unnamed_sections; |
| |
| /* Return a nonzero value if DECL has a section attribute. */ |
| #define IN_NAMED_SECTION(DECL) \ |
| (VAR_OR_FUNCTION_DECL_P (DECL) && DECL_SECTION_NAME (DECL) != NULL) |
| |
| struct section_hasher : ggc_ptr_hash<section> |
| { |
| typedef const char *compare_type; |
| |
| static hashval_t hash (section *); |
| static bool equal (section *, const char *); |
| }; |
| |
| /* Hash table of named sections. */ |
| static GTY(()) hash_table<section_hasher> *section_htab; |
| |
| struct object_block_hasher : ggc_ptr_hash<object_block> |
| { |
| typedef const section *compare_type; |
| |
| static hashval_t hash (object_block *); |
| static bool equal (object_block *, const section *); |
| }; |
| |
| /* A table of object_blocks, indexed by section. */ |
| static GTY(()) hash_table<object_block_hasher> *object_block_htab; |
| |
| /* The next number to use for internal anchor labels. */ |
| static GTY(()) int anchor_labelno; |
| |
| /* A pool of constants that can be shared between functions. */ |
| static GTY(()) struct rtx_constant_pool *shared_constant_pool; |
| |
| /* Helper routines for maintaining section_htab. */ |
| |
| bool |
| section_hasher::equal (section *old, const char *new_name) |
| { |
| return strcmp (old->named.name, new_name) == 0; |
| } |
| |
| hashval_t |
| section_hasher::hash (section *old) |
| { |
| return htab_hash_string (old->named.name); |
| } |
| |
| /* Return a hash value for section SECT. */ |
| |
| static hashval_t |
| hash_section (section *sect) |
| { |
| if (sect->common.flags & SECTION_NAMED) |
| return htab_hash_string (sect->named.name); |
| return sect->common.flags & ~SECTION_DECLARED; |
| } |
| |
| /* Helper routines for maintaining object_block_htab. */ |
| |
| inline bool |
| object_block_hasher::equal (object_block *old, const section *new_section) |
| { |
| return old->sect == new_section; |
| } |
| |
| hashval_t |
| object_block_hasher::hash (object_block *old) |
| { |
| return hash_section (old->sect); |
| } |
| |
| /* Return a new unnamed section with the given fields. */ |
| |
| section * |
| get_unnamed_section (unsigned int flags, void (*callback) (const char *), |
| const char *data) |
| { |
| section *sect; |
| |
| sect = ggc_alloc<section> (); |
| sect->unnamed.common.flags = flags | SECTION_UNNAMED; |
| sect->unnamed.callback = callback; |
| sect->unnamed.data = data; |
| sect->unnamed.next = unnamed_sections; |
| |
| unnamed_sections = sect; |
| return sect; |
| } |
| |
| /* Return a SECTION_NOSWITCH section with the given fields. */ |
| |
| static section * |
| get_noswitch_section (unsigned int flags, noswitch_section_callback callback) |
| { |
| section *sect; |
| |
| sect = ggc_alloc<section> (); |
| sect->noswitch.common.flags = flags | SECTION_NOSWITCH; |
| sect->noswitch.callback = callback; |
| |
| return sect; |
| } |
| |
| /* Return the named section structure associated with NAME. Create |
| a new section with the given fields if no such structure exists. |
| When NOT_EXISTING, then fail if the section already exists. Return |
| the existing section if the SECTION_RETAIN bit doesn't match. Set |
| the SECTION_WRITE | SECTION_RELRO bits on the existing section |
| if one of the section flags is SECTION_WRITE | SECTION_RELRO and the |
| other has none of these flags in named sections and either the section |
| hasn't been declared yet or has been declared as writable. */ |
| |
| section * |
| get_section (const char *name, unsigned int flags, tree decl, |
| bool not_existing) |
| { |
| section *sect, **slot; |
| |
| slot = section_htab->find_slot_with_hash (name, htab_hash_string (name), |
| INSERT); |
| flags |= SECTION_NAMED; |
| if (decl != nullptr |
| && DECL_P (decl) |
| && lookup_attribute ("retain", DECL_ATTRIBUTES (decl))) |
| flags |= SECTION_RETAIN; |
| if (*slot == NULL) |
| { |
| sect = ggc_alloc<section> (); |
| sect->named.common.flags = flags; |
| sect->named.name = ggc_strdup (name); |
| sect->named.decl = decl; |
| *slot = sect; |
| } |
| else |
| { |
| if (not_existing) |
| internal_error ("section already exists: %qs", name); |
| |
| sect = *slot; |
| /* It is fine if one of the sections has SECTION_NOTYPE as long as |
| the other has none of the contrary flags (see the logic at the end |
| of default_section_type_flags, below). */ |
| if (((sect->common.flags ^ flags) & SECTION_NOTYPE) |
| && !((sect->common.flags | flags) |
| & (SECTION_CODE | SECTION_BSS | SECTION_TLS | SECTION_ENTSIZE |
| | (HAVE_COMDAT_GROUP ? SECTION_LINKONCE : 0)))) |
| { |
| sect->common.flags |= SECTION_NOTYPE; |
| flags |= SECTION_NOTYPE; |
| } |
| if ((sect->common.flags & ~SECTION_DECLARED) != flags |
| && ((sect->common.flags | flags) & SECTION_OVERRIDE) == 0) |
| { |
| /* It is fine if one of the section flags is |
| SECTION_WRITE | SECTION_RELRO and the other has none of these |
| flags (i.e. read-only) in named sections and either the |
| section hasn't been declared yet or has been declared as writable. |
| In that case just make sure the resulting flags are |
| SECTION_WRITE | SECTION_RELRO, ie. writable only because of |
| relocations. */ |
| if (((sect->common.flags ^ flags) & (SECTION_WRITE | SECTION_RELRO)) |
| == (SECTION_WRITE | SECTION_RELRO) |
| && (sect->common.flags |
| & ~(SECTION_DECLARED | SECTION_WRITE | SECTION_RELRO)) |
| == (flags & ~(SECTION_WRITE | SECTION_RELRO)) |
| && ((sect->common.flags & SECTION_DECLARED) == 0 |
| || (sect->common.flags & SECTION_WRITE))) |
| { |
| sect->common.flags |= (SECTION_WRITE | SECTION_RELRO); |
| return sect; |
| } |
| /* If the SECTION_RETAIN bit doesn't match, return and switch |
| to a new section later. */ |
| if ((sect->common.flags & SECTION_RETAIN) |
| != (flags & SECTION_RETAIN)) |
| return sect; |
| /* Sanity check user variables for flag changes. */ |
| if (sect->named.decl != NULL |
| && DECL_P (sect->named.decl) |
| && decl != sect->named.decl) |
| { |
| if (decl != NULL && DECL_P (decl)) |
| error ("%+qD causes a section type conflict with %qD", |
| decl, sect->named.decl); |
| else |
| error ("section type conflict with %qD", sect->named.decl); |
| inform (DECL_SOURCE_LOCATION (sect->named.decl), |
| "%qD was declared here", sect->named.decl); |
| } |
| else if (decl != NULL && DECL_P (decl)) |
| error ("%+qD causes a section type conflict", decl); |
| else |
| error ("section type conflict"); |
| /* Make sure we don't error about one section multiple times. */ |
| sect->common.flags |= SECTION_OVERRIDE; |
| } |
| } |
| return sect; |
| } |
| |
| /* Return true if the current compilation mode benefits from having |
| objects grouped into blocks. */ |
| |
| static bool |
| use_object_blocks_p (void) |
| { |
| return flag_section_anchors; |
| } |
| |
| /* Return the object_block structure for section SECT. Create a new |
| structure if we haven't created one already. Return null if SECT |
| itself is null. Return also null for mergeable sections since |
| section anchors can't be used in mergeable sections anyway, |
| because the linker might move objects around, and using the |
| object blocks infrastructure in that case is both a waste and a |
| maintenance burden. */ |
| |
| static struct object_block * |
| get_block_for_section (section *sect) |
| { |
| struct object_block *block; |
| |
| if (sect == NULL) |
| return NULL; |
| |
| if (sect->common.flags & SECTION_MERGE) |
| return NULL; |
| |
| object_block **slot |
| = object_block_htab->find_slot_with_hash (sect, hash_section (sect), |
| INSERT); |
| block = *slot; |
| if (block == NULL) |
| { |
| block = ggc_cleared_alloc<object_block> (); |
| block->sect = sect; |
| *slot = block; |
| } |
| return block; |
| } |
| |
| /* Create a symbol with label LABEL and place it at byte offset |
| OFFSET in BLOCK. OFFSET can be negative if the symbol's offset |
| is not yet known. LABEL must be a garbage-collected string. */ |
| |
| static rtx |
| create_block_symbol (const char *label, struct object_block *block, |
| HOST_WIDE_INT offset) |
| { |
| rtx symbol; |
| unsigned int size; |
| |
| /* Create the extended SYMBOL_REF. */ |
| size = RTX_HDR_SIZE + sizeof (struct block_symbol); |
| symbol = (rtx) ggc_internal_alloc (size); |
| |
| /* Initialize the normal SYMBOL_REF fields. */ |
| memset (symbol, 0, size); |
| PUT_CODE (symbol, SYMBOL_REF); |
| PUT_MODE (symbol, Pmode); |
| XSTR (symbol, 0) = label; |
| SYMBOL_REF_FLAGS (symbol) = SYMBOL_FLAG_HAS_BLOCK_INFO; |
| |
| /* Initialize the block_symbol stuff. */ |
| SYMBOL_REF_BLOCK (symbol) = block; |
| SYMBOL_REF_BLOCK_OFFSET (symbol) = offset; |
| |
| return symbol; |
| } |
| |
| /* Return a section with a particular name and with whatever SECTION_* |
| flags section_type_flags deems appropriate. The name of the section |
| is taken from NAME if nonnull, otherwise it is taken from DECL's |
| DECL_SECTION_NAME. DECL is the decl associated with the section |
| (see the section comment for details) and RELOC is as for |
| section_type_flags. */ |
| |
| section * |
| get_named_section (tree decl, const char *name, int reloc) |
| { |
| unsigned int flags; |
| |
| if (name == NULL) |
| { |
| gcc_assert (decl && DECL_P (decl) && DECL_SECTION_NAME (decl)); |
| name = DECL_SECTION_NAME (decl); |
| } |
| |
| flags = targetm.section_type_flags (decl, name, reloc); |
| return get_section (name, flags, decl); |
| } |
| |
| /* Worker for resolve_unique_section. */ |
| |
| static bool |
| set_implicit_section (struct symtab_node *n, void *data ATTRIBUTE_UNUSED) |
| { |
| n->implicit_section = true; |
| return false; |
| } |
| |
| /* If required, set DECL_SECTION_NAME to a unique name. */ |
| |
| void |
| resolve_unique_section (tree decl, int reloc ATTRIBUTE_UNUSED, |
| int flag_function_or_data_sections) |
| { |
| if (DECL_SECTION_NAME (decl) == NULL |
| && targetm_common.have_named_sections |
| && (flag_function_or_data_sections |
| || lookup_attribute ("retain", DECL_ATTRIBUTES (decl)) |
| || DECL_COMDAT_GROUP (decl))) |
| { |
| targetm.asm_out.unique_section (decl, reloc); |
| if (DECL_SECTION_NAME (decl)) |
| symtab_node::get (decl)->call_for_symbol_and_aliases |
| (set_implicit_section, NULL, true); |
| } |
| } |
| |
| #ifdef BSS_SECTION_ASM_OP |
| |
| #ifdef ASM_OUTPUT_ALIGNED_BSS |
| |
| /* Utility function for targets to use in implementing |
| ASM_OUTPUT_ALIGNED_BSS. |
| ??? It is believed that this function will work in most cases so such |
| support is localized here. */ |
| |
| static void |
| asm_output_aligned_bss (FILE *file, tree decl ATTRIBUTE_UNUSED, |
| const char *name, unsigned HOST_WIDE_INT size, |
| int align) |
| { |
| switch_to_section (bss_section); |
| ASM_OUTPUT_ALIGN (file, floor_log2 (align / BITS_PER_UNIT)); |
| #ifdef ASM_DECLARE_OBJECT_NAME |
| last_assemble_variable_decl = decl; |
| ASM_DECLARE_OBJECT_NAME (file, name, decl); |
| #else |
| /* Standard thing is just output label for the object. */ |
| ASM_OUTPUT_LABEL (file, name); |
| #endif /* ASM_DECLARE_OBJECT_NAME */ |
| ASM_OUTPUT_SKIP (file, size ? size : 1); |
| } |
| |
| #endif |
| |
| #endif /* BSS_SECTION_ASM_OP */ |
| |
| #ifndef USE_SELECT_SECTION_FOR_FUNCTIONS |
| /* Return the hot section for function DECL. Return text_section for |
| null DECLs. */ |
| |
| static section * |
| hot_function_section (tree decl) |
| { |
| if (decl != NULL_TREE |
| && DECL_SECTION_NAME (decl) != NULL |
| && targetm_common.have_named_sections) |
| return get_named_section (decl, NULL, 0); |
| else |
| return text_section; |
| } |
| #endif |
| |
| /* Return section for TEXT_SECTION_NAME if DECL or DECL_SECTION_NAME (DECL) |
| is NULL. |
| |
| When DECL_SECTION_NAME is non-NULL and it is implicit section and |
| NAMED_SECTION_SUFFIX is non-NULL, then produce section called |
| concatenate the name with NAMED_SECTION_SUFFIX. |
| Otherwise produce "TEXT_SECTION_NAME.IMPLICIT_NAME". */ |
| |
| section * |
| get_named_text_section (tree decl, |
| const char *text_section_name, |
| const char *named_section_suffix) |
| { |
| if (decl && DECL_SECTION_NAME (decl)) |
| { |
| if (named_section_suffix) |
| { |
| const char *dsn = DECL_SECTION_NAME (decl); |
| const char *stripped_name; |
| char *name, *buffer; |
| |
| name = (char *) alloca (strlen (dsn) + 1); |
| memcpy (name, dsn, |
| strlen (dsn) + 1); |
| |
| stripped_name = targetm.strip_name_encoding (name); |
| |
| buffer = ACONCAT ((stripped_name, named_section_suffix, NULL)); |
| return get_named_section (decl, buffer, 0); |
| } |
| else if (symtab_node::get (decl)->implicit_section) |
| { |
| const char *name; |
| |
| /* Do not try to split gnu_linkonce functions. This gets somewhat |
| slipperly. */ |
| if (DECL_COMDAT_GROUP (decl) && !HAVE_COMDAT_GROUP) |
| return NULL; |
| name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)); |
| name = targetm.strip_name_encoding (name); |
| return get_named_section (decl, ACONCAT ((text_section_name, ".", |
| name, NULL)), 0); |
| } |
| else |
| return NULL; |
| } |
| return get_named_section (decl, text_section_name, 0); |
| } |
| |
| /* Choose named function section based on its frequency. */ |
| |
| section * |
| default_function_section (tree decl, enum node_frequency freq, |
| bool startup, bool exit) |
| { |
| #if defined HAVE_LD_EH_GC_SECTIONS && defined HAVE_LD_EH_GC_SECTIONS_BUG |
| /* Old GNU linkers have buggy --gc-section support, which sometimes |
| results in .gcc_except_table* sections being garbage collected. */ |
| if (decl |
| && symtab_node::get (decl)->implicit_section) |
| return NULL; |
| #endif |
| |
| if (!flag_reorder_functions |
| || !targetm_common.have_named_sections) |
| return NULL; |
| /* Startup code should go to startup subsection unless it is |
| unlikely executed (this happens especially with function splitting |
| where we can split away unnecessary parts of static constructors. */ |
| if (startup && freq != NODE_FREQUENCY_UNLIKELY_EXECUTED) |
| { |
| /* During LTO the tp_first_run profiling will naturally place all |
| initialization code first. Using separate section is counter-productive |
| because startup only code may call functions which are no longer |
| startup only. */ |
| if (!in_lto_p |
| || !cgraph_node::get (decl)->tp_first_run |
| || !opt_for_fn (decl, flag_profile_reorder_functions)) |
| return get_named_text_section (decl, ".text.startup", NULL); |
| else |
| return NULL; |
| } |
| |
| /* Similarly for exit. */ |
| if (exit && freq != NODE_FREQUENCY_UNLIKELY_EXECUTED) |
| return get_named_text_section (decl, ".text.exit", NULL); |
| |
| /* Group cold functions together, similarly for hot code. */ |
| switch (freq) |
| { |
| case NODE_FREQUENCY_UNLIKELY_EXECUTED: |
| return get_named_text_section (decl, ".text.unlikely", NULL); |
| case NODE_FREQUENCY_HOT: |
| return get_named_text_section (decl, ".text.hot", NULL); |
| /* FALLTHRU */ |
| default: |
| return NULL; |
| } |
| } |
| |
| /* Return the section for function DECL. |
| |
| If DECL is NULL_TREE, return the text section. We can be passed |
| NULL_TREE under some circumstances by dbxout.cc at least. |
| |
| If FORCE_COLD is true, return cold function section ignoring |
| the frequency info of cgraph_node. */ |
| |
| static section * |
| function_section_1 (tree decl, bool force_cold) |
| { |
| section *section = NULL; |
| enum node_frequency freq = NODE_FREQUENCY_NORMAL; |
| bool startup = false, exit = false; |
| |
| if (decl) |
| { |
| struct cgraph_node *node = cgraph_node::get (decl); |
| |
| if (node) |
| { |
| freq = node->frequency; |
| startup = node->only_called_at_startup; |
| exit = node->only_called_at_exit; |
| } |
| } |
| if (force_cold) |
| freq = NODE_FREQUENCY_UNLIKELY_EXECUTED; |
| |
| #ifdef USE_SELECT_SECTION_FOR_FUNCTIONS |
| if (decl != NULL_TREE |
| && DECL_SECTION_NAME (decl) != NULL) |
| { |
| if (targetm.asm_out.function_section) |
| section = targetm.asm_out.function_section (decl, freq, |
| startup, exit); |
| if (section) |
| return section; |
| return get_named_section (decl, NULL, 0); |
| } |
| else |
| return targetm.asm_out.select_section |
| (decl, freq == NODE_FREQUENCY_UNLIKELY_EXECUTED, |
| symtab_node::get (decl)->definition_alignment ()); |
| #else |
| if (targetm.asm_out.function_section) |
| section = targetm.asm_out.function_section (decl, freq, startup, exit); |
| if (section) |
| return section; |
| return hot_function_section (decl); |
| #endif |
| } |
| |
| /* Return the section for function DECL. |
| |
| If DECL is NULL_TREE, return the text section. We can be passed |
| NULL_TREE under some circumstances by dbxout.cc at least. */ |
| |
| section * |
| function_section (tree decl) |
| { |
| /* Handle cases where function splitting code decides |
| to put function entry point into unlikely executed section |
| despite the fact that the function itself is not cold |
| (i.e. it is called rarely but contains a hot loop that is |
| better to live in hot subsection for the code locality). */ |
| return function_section_1 (decl, |
| first_function_block_is_cold); |
| } |
| |
| /* Return the section for the current function, take IN_COLD_SECTION_P |
| into account. */ |
| |
| section * |
| current_function_section (void) |
| { |
| return function_section_1 (current_function_decl, in_cold_section_p); |
| } |
| |
| /* Tell assembler to switch to unlikely-to-be-executed text section. */ |
| |
| section * |
| unlikely_text_section (void) |
| { |
| return function_section_1 (current_function_decl, true); |
| } |
| |
| /* When called within a function context, return true if the function |
| has been assigned a cold text section and if SECT is that section. |
| When called outside a function context, return true if SECT is the |
| default cold section. */ |
| |
| bool |
| unlikely_text_section_p (section *sect) |
| { |
| return sect == function_section_1 (current_function_decl, true); |
| } |
| |
| /* Switch to the other function partition (if inside of hot section |
| into cold section, otherwise into the hot section). */ |
| |
| void |
| switch_to_other_text_partition (void) |
| { |
| in_cold_section_p = !in_cold_section_p; |
| switch_to_section (current_function_section ()); |
| } |
| |
| /* Return the read-only or relocated read-only data section |
| associated with function DECL. */ |
| |
| section * |
| default_function_rodata_section (tree decl, bool relocatable) |
| { |
| const char* sname; |
| unsigned int flags; |
| |
| flags = 0; |
| |
| if (relocatable) |
| { |
| sname = ".data.rel.ro.local"; |
| flags = (SECTION_WRITE | SECTION_RELRO); |
| } |
| else |
| sname = ".rodata"; |
| |
| if (decl && DECL_SECTION_NAME (decl)) |
| { |
| const char *name = DECL_SECTION_NAME (decl); |
| |
| if (DECL_COMDAT_GROUP (decl) && HAVE_COMDAT_GROUP) |
| { |
| const char *dot; |
| size_t len; |
| char* rname; |
| |
| dot = strchr (name + 1, '.'); |
| if (!dot) |
| dot = name; |
| len = strlen (dot) + strlen (sname) + 1; |
| rname = (char *) alloca (len); |
| |
| strcpy (rname, sname); |
| strcat (rname, dot); |
| return get_section (rname, (SECTION_LINKONCE | flags), decl); |
| } |
| /* For .gnu.linkonce.t.foo we want to use .gnu.linkonce.r.foo or |
| .gnu.linkonce.d.rel.ro.local.foo if the jump table is relocatable. */ |
| else if (DECL_COMDAT_GROUP (decl) |
| && startswith (name, ".gnu.linkonce.t.")) |
| { |
| size_t len; |
| char *rname; |
| |
| if (relocatable) |
| { |
| len = strlen (name) + strlen (".rel.ro.local") + 1; |
| rname = (char *) alloca (len); |
| |
| strcpy (rname, ".gnu.linkonce.d.rel.ro.local"); |
| strcat (rname, name + 15); |
| } |
| else |
| { |
| len = strlen (name) + 1; |
| rname = (char *) alloca (len); |
| |
| memcpy (rname, name, len); |
| rname[14] = 'r'; |
| } |
| return get_section (rname, (SECTION_LINKONCE | flags), decl); |
| } |
| /* For .text.foo we want to use .rodata.foo. */ |
| else if (flag_function_sections && flag_data_sections |
| && startswith (name, ".text.")) |
| { |
| size_t len = strlen (name) + 1; |
| char *rname = (char *) alloca (len + strlen (sname) - 5); |
| |
| memcpy (rname, sname, strlen (sname)); |
| memcpy (rname + strlen (sname), name + 5, len - 5); |
| return get_section (rname, flags, decl); |
| } |
| } |
| |
| if (relocatable) |
| return get_section (sname, flags, decl); |
| else |
| return readonly_data_section; |
| } |
| |
| /* Return the read-only data section associated with function DECL |
| for targets where that section should be always the single |
| readonly data section. */ |
| |
| section * |
| default_no_function_rodata_section (tree, bool) |
| { |
| return readonly_data_section; |
| } |
| |
| /* A subroutine of mergeable_string_section and mergeable_constant_section. */ |
| |
| static const char * |
| function_mergeable_rodata_prefix (void) |
| { |
| section *s = targetm.asm_out.function_rodata_section (current_function_decl, |
| false); |
| if (SECTION_STYLE (s) == SECTION_NAMED) |
| return s->named.name; |
| else |
| return targetm.asm_out.mergeable_rodata_prefix; |
| } |
| |
| /* Return the section to use for string merging. */ |
| |
| static section * |
| mergeable_string_section (tree decl ATTRIBUTE_UNUSED, |
| unsigned HOST_WIDE_INT align ATTRIBUTE_UNUSED, |
| unsigned int flags ATTRIBUTE_UNUSED) |
| { |
| HOST_WIDE_INT len; |
| |
| if (HAVE_GAS_SHF_MERGE && flag_merge_constants |
| && TREE_CODE (decl) == STRING_CST |
| && TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE |
| && align <= 256 |
| && (len = int_size_in_bytes (TREE_TYPE (decl))) > 0 |
| && TREE_STRING_LENGTH (decl) == len) |
| { |
| scalar_int_mode mode; |
| unsigned int modesize; |
| const char *str; |
| HOST_WIDE_INT i; |
| int j, unit; |
| const char *prefix = function_mergeable_rodata_prefix (); |
| char *name = (char *) alloca (strlen (prefix) + 30); |
| |
| mode = SCALAR_INT_TYPE_MODE (TREE_TYPE (TREE_TYPE (decl))); |
| modesize = GET_MODE_BITSIZE (mode); |
| if (modesize >= 8 && modesize <= 256 |
| && (modesize & (modesize - 1)) == 0) |
| { |
| if (align < modesize) |
| align = modesize; |
| |
| if (!HAVE_LD_ALIGNED_SHF_MERGE && align > 8) |
| return readonly_data_section; |
| |
| str = TREE_STRING_POINTER (decl); |
| unit = GET_MODE_SIZE (mode); |
| |
| /* Check for embedded NUL characters. */ |
| for (i = 0; i < len; i += unit) |
| { |
| for (j = 0; j < unit; j++) |
| if (str[i + j] != '\0') |
| break; |
| if (j == unit) |
| break; |
| } |
| if (i == len - unit || (unit == 1 && i == len)) |
| { |
| sprintf (name, "%s.str%d.%d", prefix, |
| modesize / 8, (int) (align / 8)); |
| flags |= (modesize / 8) | SECTION_MERGE | SECTION_STRINGS; |
| return get_section (name, flags, NULL); |
| } |
| } |
| } |
| |
| return readonly_data_section; |
| } |
| |
| /* Return the section to use for constant merging. */ |
| |
| section * |
| mergeable_constant_section (machine_mode mode ATTRIBUTE_UNUSED, |
| unsigned HOST_WIDE_INT align ATTRIBUTE_UNUSED, |
| unsigned int flags ATTRIBUTE_UNUSED) |
| { |
| if (HAVE_GAS_SHF_MERGE && flag_merge_constants |
| && mode != VOIDmode |
| && mode != BLKmode |
| && known_le (GET_MODE_BITSIZE (mode), align) |
| && align >= 8 |
| && align <= 256 |
| && (align & (align - 1)) == 0 |
| && (HAVE_LD_ALIGNED_SHF_MERGE ? 1 : align == 8)) |
| { |
| const char *prefix = function_mergeable_rodata_prefix (); |
| char *name = (char *) alloca (strlen (prefix) + 30); |
| |
| sprintf (name, "%s.cst%d", prefix, (int) (align / 8)); |
| flags |= (align / 8) | SECTION_MERGE; |
| return get_section (name, flags, NULL); |
| } |
| return readonly_data_section; |
| } |
| |
| /* Given NAME, a putative register name, discard any customary prefixes. */ |
| |
| static const char * |
| strip_reg_name (const char *name) |
| { |
| #ifdef REGISTER_PREFIX |
| if (!strncmp (name, REGISTER_PREFIX, strlen (REGISTER_PREFIX))) |
| name += strlen (REGISTER_PREFIX); |
| #endif |
| if (name[0] == '%' || name[0] == '#') |
| name++; |
| return name; |
| } |
| |
| /* The user has asked for a DECL to have a particular name. Set (or |
| change) it in such a way that we don't prefix an underscore to |
| it. */ |
| void |
| set_user_assembler_name (tree decl, const char *name) |
| { |
| char *starred = (char *) alloca (strlen (name) + 2); |
| starred[0] = '*'; |
| strcpy (starred + 1, name); |
| symtab->change_decl_assembler_name (decl, get_identifier (starred)); |
| SET_DECL_RTL (decl, NULL_RTX); |
| } |
| |
| /* Decode an `asm' spec for a declaration as a register name. |
| Return the register number, or -1 if nothing specified, |
| or -2 if the ASMSPEC is not `cc' or `memory' and is not recognized, |
| or -3 if ASMSPEC is `cc' and is not recognized, |
| or -4 if ASMSPEC is `memory' and is not recognized. |
| Accept an exact spelling or a decimal number. |
| Prefixes such as % are optional. */ |
| |
| int |
| decode_reg_name_and_count (const char *asmspec, int *pnregs) |
| { |
| /* Presume just one register is clobbered. */ |
| *pnregs = 1; |
| |
| if (asmspec != 0) |
| { |
| int i; |
| |
| /* Get rid of confusing prefixes. */ |
| asmspec = strip_reg_name (asmspec); |
| |
| /* Allow a decimal number as a "register name". */ |
| for (i = strlen (asmspec) - 1; i >= 0; i--) |
| if (! ISDIGIT (asmspec[i])) |
| break; |
| if (asmspec[0] != 0 && i < 0) |
| { |
| i = atoi (asmspec); |
| if (i < FIRST_PSEUDO_REGISTER && i >= 0 && reg_names[i][0]) |
| return i; |
| else |
| return -2; |
| } |
| |
| for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) |
| if (reg_names[i][0] |
| && ! strcmp (asmspec, strip_reg_name (reg_names[i]))) |
| return i; |
| |
| #ifdef OVERLAPPING_REGISTER_NAMES |
| { |
| static const struct |
| { |
| const char *const name; |
| const int number; |
| const int nregs; |
| } table[] = OVERLAPPING_REGISTER_NAMES; |
| |
| for (i = 0; i < (int) ARRAY_SIZE (table); i++) |
| if (table[i].name[0] |
| && ! strcmp (asmspec, table[i].name)) |
| { |
| *pnregs = table[i].nregs; |
| return table[i].number; |
| } |
| } |
| #endif /* OVERLAPPING_REGISTER_NAMES */ |
| |
| #ifdef ADDITIONAL_REGISTER_NAMES |
| { |
| static const struct { const char *const name; const int number; } table[] |
| = ADDITIONAL_REGISTER_NAMES; |
| |
| for (i = 0; i < (int) ARRAY_SIZE (table); i++) |
| if (table[i].name[0] |
| && ! strcmp (asmspec, table[i].name) |
| && reg_names[table[i].number][0]) |
| return table[i].number; |
| } |
| #endif /* ADDITIONAL_REGISTER_NAMES */ |
| |
| if (!strcmp (asmspec, "memory")) |
| return -4; |
| |
| if (!strcmp (asmspec, "cc")) |
| return -3; |
| |
| return -2; |
| } |
| |
| return -1; |
| } |
| |
| int |
| decode_reg_name (const char *name) |
| { |
| int count; |
| return decode_reg_name_and_count (name, &count); |
| } |
| |
| |
| /* Return true if DECL's initializer is suitable for a BSS section. */ |
| |
| bool |
| bss_initializer_p (const_tree decl, bool named) |
| { |
| /* Do not put non-common constants into the .bss section, they belong in |
| a readonly section, except when NAMED is true. */ |
| return ((!TREE_READONLY (decl) || DECL_COMMON (decl) || named) |
| && (DECL_INITIAL (decl) == NULL |
| /* In LTO we have no errors in program; error_mark_node is used |
| to mark offlined constructors. */ |
| || (DECL_INITIAL (decl) == error_mark_node |
| && !in_lto_p) |
| || (flag_zero_initialized_in_bss |
| && initializer_zerop (DECL_INITIAL (decl)) |
| /* A decl with the "persistent" attribute applied and |
| explicitly initialized to 0 should not be treated as a BSS |
| variable. */ |
| && !DECL_PERSISTENT_P (decl)))); |
| } |
| |
| /* Compute the alignment of variable specified by DECL. |
| DONT_OUTPUT_DATA is from assemble_variable. */ |
| |
| void |
| align_variable (tree decl, bool dont_output_data) |
| { |
| unsigned int align = DECL_ALIGN (decl); |
| |
| /* In the case for initialing an array whose length isn't specified, |
| where we have not yet been able to do the layout, |
| figure out the proper alignment now. */ |
| if (dont_output_data && DECL_SIZE (decl) == 0 |
| && TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE) |
| align = MAX (align, TYPE_ALIGN (TREE_TYPE (TREE_TYPE (decl)))); |
| |
| /* Some object file formats have a maximum alignment which they support. |
| In particular, a.out format supports a maximum alignment of 4. */ |
| if (align > MAX_OFILE_ALIGNMENT) |
| { |
| error ("alignment of %q+D is greater than maximum object " |
| "file alignment %d", decl, |
| MAX_OFILE_ALIGNMENT/BITS_PER_UNIT); |
| align = MAX_OFILE_ALIGNMENT; |
| } |
| |
| if (! DECL_USER_ALIGN (decl)) |
| { |
| #ifdef DATA_ABI_ALIGNMENT |
| unsigned int data_abi_align |
| = DATA_ABI_ALIGNMENT (TREE_TYPE (decl), align); |
| /* For backwards compatibility, don't assume the ABI alignment for |
| TLS variables. */ |
| if (! DECL_THREAD_LOCAL_P (decl) || data_abi_align <= BITS_PER_WORD) |
| align = data_abi_align; |
| #endif |
| |
| /* On some machines, it is good to increase alignment sometimes. |
| But as DECL_ALIGN is used both for actually emitting the variable |
| and for code accessing the variable as guaranteed alignment, we |
| can only increase the alignment if it is a performance optimization |
| if the references to it must bind to the current definition. */ |
| if (decl_binds_to_current_def_p (decl) |
| && !DECL_VIRTUAL_P (decl)) |
| { |
| #ifdef DATA_ALIGNMENT |
| unsigned int data_align = DATA_ALIGNMENT (TREE_TYPE (decl), align); |
| /* Don't increase alignment too much for TLS variables - TLS space |
| is too precious. */ |
| if (! DECL_THREAD_LOCAL_P (decl) || data_align <= BITS_PER_WORD) |
| align = data_align; |
| #endif |
| if (DECL_INITIAL (decl) != 0 |
| /* In LTO we have no errors in program; error_mark_node is used |
| to mark offlined constructors. */ |
| && (in_lto_p || DECL_INITIAL (decl) != error_mark_node)) |
| { |
| unsigned int const_align |
| = targetm.constant_alignment (DECL_INITIAL (decl), align); |
| /* Don't increase alignment too much for TLS variables - TLS |
| space is too precious. */ |
| if (! DECL_THREAD_LOCAL_P (decl) || const_align <= BITS_PER_WORD) |
| align = const_align; |
| } |
| } |
| } |
| |
| /* Reset the alignment in case we have made it tighter, so we can benefit |
| from it in get_pointer_alignment. */ |
| SET_DECL_ALIGN (decl, align); |
| } |
| |
| /* Return DECL_ALIGN (decl), possibly increased for optimization purposes |
| beyond what align_variable returned. */ |
| |
| static unsigned int |
| get_variable_align (tree decl) |
| { |
| unsigned int align = DECL_ALIGN (decl); |
| |
| /* For user aligned vars or static vars align_variable already did |
| everything. */ |
| if (DECL_USER_ALIGN (decl) || !TREE_PUBLIC (decl)) |
| return align; |
| |
| #ifdef DATA_ABI_ALIGNMENT |
| if (DECL_THREAD_LOCAL_P (decl)) |
| align = DATA_ABI_ALIGNMENT (TREE_TYPE (decl), align); |
| #endif |
| |
| /* For decls that bind to the current definition, align_variable |
| did also everything, except for not assuming ABI required alignment |
| of TLS variables. For other vars, increase the alignment here |
| as an optimization. */ |
| if (!decl_binds_to_current_def_p (decl)) |
| { |
| /* On some machines, it is good to increase alignment sometimes. */ |
| #ifdef DATA_ALIGNMENT |
| unsigned int data_align = DATA_ALIGNMENT (TREE_TYPE (decl), align); |
| /* Don't increase alignment too much for TLS variables - TLS space |
| is too precious. */ |
| if (! DECL_THREAD_LOCAL_P (decl) || data_align <= BITS_PER_WORD) |
| align = data_align; |
| #endif |
| if (DECL_INITIAL (decl) != 0 |
| /* In LTO we have no errors in program; error_mark_node is used |
| to mark offlined constructors. */ |
| && (in_lto_p || DECL_INITIAL (decl) != error_mark_node)) |
| { |
| unsigned int const_align |
| = targetm.constant_alignment (DECL_INITIAL (decl), align); |
| /* Don't increase alignment too much for TLS variables - TLS space |
| is too precious. */ |
| if (! DECL_THREAD_LOCAL_P (decl) || const_align <= BITS_PER_WORD) |
| align = const_align; |
| } |
| } |
| |
| return align; |
| } |
| |
| /* Compute reloc for get_variable_section. The return value |
| is a mask for which bit 1 indicates a global relocation, and bit 0 |
| indicates a local relocation. */ |
| |
| int |
| compute_reloc_for_var (tree decl) |
| { |
| int reloc; |
| |
| if (DECL_INITIAL (decl) == error_mark_node) |
| reloc = contains_pointers_p (TREE_TYPE (decl)) ? 3 : 0; |
| else if (DECL_INITIAL (decl)) |
| reloc = compute_reloc_for_constant (DECL_INITIAL (decl)); |
| else |
| reloc = 0; |
| |
| return reloc; |
| } |
| |
| /* Return the section into which the given VAR_DECL or CONST_DECL |
| should be placed. PREFER_NOSWITCH_P is true if a noswitch |
| section should be used wherever possible. */ |
| |
| section * |
| get_variable_section (tree decl, bool prefer_noswitch_p) |
| { |
| addr_space_t as = ADDR_SPACE_GENERIC; |
| int reloc; |
| varpool_node *vnode = varpool_node::get (decl); |
| if (vnode) |
| { |
| vnode = vnode->ultimate_alias_target (); |
| decl = vnode->decl; |
| } |
| |
| if (TREE_TYPE (decl) != error_mark_node) |
| as = TYPE_ADDR_SPACE (TREE_TYPE (decl)); |
| |
| /* We need the constructor to figure out reloc flag. */ |
| if (vnode) |
| vnode->get_constructor (); |
| |
| if (DECL_COMMON (decl) |
| && !lookup_attribute ("retain", DECL_ATTRIBUTES (decl))) |
| { |
| /* If the decl has been given an explicit section name, or it resides |
| in a non-generic address space, then it isn't common, and shouldn't |
| be handled as such. */ |
| gcc_assert (DECL_SECTION_NAME (decl) == NULL |
| && ADDR_SPACE_GENERIC_P (as)); |
| if (DECL_THREAD_LOCAL_P (decl)) |
| return tls_comm_section; |
| else if (TREE_PUBLIC (decl) && bss_initializer_p (decl)) |
| return comm_section; |
| } |
| |
| reloc = compute_reloc_for_var (decl); |
| |
| resolve_unique_section (decl, reloc, flag_data_sections); |
| if (IN_NAMED_SECTION (decl)) |
| { |
| section *sect = get_named_section (decl, NULL, reloc); |
| |
| if ((sect->common.flags & SECTION_BSS) |
| && !bss_initializer_p (decl, true)) |
| { |
| error_at (DECL_SOURCE_LOCATION (decl), |
| "only zero initializers are allowed in section %qs", |
| sect->named.name); |
| DECL_INITIAL (decl) = error_mark_node; |
| } |
| return sect; |
| } |
| |
| if (ADDR_SPACE_GENERIC_P (as) |
| && !DECL_THREAD_LOCAL_P (decl) |
| && !DECL_NOINIT_P (decl) |
| && !(prefer_noswitch_p && targetm.have_switchable_bss_sections) |
| && bss_initializer_p (decl)) |
| { |
| if (!TREE_PUBLIC (decl) |
| && !((flag_sanitize & SANITIZE_ADDRESS) |
| && asan_protect_global (decl))) |
| return lcomm_section; |
| if (bss_noswitch_section) |
| return bss_noswitch_section; |
| } |
| |
| return targetm.asm_out.select_section (decl, reloc, |
| get_variable_align (decl)); |
| } |
| |
| /* Return the block into which object_block DECL should be placed. */ |
| |
| static struct object_block * |
| get_block_for_decl (tree decl) |
| { |
| section *sect; |
| |
| if (VAR_P (decl)) |
| { |
| /* The object must be defined in this translation unit. */ |
| if (DECL_EXTERNAL (decl)) |
| return NULL; |
| |
| /* There's no point using object blocks for something that is |
| isolated by definition. */ |
| if (DECL_COMDAT_GROUP (decl)) |
| return NULL; |
| } |
| |
| /* We can only calculate block offsets if the decl has a known |
| constant size. */ |
| if (DECL_SIZE_UNIT (decl) == NULL) |
| return NULL; |
| if (!tree_fits_uhwi_p (DECL_SIZE_UNIT (decl))) |
| return NULL; |
| |
| /* Find out which section should contain DECL. We cannot put it into |
| an object block if it requires a standalone definition. */ |
| if (VAR_P (decl)) |
| align_variable (decl, 0); |
| sect = get_variable_section (decl, true); |
| if (SECTION_STYLE (sect) == SECTION_NOSWITCH) |
| return NULL; |
| |
| if (bool (lookup_attribute ("retain", DECL_ATTRIBUTES (decl))) |
| != bool (sect->common.flags & SECTION_RETAIN)) |
| return NULL; |
| |
| return get_block_for_section (sect); |
| } |
| |
| /* Make sure block symbol SYMBOL is in block BLOCK. */ |
| |
| static void |
| change_symbol_block (rtx symbol, struct object_block *block) |
| { |
| if (block != SYMBOL_REF_BLOCK (symbol)) |
| { |
| gcc_assert (SYMBOL_REF_BLOCK_OFFSET (symbol) < 0); |
| SYMBOL_REF_BLOCK (symbol) = block; |
| } |
| } |
| |
| /* Return true if it is possible to put DECL in an object_block. */ |
| |
| static bool |
| use_blocks_for_decl_p (tree decl) |
| { |
| struct symtab_node *snode; |
| |
| /* Don't create object blocks if each DECL is placed into a separate |
| section because that will uselessly create a section anchor for |
| each DECL. */ |
| if (flag_data_sections) |
| return false; |
| |
| /* Only data DECLs can be placed into object blocks. */ |
| if (!VAR_P (decl) && TREE_CODE (decl) != CONST_DECL) |
| return false; |
| |
| /* DECL_INITIAL (decl) set to decl is a hack used for some decls that |
| are never used from code directly and we never want object block handling |
| for those. */ |
| if (DECL_INITIAL (decl) == decl) |
| return false; |
| |
| /* If this decl is an alias, then we don't want to emit a |
| definition. */ |
| if (VAR_P (decl) |
| && (snode = symtab_node::get (decl)) != NULL |
| && snode->alias) |
| return false; |
| |
| return targetm.use_blocks_for_decl_p (decl); |
| } |
| |
| /* Follow the IDENTIFIER_TRANSPARENT_ALIAS chain starting at *ALIAS |
| until we find an identifier that is not itself a transparent alias. |
| Modify the alias passed to it by reference (and all aliases on the |
| way to the ultimate target), such that they do not have to be |
| followed again, and return the ultimate target of the alias |
| chain. */ |
| |
| static inline tree |
| ultimate_transparent_alias_target (tree *alias) |
| { |
| tree target = *alias; |
| |
| if (IDENTIFIER_TRANSPARENT_ALIAS (target)) |
| { |
| gcc_assert (TREE_CHAIN (target)); |
| target = ultimate_transparent_alias_target (&TREE_CHAIN (target)); |
| gcc_assert (! IDENTIFIER_TRANSPARENT_ALIAS (target) |
| && ! TREE_CHAIN (target)); |
| *alias = target; |
| } |
| |
| return target; |
| } |
| |
| /* Return true if REGNUM is mentioned in ELIMINABLE_REGS as a from |
| register number. */ |
| |
| static bool |
| eliminable_regno_p (int regnum) |
| { |
| static const struct |
| { |
| const int from; |
| const int to; |
| } eliminables[] = ELIMINABLE_REGS; |
| for (size_t i = 0; i < ARRAY_SIZE (eliminables); i++) |
| if (regnum == eliminables[i].from) |
| return true; |
| return false; |
| } |
| |
| /* 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 is never called for PARM_DECL nodes. */ |
| |
| void |
| make_decl_rtl (tree decl) |
| { |
| const char *name = 0; |
| int reg_number; |
| tree id; |
| rtx x; |
| |
| /* Check that we are not being given an automatic variable. */ |
| gcc_assert (TREE_CODE (decl) != PARM_DECL |
| && TREE_CODE (decl) != RESULT_DECL); |
| |
| /* A weak alias has TREE_PUBLIC set but not the other bits. */ |
| gcc_assert (!VAR_P (decl) |
| || TREE_STATIC (decl) |
| || TREE_PUBLIC (decl) |
| || DECL_EXTERNAL (decl) |
| || DECL_REGISTER (decl)); |
| |
| /* And that we were not given a type or a label. */ |
| gcc_assert (TREE_CODE (decl) != TYPE_DECL |
| && TREE_CODE (decl) != LABEL_DECL); |
| |
| /* For a duplicate declaration, we can be called twice on the |
| same DECL node. Don't discard the RTL already made. */ |
| if (DECL_RTL_SET_P (decl)) |
| { |
| /* If the old RTL had the wrong mode, fix the mode. */ |
| x = DECL_RTL (decl); |
| if (GET_MODE (x) != DECL_MODE (decl)) |
| SET_DECL_RTL (decl, adjust_address_nv (x, DECL_MODE (decl), 0)); |
| |
| if (TREE_CODE (decl) != FUNCTION_DECL && DECL_REGISTER (decl)) |
| return; |
| |
| /* ??? Another way to do this would be to maintain a hashed |
| table of such critters. Instead of adding stuff to a DECL |
| to give certain attributes to it, we could use an external |
| hash map from DECL to set of attributes. */ |
| |
| /* Let the target reassign the RTL if it wants. |
| This is necessary, for example, when one machine specific |
| decl attribute overrides another. */ |
| targetm.encode_section_info (decl, DECL_RTL (decl), false); |
| |
| /* If the symbol has a SYMBOL_REF_BLOCK field, update it based |
| on the new decl information. */ |
| if (MEM_P (x) |
| && GET_CODE (XEXP (x, 0)) == SYMBOL_REF |
| && SYMBOL_REF_HAS_BLOCK_INFO_P (XEXP (x, 0))) |
| change_symbol_block (XEXP (x, 0), get_block_for_decl (decl)); |
| |
| return; |
| } |
| |
| /* If this variable belongs to the global constant pool, retrieve the |
| pre-computed RTL or recompute it in LTO mode. */ |
| if (VAR_P (decl) && DECL_IN_CONSTANT_POOL (decl)) |
| { |
| SET_DECL_RTL (decl, output_constant_def (DECL_INITIAL (decl), 1)); |
| return; |
| } |
| |
| id = DECL_ASSEMBLER_NAME (decl); |
| name = IDENTIFIER_POINTER (id); |
| |
| if (name[0] != '*' && TREE_CODE (decl) != FUNCTION_DECL |
| && DECL_REGISTER (decl)) |
| { |
| error ("register name not specified for %q+D", decl); |
| } |
| else if (TREE_CODE (decl) != FUNCTION_DECL && DECL_REGISTER (decl)) |
| { |
| const char *asmspec = name+1; |
| machine_mode mode = DECL_MODE (decl); |
| reg_number = decode_reg_name (asmspec); |
| /* First detect errors in declaring global registers. */ |
| if (reg_number == -1) |
| error ("register name not specified for %q+D", decl); |
| else if (reg_number < 0) |
| error ("invalid register name for %q+D", decl); |
| else if (mode == BLKmode) |
| error ("data type of %q+D isn%'t suitable for a register", |
| decl); |
| else if (!in_hard_reg_set_p (accessible_reg_set, mode, reg_number)) |
| error ("the register specified for %q+D cannot be accessed" |
| " by the current target", decl); |
| else if (!in_hard_reg_set_p (operand_reg_set, mode, reg_number)) |
| error ("the register specified for %q+D is not general enough" |
| " to be used as a register variable", decl); |
| else if (!targetm.hard_regno_mode_ok (reg_number, mode)) |
| error ("register specified for %q+D isn%'t suitable for data type", |
| decl); |
| else if (reg_number != HARD_FRAME_POINTER_REGNUM |
| && (reg_number == FRAME_POINTER_REGNUM |
| #ifdef RETURN_ADDRESS_POINTER_REGNUM |
| || reg_number == RETURN_ADDRESS_POINTER_REGNUM |
| #endif |
| || reg_number == ARG_POINTER_REGNUM) |
| && eliminable_regno_p (reg_number)) |
| error ("register specified for %q+D is an internal GCC " |
| "implementation detail", decl); |
| /* Now handle properly declared static register variables. */ |
| else |
| { |
| int nregs; |
| |
| if (DECL_INITIAL (decl) != 0 && TREE_STATIC (decl)) |
| { |
| DECL_INITIAL (decl) = 0; |
| error ("global register variable has initial value"); |
| } |
| if (TREE_THIS_VOLATILE (decl)) |
| warning (OPT_Wvolatile_register_var, |
| "optimization may eliminate reads and/or " |
| "writes to register variables"); |
| |
| /* If the user specified one of the eliminables registers here, |
| e.g., FRAME_POINTER_REGNUM, we don't want to get this variable |
| confused with that register and be eliminated. This usage is |
| somewhat suspect... */ |
| |
| SET_DECL_RTL (decl, gen_raw_REG (mode, reg_number)); |
| ORIGINAL_REGNO (DECL_RTL (decl)) = reg_number; |
| REG_USERVAR_P (DECL_RTL (decl)) = 1; |
| |
| if (TREE_STATIC (decl)) |
| { |
| /* Make this register global, so not usable for anything |
| else. */ |
| #ifdef ASM_DECLARE_REGISTER_GLOBAL |
| name = IDENTIFIER_POINTER (DECL_NAME (decl)); |
| ASM_DECLARE_REGISTER_GLOBAL (asm_out_file, decl, reg_number, name); |
| #endif |
| nregs = hard_regno_nregs (reg_number, mode); |
| while (nregs > 0) |
| globalize_reg (decl, reg_number + --nregs); |
| } |
| |
| /* As a register variable, it has no section. */ |
| return; |
| } |
| /* Avoid internal errors from invalid register |
| specifications. */ |
| SET_DECL_ASSEMBLER_NAME (decl, NULL_TREE); |
| DECL_HARD_REGISTER (decl) = 0; |
| /* Also avoid SSA inconsistencies by pretending this is an external |
| decl now. */ |
| DECL_EXTERNAL (decl) = 1; |
| return; |
| } |
| /* Now handle ordinary static variables and functions (in memory). |
| Also handle vars declared register invalidly. */ |
| else if (name[0] == '*') |
| { |
| #ifdef REGISTER_PREFIX |
| if (strlen (REGISTER_PREFIX) != 0) |
| { |
| reg_number = decode_reg_name (name); |
| if (reg_number >= 0 || reg_number == -3) |
| error ("register name given for non-register variable %q+D", decl); |
| } |
| #endif |
| } |
| |
| /* Specifying a section attribute on a variable forces it into a |
| non-.bss section, and thus it cannot be common. */ |
| /* FIXME: In general this code should not be necessary because |
| visibility pass is doing the same work. But notice_global_symbol |
| is called early and it needs to make DECL_RTL to get the name. |
| we take care of recomputing the DECL_RTL after visibility is changed. */ |
| if (VAR_P (decl) |
| && (TREE_STATIC (decl) || DECL_EXTERNAL (decl)) |
| && DECL_SECTION_NAME (decl) != NULL |
| && DECL_INITIAL (decl) == NULL_TREE |
| && DECL_COMMON (decl)) |
| DECL_COMMON (decl) = 0; |
| |
| /* Variables can't be both common and weak. */ |
| if (VAR_P (decl) && DECL_WEAK (decl)) |
| DECL_COMMON (decl) = 0; |
| |
| if (use_object_blocks_p () && use_blocks_for_decl_p (decl)) |
| x = create_block_symbol (name, get_block_for_decl (decl), -1); |
| else |
| { |
| machine_mode address_mode = Pmode; |
| if (TREE_TYPE (decl) != error_mark_node) |
| { |
| addr_space_t as = TYPE_ADDR_SPACE (TREE_TYPE (decl)); |
| address_mode = targetm.addr_space.address_mode (as); |
| } |
| x = gen_rtx_SYMBOL_REF (address_mode, name); |
| } |
| SYMBOL_REF_WEAK (x) = DECL_WEAK (decl); |
| SET_SYMBOL_REF_DECL (x, decl); |
| |
| x = gen_rtx_MEM (DECL_MODE (decl), x); |
| if (TREE_CODE (decl) != FUNCTION_DECL) |
| set_mem_attributes (x, decl, 1); |
| SET_DECL_RTL (decl, x); |
| |
| /* Optionally set flags or add text to the name to record information |
| such as that it is a function name. |
| If the name is changed, the macro ASM_OUTPUT_LABELREF |
| will have to know how to strip this information. */ |
| targetm.encode_section_info (decl, DECL_RTL (decl), true); |
| } |
| |
| /* Like make_decl_rtl, but inhibit creation of new alias sets when |
| calling make_decl_rtl. Also, reset DECL_RTL before returning the |
| rtl. */ |
| |
| rtx |
| make_decl_rtl_for_debug (tree decl) |
| { |
| unsigned int save_aliasing_flag; |
| rtx rtl; |
| |
| if (DECL_RTL_SET_P (decl)) |
| return DECL_RTL (decl); |
| |
| /* Kludge alert! Somewhere down the call chain, make_decl_rtl will |
| call new_alias_set. If running with -fcompare-debug, sometimes |
| we do not want to create alias sets that will throw the alias |
| numbers off in the comparison dumps. So... clearing |
| flag_strict_aliasing will keep new_alias_set() from creating a |
| new set. */ |
| save_aliasing_flag = flag_strict_aliasing; |
| flag_strict_aliasing = 0; |
| |
| rtl = DECL_RTL (decl); |
| /* Reset DECL_RTL back, as various parts of the compiler expects |
| DECL_RTL set meaning it is actually going to be output. */ |
| SET_DECL_RTL (decl, NULL); |
| |
| flag_strict_aliasing = save_aliasing_flag; |
| return rtl; |
| } |
| |
| /* Output a string of literal assembler code |
| for an `asm' keyword used between functions. */ |
| |
| void |
| assemble_asm (tree string) |
| { |
| const char *p; |
| app_enable (); |
| |
| if (TREE_CODE (string) == ADDR_EXPR) |
| string = TREE_OPERAND (string, 0); |
| |
| p = TREE_STRING_POINTER (string); |
| fprintf (asm_out_file, "%s%s\n", p[0] == '\t' ? "" : "\t", p); |
| } |
| |
| /* Write the address of the entity given by SYMBOL to SEC. */ |
| void |
| assemble_addr_to_section (rtx symbol, section *sec) |
| { |
| switch_to_section (sec); |
| assemble_align (POINTER_SIZE); |
| assemble_integer (symbol, POINTER_SIZE_UNITS, POINTER_SIZE, 1); |
| } |
| |
| /* Return the numbered .ctors.N (if CONSTRUCTOR_P) or .dtors.N (if |
| not) section for PRIORITY. */ |
| section * |
| get_cdtor_priority_section (int priority, bool constructor_p) |
| { |
| /* Buffer conservatively large enough for the full range of a 32-bit |
| int plus the text below. */ |
| char buf[18]; |
| |
| /* ??? This only works reliably with the GNU linker. */ |
| sprintf (buf, "%s.%.5u", |
| constructor_p ? ".ctors" : ".dtors", |
| /* Invert the numbering so the linker puts us in the proper |
| order; constructors are run from right to left, and the |
| linker sorts in increasing order. */ |
| MAX_INIT_PRIORITY - priority); |
| return get_section (buf, SECTION_WRITE, NULL); |
| } |
| |
| void |
| default_named_section_asm_out_destructor (rtx symbol, int priority) |
| { |
| section *sec; |
| |
| if (priority != DEFAULT_INIT_PRIORITY) |
| sec = get_cdtor_priority_section (priority, |
| /*constructor_p=*/false); |
| else |
| sec = get_section (".dtors", SECTION_WRITE, NULL); |
| |
| assemble_addr_to_section (symbol, sec); |
| } |
| |
| #ifdef DTORS_SECTION_ASM_OP |
| void |
| default_dtor_section_asm_out_destructor (rtx symbol, |
| int priority ATTRIBUTE_UNUSED) |
| { |
| assemble_addr_to_section (symbol, dtors_section); |
| } |
| #endif |
| |
| void |
| default_named_section_asm_out_constructor (rtx symbol, int priority) |
| { |
| section *sec; |
| |
| if (priority != DEFAULT_INIT_PRIORITY) |
| sec = get_cdtor_priority_section (priority, |
| /*constructor_p=*/true); |
| else |
| sec = get_section (".ctors", SECTION_WRITE, NULL); |
| |
| assemble_addr_to_section (symbol, sec); |
| } |
| |
| #ifdef CTORS_SECTION_ASM_OP |
| void |
| default_ctor_section_asm_out_constructor (rtx symbol, |
| int priority ATTRIBUTE_UNUSED) |
| { |
| assemble_addr_to_section (symbol, ctors_section); |
| } |
| #endif |
| |
| /* CONSTANT_POOL_BEFORE_FUNCTION may be defined as an expression with |
| a nonzero value if the constant pool should be output before the |
| start of the function, or a zero value if the pool should output |
| after the end of the function. The default is to put it before the |
| start. */ |
| |
| #ifndef CONSTANT_POOL_BEFORE_FUNCTION |
| #define CONSTANT_POOL_BEFORE_FUNCTION 1 |
| #endif |
| |
| /* DECL is an object (either VAR_DECL or FUNCTION_DECL) which is going |
| to be output to assembler. |
| Set first_global_object_name and weak_global_object_name as appropriate. */ |
| |
| void |
| notice_global_symbol (tree decl) |
| { |
| const char **t = &first_global_object_name; |
| |
| if (first_global_object_name |
| || !TREE_PUBLIC (decl) |
| || DECL_EXTERNAL (decl) |
| || !DECL_NAME (decl) |
| || (VAR_P (decl) && DECL_HARD_REGISTER (decl)) |
| || (TREE_CODE (decl) != FUNCTION_DECL |
| && (!VAR_P (decl) |
| || (DECL_COMMON (decl) |
| && (DECL_INITIAL (decl) == 0 |
| || DECL_INITIAL (decl) == error_mark_node))))) |
| return; |
| |
| /* We win when global object is found, but it is useful to know about weak |
| symbol as well so we can produce nicer unique names. */ |
| if (DECL_WEAK (decl) || DECL_ONE_ONLY (decl) || flag_shlib) |
| t = &weak_global_object_name; |
| |
| if (!*t) |
| { |
| tree id = DECL_ASSEMBLER_NAME (decl); |
| ultimate_transparent_alias_target (&id); |
| *t = ggc_strdup (targetm.strip_name_encoding (IDENTIFIER_POINTER (id))); |
| } |
| } |
| |
| /* If not using flag_reorder_blocks_and_partition, decide early whether the |
| current function goes into the cold section, so that targets can use |
| current_function_section during RTL expansion. DECL describes the |
| function. */ |
| |
| void |
| decide_function_section (tree decl) |
| { |
| first_function_block_is_cold = false; |
| |
| if (DECL_SECTION_NAME (decl)) |
| { |
| struct cgraph_node *node = cgraph_node::get (current_function_decl); |
| /* Calls to function_section rely on first_function_block_is_cold |
| being accurate. */ |
| first_function_block_is_cold = (node |
| && node->frequency |
| == NODE_FREQUENCY_UNLIKELY_EXECUTED); |
| } |
| |
| in_cold_section_p = first_function_block_is_cold; |
| } |
| |
| /* Get the function's name, as described by its RTL. This may be |
| different from the DECL_NAME name used in the source file. */ |
| const char * |
| get_fnname_from_decl (tree decl) |
| { |
| rtx x = DECL_RTL (decl); |
| gcc_assert (MEM_P (x)); |
| x = XEXP (x, 0); |
| gcc_assert (GET_CODE (x) == SYMBOL_REF); |
| return XSTR (x, 0); |
| } |
| |
| /* Output function label, possibly with accompanying metadata. No additional |
| code or data is output after the label. */ |
| |
| void |
| assemble_function_label_raw (FILE *file, const char *name) |
| { |
| ASM_OUTPUT_LABEL (file, name); |
| assemble_function_label_final (); |
| } |
| |
| /* Finish outputting function label. Needs to be called when outputting |
| function label without using assemble_function_label_raw (). */ |
| |
| void |
| assemble_function_label_final (void) |
| { |
| if ((flag_sanitize & SANITIZE_ADDRESS) |
| /* Notify ASAN only about the first function label. */ |
| && (in_cold_section_p == first_function_block_is_cold) |
| /* Do not notify ASAN when called from, e.g., code_end (). */ |
| && cfun) |
| asan_function_start (); |
| } |
| |
| /* Output assembler code for the constant pool of a function and associated |
| with defining the name of the function. DECL describes the function. |
| NAME is the function's name. For the constant pool, we use the current |
| constant pool data. */ |
| |
| void |
| assemble_start_function (tree decl, const char *fnname) |
| { |
| int align; |
| char tmp_label[100]; |
| bool hot_label_written = false; |
| |
| if (crtl->has_bb_partition) |
| { |
| ASM_GENERATE_INTERNAL_LABEL (tmp_label, "LHOTB", const_labelno); |
| crtl->subsections.hot_section_label = ggc_strdup (tmp_label); |
| ASM_GENERATE_INTERNAL_LABEL (tmp_label, "LCOLDB", const_labelno); |
| crtl->subsections.cold_section_label = ggc_strdup (tmp_label); |
| ASM_GENERATE_INTERNAL_LABEL (tmp_label, "LHOTE", const_labelno); |
| crtl->subsections.hot_section_end_label = ggc_strdup (tmp_label); |
| ASM_GENERATE_INTERNAL_LABEL (tmp_label, "LCOLDE", const_labelno); |
| crtl->subsections.cold_section_end_label = ggc_strdup (tmp_label); |
| const_labelno++; |
| cold_function_name = NULL_TREE; |
| } |
| else |
| { |
| crtl->subsections.hot_section_label = NULL; |
| crtl->subsections.cold_section_label = NULL; |
| crtl->subsections.hot_section_end_label = NULL; |
| crtl->subsections.cold_section_end_label = NULL; |
| } |
| |
| /* The following code does not need preprocessing in the assembler. */ |
| |
| app_disable (); |
| |
| if (CONSTANT_POOL_BEFORE_FUNCTION) |
| output_constant_pool (fnname, decl); |
| |
| align = symtab_node::get (decl)->definition_alignment (); |
| |
| /* Make sure the not and cold text (code) sections are properly |
| aligned. This is necessary here in the case where the function |
| has both hot and cold sections, because we don't want to re-set |
| the alignment when the section switch happens mid-function. */ |
| |
| if (crtl->has_bb_partition) |
| { |
| first_function_block_is_cold = false; |
| |
| switch_to_section (unlikely_text_section ()); |
| assemble_align (align); |
| ASM_OUTPUT_LABEL (asm_out_file, crtl->subsections.cold_section_label); |
| |
| /* When the function starts with a cold section, we need to explicitly |
| align the hot section and write out the hot section label. |
| But if the current function is a thunk, we do not have a CFG. */ |
| if (!cfun->is_thunk |
| && BB_PARTITION (ENTRY_BLOCK_PTR_FOR_FN (cfun)->next_bb) == BB_COLD_PARTITION) |
| { |
| switch_to_section (text_section); |
| assemble_align (align); |
| ASM_OUTPUT_LABEL (asm_out_file, crtl->subsections.hot_section_label); |
| hot_label_written = true; |
| first_function_block_is_cold = true; |
| } |
| in_cold_section_p = first_function_block_is_cold; |
| } |
| |
| |
| /* Switch to the correct text section for the start of the function. */ |
| |
| switch_to_section (function_section (decl), decl); |
| if (crtl->has_bb_partition && !hot_label_written) |
| ASM_OUTPUT_LABEL (asm_out_file, crtl->subsections.hot_section_label); |
| |
| /* Tell assembler to move to target machine's alignment for functions. */ |
| align = floor_log2 (align / BITS_PER_UNIT); |
| /* Handle forced alignment. This really ought to apply to all functions, |
| since it is used by patchable entries. */ |
| if (flag_min_function_alignment) |
| align = MAX (align, floor_log2 (flag_min_function_alignment)); |
| |
| if (align > 0) |
| { |
| ASM_OUTPUT_ALIGN (asm_out_file, align); |
| } |
| |
| /* Handle a user-specified function alignment. |
| Note that we still need to align to DECL_ALIGN, as above, |
| because ASM_OUTPUT_MAX_SKIP_ALIGN might not do any alignment at all. */ |
| if (! DECL_USER_ALIGN (decl) |
| && align_functions.levels[0].log > align |
| && optimize_function_for_speed_p (cfun)) |
| { |
| #ifdef ASM_OUTPUT_MAX_SKIP_ALIGN |
| int align_log = align_functions.levels[0].log; |
| #endif |
| int max_skip = align_functions.levels[0].maxskip; |
| if (flag_limit_function_alignment && crtl->max_insn_address > 0 |
| && max_skip >= crtl->max_insn_address) |
| max_skip = crtl->max_insn_address - 1; |
| |
| #ifdef ASM_OUTPUT_MAX_SKIP_ALIGN |
| ASM_OUTPUT_MAX_SKIP_ALIGN (asm_out_file, align_log, max_skip); |
| if (max_skip == align_functions.levels[0].maxskip) |
| ASM_OUTPUT_MAX_SKIP_ALIGN (asm_out_file, |
| align_functions.levels[1].log, |
| align_functions.levels[1].maxskip); |
| #else |
| ASM_OUTPUT_ALIGN (asm_out_file, align_functions.levels[0].log); |
| #endif |
| } |
| |
| #ifdef ASM_OUTPUT_FUNCTION_PREFIX |
| ASM_OUTPUT_FUNCTION_PREFIX (asm_out_file, fnname); |
| #endif |
| |
| if (!DECL_IGNORED_P (decl)) |
| (*debug_hooks->begin_function) (decl); |
| |
| /* Make function name accessible from other files, if appropriate. */ |
| |
| if (TREE_PUBLIC (decl)) |
| { |
| notice_global_symbol (decl); |
| |
| globalize_decl (decl); |
| |
| maybe_assemble_visibility (decl); |
| } |
| |
| if (DECL_PRESERVE_P (decl)) |
| targetm.asm_out.mark_decl_preserved (fnname); |
| |
| unsigned short patch_area_size = crtl->patch_area_size; |
| unsigned short patch_area_entry = crtl->patch_area_entry; |
| |
| /* Emit the patching area before the entry label, if any. */ |
| if (patch_area_entry > 0) |
| targetm.asm_out.print_patchable_function_entry (asm_out_file, |
| patch_area_entry, true); |
| |
| /* Do any machine/system dependent processing of the function name. */ |
| #ifdef ASM_DECLARE_FUNCTION_NAME |
| ASM_DECLARE_FUNCTION_NAME (asm_out_file, fnname, current_function_decl); |
| #else |
| /* Standard thing is just output label for the function. */ |
| ASM_OUTPUT_FUNCTION_LABEL (asm_out_file, fnname, current_function_decl); |
| #endif /* ASM_DECLARE_FUNCTION_NAME */ |
| |
| /* And the area after the label. Record it if we haven't done so yet. */ |
| if (patch_area_size > patch_area_entry) |
| targetm.asm_out.print_patchable_function_entry (asm_out_file, |
| patch_area_size |
| - patch_area_entry, |
| patch_area_entry == 0); |
| |
| if (lookup_attribute ("no_split_stack", DECL_ATTRIBUTES (decl))) |
| saw_no_split_stack = true; |
| } |
| |
| /* Output assembler code associated with defining the size of the |
| function. DECL describes the function. NAME is the function's name. */ |
| |
| void |
| assemble_end_function (tree decl, const char *fnname ATTRIBUTE_UNUSED) |
| { |
| #ifdef ASM_DECLARE_FUNCTION_SIZE |
| /* We could have switched section in the middle of the function. */ |
| if (crtl->has_bb_partition) |
| switch_to_section (function_section (decl)); |
| ASM_DECLARE_FUNCTION_SIZE (asm_out_file, fnname, decl); |
| #endif |
| if (! CONSTANT_POOL_BEFORE_FUNCTION) |
| { |
| output_constant_pool (fnname, decl); |
| switch_to_section (function_section (decl)); /* need to switch back */ |
| } |
| /* Output labels for end of hot/cold text sections (to be used by |
| debug info.) */ |
| if (crtl->has_bb_partition) |
| { |
| section *save_text_section; |
| |
| save_text_section = in_section; |
| switch_to_section (unlikely_text_section ()); |
| #ifdef ASM_DECLARE_COLD_FUNCTION_SIZE |
| if (cold_function_name != NULL_TREE) |
| ASM_DECLARE_COLD_FUNCTION_SIZE (asm_out_file, |
| IDENTIFIER_POINTER (cold_function_name), |
| decl); |
| #endif |
| ASM_OUTPUT_LABEL (asm_out_file, crtl->subsections.cold_section_end_label); |
| if (first_function_block_is_cold) |
| switch_to_section (text_section); |
| else |
| switch_to_section (function_section (decl)); |
| ASM_OUTPUT_LABEL (asm_out_file, crtl->subsections.hot_section_end_label); |
| switch_to_section (save_text_section); |
| } |
| } |
| |
| /* Assemble code to leave SIZE bytes of zeros. */ |
| |
| void |
| assemble_zeros (unsigned HOST_WIDE_INT size) |
| { |
| /* Do no output if -fsyntax-only. */ |
| if (flag_syntax_only) |
| return; |
| |
| #ifdef ASM_NO_SKIP_IN_TEXT |
| /* The `space' pseudo in the text section outputs nop insns rather than 0s, |
| so we must output 0s explicitly in the text section. */ |
| if (ASM_NO_SKIP_IN_TEXT && (in_section->common.flags & SECTION_CODE) != 0) |
| { |
| unsigned HOST_WIDE_INT i; |
| for (i = 0; i < size; i++) |
| assemble_integer (const0_rtx, 1, BITS_PER_UNIT, 1); |
| } |
| else |
| #endif |
| if (size > 0) |
| ASM_OUTPUT_SKIP (asm_out_file, size); |
| } |
| |
| /* Assemble an alignment pseudo op for an ALIGN-bit boundary. */ |
| |
| void |
| assemble_align (unsigned int align) |
| { |
| if (align > BITS_PER_UNIT) |
| { |
| ASM_OUTPUT_ALIGN (asm_out_file, floor_log2 (align / BITS_PER_UNIT)); |
| } |
| } |
| |
| /* Assemble a string constant with the specified C string as contents. */ |
| |
| void |
| assemble_string (const char *p, int size) |
| { |
| int pos = 0; |
| int maximum = 2000; |
| |
| /* If the string is very long, split it up. */ |
| |
| while (pos < size) |
| { |
| int thissize = size - pos; |
| if (thissize > maximum) |
| thissize = maximum; |
| |
| ASM_OUTPUT_ASCII (asm_out_file, p, thissize); |
| |
| pos += thissize; |
| p += thissize; |
| } |
| } |
| |
| |
| /* A noswitch_section_callback for lcomm_section. */ |
| |
| static bool |
| emit_local (tree decl ATTRIBUTE_UNUSED, |
| const char *name ATTRIBUTE_UNUSED, |
| unsigned HOST_WIDE_INT size ATTRIBUTE_UNUSED, |
| unsigned HOST_WIDE_INT rounded ATTRIBUTE_UNUSED) |
| { |
| #if defined ASM_OUTPUT_ALIGNED_DECL_LOCAL |
| unsigned int align = symtab_node::get (decl)->definition_alignment (); |
| ASM_OUTPUT_ALIGNED_DECL_LOCAL (asm_out_file, decl, name, |
| size, align); |
| return true; |
| #elif defined ASM_OUTPUT_ALIGNED_LOCAL |
| unsigned int align = symtab_node::get (decl)->definition_alignment (); |
| ASM_OUTPUT_ALIGNED_LOCAL (asm_out_file, name, size, align); |
| return true; |
| #else |
| ASM_OUTPUT_LOCAL (asm_out_file, name, size, rounded); |
| return false; |
| #endif |
| } |
| |
| /* A noswitch_section_callback for bss_noswitch_section. */ |
| |
| #if defined ASM_OUTPUT_ALIGNED_BSS |
| static bool |
| emit_bss (tree decl ATTRIBUTE_UNUSED, |
| const char *name ATTRIBUTE_UNUSED, |
| unsigned HOST_WIDE_INT size ATTRIBUTE_UNUSED, |
| unsigned HOST_WIDE_INT rounded ATTRIBUTE_UNUSED) |
| { |
| ASM_OUTPUT_ALIGNED_BSS (asm_out_file, decl, name, size, |
| get_variable_align (decl)); |
| return true; |
| } |
| #endif |
| |
| /* A noswitch_section_callback for comm_section. */ |
| |
| static bool |
| emit_common (tree decl ATTRIBUTE_UNUSED, |
| const char *name ATTRIBUTE_UNUSED, |
| unsigned HOST_WIDE_INT size ATTRIBUTE_UNUSED, |
| unsigned HOST_WIDE_INT rounded ATTRIBUTE_UNUSED) |
| { |
| #if defined ASM_OUTPUT_ALIGNED_DECL_COMMON |
| ASM_OUTPUT_ALIGNED_DECL_COMMON (asm_out_file, decl, name, |
| size, get_variable_align (decl)); |
| return true; |
| #elif defined ASM_OUTPUT_ALIGNED_COMMON |
| ASM_OUTPUT_ALIGNED_COMMON (asm_out_file, name, size, |
| get_variable_align (decl)); |
| return true; |
| #else |
| ASM_OUTPUT_COMMON (asm_out_file, name, size, rounded); |
| return false; |
| #endif |
| } |
| |
| /* A noswitch_section_callback for tls_comm_section. */ |
| |
| static bool |
| emit_tls_common (tree decl ATTRIBUTE_UNUSED, |
| const char *name ATTRIBUTE_UNUSED, |
| unsigned HOST_WIDE_INT size ATTRIBUTE_UNUSED, |
| unsigned HOST_WIDE_INT rounded ATTRIBUTE_UNUSED) |
| { |
| #ifdef ASM_OUTPUT_TLS_COMMON |
| ASM_OUTPUT_TLS_COMMON (asm_out_file, decl, name, size); |
| return true; |
| #else |
| sorry ("thread-local COMMON data not implemented"); |
| return true; |
| #endif |
| } |
| |
| /* Assemble DECL given that it belongs in SECTION_NOSWITCH section SECT. |
| NAME is the name of DECL's SYMBOL_REF. */ |
| |
| static void |
| assemble_noswitch_variable (tree decl, const char *name, section *sect, |
| unsigned int align) |
| { |
| unsigned HOST_WIDE_INT size, rounded; |
| |
| size = tree_to_uhwi (DECL_SIZE_UNIT (decl)); |
| rounded = size; |
| |
| if ((flag_sanitize & SANITIZE_ADDRESS) && asan_protect_global (decl)) |
| size += asan_red_zone_size (size); |
| |
| /* Don't allocate zero bytes of common, |
| since that means "undefined external" in the linker. */ |
| if (size == 0) |
| rounded = 1; |
| |
| /* Round size up to multiple of BIGGEST_ALIGNMENT bits |
| so that each uninitialized object starts on such a boundary. */ |
| rounded += (BIGGEST_ALIGNMENT / BITS_PER_UNIT) - 1; |
| rounded = (rounded / (BIGGEST_ALIGNMENT / BITS_PER_UNIT) |
| * (BIGGEST_ALIGNMENT / BITS_PER_UNIT)); |
| |
| if (!sect->noswitch.callback (decl, name, size, rounded) |
| && (unsigned HOST_WIDE_INT) (align / BITS_PER_UNIT) > rounded) |
| error ("requested alignment for %q+D is greater than " |
| "implemented alignment of %wu", decl, rounded); |
| } |
| |
| /* A subroutine of assemble_variable. Output the label and contents of |
| DECL, whose address is a SYMBOL_REF with name NAME. DONT_OUTPUT_DATA |
| is as for assemble_variable. */ |
| |
| static void |
| assemble_variable_contents (tree decl, const char *name, |
| bool dont_output_data, bool merge_strings) |
| { |
| /* Do any machine/system dependent processing of the object. */ |
| #ifdef ASM_DECLARE_OBJECT_NAME |
| last_assemble_variable_decl = decl; |
| ASM_DECLARE_OBJECT_NAME (asm_out_file, name, decl); |
| #else |
| /* Standard thing is just output label for the object. */ |
| ASM_OUTPUT_LABEL (asm_out_file, name); |
| #endif /* ASM_DECLARE_OBJECT_NAME */ |
| |
| if (!dont_output_data) |
| { |
| /* Caller is supposed to use varpool_get_constructor when it wants |
| to output the body. */ |
| gcc_assert (!in_lto_p || DECL_INITIAL (decl) != error_mark_node); |
| if (DECL_INITIAL (decl) |
| && DECL_INITIAL (decl) != error_mark_node |
| && !initializer_zerop (DECL_INITIAL (decl))) |
| /* Output the actual data. */ |
| output_constant (DECL_INITIAL (decl), |
| tree_to_uhwi (DECL_SIZE_UNIT (decl)), |
| get_variable_align (decl), |
| false, merge_strings); |
| else |
| /* Leave space for it. */ |
| assemble_zeros (tree_to_uhwi (DECL_SIZE_UNIT (decl))); |
| targetm.asm_out.decl_end (); |
| } |
| } |
| |
| /* Write out assembly for the variable DECL, which is not defined in |
| the current translation unit. */ |
| void |
| assemble_undefined_decl (tree decl) |
| { |
| const char *name = XSTR (XEXP (DECL_RTL (decl), 0), 0); |
| targetm.asm_out.assemble_undefined_decl (asm_out_file, name, decl); |
| } |
| |
| /* Assemble everything that is needed for a variable or function declaration. |
| Not used for automatic variables, and not used for function definitions. |
| Should not be called for variables of incomplete structure type. |
| |
| TOP_LEVEL is nonzero if this variable has file scope. |
| AT_END is nonzero if this is the special handling, at end of compilation, |
| to define things that have had only tentative definitions. |
| DONT_OUTPUT_DATA if nonzero means don't actually output the |
| initial value (that will be done by the caller). */ |
| |
| void |
| assemble_variable (tree decl, int top_level ATTRIBUTE_UNUSED, |
| int at_end ATTRIBUTE_UNUSED, int dont_output_data) |
| { |
| const char *name; |
| rtx decl_rtl, symbol; |
| section *sect; |
| unsigned int align; |
| bool asan_protected = false; |
| |
| /* This function is supposed to handle VARIABLES. Ensure we have one. */ |
| gcc_assert (VAR_P (decl)); |
| |
| /* Emulated TLS had better not get this far. */ |
| gcc_checking_assert (targetm.have_tls || !DECL_THREAD_LOCAL_P (decl)); |
| |
| last_assemble_variable_decl = 0; |
| |
| /* Normally no need to say anything here for external references, |
| since assemble_external is called by the language-specific code |
| when a declaration is first seen. */ |
| |
| if (DECL_EXTERNAL (decl)) |
| return; |
| |
| /* Do nothing for global register variables. */ |
| if (DECL_RTL_SET_P (decl) && REG_P (DECL_RTL (decl))) |
| { |
| TREE_ASM_WRITTEN (decl) = 1; |
| return; |
| } |
| |
| /* If type was incomplete when the variable was declared, |
| see if it is complete now. */ |
| |
| if (DECL_SIZE (decl) == 0) |
| layout_decl (decl, 0); |
| |
| /* Still incomplete => don't allocate it; treat the tentative defn |
| (which is what it must have been) as an `extern' reference. */ |
| |
| if (!dont_output_data && DECL_SIZE (decl) == 0) |
| { |
| error ("storage size of %q+D isn%'t known", decl); |
| TREE_ASM_WRITTEN (decl) = 1; |
| return; |
| } |
| |
| /* 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 (TREE_ASM_WRITTEN (decl)) |
| return; |
| |
| /* Make sure targetm.encode_section_info is invoked before we set |
| ASM_WRITTEN. */ |
| decl_rtl = DECL_RTL (decl); |
| |
| TREE_ASM_WRITTEN (decl) = 1; |
| |
| /* Do no output if -fsyntax-only. */ |
| if (flag_syntax_only) |
| return; |
| |
| if (! dont_output_data |
| && ! valid_constant_size_p (DECL_SIZE_UNIT (decl))) |
| { |
| error ("size of variable %q+D is too large", decl); |
| return; |
| } |
| |
| gcc_assert (MEM_P (decl_rtl)); |
| gcc_assert (GET_CODE (XEXP (decl_rtl, 0)) == SYMBOL_REF); |
| symbol = XEXP (decl_rtl, 0); |
| |
| /* If this symbol belongs to the tree constant pool, output the constant |
| if it hasn't already been written. */ |
| if (TREE_CONSTANT_POOL_ADDRESS_P (symbol)) |
| { |
| tree decl = SYMBOL_REF_DECL (symbol); |
| if (!TREE_ASM_WRITTEN (DECL_INITIAL (decl))) |
| output_constant_def_contents (symbol); |
| return; |
| } |
| |
| app_disable (); |
| |
| name = XSTR (symbol, 0); |
| if (TREE_PUBLIC (decl) && DECL_NAME (decl)) |
| notice_global_symbol (decl); |
| |
| /* Compute the alignment of this data. */ |
| |
| align_variable (decl, dont_output_data); |
| |
| if ((flag_sanitize & SANITIZE_ADDRESS) |
| && asan_protect_global (decl)) |
| { |
| asan_protected = true; |
| SET_DECL_ALIGN (decl, MAX (DECL_ALIGN (decl), |
| ASAN_RED_ZONE_SIZE * BITS_PER_UNIT)); |
| } |
| |
| set_mem_align (decl_rtl, DECL_ALIGN (decl)); |
| |
| align = get_variable_align (decl); |
| |
| if (TREE_PUBLIC (decl)) |
| maybe_assemble_visibility (decl); |
| |
| if (DECL_PRESERVE_P (decl)) |
| targetm.asm_out.mark_decl_preserved (name); |
| |
| /* First make the assembler name(s) global if appropriate. */ |
| sect = get_variable_section (decl, false); |
| if (TREE_PUBLIC (decl) |
| && (sect->common.flags & SECTION_COMMON) == 0) |
| globalize_decl (decl); |
| |
| /* Output any data that we will need to use the address of. */ |
| if (DECL_INITIAL (decl) && DECL_INITIAL (decl) != error_mark_node) |
| output_addressed_constants (DECL_INITIAL (decl), 0); |
| |
| /* dbxout.cc needs to know this. */ |
| if (sect && (sect->common.flags & SECTION_CODE) != 0) |
| DECL_IN_TEXT_SECTION (decl) = 1; |
| |
| /* If the decl is part of an object_block, make sure that the decl |
| has been positioned within its block, but do not write out its |
| definition yet. output_object_blocks will do that later. */ |
| if (SYMBOL_REF_HAS_BLOCK_INFO_P (symbol) && SYMBOL_REF_BLOCK (symbol)) |
| { |
| gcc_assert (!dont_output_data); |
| place_block_symbol (symbol); |
| } |
| else if (SECTION_STYLE (sect) == SECTION_NOSWITCH) |
| assemble_noswitch_variable (decl, name, sect, align); |
| else |
| { |
| /* Special-case handling of vtv comdat sections. */ |
| if (SECTION_STYLE (sect) == SECTION_NAMED |
| && (strcmp (sect->named.name, ".vtable_map_vars") == 0)) |
| handle_vtv_comdat_section (sect, decl); |
| else |
| switch_to_section (sect, decl); |
| if (align > BITS_PER_UNIT) |
| ASM_OUTPUT_ALIGN (asm_out_file, floor_log2 (align / BITS_PER_UNIT)); |
| assemble_variable_contents (decl, name, dont_output_data, |
| (sect->common.flags & SECTION_MERGE) |
| && (sect->common.flags & SECTION_STRINGS)); |
| if (asan_protected) |
| { |
| unsigned HOST_WIDE_INT int size |
| = tree_to_uhwi (DECL_SIZE_UNIT (decl)); |
| assemble_zeros (asan_red_zone_size (size)); |
| } |
| } |
| } |
| |
| /* Return true if type TYPE contains any pointers. */ |
| |
| static bool |
| contains_pointers_p (tree type) |
| { |
| switch (TREE_CODE (type)) |
| { |
| case POINTER_TYPE: |
| case REFERENCE_TYPE: |
| /* I'm not sure whether OFFSET_TYPE needs this treatment, |
| so I'll play safe and return 1. */ |
| case OFFSET_TYPE: |
| return true; |
| |
| case RECORD_TYPE: |
| case UNION_TYPE: |
| case QUAL_UNION_TYPE: |
| { |
| tree fields; |
| /* For a type that has fields, see if the fields have pointers. */ |
| for (fields = TYPE_FIELDS (type); fields; fields = DECL_CHAIN (fields)) |
| if (TREE_CODE (fields) == FIELD_DECL |
| && contains_pointers_p (TREE_TYPE (fields))) |
| return true; |
| return false; |
| } |
| |
| case ARRAY_TYPE: |
| /* An array type contains pointers if its element type does. */ |
| return contains_pointers_p (TREE_TYPE (type)); |
| |
| default: |
| return false; |
| } |
| } |
| |
| /* We delay assemble_external processing until |
| the compilation unit is finalized. This is the best we can do for |
| right now (i.e. stage 3 of GCC 4.0) - the right thing is to delay |
| it all the way to final. See PR 17982 for further discussion. */ |
| static GTY(()) tree pending_assemble_externals; |
| |
| /* A similar list of pending libcall symbols. We only want to declare |
| symbols that are actually used in the final assembly. */ |
| static GTY(()) rtx pending_libcall_symbols; |
| |
| #ifdef ASM_OUTPUT_EXTERNAL |
| /* Some targets delay some output to final using TARGET_ASM_FILE_END. |
| As a result, assemble_external can be called after the list of externals |
| is processed and the pointer set destroyed. */ |
| static bool pending_assemble_externals_processed; |
| |
| /* Avoid O(external_decls**2) lookups in the pending_assemble_externals |
| TREE_LIST in assemble_external. */ |
| static hash_set<tree> *pending_assemble_externals_set; |
| |
| /* True if DECL is a function decl for which no out-of-line copy exists. |
| It is assumed that DECL's assembler name has been set. */ |
| |
| static bool |
| incorporeal_function_p (tree decl) |
| { |
| if (TREE_CODE (decl) == FUNCTION_DECL && fndecl_built_in_p (decl)) |
| { |
| const char *name; |
| |
| if (DECL_BUILT_IN_CLASS (decl) == BUILT_IN_NORMAL |
| && ALLOCA_FUNCTION_CODE_P (DECL_FUNCTION_CODE (decl))) |
| return true; |
| |
| name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)); |
| /* Atomic or sync builtins which have survived this far will be |
| resolved externally and therefore are not incorporeal. */ |
| if (startswith (name, "__builtin_")) |
| return true; |
| } |
| return false; |
| } |
| |
| /* Actually do the tests to determine if this is necessary, and invoke |
| ASM_OUTPUT_EXTERNAL. */ |
| static void |
| assemble_external_real (tree decl) |
| { |
| rtx rtl = DECL_RTL (decl); |
| |
| if (MEM_P (rtl) && GET_CODE (XEXP (rtl, 0)) == SYMBOL_REF |
| && !SYMBOL_REF_USED (XEXP (rtl, 0)) |
| && !incorporeal_function_p (decl)) |
| { |
| /* Some systems do require some output. */ |
| SYMBOL_REF_USED (XEXP (rtl, 0)) = 1; |
| ASM_OUTPUT_EXTERNAL (asm_out_file, decl, XSTR (XEXP (rtl, 0), 0)); |
| } |
| } |
| #endif |
| |
| void |
| process_pending_assemble_externals (void) |
| { |
| #ifdef ASM_OUTPUT_EXTERNAL |
| tree list; |
| for (list = pending_assemble_externals; list; list = TREE_CHAIN (list)) |
| assemble_external_real (TREE_VALUE (list)); |
| |
| for (rtx list = pending_libcall_symbols; list; list = XEXP (list, 1)) |
| { |
| rtx symbol = XEXP (list, 0); |
| const char *name = targetm.strip_name_encoding (XSTR (symbol, 0)); |
| tree id = get_identifier (name); |
| if (TREE_SYMBOL_REFERENCED (id)) |
| targetm.asm_out.external_libcall (symbol); |
| } |
| |
| pending_assemble_externals = 0; |
| pending_assemble_externals_processed = true; |
| pending_libcall_symbols = NULL_RTX; |
| delete pending_assemble_externals_set; |
| pending_assemble_externals_set = nullptr; |
| #endif |
| } |
| |
| /* This TREE_LIST contains any weak symbol declarations waiting |
| to be emitted. */ |
| static GTY(()) tree weak_decls; |
| |
| /* Output something to declare an external symbol to the assembler, |
| and qualifiers such as weakness. (Most assemblers don't need |
| extern declaration, so we normally output nothing.) Do nothing if |
| DECL is not external. */ |
| |
| void |
| assemble_external (tree decl ATTRIBUTE_UNUSED) |
| { |
| /* Make sure that the ASM_OUT_FILE is open. |
| If it's not, we should not be calling this function. */ |
| gcc_assert (asm_out_file); |
| |
| /* In a perfect world, the following condition would be true. |
| Sadly, the Go front end emit assembly *from the front end*, |
| bypassing the call graph. See PR52739. Fix before GCC 4.8. */ |
| #if 0 |
| /* This function should only be called if we are expanding, or have |
| expanded, to RTL. |
| Ideally, only final.cc would be calling this function, but it is |
| not clear whether that would break things somehow. See PR 17982 |
| for further discussion. */ |
| gcc_assert (state == EXPANSION |
| || state == FINISHED); |
| #endif |
| |
| if (!DECL_P (decl) || !DECL_EXTERNAL (decl) || !TREE_PUBLIC (decl)) |
| return; |
| |
| /* We want to output annotation for weak and external symbols at |
| very last to check if they are references or not. */ |
| |
| if (TARGET_SUPPORTS_WEAK |
| && DECL_WEAK (decl) |
| /* TREE_STATIC is a weird and abused creature which is not |
| generally the right test for whether an entity has been |
| locally emitted, inlined or otherwise not-really-extern, but |
| for declarations that can be weak, it happens to be |
| match. */ |
| && !TREE_STATIC (decl) |
| && lookup_attribute ("weak", DECL_ATTRIBUTES (decl)) |
| && value_member (decl, weak_decls) == NULL_TREE) |
| weak_decls = tree_cons (NULL, decl, weak_decls); |
| |
| #ifdef ASM_OUTPUT_EXTERNAL |
| if (pending_assemble_externals_processed) |
| { |
| assemble_external_real (decl); |
| return; |
| } |
| |
| if (! pending_assemble_externals_set->add (decl)) |
| pending_assemble_externals = tree_cons (NULL, decl, |
| pending_assemble_externals); |
| #endif |
| } |
| |
| /* Similar, for calling a library function FUN. */ |
| |
| void |
| assemble_external_libcall (rtx fun) |
| { |
| /* Declare library function name external when first used, if nec. */ |
| if (! SYMBOL_REF_USED (fun)) |
| { |
| #ifdef ASM_OUTPUT_EXTERNAL |
| gcc_assert (!pending_assemble_externals_processed); |
| #endif |
| SYMBOL_REF_USED (fun) = 1; |
| /* Make sure the libcall symbol is in the symtab so any |
| reference to it will mark its tree node as referenced, via |
| assemble_name_resolve. These are eventually emitted, if |
| used, in process_pending_assemble_externals. */ |
| const char *name = targetm.strip_name_encoding (XSTR (fun, 0)); |
| get_identifier (name); |
| pending_libcall_symbols = gen_rtx_EXPR_LIST (VOIDmode, fun, |
| pending_libcall_symbols); |
| } |
| } |
| |
| /* Assemble a label named NAME. */ |
| |
| void |
| assemble_label (FILE *file, const char *name) |
| { |
| ASM_OUTPUT_LABEL (file, name); |
| } |
| |
| /* Set the symbol_referenced flag for ID. */ |
| void |
| mark_referenced (tree id) |
| { |
| TREE_SYMBOL_REFERENCED (id) = 1; |
| } |
| |
| /* Set the symbol_referenced flag for DECL and notify callgraph. */ |
| void |
| mark_decl_referenced (tree decl) |
| { |
| if (TREE_CODE (decl) == FUNCTION_DECL) |
| { |
| /* Extern inline functions don't become needed when referenced. |
| If we know a method will be emitted in other TU and no new |
| functions can be marked reachable, just use the external |
| definition. */ |
| struct cgraph_node *node = cgraph_node::get_create (decl); |
| if (!DECL_EXTERNAL (decl) |
| && !node->definition) |
| node->mark_force_output (); |
| } |
| else if (VAR_P (decl)) |
| { |
| varpool_node *node = varpool_node::get_create (decl); |
| /* C++ frontend use mark_decl_references to force COMDAT variables |
| to be output that might appear dead otherwise. */ |
| node->force_output = true; |
| } |
| /* else do nothing - we can get various sorts of CST nodes here, |
| which do not need to be marked. */ |
| } |
| |
| |
| /* Output to FILE (an assembly file) a reference to NAME. If NAME |
| starts with a *, the rest of NAME is output verbatim. Otherwise |
| NAME is transformed in a target-specific way (usually by the |
| addition of an underscore). */ |
| |
| void |
| assemble_name_raw (FILE *file, const char *name) |
| { |
| if (name[0] == '*') |
| fputs (&name[1], file); |
| else |
| ASM_OUTPUT_LABELREF (file, name); |
| } |
| |
| /* Return NAME that should actually be emitted, looking through |
| transparent aliases. If NAME refers to an entity that is also |
| represented as a tree (like a function or variable), mark the entity |
| as referenced. */ |
| const char * |
| assemble_name_resolve (const char *name) |
| { |
| const char *real_name = targetm.strip_name_encoding (name); |
| tree id = maybe_get_identifier (real_name); |
| |
| if (id) |
| { |
| tree id_orig = id; |
| |
| mark_referenced (id); |
| ultimate_transparent_alias_target (&id); |
| if (id != id_orig) |
| name = IDENTIFIER_POINTER (id); |
| gcc_assert (! TREE_CHAIN (id)); |
| } |
| |
| return name; |
| } |
| |
| /* Like assemble_name_raw, but should be used when NAME might refer to |
| an entity that is also represented as a tree (like a function or |
| variable). If NAME does refer to such an entity, that entity will |
| be marked as referenced. */ |
| |
| void |
| assemble_name (FILE *file, const char *name) |
| { |
| assemble_name_raw (file, assemble_name_resolve (name)); |
| } |
| |
| /* Allocate SIZE bytes writable static space with a gensym name |
| and return an RTX to refer to its address. */ |
| |
| rtx |
| assemble_static_space (unsigned HOST_WIDE_INT size) |
| { |
| char name[17]; |
| const char *namestring; |
| rtx x; |
| |
| ASM_GENERATE_INTERNAL_LABEL (name, "LF", const_labelno); |
| ++const_labelno; |
| namestring = ggc_strdup (name); |
| |
| x = gen_rtx_SYMBOL_REF (Pmode, namestring); |
| SYMBOL_REF_FLAGS (x) = SYMBOL_FLAG_LOCAL; |
| |
| #ifdef ASM_OUTPUT_ALIGNED_DECL_LOCAL |
| ASM_OUTPUT_ALIGNED_DECL_LOCAL (asm_out_file, NULL_TREE, name, size, |
| BIGGEST_ALIGNMENT); |
| #else |
| #ifdef ASM_OUTPUT_ALIGNED_LOCAL |
| ASM_OUTPUT_ALIGNED_LOCAL (asm_out_file, name, size, BIGGEST_ALIGNMENT); |
| #else |
| { |
| /* Round size up to multiple of BIGGEST_ALIGNMENT bits |
| so that each uninitialized object starts on such a boundary. */ |
| /* Variable `rounded' might or might not be used in ASM_OUTPUT_LOCAL. */ |
| unsigned HOST_WIDE_INT rounded ATTRIBUTE_UNUSED |
| = ((size + (BIGGEST_ALIGNMENT / BITS_PER_UNIT) - 1) |
| / (BIGGEST_ALIGNMENT / BITS_PER_UNIT) |
| * (BIGGEST_ALIGNMENT / BITS_PER_UNIT)); |
| ASM_OUTPUT_LOCAL (asm_out_file, name, size, rounded); |
| } |
| #endif |
| #endif |
| return x; |
| } |
| |
| /* Assemble the static constant template for function entry trampolines. |
| This is done at most once per compilation. |
| Returns an RTX for the address of the template. */ |
| |
| static GTY(()) rtx initial_trampoline; |
| |
| rtx |
| assemble_trampoline_template (void) |
| { |
| char label[256]; |
| const char *name; |
| int align; |
| rtx symbol; |
| |
| gcc_assert (targetm.asm_out.trampoline_template != NULL); |
| |
| if (initial_trampoline) |
| return initial_trampoline; |
| |
| /* By default, put trampoline templates in read-only data section. */ |
| |
| #ifdef TRAMPOLINE_SECTION |
| switch_to_section (TRAMPOLINE_SECTION); |
| #else |
| switch_to_section (readonly_data_section); |
| #endif |
| |
| /* Write the assembler code to define one. */ |
| align = floor_log2 (TRAMPOLINE_ALIGNMENT / BITS_PER_UNIT); |
| if (align > 0) |
| ASM_OUTPUT_ALIGN (asm_out_file, align); |
| |
| targetm.asm_out.internal_label (asm_out_file, "LTRAMP", 0); |
| targetm.asm_out.trampoline_template (asm_out_file); |
| |
| /* Record the rtl to refer to it. */ |
| ASM_GENERATE_INTERNAL_LABEL (label, "LTRAMP", 0); |
| name = ggc_strdup (label); |
| symbol = gen_rtx_SYMBOL_REF (Pmode, name); |
| SYMBOL_REF_FLAGS (symbol) = SYMBOL_FLAG_LOCAL; |
| |
| initial_trampoline = gen_const_mem (BLKmode, symbol); |
| set_mem_align (initial_trampoline, TRAMPOLINE_ALIGNMENT); |
| set_mem_size (initial_trampoline, TRAMPOLINE_SIZE); |
| |
| return initial_trampoline; |
| } |
| |
| /* A and B are either alignments or offsets. Return the minimum alignment |
| that may be assumed after adding the two together. */ |
| |
| static inline unsigned |
| min_align (unsigned int a, unsigned int b) |
| { |
| return least_bit_hwi (a | b); |
| } |
| |
| /* Return the assembler directive for creating a given kind of integer |
| object. SIZE is the number of bytes in the object and ALIGNED_P |
| indicates whether it is known to be aligned. Return NULL if the |
| assembly dialect has no such directive. |
| |
| The returned string should be printed at the start of a new line and |
| be followed immediately by the object's initial value. */ |
| |
| const char * |
| integer_asm_op (int size, int aligned_p) |
| { |
| struct asm_int_op *ops; |
| |
| if (aligned_p) |
| ops = &targetm.asm_out.aligned_op; |
| else |
| ops = &targetm.asm_out.unaligned_op; |
| |
| switch (size) |
| { |
| case 1: |
| return targetm.asm_out.byte_op; |
| case 2: |
| return ops->hi; |
| case 3: |
| return ops->psi; |
| case 4: |
| return ops->si; |
| case 5: |
| case 6: |
| case 7: |
| return ops->pdi; |
| case 8: |
| return ops->di; |
| case 9: |
| case 10: |
| case 11: |
| case 12: |
| case 13: |
| case 14: |
| case 15: |
| return ops->pti; |
| case 16: |
| return ops->ti; |
| default: |
| return NULL; |
| } |
| } |
| |
| /* Use directive OP to assemble an integer object X. Print OP at the |
| start of the line, followed immediately by the value of X. */ |
| |
| void |
| assemble_integer_with_op (const char *op, rtx x) |
| { |
| fputs (op, asm_out_file); |
| output_addr_const (asm_out_file, x); |
| fputc ('\n', asm_out_file); |
| } |
| |
| /* The default implementation of the asm_out.integer target hook. */ |
| |
| bool |
| default_assemble_integer (rtx x ATTRIBUTE_UNUSED, |
| unsigned int size ATTRIBUTE_UNUSED, |
| int aligned_p ATTRIBUTE_UNUSED) |
| { |
| const char *op = integer_asm_op (size, aligned_p); |
| /* Avoid GAS bugs for large values. Specifically negative values whose |
| absolute value fits in a bfd_vma, but not in a bfd_signed_vma. */ |
| if (size > UNITS_PER_WORD && size > POINTER_SIZE_UNITS) |
| return false; |
| return op && (assemble_integer_with_op (op, x), true); |
| } |
| |
| /* Assemble the integer constant X into an object of SIZE bytes. ALIGN is |
| the alignment of the integer in bits. Return 1 if we were able to output |
| the constant, otherwise 0. We must be able to output the constant, |
| if FORCE is nonzero. */ |
| |
| bool |
| assemble_integer (rtx x, unsigned int size, unsigned int align, int force) |
| { |
| int aligned_p; |
| |
| aligned_p = (align >= MIN (size * BITS_PER_UNIT, BIGGEST_ALIGNMENT)); |
| |
| /* See if the target hook can handle this kind of object. */ |
| if (targetm.asm_out.integer (x, size, aligned_p)) |
| return true; |
| |
| /* If the object is a multi-byte one, try splitting it up. Split |
| it into words it if is multi-word, otherwise split it into bytes. */ |
| if (size > 1) |
| { |
| machine_mode omode, imode; |
| unsigned int subalign; |
| unsigned int subsize, i; |
| enum mode_class mclass; |
| |
| subsize = size > UNITS_PER_WORD? UNITS_PER_WORD : 1; |
| subalign = MIN (align, subsize * BITS_PER_UNIT); |
| if (GET_CODE (x) == CONST_FIXED) |
| mclass = GET_MODE_CLASS (GET_MODE (x)); |
| else |
| mclass = MODE_INT; |
| |
| omode = mode_for_size (subsize * BITS_PER_UNIT, mclass, 0).require (); |
| imode = mode_for_size (size * BITS_PER_UNIT, mclass, 0).require (); |
| |
| for (i = 0; i < size; i += subsize) |
| { |
| rtx partial = simplify_subreg (omode, x, imode, i); |
| if (!partial || !assemble_integer (partial, subsize, subalign, 0)) |
| break; |
| } |
| if (i == size) |
| return true; |
| |
| /* If we've printed some of it, but not all of it, there's no going |
| back now. */ |
| gcc_assert (!i); |
| } |
| |
| gcc_assert (!force); |
| |
| return false; |
| } |
| |
| /* Assemble the floating-point constant D into an object of size MODE. ALIGN |
| is the alignment of the constant in bits. If REVERSE is true, D is output |
| in reverse storage order. */ |
| |
| void |
| assemble_real (REAL_VALUE_TYPE d, scalar_float_mode mode, unsigned int align, |
| bool reverse) |
| { |
| long data[4] = {0, 0, 0, 0}; |
| int bitsize, nelts, nunits, units_per; |
| rtx elt; |
| |
| /* This is hairy. We have a quantity of known size. real_to_target |
| will put it into an array of *host* longs, 32 bits per element |
| (even if long is more than 32 bits). We need to determine the |
| number of array elements that are occupied (nelts) and the number |
| of *target* min-addressable units that will be occupied in the |
| object file (nunits). We cannot assume that 32 divides the |
| mode's bitsize (size * BITS_PER_UNIT) evenly. |
| |
| size * BITS_PER_UNIT is used here to make sure that padding bits |
| (which might appear at either end of the value; real_to_target |
| will include the padding bits in its output array) are included. */ |
| |
| nunits = GET_MODE_SIZE (mode); |
| bitsize = nunits * BITS_PER_UNIT; |
| nelts = CEIL (bitsize, 32); |
| units_per = 32 / BITS_PER_UNIT; |
| |
| real_to_target (data, &d, mode); |
| |
| /* Put out the first word with the specified alignment. */ |
| unsigned int chunk_nunits = MIN (nunits, units_per); |
| if (reverse) |
| elt = flip_storage_order (SImode, gen_int_mode (data[nelts - 1], SImode)); |
| else |
| elt = GEN_INT (sext_hwi (data[0], chunk_nunits * BITS_PER_UNIT)); |
| assemble_integer (elt, chunk_nunits, align, 1); |
| nunits -= chunk_nunits; |
| |
| /* Subsequent words need only 32-bit alignment. */ |
| align = min_align (align, 32); |
| |
| for (int i = 1; i < nelts; i++) |
| { |
| chunk_nunits = MIN (nunits, units_per); |
| if (reverse) |
| elt = flip_storage_order (SImode, |
| gen_int_mode (data[nelts - 1 - i], SImode)); |
| else |
| elt = GEN_INT (sext_hwi (data[i], chunk_nunits * BITS_PER_UNIT)); |
| assemble_integer (elt, chunk_nunits, align, 1); |
| nunits -= chunk_nunits; |
| } |
| } |
| |
| /* Given an expression EXP with a constant value, |
| reduce it to the sum of an assembler symbol and an integer. |
| Store them both in the structure *VALUE. |
| EXP must be reducible. */ |
| |
| class addr_const { |
| public: |
| rtx base; |
| poly_int64 offset; |
| }; |
| |
| static void |
| decode_addr_const (tree exp, class addr_const *value) |
| { |
| tree target = TREE_OPERAND (exp, 0); |
| poly_int64 offset = 0; |
| rtx x; |
| |
| while (1) |
| { |
| poly_int64 bytepos; |
| if (TREE_CODE (target) == COMPONENT_REF |
| && poly_int_tree_p (byte_position (TREE_OPERAND (target, 1)), |
| &bytepos)) |
| { |
| offset += bytepos; |
| target = TREE_OPERAND (target, 0); |
| } |
| else if (TREE_CODE (target) == ARRAY_REF |
| || TREE_CODE (target) == ARRAY_RANGE_REF) |
| { |
| /* Truncate big offset. */ |
| offset |
| += (TREE_INT_CST_LOW (TYPE_SIZE_UNIT (TREE_TYPE (target))) |
| * wi::to_poly_widest (TREE_OPERAND (target, 1)).force_shwi ()); |
| target = TREE_OPERAND (target, 0); |
| } |
| else if (TREE_CODE (target) == MEM_REF |
| && TREE_CODE (TREE_OPERAND (target, 0)) == ADDR_EXPR) |
| { |
| offset += mem_ref_offset (target).force_shwi (); |
| target = TREE_OPERAND (TREE_OPERAND (target, 0), 0); |
| } |
| else if (INDIRECT_REF_P (target) |
| && TREE_CODE (TREE_OPERAND (target, 0)) == NOP_EXPR |
| && TREE_CODE (TREE_OPERAND (TREE_OPERAND (target, 0), 0)) |
| == ADDR_EXPR) |
| target = TREE_OPERAND (TREE_OPERAND (TREE_OPERAND (target, 0), 0), 0); |
| else |
| break; |
| } |
| |
| switch (TREE_CODE (target)) |
| { |
| case VAR_DECL: |
| case FUNCTION_DECL: |
| x = DECL_RTL (target); |
| break; |
| |
| case LABEL_DECL: |
| x = gen_rtx_MEM (FUNCTION_MODE, |
| gen_rtx_LABEL_REF (Pmode, force_label_rtx (target))); |
| break; |
| |
| case REAL_CST: |
| case FIXED_CST: |
| case STRING_CST: |
| case COMPLEX_CST: |
| case CONSTRUCTOR: |
| case INTEGER_CST: |
| x = lookup_constant_def (target); |
| /* Should have been added by output_addressed_constants. */ |
| gcc_assert (x); |
| break; |
| |
| case INDIRECT_REF: |
| /* This deals with absolute addresses. */ |
| offset += tree_to_shwi (TREE_OPERAND (target, 0)); |
| x = gen_rtx_MEM (QImode, |
| gen_rtx_SYMBOL_REF (Pmode, "origin of addresses")); |
| break; |
| |
| case COMPOUND_LITERAL_EXPR: |
| gcc_assert (COMPOUND_LITERAL_EXPR_DECL (target)); |
| x = DECL_RTL (COMPOUND_LITERAL_EXPR_DECL (target)); |
| break; |
| |
| default: |
| gcc_unreachable (); |
| } |
| |
| gcc_assert (MEM_P (x)); |
| x = XEXP (x, 0); |
| |
| value->base = x; |
| value->offset = offset; |
| } |
| |
| static GTY(()) hash_table<tree_descriptor_hasher> *const_desc_htab; |
| |
| static void maybe_output_constant_def_contents (struct constant_descriptor_tree *, int); |
| |
| /* Constant pool accessor function. */ |
| |
| hash_table<tree_descriptor_hasher> * |
| constant_pool_htab (void) |
| { |
| return const_desc_htab; |
| } |
| |
| /* Compute a hash code for a constant expression. */ |
| |
| hashval_t |
| tree_descriptor_hasher::hash (constant_descriptor_tree *ptr) |
| { |
| return ptr->hash; |
| } |
| |
| static hashval_t |
| const_hash_1 (const tree exp) |
| { |
| const char *p; |
| hashval_t hi; |
| int len, i; |
| enum tree_code code = TREE_CODE (exp); |
| |
| /* Either set P and LEN to the address and len of something to hash and |
| exit the switch or return a value. */ |
| |
| switch (code) |
| { |
| case INTEGER_CST: |
| p = (char *) &TREE_INT_CST_ELT (exp, 0); |
| len = TREE_INT_CST_NUNITS (exp) * sizeof (HOST_WIDE_INT); |
| break; |
| |
| case REAL_CST: |
| return real_hash (TREE_REAL_CST_PTR (exp)); |
| |
| case FIXED_CST: |
| return fixed_hash (TREE_FIXED_CST_PTR (exp)); |
| |
| case STRING_CST: |
| p = TREE_STRING_POINTER (exp); |
| len = TREE_STRING_LENGTH (exp); |
| break; |
| |
| case COMPLEX_CST: |
| return (const_hash_1 (TREE_REALPART (exp)) * 5 |
| + const_hash_1 (TREE_IMAGPART (exp))); |
| |
| case VECTOR_CST: |
| { |
| hi = 7 + VECTOR_CST_NPATTERNS (exp); |
| hi = hi * 563 + VECTOR_CST_NELTS_PER_PATTERN (exp); |
| unsigned int count = vector_cst_encoded_nelts (exp); |
| for (unsigned int i = 0; i < count; ++i) |
| hi = hi * 563 + const_hash_1 (VECTOR_CST_ENCODED_ELT (exp, i)); |
| return hi; |
| } |
| |
| case CONSTRUCTOR: |
| { |
| unsigned HOST_WIDE_INT idx; |
| tree value; |
| |
| hi = 5 + int_size_in_bytes (TREE_TYPE (exp)); |
| |
| FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (exp), idx, value) |
| if (value) |
| hi = hi * 603 + const_hash_1 (value); |
| |
| return hi; |
| } |
| |
| case ADDR_EXPR: |
| if (CONSTANT_CLASS_P (TREE_OPERAND (exp, 0))) |
| return const_hash_1 (TREE_OPERAND (exp, 0)); |
| |
| /* Fallthru. */ |
| case FDESC_EXPR: |
| { |
| class addr_const value; |
| |
| decode_addr_const (exp, &value); |
| switch (GET_CODE (value.base)) |
| { |
| case SYMBOL_REF: |
| /* Don't hash the address of the SYMBOL_REF; |
| only use the offset and the symbol name. */ |
| hi = value.offset.coeffs[0]; |
| p = XSTR (value.base, 0); |
| for (i = 0; p[i] != 0; i++) |
| hi = ((hi * 613) + (unsigned) (p[i])); |
| break; |
| |
| case LABEL_REF: |
| hi = (value.offset.coeffs[0] |
| + CODE_LABEL_NUMBER (label_ref_label (value.base)) * 13); |
| break; |
| |
| default: |
| gcc_unreachable (); |
| } |
| } |
| return hi; |
| |
| case PLUS_EXPR: |
| case POINTER_PLUS_EXPR: |
| case MINUS_EXPR: |
| return (const_hash_1 (TREE_OPERAND (exp, 0)) * 9 |
| + const_hash_1 (TREE_OPERAND (exp, 1))); |
| |
| CASE_CONVERT: |
| return const_hash_1 (TREE_OPERAND (exp, 0)) * 7 + 2; |
| |
| default: |
| /* A language specific constant. Just hash the code. */ |
| return code; |
| } |
| |
| /* Compute hashing function. */ |
| hi = len; |
| for (i = 0; i < len; i++) |
| hi = ((hi * 613) + (unsigned) (p[i])); |
| |
| return hi; |
| } |
| |
| /* Wrapper of compare_constant, for the htab interface. */ |
| bool |
| tree_descriptor_hasher::equal (constant_descriptor_tree *c1, |
| constant_descriptor_tree *c2) |
| { |
| if (c1->hash != c2->hash) |
| return false; |
| return compare_constant (c1->value, c2->value); |
| } |
| |
| /* Compare t1 and t2, and return true only if they are known to result in |
| the same bit pattern on output. */ |
| |
| static bool |
| compare_constant (const tree t1, const tree t2) |
| { |
| enum tree_code typecode; |
| |
| if (t1 == NULL_TREE) |
| return t2 == NULL_TREE; |
| if (t2 == NULL_TREE) |
| return false; |
| |
| if (TREE_CODE (t1) != TREE_CODE (t2)) |
| return false; |
| |
| switch (TREE_CODE (t1)) |
| { |
| case INTEGER_CST: |
| /* Integer constants are the same only if the same width of type. */ |
| if (TYPE_PRECISION (TREE_TYPE (t1)) != TYPE_PRECISION (TREE_TYPE (t2))) |
| return false; |
| if (TYPE_MODE (TREE_TYPE (t1)) != TYPE_MODE (TREE_TYPE (t2))) |
| return false; |
| return tree_int_cst_equal (t1, t2); |
| |
| case REAL_CST: |
| /* Real constants are the same only if the same width of type. In |
| addition to the same width, we need to check whether the modes are the |
| same. There might be two floating point modes that are the same size |
| but have different representations, such as the PowerPC that has 2 |
| different 128-bit floating point types (IBM extended double and IEEE |
| 128-bit floating point). */ |
| if (TYPE_PRECISION (TREE_TYPE (t1)) != TYPE_PRECISION (TREE_TYPE (t2))) |
| return false; |
| if (TYPE_MODE (TREE_TYPE (t1)) != TYPE_MODE (TREE_TYPE (t2))) |
| return false; |
| return real_identical (&TREE_REAL_CST (t1), &TREE_REAL_CST (t2)); |
| |
| case FIXED_CST: |
| /* Fixed constants are the same only if the same width of type. */ |
| if (TYPE_PRECISION (TREE_TYPE (t1)) != TYPE_PRECISION (TREE_TYPE (t2))) |
| return false; |
| |
| return FIXED_VALUES_IDENTICAL (TREE_FIXED_CST (t1), TREE_FIXED_CST (t2)); |
| |
| case STRING_CST: |
| if (TYPE_MODE (TREE_TYPE (t1)) != TYPE_MODE (TREE_TYPE (t2)) |
| || int_size_in_bytes (TREE_TYPE (t1)) |
| != int_size_in_bytes (TREE_TYPE (t2))) |
| return false; |
| |
| return (TREE_STRING_LENGTH (t1) == TREE_STRING_LENGTH (t2) |
| && ! memcmp (TREE_STRING_POINTER (t1), TREE_STRING_POINTER (t2), |
| TREE_STRING_LENGTH (t1))); |
| |
| case COMPLEX_CST: |
| return (compare_constant (TREE_REALPART (t1), TREE_REALPART (t2)) |
| && compare_constant (TREE_IMAGPART (t1), TREE_IMAGPART (t2))); |
| |
| case VECTOR_CST: |
| { |
| if (VECTOR_CST_NPATTERNS (t1) |
| != VECTOR_CST_NPATTERNS (t2)) |
| return false; |
| |
| if (VECTOR_CST_NELTS_PER_PATTERN (t1) |
| != VECTOR_CST_NELTS_PER_PATTERN (t2)) |
| return false; |
| |
| unsigned int count = vector_cst_encoded_nelts (t1); |
| for (unsigned int i = 0; i < count; ++i) |
| if (!compare_constant (VECTOR_CST_ENCODED_ELT (t1, i), |
| VECTOR_CST_ENCODED_ELT (t2, i))) |
| return false; |
| |
| return true; |
| } |
| |
| case CONSTRUCTOR: |
| { |
| vec<constructor_elt, va_gc> *v1, *v2; |
| unsigned HOST_WIDE_INT idx; |
| |
| typecode = TREE_CODE (TREE_TYPE (t1)); |
| if (typecode != TREE_CODE (TREE_TYPE (t2))) |
| return false; |
| |
| if (typecode == ARRAY_TYPE) |
| { |
| HOST_WIDE_INT size_1 = int_size_in_bytes (TREE_TYPE (t1)); |
| /* For arrays, check that mode, size and storage order match. */ |
| if (TYPE_MODE (TREE_TYPE (t1)) != TYPE_MODE (TREE_TYPE (t2)) |
| || size_1 == -1 |
| || size_1 != int_size_in_bytes (TREE_TYPE (t2)) |
| || TYPE_REVERSE_STORAGE_ORDER (TREE_TYPE (t1)) |
| != TYPE_REVERSE_STORAGE_ORDER (TREE_TYPE (t2))) |
| return false; |
| } |
| else |
| { |
| /* For record and union constructors, require exact type |
| equality. */ |
| if (TREE_TYPE (t1) != TREE_TYPE (t2)) |
| return false; |
| } |
| |
| v1 = CONSTRUCTOR_ELTS (t1); |
| v2 = CONSTRUCTOR_ELTS (t2); |
| if (vec_safe_length (v1) != vec_safe_length (v2)) |
| return false; |
| |
| for (idx = 0; idx < vec_safe_length (v1); ++idx) |
| { |
| constructor_elt *c1 = &(*v1)[idx]; |
| constructor_elt *c2 = &(*v2)[idx]; |
| |
| /* Check that each value is the same... */ |
| if (!compare_constant (c1->value, c2->value)) |
| return false; |
| /* ... and that they apply to the same fields! */ |
| if (typecode == ARRAY_TYPE) |
| { |
| if (!compare_constant (c1->index, c2->index)) |
| return false; |
| } |
| else |
| { |
| if (c1->index != c2->index) |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| case ADDR_EXPR: |
| case FDESC_EXPR: |
| { |
| class addr_const value1, value2; |
| enum rtx_code code; |
| bool ret; |
| |
| decode_addr_const (t1, &value1); |
| decode_addr_const (t2, &value2); |
| |
| if (maybe_ne (value1.offset, value2.offset)) |
| return false; |
| |
| code = GET_CODE (value1.base); |
| if (code != GET_CODE (value2.base)) |
| return false; |
| |
| switch (code) |
| { |
| case SYMBOL_REF: |
| ret = (strcmp (XSTR (value1.base, 0), XSTR (value2.base, 0)) == 0); |
| break; |
| |
| case LABEL_REF: |
| ret = (CODE_LABEL_NUMBER (label_ref_label (value1.base)) |
| == CODE_LABEL_NUMBER (label_ref_label (value2.base))); |
| break; |
| |
| default: |
| gcc_unreachable (); |
| } |
| return ret; |
| } |
| |
| case PLUS_EXPR: |
| case POINTER_PLUS_EXPR: |
| case MINUS_EXPR: |
| case RANGE_EXPR: |
| return (compare_constant (TREE_OPERAND (t1, 0), TREE_OPERAND (t2, 0)) |
| && compare_constant (TREE_OPERAND (t1, 1), TREE_OPERAND (t2, 1))); |
| |
| CASE_CONVERT: |
| case VIEW_CONVERT_EXPR: |
| return compare_constant (TREE_OPERAND (t1, 0), TREE_OPERAND (t2, 0)); |
| |
| default: |
| return false; |
| } |
| } |
| |
| /* Return the section into which constant EXP should be placed. */ |
| |
| static section * |
| get_constant_section (tree exp, unsigned int align) |
| { |
| return targetm.asm_out.select_section (exp, |
| compute_reloc_for_constant (exp), |
| align); |
| } |
| |
| /* Return the size of constant EXP in bytes. */ |
| |
| static HOST_WIDE_INT |
| get_constant_size (tree exp) |
| { |
| HOST_WIDE_INT size; |
| |
| size = int_size_in_bytes (TREE_TYPE (exp)); |
| gcc_checking_assert (size >= 0); |
| gcc_checking_assert (TREE_CODE (exp) != STRING_CST |
| || size >= TREE_STRING_LENGTH (exp)); |
| return size; |
| } |
| |
| /* Subroutine of output_constant_def: |
| No constant equal to EXP is known to have been output. |
| Make a constant descriptor to enter EXP in the hash table. |
| Assign the label number and construct RTL to refer to the |
| constant's location in memory. |
| Caller is responsible for updating the hash table. */ |
| |
| static struct constant_descriptor_tree * |
| build_constant_desc (tree exp) |
| { |
| struct constant_descriptor_tree *desc; |
| rtx symbol, rtl; |
| char label[256]; |
| int labelno; |
| tree decl; |
| |
| desc = ggc_alloc<constant_descriptor_tree> (); |
| desc->value = exp; |
| |
| /* Create a string containing the label name, in LABEL. */ |
| labelno = const_labelno++; |
| ASM_GENERATE_INTERNAL_LABEL (label, "LC", labelno); |
| |
| /* Construct the VAR_DECL associated with the constant. */ |
| decl = build_decl (UNKNOWN_LOCATION, VAR_DECL, get_identifier (label), |
| TREE_TYPE (exp)); |
| DECL_ARTIFICIAL (decl) = 1; |
| DECL_IGNORED_P (decl) = 1; |
| TREE_READONLY (decl) = 1; |
| TREE_STATIC (decl) = 1; |
| TREE_ADDRESSABLE (decl) = 1; |
| /* We don't set the RTL yet as this would cause varpool to assume that the |
| variable is referenced. Moreover, it would just be dropped in LTO mode. |
| Instead we set the flag that will be recognized in make_decl_rtl. */ |
| DECL_IN_CONSTANT_POOL (decl) = 1; |
| DECL_INITIAL (decl) = desc->value; |
| /* ??? targetm.constant_alignment hasn't been updated for vector types on |
| most architectures so use DATA_ALIGNMENT as well, except for strings. */ |
| if (TREE_CODE (exp) == STRING_CST) |
| SET_DECL_ALIGN (decl, targetm.constant_alignment (exp, DECL_ALIGN (decl))); |
| else |
| { |
| align_variable (decl, 0); |
| if (DECL_ALIGN (decl) < GET_MODE_ALIGNMENT (DECL_MODE (decl)) |
| && ((optab_handler (movmisalign_optab, DECL_MODE (decl)) |
| != CODE_FOR_nothing) |
| || targetm.slow_unaligned_access (DECL_MODE (decl), |
| DECL_ALIGN (decl)))) |
| SET_DECL_ALIGN (decl, GET_MODE_ALIGNMENT (DECL_MODE (decl))); |
| } |
| |
| /* Now construct the SYMBOL_REF and the MEM. */ |
| if (use_object_blocks_p ()) |
| { |
| int align = (TREE_CODE (decl) == CONST_DECL |
| || (VAR_P (decl) && DECL_IN_CONSTANT_POOL (decl)) |
| ? DECL_ALIGN (decl) |
| : symtab_node::get (decl)->definition_alignment ()); |
| section *sect = get_constant_section (exp, align); |
| symbol = create_block_symbol (ggc_strdup (label), |
| get_block_for_section (sect), -1); |
| } |
| else |
| symbol = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (label)); |
| SYMBOL_REF_FLAGS (symbol) |= SYMBOL_FLAG_LOCAL; |
| SET_SYMBOL_REF_DECL (symbol, decl); |
| TREE_CONSTANT_POOL_ADDRESS_P (symbol) = 1; |
| |
| rtl = gen_const_mem (TYPE_MODE (TREE_TYPE (exp)), symbol); |
| set_mem_alias_set (rtl, 0); |
| |
| /* Putting EXP into the literal pool might have imposed a different |
| alignment which should be visible in the RTX as well. */ |
| set_mem_align (rtl, DECL_ALIGN (decl)); |
| |
| /* We cannot share RTX'es in pool entries. |
| Mark this piece of RTL as required for unsharing. */ |
| RTX_FLAG (rtl, used) = 1; |
| |
| /* Set flags or add text to the name to record information, such as |
| that it is a local symbol. If the name is changed, the macro |
| ASM_OUTPUT_LABELREF will have to know how to strip this |
| information. This call might invalidate our local variable |
| SYMBOL; we can't use it afterward. */ |
| targetm.encode_section_info (exp, rtl, true); |
| |
| desc->rtl = rtl; |
| |
| return desc; |
| } |
| |
| /* Subroutine of output_constant_def and tree_output_constant_def: |
| Add a constant to the hash table that tracks which constants |
| already have labels. */ |
| |
| static constant_descriptor_tree * |
| add_constant_to_table (tree exp, int defer) |
| { |
| /* The hash table methods may call output_constant_def for addressed |
| constants, so handle them first. */ |
| output_addressed_constants (exp, defer); |
| |
| /* Sanity check to catch recursive insertion. */ |
| static bool inserting; |
| gcc_assert (!inserting); |
| inserting = true; |
| |
| /* Look up EXP in the table of constant descriptors. If we didn't |
| find it, create a new one. */ |
| struct constant_descriptor_tree key; |
| key.value = exp; |
| key.hash = const_hash_1 (exp); |
| constant_descriptor_tree **loc |
| = const_desc_htab->find_slot_with_hash (&key, key.hash, INSERT); |
| |
| inserting = false; |
| |
| struct constant_descriptor_tree *desc = *loc; |
| if (!desc) |
| { |
| desc = build_constant_desc (exp); |
| desc->hash = key.hash; |
| *loc = desc; |
| } |
| |
| return desc; |
| } |
| |
| /* Return an rtx representing a reference to constant data in memory |
| for the constant expression EXP. |
| |
| If assembler code for such a constant has already been output, |
| return an rtx to refer to it. |
| Otherwise, output such a constant in memory |
| and generate an rtx for it. |
| |
| If DEFER is nonzero, this constant can be deferred and output only |
| if referenced in the function after all optimizations. |
| |
| `const_desc_table' records which constants already have label strings. */ |
| |
| rtx |
| output_constant_def (tree exp, int defer) |
| { |
| struct constant_descriptor_tree *desc = add_constant_to_table (exp, defer); |
| maybe_output_constant_def_contents (desc, defer); |
| return desc->rtl; |
| } |
| |
| /* Subroutine of output_constant_def: Decide whether or not we need to |
| output the constant DESC now, and if so, do it. */ |
| static void |
| maybe_output_constant_def_contents (struct constant_descriptor_tree *desc, |
| int defer) |
| { |
| rtx symbol = XEXP (desc->rtl, 0); |
| tree exp = desc->value; |
| |
| if (flag_syntax_only) |
| return; |
| |
| if (TREE_ASM_WRITTEN (exp)) |
| /* Already output; don't do it again. */ |
| return; |
| |
| /* We can always defer constants as long as the context allows |
| doing so. */ |
| if (defer) |
| { |
| /* Increment n_deferred_constants if it exists. It needs to be at |
| least as large as the number of constants actually referred to |
| by the function. If it's too small we'll stop looking too early |
| and fail to emit constants; if it's too large we'll only look |
| through the entire function when we could have stopped earlier. */ |
| if (cfun) |
| n_deferred_constants++; |
| return; |
| } |
| |
| output_constant_def_contents (symbol); |
| } |
| |
| /* Subroutine of output_constant_def_contents. Output the definition |
| of constant EXP, which is pointed to by label LABEL. ALIGN is the |
| constant's alignment in bits. */ |
| |
| static void |
| assemble_constant_contents (tree exp, const char *label, unsigned int align, |
| bool merge_strings) |
| { |
| HOST_WIDE_INT size; |
| |
| size = get_constant_size (exp); |
| |
| /* Do any machine/system dependent processing of the constant. */ |
| targetm.asm_out.declare_constant_name (asm_out_file, label, exp, size); |
| |
| /* Output the value of EXP. */ |
| output_constant (exp, size, align, false, merge_strings); |
| |
| targetm.asm_out.decl_end (); |
| } |
| |
| /* We must output the constant data referred to by SYMBOL; do so. */ |
| |
| static void |
| output_constant_def_contents (rtx symbol) |
| { |
| tree decl = SYMBOL_REF_DECL (symbol); |
| tree exp = DECL_INITIAL (decl); |
| bool asan_protected = false; |
| |
| /* Make sure any other constants whose addresses appear in EXP |
| are assigned label numbers. */ |
| output_addressed_constants (exp, 0); |
| |
| /* We are no longer deferring this constant. */ |
| TREE_ASM_WRITTEN (decl) = TREE_ASM_WRITTEN (exp) = 1; |
| |
| if ((flag_sanitize & SANITIZE_ADDRESS) |
| && TREE_CODE (exp) == STRING_CST |
| && asan_protect_global (exp)) |
| { |
| asan_protected = true; |
| SET_DECL_ALIGN (decl, MAX (DECL_ALIGN (decl), |
| ASAN_RED_ZONE_SIZE * BITS_PER_UNIT)); |
| } |
| |
| /* If the constant is part of an object block, make sure that the |
| decl has been positioned within its block, but do not write out |
| its definition yet. output_object_blocks will do that later. */ |
| if (SYMBOL_REF_HAS_BLOCK_INFO_P (symbol) && SYMBOL_REF_BLOCK (symbol)) |
| place_block_symbol (symbol); |
| else |
| { |
| int align = (TREE_CODE (decl) == CONST_DECL |
| || (VAR_P (decl) && DECL_IN_CONSTANT_POOL (decl)) |
| ? DECL_ALIGN (decl) |
| : symtab_node::get (decl)->definition_alignment ()); |
| section *sect = get_constant_section (exp, align); |
| switch_to_section (sect); |
| if (align > BITS_PER_UNIT) |
| ASM_OUTPUT_ALIGN (asm_out_file, floor_log2 (align / BITS_PER_UNIT)); |
| assemble_constant_contents (exp, XSTR (symbol, 0), align, |
| (sect->common.flags & SECTION_MERGE) |
| && (sect->common.flags & SECTION_STRINGS)); |
| if (asan_protected) |
| { |
| HOST_WIDE_INT size = get_constant_size (exp); |
| assemble_zeros (asan_red_zone_size (size)); |
| } |
| } |
| } |
| |
| /* Look up EXP in the table of constant descriptors. Return the rtl |
| if it has been emitted, else null. */ |
| |
| rtx |
| lookup_constant_def (tree exp) |
| { |
| struct constant_descriptor_tree key; |
| |
| key.value = exp; |
| key.hash = const_hash_1 (exp); |
| constant_descriptor_tree *desc |
| = const_desc_htab->find_with_hash (&key, key.hash); |
| |
| return (desc ? desc->rtl : NULL_RTX); |
| } |
| |
| /* Return a tree representing a reference to constant data in memory |
| for the constant expression EXP. |
| |
| This is the counterpart of output_constant_def at the Tree level. */ |
| |
| tree |
| tree_output_constant_def (tree exp) |
| { |
| struct constant_descriptor_tree *desc = add_constant_to_table (exp, 1); |
| tree decl = SYMBOL_REF_DECL (XEXP (desc->rtl, 0)); |
| varpool_node::finalize_decl (decl); |
| return decl; |
| } |
| |
| class GTY((chain_next ("%h.next"), for_user)) constant_descriptor_rtx { |
| public: |
| class constant_descriptor_rtx *next; |
| rtx mem; |
| rtx sym; |
| rtx constant; |
| HOST_WIDE_INT offset; |
| hashval_t hash; |
| fixed_size_mode mode; |
| unsigned int align; |
| int labelno; |
| int mark; |
| }; |
| |
| struct const_rtx_desc_hasher : ggc_ptr_hash<constant_descriptor_rtx> |
| { |
| static hashval_t hash (constant_descriptor_rtx *); |
| static bool equal (constant_descriptor_rtx *, constant_descriptor_rtx *); |
| }; |
| |
| /* Used in the hash tables to avoid outputting the same constant |
| twice. Unlike 'struct constant_descriptor_tree', RTX constants |
| are output once per function, not once per file. */ |
| /* ??? Only a few targets need per-function constant pools. Most |
| can use one per-file pool. Should add a targetm bit to tell the |
| difference. */ |
| |
| struct GTY(()) rtx_constant_pool { |
| /* Pointers to first and last constant in pool, as ordered by offset. */ |
| class constant_descriptor_rtx *first; |
| class constant_descriptor_rtx *last; |
| |
| /* Hash facility for making memory-constants from constant rtl-expressions. |
| It is used on RISC machines where immediate integer arguments and |
| constant addresses are restricted so that such constants must be stored |
| in memory. */ |
| hash_table<const_rtx_desc_hasher> *const_rtx_htab; |
| |
| /* Current offset in constant pool (does not include any |
| machine-specific header). */ |
| HOST_WIDE_INT offset; |
| }; |
| |
| /* Hash and compare functions for const_rtx_htab. */ |
| |
| hashval_t |
| const_rtx_desc_hasher::hash (constant_descriptor_rtx *desc) |
| { |
| return desc->hash; |
| } |
| |
| bool |
| const_rtx_desc_hasher::equal (constant_descriptor_rtx *x, |
| constant_descriptor_rtx *y) |
| { |
| if (x->mode != y->mode) |
| return false; |
| return rtx_equal_p (x->constant, y->constant); |
| } |
| |
| /* Hash one component of a constant. */ |
| |
| static hashval_t |
| const_rtx_hash_1 (const_rtx x) |
| { |
| unsigned HOST_WIDE_INT hwi; |
| machine_mode mode; |
| enum rtx_code code; |
| hashval_t h; |
| int i; |
| |
| code = GET_CODE (x); |
| mode = GET_MODE (x); |
| h = (hashval_t) code * 1048573 + mode; |
| |
| switch (code) |
| { |
| case CONST_INT: |
| hwi = INTVAL (x); |
| |
| fold_hwi: |
| { |
| int shift = sizeof (hashval_t) * CHAR_BIT; |
| const int n = sizeof (HOST_WIDE_INT) / sizeof (hashval_t); |
| |
| h ^= (hashval_t) hwi; |
| for (i = 1; i < n; ++i) |
| { |
| hwi >>= shift; |
| h ^= (hashval_t) hwi; |
| } |
| } |
| break; |
| |
| case CONST_WIDE_INT: |
| hwi = 0; |
| { |
| for (i = 0; i < CONST_WIDE_INT_NUNITS (x); i++) |
| hwi ^= CONST_WIDE_INT_ELT (x, i); |
| goto fold_hwi; |
| } |
| |
| case CONST_DOUBLE: |
| if (TARGET_SUPPORTS_WIDE_INT == 0 && mode == VOIDmode) |
| { |
| hwi = CONST_DOUBLE_LOW (x) ^ CONST_DOUBLE_HIGH (x); |
| goto fold_hwi; |
| } |
| else |
| h ^= real_hash (CONST_DOUBLE_REAL_VALUE (x)); |
| break; |
| |
| case CONST_FIXED: |
| h ^= fixed_hash (CONST_FIXED_VALUE (x)); |
| break; |
| |
| case SYMBOL_REF: |
| h ^= htab_hash_string (XSTR (x, 0)); |
| break; |
| |
| case LABEL_REF: |
| h = h * 251 + CODE_LABEL_NUMBER (label_ref_label (x)); |
| break; |
| |
| case UNSPEC: |
| case UNSPEC_VOLATILE: |
| h = h * 251 + XINT (x, 1); |
| break; |
| |
| default: |
| break; |
| } |
| |
| return h; |
| } |
| |
| /* Compute a hash value for X, which should be a constant. */ |
| |
| static hashval_t |
| const_rtx_hash (rtx x) |
| { |
| hashval_t h = 0; |
| subrtx_iterator::array_type array; |
| FOR_EACH_SUBRTX (iter, array, x, ALL) |
| h = h * 509 + const_rtx_hash_1 (*iter); |
| return h; |
| } |
| |
| |
| /* Create and return a new rtx constant pool. */ |
| |
| static struct rtx_constant_pool * |
| create_constant_pool (void) |
| { |
| struct rtx_constant_pool *pool; |
| |
| pool = ggc_alloc<rtx_constant_pool> (); |
| pool->const_rtx_htab = hash_table<const_rtx_desc_hasher>::create_ggc (31); |
| pool->first = NULL; |
| pool->last = NULL; |
| pool->offset = 0; |
| return pool; |
| } |
| |
| /* Initialize constant pool hashing for a new function. */ |
| |
| void |
| init_varasm_status (void) |
| { |
| crtl->varasm.pool = create_constant_pool (); |
| crtl->varasm.deferred_constants = 0; |
| } |
| |
| /* Given a MINUS expression, simplify it if both sides |
| include the same symbol. */ |
| |
| rtx |
| simplify_subtraction (rtx x) |
| { |
| rtx r = simplify_rtx (x); |
| return r ? r : x; |
| } |
| |
| /* Given a constant rtx X, make (or find) a memory constant for its value |
| and return a MEM rtx to refer to it in memory. IN_MODE is the mode |
| of X. */ |
| |
| rtx |
| force_const_mem (machine_mode in_mode, rtx x) |
| { |
| class constant_descriptor_rtx *desc, tmp; |
| struct rtx_constant_pool *pool; |
| char label[256]; |
| rtx def, symbol; |
| hashval_t hash; |
| unsigned int align; |
| constant_descriptor_rtx **slot; |
| fixed_size_mode mode; |
| |
| /* We can't force variable-sized objects to memory. */ |
| if (!is_a <fixed_size_mode> (in_mode, &mode)) |
| return NULL_RTX; |
| |
| /* If we're not allowed to drop X into the constant pool, don't. */ |
| if (targetm.cannot_force_const_mem (mode, x)) |
| return NULL_RTX; |
| |
| /* Record that this function has used a constant pool entry. */ |
| crtl->uses_const_pool = 1; |
| |
| /* Decide which pool to use. */ |
| pool = (targetm.use_blocks_for_constant_p (mode, x) |
| ? shared_constant_pool |
| : crtl->varasm.pool); |
| |
| /* Lookup the value in the hashtable. */ |
| tmp.constant = x; |
| tmp.mode = mode; |
| hash = const_rtx_hash (x); |
| slot = pool->const_rtx_htab->find_slot_with_hash (&tmp, hash, INSERT); |
| desc = *slot; |
| |
| /* If the constant was already present, return its memory. */ |
| if (desc) |
| return copy_rtx (desc->mem); |
| |
| /* Otherwise, create a new descriptor. */ |
| desc = ggc_alloc<constant_descriptor_rtx> (); |
| *slot = desc; |
| |
| /* Align the location counter as required by EXP's data type. */ |
| machine_mode align_mode = (mode == VOIDmode ? word_mode : mode); |
| align = targetm.static_rtx_alignment (align_mode); |
| |
| pool->offset += (align / BITS_PER_UNIT) - 1; |
| pool->offset &= ~ ((align / BITS_PER_UNIT) - 1); |
| |
| desc->next = NULL; |
| desc->constant = copy_rtx (tmp.constant); |
| desc->offset = pool->offset; |
| desc->hash = hash; |
| desc->mode = mode; |
| desc->align = align; |
| desc->labelno = const_labelno; |
| desc->mark = 0; |
| |
| pool->offset += GET_MODE_SIZE (mode); |
| if (pool->last) |
| pool->last->next = desc; |
| else |
| pool->first = pool->last = desc; |
| pool->last = desc; |
| |
| /* Create a string containing the label name, in LABEL. */ |
| ASM_GENERATE_INTERNAL_LABEL (label, "LC", const_labelno); |
| ++const_labelno; |
| |
| /* Construct the SYMBOL_REF. Make sure to mark it as belonging to |
| the constants pool. */ |
| if (use_object_blocks_p () && targetm.use_blocks_for_constant_p (mode, x)) |
| { |
| section *sect = targetm.asm_out.select_rtx_section (mode, x, align); |
| symbol = create_block_symbol (ggc_strdup (label), |
| get_block_for_section (sect), -1); |
| } |
| else |
| symbol = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (label)); |
| desc->sym = symbol; |
| SYMBOL_REF_FLAGS (symbol) |= SYMBOL_FLAG_LOCAL; |
| CONSTANT_POOL_ADDRESS_P (symbol) = 1; |
| SET_SYMBOL_REF_CONSTANT (symbol, desc); |
| |
| /* Construct the MEM. */ |
| desc->mem = def = gen_const_mem (mode, symbol); |
| set_mem_align (def, align); |
| |
| /* If we're dropping a label to the constant pool, make sure we |
| don't delete it. */ |
| if (GET_CODE (x) == LABEL_REF) |
| LABEL_PRESERVE_P (XEXP (x, 0)) = 1; |
| |
| return copy_rtx (def); |
| } |
| |
| /* Given a constant pool SYMBOL_REF, return the corresponding constant. */ |
| |
| rtx |
| get_pool_constant (const_rtx addr) |
| { |
| return SYMBOL_REF_CONSTANT (addr)->constant; |
| } |
| |
| /* Given a constant pool SYMBOL_REF, return the corresponding constant |
| and whether it has been output or not. */ |
| |
| rtx |
| get_pool_constant_mark (rtx addr, bool *pmarked) |
| { |
| class constant_descriptor_rtx *desc; |
| |
| desc = SYMBOL_REF_CONSTANT (addr); |
| *pmarked = (desc->mark != 0); |
| return desc->constant; |
| } |
| |
| /* Similar, return the mode. */ |
| |
| fixed_size_mode |
| get_pool_mode (const_rtx addr) |
| { |
| return SYMBOL_REF_CONSTANT (addr)->mode; |
| } |
| |
| /* Return TRUE if and only if the constant pool has no entries. Note |
| that even entries we might end up choosing not to emit are counted |
| here, so there is the potential for missed optimizations. */ |
| |
| bool |
| constant_pool_empty_p (void) |
| { |
| return crtl->varasm.pool->first == NULL; |
| } |
| |
| /* Worker function for output_constant_pool_1. Emit assembly for X |
| in MODE with known alignment ALIGN. */ |
| |
| static void |
| output_constant_pool_2 (fixed_size_mode mode, rtx x, unsigned int align) |
| { |
| switch (GET_MODE_CLASS (mode)) |
| { |
| case MODE_FLOAT: |
| case MODE_DECIMAL_FLOAT: |
| { |
| gcc_assert (CONST_DOUBLE_AS_FLOAT_P (x)); |
| assemble_real (*CONST_DOUBLE_REAL_VALUE (x), |
| as_a <scalar_float_mode> (mode), align, false); |
| break; |
| } |
| |
| case MODE_INT: |
| case MODE_PARTIAL_INT: |
| case MODE_FRACT: |
| case MODE_UFRACT: |
| case MODE_ACCUM: |
| case MODE_UACCUM: |
| assemble_integer (x, GET_MODE_SIZE (mode), align, 1); |
| break; |
| |
| case MODE_VECTOR_BOOL: |
| { |
| gcc_assert (GET_CODE (x) == CONST_VECTOR); |
| |
| /* Pick the smallest integer mode that contains at least one |
| whole element. Often this is byte_mode and contains more |
| than one element. */ |
| unsigned int nelts = GET_MODE_NUNITS (mode); |
| unsigned int elt_bits = GET_MODE_PRECISION (mode) / nelts; |
| unsigned int int_bits = MAX (elt_bits, BITS_PER_UNIT); |
| scalar_int_mode int_mode = int_mode_for_size (int_bits, 0).require (); |
| unsigned int mask = GET_MODE_MASK (GET_MODE_INNER (mode)); |
| |
| /* We allow GET_MODE_PRECISION (mode) <= GET_MODE_BITSIZE (mode) but |
| only properly handle cases where the difference is less than a |
| byte. */ |
| gcc_assert (GET_MODE_BITSIZE (mode) - GET_MODE_PRECISION (mode) < |
| BITS_PER_UNIT); |
| |
| /* Build the constant up one integer at a time. */ |
| unsigned int elts_per_int = int_bits / elt_bits; |
| for (unsigned int i = 0; i < nelts; i += elts_per_int) |
| { |
| unsigned HOST_WIDE_INT value = 0; |
| unsigned int limit = MIN (nelts - i, elts_per_int); |
| for (unsigned int j = 0; j < limit; ++j) |
| { |
| auto elt = INTVAL (CONST_VECTOR_ELT (x, i + j)); |
| value |= (elt & mask) << (j * elt_bits); |
| } |
| output_constant_pool_2 (int_mode, gen_int_mode (value, int_mode), |
| i != 0 ? MIN (align, int_bits) : align); |
| } |
| break; |
| } |
| case MODE_VECTOR_FLOAT: |
| case MODE_VECTOR_INT: |
| case MODE_VECTOR_FRACT: |
| case MODE_VECTOR_UFRACT: |
| case MODE_VECTOR_ACCUM: |
| case MODE_VECTOR_UACCUM: |
| { |
| int i, units; |
| scalar_mode submode = GET_MODE_INNER (mode); |
| unsigned int subalign = MIN (align, GET_MODE_BITSIZE (submode)); |
| |
| gcc_assert (GET_CODE (x) == CONST_VECTOR); |
| units = GET_MODE_NUNITS (mode); |
| |
| for (i = 0; i < units; i++) |
| { |
| rtx elt = CONST_VECTOR_ELT (x, i); |
| output_constant_pool_2 (submode, elt, i ? subalign : align); |
| } |
| } |
| break; |
| |
| default: |
| gcc_unreachable (); |
| } |
| } |
| |
| /* Worker function for output_constant_pool. Emit constant DESC, |
| giving it ALIGN bits of alignment. */ |
| |
| static void |
| output_constant_pool_1 (class constant_descriptor_rtx *desc, |
| unsigned int align) |
| { |
| rtx x, tmp; |
| |
| x = desc->constant; |
| |
| /* See if X is a LABEL_REF (or a CONST referring to a LABEL_REF) |
| whose CODE_LABEL has been deleted. This can occur if a jump table |
| is eliminated by optimization. If so, write a constant of zero |
| instead. Note that this can also happen by turning the |
| CODE_LABEL into a NOTE. */ |
| /* ??? This seems completely and utterly wrong. Certainly it's |
| not true for NOTE_INSN_DELETED_LABEL, but I disbelieve proper |
| functioning even with rtx_insn::deleted and friends. */ |
| |
| tmp = x; |
| switch (GET_CODE (tmp)) |
| { |
| case CONST: |
| if (GET_CODE (XEXP (tmp, 0)) != PLUS |
| || GET_CODE (XEXP (XEXP (tmp, 0), 0)) != LABEL_REF) |
| break; |
| tmp = XEXP (XEXP (tmp, 0), 0); |
| /* FALLTHRU */ |
| |
| case LABEL_REF: |
| { |
| rtx_insn *insn = label_ref_label (tmp); |
| gcc_assert (!insn->deleted ()); |
| gcc_assert (!NOTE_P (insn) |
| || NOTE_KIND (insn) != NOTE_INSN_DELETED); |
| break; |
| } |
| |
| default: |
| break; |
| } |
| |
| #ifdef ASM_OUTPUT_SPECIAL_POOL_ENTRY |
| ASM_OUTPUT_SPECIAL_POOL_ENTRY (asm_out_file, x, desc->mode, |
| align, desc->labelno, done); |
| #endif |
| |
| assemble_align (align); |
| |
| /* Output the label. */ |
| targetm.asm_out.internal_label (asm_out_file, "LC", desc->labelno); |
| |
| /* Output the data. |
| Pass actual alignment value while emitting string constant to asm code |
| as function 'output_constant_pool_1' explicitly passes the alignment as 1 |
| assuming that the data is already aligned which prevents the generation |
| of fix-up table entries. */ |
| output_constant_pool_2 (desc->mode, x, desc->align); |
| |
| /* Make sure all constants in SECTION_MERGE and not SECTION_STRINGS |
| sections have proper size. */ |
| if (align > GET_MODE_BITSIZE (desc->mode) |
| && in_section |
| && (in_section->common.flags & SECTION_MERGE)) |
| assemble_align (align); |
| |
| #ifdef ASM_OUTPUT_SPECIAL_POOL_ENTRY |
| done: |
| #endif |
| return; |
| } |
| |
| /* Recompute the offsets of entries in POOL, and the overall size of |
| POOL. Do this after calling mark_constant_pool to ensure that we |
| are computing the offset values for the pool which we will actually |
| emit. */ |
| |
| static void |
| recompute_pool_offsets (struct rtx_constant_pool *pool) |
| { |
| class constant_descriptor_rtx *desc; |
| pool->offset = 0; |
| |
| for (desc = pool->first; desc ; desc = desc->next) |
| if (desc->mark) |
| { |
| /* Recalculate offset. */ |
| unsigned int align = desc->align; |
| pool->offset += (align / BITS_PER_UNIT) - 1; |
| pool->offset &= ~ ((align / BITS_PER_UNIT) - 1); |
| desc->offset = pool->offset; |
| pool->offset += GET_MODE_SIZE (desc->mode); |
| } |
| } |
| |
| /* Mark all constants that are referenced by SYMBOL_REFs in X. |
| Emit referenced deferred strings. */ |
| |
| static void |
| mark_constants_in_pattern (rtx insn) |
| { |
| subrtx_iterator::array_type array; |
| FOR_EACH_SUBRTX (iter, array, PATTERN (insn), ALL) |
| { |
| const_rtx x = *iter; |
| if (GET_CODE (x) == SYMBOL_REF) |
| { |
| if (CONSTANT_POOL_ADDRESS_P (x)) |
| { |
| class constant_descriptor_rtx *desc = SYMBOL_REF_CONSTANT (x); |
| if (desc->mark == 0) |
| { |
| desc->mark = 1; |
| iter.substitute (desc->constant); |
| } |
| } |
| else if (TREE_CONSTANT_POOL_ADDRESS_P (x)) |
| { |
| tree decl = SYMBOL_REF_DECL (x); |
| if (!TREE_ASM_WRITTEN (DECL_INITIAL (decl))) |
| { |
| n_deferred_constants--; |
| output_constant_def_contents (CONST_CAST_RTX (x)); |
| } |
| } |
| } |
| } |
| } |
| |
| /* Look through appropriate parts of INSN, marking all entries in the |
| constant pool which are actually being used. Entries that are only |
| referenced by other constants are also marked as used. Emit |
| deferred strings that are used. */ |
| |
| static void |
| mark_constants (rtx_insn *insn) |
| { |
| if (!INSN_P (insn)) |
| return; |
| |
| /* Insns may appear inside a SEQUENCE. Only check the patterns of |
| insns, not any notes that may be attached. We don't want to mark |
| a constant just because it happens to appear in a REG_EQUIV note. */ |
| if (rtx_sequence *seq = dyn_cast <rtx_sequence *> (PATTERN (insn))) |
| { |
| int i, n = seq->len (); |
| for (i = 0; i < n; ++i) |
| { |
| rtx subinsn = seq->element (i); |
| if (INSN_P (subinsn)) |
| mark_constants_in_pattern (subinsn); |
| } |
| } |
| else |
| mark_constants_in_pattern (insn); |
| } |
| |
| /* Look through the instructions for this function, and mark all the |
| entries in POOL which are actually being used. Emit deferred constants |
| which have indeed been used. */ |
| |
| static void |
| mark_constant_pool (void) |
| { |
| rtx_insn *insn; |
| |
| if (!crtl->uses_const_pool && n_deferred_constants == 0) |
| return; |
| |
| for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) |
| mark_constants (insn); |
| } |
| |
| /* Write all the constants in POOL. */ |
| |
| static void |
| output_constant_pool_contents (struct rtx_constant_pool *pool) |
| { |
| class constant_descriptor_rtx *desc; |
| |
| for (desc = pool->first; desc ; desc = desc->next) |
| if (desc->mark < 0) |
| { |
| #ifdef ASM_OUTPUT_DEF |
| gcc_checking_assert (TARGET_SUPPORTS_ALIASES); |
| |
| const char *name = XSTR (desc->sym, 0); |
| char label[256]; |
| char buffer[256 + 32]; |
| const char *p; |
| |
| ASM_GENERATE_INTERNAL_LABEL (label, "LC", ~desc->mark); |
| p = label; |
| if (desc->offset) |
| { |
| sprintf (buffer, "%s+" HOST_WIDE_INT_PRINT_DEC, p, desc->offset); |
| p = buffer; |
| } |
| ASM_OUTPUT_DEF (asm_out_file, name, p); |
| #else |
| gcc_unreachable (); |
| #endif |
| } |
| else if (desc->mark) |
| { |
| /* If the constant is part of an object_block, make sure that |
| the constant has been positioned within its block, but do not |
| write out its definition yet. output_object_blocks will do |
| that later. */ |
| if (SYMBOL_REF_HAS_BLOCK_INFO_P (desc->sym) |
| && SYMBOL_REF_BLOCK (desc->sym)) |
| place_block_symbol (desc->sym); |
| else |
| { |
| switch_to_section (targetm.asm_out.select_rtx_section |
| (desc->mode, desc->constant, desc->align)); |
| output_constant_pool_1 (desc, desc->align); |
| } |
| } |
| } |
| |
| struct constant_descriptor_rtx_data { |
| constant_descriptor_rtx *desc; |
| target_unit *bytes; |
| unsigned short size; |
| unsigned short offset; |
| unsigned int hash; |
| }; |
| |
| /* qsort callback to sort constant_descriptor_rtx_data * vector by |
| decreasing size. */ |
| |
| static int |
| constant_descriptor_rtx_data_cmp (const void *p1, const void *p2) |
| { |
| constant_descriptor_rtx_data *const data1 |
| = *(constant_descriptor_rtx_data * const *) p1; |
| constant_descriptor_rtx_data *const data2 |
| = *(constant_descriptor_rtx_data * const *) p2; |
| if (data1->size > data2->size) |
| return -1; |
| if (data1->size < data2->size) |
| return 1; |
| if (data1->hash < data2->hash) |
| return -1; |
| gcc_assert (data1->hash > data2->hash); |
| return 1; |
| } |
| |
| struct const_rtx_data_hasher : nofree_ptr_hash<constant_descriptor_rtx_data> |
| { |
| static hashval_t hash (constant_descriptor_rtx_data *); |
| static bool equal (constant_descriptor_rtx_data *, |
| constant_descriptor_rtx_data *); |
| }; |
| |
| /* Hash and compare functions for const_rtx_data_htab. */ |
| |
| hashval_t |
| const_rtx_data_hasher::hash (constant_descriptor_rtx_data *data) |
| { |
| return data->hash; |
| } |
| |
| bool |
| const_rtx_data_hasher::equal (constant_descriptor_rtx_data *x, |
| constant_descriptor_rtx_data *y) |
| { |
| if (x->hash != y->hash || x->size != y->size) |
| return false; |
| unsigned int align1 = x->desc->align; |
| unsigned int align2 = y->desc->align; |
| unsigned int offset1 = (x->offset * BITS_PER_UNIT) & (align1 - 1); |
| unsigned int offset2 = (y->offset * BITS_PER_UNIT) & (align2 - 1); |
| if (offset1) |
| align1 = least_bit_hwi (offset1); |
| if (offset2) |
| align2 = least_bit_hwi (offset2); |
| if (align2 > align1) |
| return false; |
| if (memcmp (x->bytes, y->bytes, x->size * sizeof (target_unit)) != 0) |
| return false; |
| return true; |
| } |
| |
| /* Attempt to optimize constant pool POOL. If it contains both CONST_VECTOR |
| constants and scalar constants with the values of CONST_VECTOR elements, |
| try to alias the scalar constants with the CONST_VECTOR elements. */ |
| |
| static void |
| optimize_constant_pool (struct rtx_constant_pool *pool) |
| { |
| auto_vec<target_unit, 128> buffer; |
| auto_vec<constant_descriptor_rtx_data *, 128> vec; |
| object_allocator<constant_descriptor_rtx_data> |
| data_pool ("constant_descriptor_rtx_data_pool"); |
| int idx = 0; |
| size_t size = 0; |
| for (constant_descriptor_rtx *desc = pool->first; desc; desc = desc->next) |
| if (desc->mark > 0 |
| && ! (SYMBOL_REF_HAS_BLOCK_INFO_P (desc->sym) |
| && SYMBOL_REF_BLOCK (desc->sym))) |
| { |
| buffer.truncate (0); |
| buffer.reserve (GET_MODE_SIZE (desc->mode)); |
| if (native_encode_rtx (desc->mode, desc->constant, buffer, 0, |
| GET_MODE_SIZE (desc->mode))) |
| { |
| constant_descriptor_rtx_data *data = data_pool.allocate (); |
| data->desc = desc; |
| data->bytes = NULL; |
| data->size = GET_MODE_SIZE (desc->mode); |
| data->offset = 0; |
| data->hash = idx++; |
| size += data->size; |
| vec.safe_push (data); |
| } |
| } |
| if (idx) |
| { |
| vec.qsort (constant_descriptor_rtx_data_cmp); |
| unsigned min_size = vec.last ()->size; |
| target_unit *bytes = XNEWVEC (target_unit, size); |
| unsigned int i; |
| constant_descriptor_rtx_data *data; |
| hash_table<const_rtx_data_hasher> * htab |
| = new hash_table<const_rtx_data_hasher> (31); |
| size = 0; |
| FOR_EACH_VEC_ELT (vec, i, data) |
| { |
| buffer.truncate (0); |
| native_encode_rtx (data->desc->mode, data->desc->constant, |
| buffer, 0, data->size); |
| memcpy (bytes + size, buffer.address (), data->size); |
| data->bytes = bytes + size; |
| data->hash = iterative_hash (data->bytes, |
| data->size * sizeof (target_unit), 0); |
| size += data->size; |
| constant_descriptor_rtx_data **slot |
| = htab->find_slot_with_hash (data, data->hash, INSERT); |
| if (*slot) |
| { |
| data->desc->mark = ~(*slot)->desc->labelno; |
| data->desc->offset = (*slot)->offset; |
| } |
| else |
| { |
| unsigned int sz = 1 << floor_log2 (data->size); |
| |
| *slot = data; |
| for (sz >>= 1; sz >= min_size; sz >>= 1) |
| for (unsigned off = 0; off + sz <= data->size; off += sz) |
| { |
| constant_descriptor_rtx_data tmp; |
| tmp.desc = data->desc; |
| tmp.bytes = data->bytes + off; |
| tmp.size = sz; |
| tmp.offset = off; |
| tmp.hash = iterative_hash (tmp.bytes, |
| sz * sizeof (target_unit), 0); |
| slot = htab->find_slot_with_hash (&tmp, tmp.hash, INSERT); |
| if (*slot == NULL) |
| { |
| *slot = data_pool.allocate (); |
| **slot = tmp; |
| } |
| } |
| } |
| } |
| delete htab; |
| XDELETE (bytes); |
| } |
| data_pool.release (); |
| } |
| |
| /* Mark all constants that are used in the current function, then write |
| out the function's private constant pool. */ |
| |
| static void |
| output_constant_pool (const char *fnname ATTRIBUTE_UNUSED, |
| tree fndecl ATTRIBUTE_UNUSED) |
| { |
| struct rtx_constant_pool *pool = crtl->varasm.pool; |
| |
| /* It is possible for gcc to call force_const_mem and then to later |
| discard the instructions which refer to the constant. In such a |
| case we do not need to output the constant. */ |
| mark_constant_pool (); |
| |
| /* Having marked the constant pool entries we'll actually emit, we |
| now need to rebuild the offset information, which may have become |
| stale. */ |
| recompute_pool_offsets (pool); |
| |
| #ifdef ASM_OUTPUT_POOL_PROLOGUE |
| ASM_OUTPUT_POOL_PROLOGUE (asm_out_file, fnname, fndecl, pool->offset); |
| #endif |
| |
| output_constant_pool_contents (pool); |
| |
| #ifdef ASM_OUTPUT_POOL_EPILOGUE |
| ASM_OUTPUT_POOL_EPILOGUE (asm_out_file, fnname, fndecl, pool->offset); |
| #endif |
| } |
| |
| /* Write the contents of the shared constant pool. */ |
| |
| void |
| output_shared_constant_pool (void) |
| { |
| if (optimize |
| && TARGET_SUPPORTS_ALIASES) |
| optimize_constant_pool (shared_constant_pool); |
| |
| output_constant_pool_contents (shared_constant_pool); |
| } |
| |
| /* Determine what kind of relocations EXP may need. */ |
| |
| int |
| compute_reloc_for_constant (tree exp) |
| { |
| int reloc = 0, reloc2; |
| tree tem; |
| |
| switch (TREE_CODE (exp)) |
| { |
| case ADDR_EXPR: |
| case FDESC_EXPR: |
| /* Go inside any operations that get_inner_reference can handle and see |
| if what's inside is a constant: no need to do anything here for |
| addresses of variables or functions. */ |
| for (tem = TREE_OPERAND (exp, 0); handled_component_p (tem); |
| tem = TREE_OPERAND (tem, 0)) |
| ; |
| |
| if (TREE_CODE (tem) == MEM_REF |
| && TREE_CODE (TREE_OPERAND (tem, 0)) == ADDR_EXPR) |
| { |
| reloc = compute_reloc_for_constant (TREE_OPERAND (tem, 0)); |
| break; |
| } |
| |
| if (!targetm.binds_local_p (tem)) |
| reloc |= 2; |
| else |
| reloc |= 1; |
| break; |
| |
| case PLUS_EXPR: |
| case POINTER_PLUS_EXPR: |
| reloc = compute_reloc_for_constant (TREE_OPERAND (exp, 0)); |
| reloc |= compute_reloc_for_constant (TREE_OPERAND (exp, 1)); |
| break; |
| |
| case MINUS_EXPR: |
| reloc = compute_reloc_for_constant (TREE_OPERAND (exp, 0)); |
| reloc2 = compute_reloc_for_constant (TREE_OPERAND (exp, 1)); |
| /* The difference of two local labels is computable at link time. */ |
| if (reloc == 1 && reloc2 == 1) |
| reloc = 0; |
| else |
| reloc |= reloc2; |
| break; |
| |
| CASE_CONVERT: |
| case VIEW_CONVERT_EXPR: |
| reloc = compute_reloc_for_constant (TREE_OPERAND (exp, 0)); |
| break; |
| |
| case CONSTRUCTOR: |
| { |
| unsigned HOST_WIDE_INT idx; |
| FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (exp), idx, tem) |
| if (tem != 0) |
| reloc |= compute_reloc_for_constant (tem); |
| } |
| break; |
| |
| default: |
| break; |
| } |
| return reloc; |
| } |
| |
| /* Find all the constants whose addresses are referenced inside of EXP, |
| and make sure assembler code with a label has been output for each one. |
| Indicate whether an ADDR_EXPR has been encountered. */ |
| |
| static void |
| output_addressed_constants (tree exp, int defer) |
| { |
| tree tem; |
| |
| switch (TREE_CODE (exp)) |
| { |
| case ADDR_EXPR: |
| case FDESC_EXPR: |
| /* Go inside any operations that get_inner_reference can handle and see |
| if what's inside is a constant: no need to do anything here for |
| addresses of variables or functions. */ |
| for (tem = TREE_OPERAND (exp, 0); handled_component_p (tem); |
| tem = TREE_OPERAND (tem, 0)) |
| ; |
| |
| /* If we have an initialized CONST_DECL, retrieve the initializer. */ |
| if (TREE_CODE (tem) == CONST_DECL && DECL_INITIAL (tem)) |
| tem = DECL_INITIAL (tem); |
| |
| if (CONSTANT_CLASS_P (tem) || TREE_CODE (tem) == CONSTRUCTOR) |
| output_constant_def (tem, defer); |
| |
| if (TREE_CODE (tem) == MEM_REF) |
| output_addressed_constants (TREE_OPERAND (tem, 0), defer); |
| break; |
| |
| case PLUS_EXPR: |
| case POINTER_PLUS_EXPR: |
| case MINUS_EXPR: |
| output_addressed_constants (TREE_OPERAND (exp, 1), defer); |
| gcc_fallthrough (); |
| |
| CASE_CONVERT: |
| case VIEW_CONVERT_EXPR: |
| output_addressed_constants (TREE_OPERAND (exp, 0), defer); |
| break; |
| |
| case CONSTRUCTOR: |
| { |
| unsigned HOST_WIDE_INT idx; |
| FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (exp), idx, tem) |
| if (tem != 0) |
| output_addressed_constants (tem, defer); |
| } |
| break; |
| |
| default: |
| break; |
| } |
| } |
| |
| /* Whether a constructor CTOR is a valid static constant initializer if all |
| its elements are. This used to be internal to initializer_constant_valid_p |
| and has been exposed to let other functions like categorize_ctor_elements |
| evaluate the property while walking a constructor for other purposes. */ |
| |
| bool |
| constructor_static_from_elts_p (const_tree ctor) |
| { |
| return (TREE_CONSTANT (ctor) |
| && (TREE_CODE (TREE_TYPE (ctor)) == UNION_TYPE |
| || TREE_CODE (TREE_TYPE (ctor)) == RECORD_TYPE |
| || TREE_CODE (TREE_TYPE (ctor)) == ARRAY_TYPE)); |
| } |
| |
| static tree initializer_constant_valid_p_1 (tree value, tree endtype, |
| tree *cache); |
| |
| /* A subroutine of initializer_constant_valid_p. VALUE is a MINUS_EXPR, |
| PLUS_EXPR or POINTER_PLUS_EXPR. This looks for cases of VALUE |
| which are valid when ENDTYPE is an integer of any size; in |
| particular, this does not accept a pointer minus a constant. This |
| returns null_pointer_node if the VALUE is an absolute constant |
| which can be used to initialize a static variable. Otherwise it |
| returns NULL. */ |
| |
| static tree |
| narrowing_initializer_constant_valid_p (tree value, tree endtype, tree *cache) |
| { |
| tree op0, op1; |
| |
| if (!INTEGRAL_TYPE_P (endtype)) |
| return NULL_TREE; |
| |
| op0 = TREE_OPERAND (value, 0); |
| op1 = TREE_OPERAND (value, 1); |
| |
| /* Like STRIP_NOPS except allow the operand mode to widen. This |
| works around a feature of fold that simplifies (int)(p1 - p2) to |
| ((int)p1 - (int)p2) under the theory that the narrower operation |
| is cheaper. */ |
| |
| while (CONVERT_EXPR_P (op0) |
| || TREE_CODE (op0) == NON_LVALUE_EXPR) |
| { |
| tree inner = TREE_OPERAND (op0, 0); |
| if (inner == error_mark_node |
| || ! INTEGRAL_TYPE_P (TREE_TYPE (op0)) |
| || ! SCALAR_INT_MODE_P (TYPE_MODE (TREE_TYPE (op0))) |
| || ! INTEGRAL_TYPE_P (TREE_TYPE (inner)) |
| || ! SCALAR_INT_MODE_P (TYPE_MODE (TREE_TYPE (inner))) |
| || (GET_MODE_SIZE (SCALAR_INT_TYPE_MODE (TREE_TYPE (op0))) |
| > GET_MODE_SIZE (SCALAR_INT_TYPE_MODE (TREE_TYPE (inner))))) |
| break; |
| op0 = inner; |
| } |
| |
| while (CONVERT_EXPR_P (op1) |
| || TREE_CODE (op1) == NON_LVALUE_EXPR) |
| { |
| tree inner = TREE_OPERAND (op1, 0); |
| if (inner == error_mark_node |
| || ! INTEGRAL_TYPE_P (TREE_TYPE (op1)) |
| || ! SCALAR_INT_MODE_P (TYPE_MODE (TREE_TYPE (op1))) |
| || ! INTEGRAL_TYPE_P (TREE_TYPE (inner)) |
| || ! SCALAR_INT_MODE_P (TYPE_MODE (TREE_TYPE (inner))) |
| || (GET_MODE_SIZE (SCALAR_INT_TYPE_MODE (TREE_TYPE (op1))) |
| > GET_MODE_SIZE (SCALAR_INT_TYPE_MODE (TREE_TYPE (inner))))) |
| break; |
| op1 = inner; |
| } |
| |
| op0 = initializer_constant_valid_p_1 (op0, endtype, cache); |
| if (!op0) |
| return NULL_TREE; |
| |
| op1 = initializer_constant_valid_p_1 (op1, endtype, |
| cache ? cache + 2 : NULL); |
| /* Both initializers must be known. */ |
| if (op1) |
| { |
| if (op0 == op1 |
| && (op0 == null_pointer_node |
| || TREE_CODE (value) == MINUS_EXPR)) |
| return null_pointer_node; |
| |
| /* Support differences between labels. */ |
| if (TREE_CODE (op0) == LABEL_DECL |
| && TREE_CODE (op1) == LABEL_DECL) |
| return null_pointer_node; |
| |
| if (TREE_CODE (op0) == STRING_CST |
| && TREE_CODE (op1) == STRING_CST |
| && operand_equal_p (op0, op1, 1)) |
| return null_pointer_node; |
| } |
| |
| return NULL_TREE; |
| } |
| |
| /* Helper function of initializer_constant_valid_p. |
| Return nonzero if VALUE is a valid constant-valued expression |
| for use in initializing a static variable; one that can be an |
| element of a "constant" initializer. |
| |
| Return null_pointer_node if the value is absolute; |
| if it is relocatable, return the variable that determines the relocation. |
| We assume that VALUE has been folded as much as possible; |
| therefore, we do not need to check for such things as |
| arithmetic-combinations of integers. |
| |
| Use CACHE (pointer to 2 tree values) for caching if non-NULL. */ |
| |
| static tree |
| initializer_constant_valid_p_1 (tree value, tree endtype, tree *cache) |
| { |
| tree ret; |
| |
| switch (TREE_CODE (value)) |
| { |
| case CONSTRUCTOR: |
| if (constructor_static_from_elts_p (value)) |
| { |
| unsigned HOST_WIDE_INT idx; |
| tree elt; |
| bool absolute = true; |
| |
| if (cache && cache[0] == value) |
| return cache[1]; |
| FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (value), idx, elt) |
| { |
| tree reloc; |
| reloc = initializer_constant_valid_p_1 (elt, TREE_TYPE (elt), |
| NULL); |
| if (!reloc |
| /* An absolute value is required with reverse SSO. */ |
| || (reloc != null_pointer_node |
| && TYPE_REVERSE_STORAGE_ORDER (TREE_TYPE (value)) |
| && !AGGREGATE_TYPE_P (TREE_TYPE (elt)))) |
| { |
| if (cache) |
| { |
| cache[0] = value; |
| cache[1] = NULL_TREE; |
| } |
| return NULL_TREE; |
| } |
| if (reloc != null_pointer_node) |
| absolute = false; |
| } |
| /* For a non-absolute relocation, there is no single |
| variable that can be "the variable that determines the |
| relocation." */ |
| if (cache) |
| { |
| cache[0] = value; |
| cache[1] = absolute ? null_pointer_node : error_mark_node; |
| } |
| return absolute ? null_pointer_node : error_mark_node; |
| } |
| |
| return TREE_STATIC (value) ? null_pointer_node : NULL_TREE; |
| |
| case INTEGER_CST: |
| case VECTOR_CST: |
| case REAL_CST: |
| case FIXED_CST: |
| case STRING_CST: |
| case COMPLEX_CST: |
| return null_pointer_node; |
| |
| case ADDR_EXPR: |
| case FDESC_EXPR: |
| { |
| tree op0 = staticp (TREE_OPERAND (value, 0)); |
| if (op0) |
| { |
| /* "&(*a).f" is like unto pointer arithmetic. If "a" turns out |
| to be a constant, this is old-skool offsetof-like nonsense. */ |
| if (TREE_CODE (op0) == INDIRECT_REF |
| && TREE_CONSTANT (TREE_OPERAND (op0, 0))) |
| return null_pointer_node; |
| /* Taking the address of a nested function involves a trampoline, |
| unless we don't need or want one. */ |
| if (TREE_CODE (op0) == FUNCTION_DECL |
| && DECL_STATIC_CHAIN (op0) |
| && !TREE_NO_TRAMPOLINE (value)) |
| return NULL_TREE; |
| /* "&{...}" requires a temporary to hold the constructed |
| object. */ |
| if (TREE_CODE (op0) == CONSTRUCTOR) |
| return NULL_TREE; |
| } |
| return op0; |
| } |
| |
| case NON_LVALUE_EXPR: |
| return initializer_constant_valid_p_1 (TREE_OPERAND (value, 0), |
| endtype, cache); |
| |
| case VIEW_CONVERT_EXPR: |
| { |
| tree src = TREE_OPERAND (value, 0); |
| tree src_type = TREE_TYPE (src); |
| tree dest_type = TREE_TYPE (value); |
| |
| /* Allow view-conversions from aggregate to non-aggregate type only |
| if the bit pattern is fully preserved afterwards; otherwise, the |
| RTL expander won't be able to apply a subsequent transformation |
| to the underlying constructor. */ |
| if (AGGREGATE_TYPE_P (src_type) && !AGGREGATE_TYPE_P (dest_type)) |
| { |
| if (TYPE_MODE (endtype) == TYPE_MODE (dest_type)) |
| return initializer_constant_valid_p_1 (src, endtype, cache); |
| else |
| return NULL_TREE; |
| } |
| |
| /* Allow all other kinds of view-conversion. */ |
| return initializer_constant_valid_p_1 (src, endtype, cache); |
| } |
| |
| CASE_CONVERT: |
| { |
| tree src = TREE_OPERAND (value, 0); |
| tree src_type = TREE_TYPE (src); |
| tree dest_type = TREE_TYPE (value); |
| |
| /* Allow conversions between pointer types and offset types. */ |
| if ((POINTER_TYPE_P (dest_type) && POINTER_TYPE_P (src_type)) |
| || (TREE_CODE (dest_type) == OFFSET_TYPE |
| && TREE_CODE (src_type) == OFFSET_TYPE)) |
| return initializer_constant_valid_p_1 (src, endtype, cache); |
| |
| /* Allow length-preserving conversions between integer types and |
| floating-point types. */ |
| if (((INTEGRAL_TYPE_P (dest_type) && INTEGRAL_TYPE_P (src_type)) |
| || (SCALAR_FLOAT_TYPE_P (dest_type) |
| && SCALAR_FLOAT_TYPE_P (src_type))) |
| && (TYPE_PRECISION (dest_type) == TYPE_PRECISION (src_type))) |
| return initializer_constant_valid_p_1 (src, endtype, cache); |
| |
| /* Allow conversions between other integer types only if |
| explicit value. Don't allow sign-extension to a type larger |
| than word and pointer, there aren't relocations that would |
| allow to sign extend it to a wider type. */ |
| if (INTEGRAL_TYPE_P (dest_type) |
| && INTEGRAL_TYPE_P (src_type) |
| && (TYPE_UNSIGNED (src_type) |
| || TYPE_PRECISION (dest_type) <= TYPE_PRECISION (src_type) |
| || TYPE_PRECISION (dest_type) <= BITS_PER_WORD |
| || TYPE_PRECISION (dest_type) <= POINTER_SIZE)) |
| { |
| tree inner = initializer_constant_valid_p_1 (src, endtype, cache); |
| if (inner == null_pointer_node) |
| return null_pointer_node; |
| break; |
| } |
| |
| /* Allow (int) &foo provided int is as wide as a pointer. */ |
| if (INTEGRAL_TYPE_P (dest_type) && POINTER_TYPE_P (src_type) |
| && (TYPE_PRECISION (dest_type) >= TYPE_PRECISION (src_type))) |
| return initializer_constant_valid_p_1 (src, endtype, cache); |
| |
| /* Likewise conversions from int to pointers, but also allow |
| conversions from 0. */ |
| if ((POINTER_TYPE_P (dest_type) |
| || TREE_CODE (dest_type) == OFFSET_TYPE) |
| && INTEGRAL_TYPE_P (src_type)) |
| { |
| if (TREE_CODE (src) == INTEGER_CST |
| && TYPE_PRECISION (dest_type) >= TYPE_PRECISION (src_type)) |
| return null_pointer_node; |
| if (integer_zerop (src)) |
| return null_pointer_node; |
| else if (TYPE_PRECISION (dest_type) <= TYPE_PRECISION (src_type)) |
| return initializer_constant_valid_p_1 (src, endtype, cache); |
| } |
| |
| /* Allow conversions to struct or union types if the value |
| inside is okay. */ |
| if (TREE_CODE (dest_type) == RECORD_TYPE |
| || TREE_CODE (dest_type) == UNION_TYPE) |
| return initializer_constant_valid_p_1 (src, endtype, cache); |
| } |
| break; |
| |
| case POINTER_PLUS_EXPR: |
| case PLUS_EXPR: |
| /* Any valid floating-point constants will have been folded by now; |
| with -frounding-math we hit this with addition of two constants. */ |
| if (TREE_CODE (endtype) == REAL_TYPE) |
| return NULL_TREE; |
| if (cache && cache[0] == value) |
| return cache[1]; |
| if (! INTEGRAL_TYPE_P (endtype) |
| || ! INTEGRAL_TYPE_P (TREE_TYPE (value)) |
| || TYPE_PRECISION (endtype) >= TYPE_PRECISION (TREE_TYPE (value))) |
| { |
| tree ncache[4] = { NULL_TREE, NULL_TREE, NULL_TREE, NULL_TREE }; |
| tree valid0 |
| = initializer_constant_valid_p_1 (TREE_OPERAND (value, 0), |
| endtype, ncache); |
| tree valid1 |
| = initializer_constant_valid_p_1 (TREE_OPERAND (value, 1), |
| endtype, ncache + 2); |
| /* If either term is absolute, use the other term's relocation. */ |
| if (valid0 == null_pointer_node) |
| ret = valid1; |
| else if (valid1 == null_pointer_node) |
| ret = valid0; |
| /* Support narrowing pointer differences. */ |
| else |
| ret = narrowing_initializer_constant_valid_p (value, endtype, |
| ncache); |
| } |
| else |
| /* Support narrowing pointer differences. */ |
| ret = narrowing_initializer_constant_valid_p (value, endtype, NULL); |
| if (cache) |
| { |
| cache[0] = value; |
| cache[1] = ret; |
| } |
| return ret; |
| |
| case POINTER_DIFF_EXPR: |
| case MINUS_EXPR: |
| if (TREE_CODE (endtype) == REAL_TYPE) |
| return NULL_TREE; |
| if (cache && cache[0] == value) |
| return cache[1]; |
| if (! INTEGRAL_TYPE_P (endtype) |
| || ! INTEGRAL_TYPE_P (TREE_TYPE (value)) |
| || TYPE_PRECISION (endtype) >= TYPE_PRECISION (TREE_TYPE (value))) |
| { |
| tree ncache[4] = { NULL_TREE, NULL_TREE, NULL_TREE, NULL_TREE }; |
| tree valid0 |
| = initializer_constant_valid_p_1 (TREE_OPERAND (value, 0), |
| endtype, ncache); |
| tree valid1 |
| = initializer_constant_valid_p_1 (TREE_OPERAND (value, 1), |
| endtype, ncache + 2); |
| /* Win if second argument is absolute. */ |
| if (valid1 == null_pointer_node) |
| ret = valid0; |
| /* Win if both arguments have the same relocation. |
| Then the value is absolute. */ |
| else if (valid0 == valid1 && valid0 != 0) |
| ret = null_pointer_node; |
| /* Since GCC guarantees that string constants are unique in the |
| generated code, a subtraction between two copies of the same |
| constant string is absolute. */ |
| else if (valid0 && TREE_CODE (valid0) == STRING_CST |
| && valid1 && TREE_CODE (valid1) == STRING_CST |
| && operand_equal_p (valid0, valid1, 1)) |
| ret = null_pointer_node; |
| /* Support narrowing differences. */ |
| else |
| ret = narrowing_initializer_constant_valid_p (value, endtype, |
| ncache); |
| } |
| else |
| /* Support narrowing differences. */ |
| ret = narrowing_initializer_constant_valid_p (value, endtype, NULL); |
| if (cache) |
| { |
| cache[0] = value; |
| cache[1] = ret; |
| } |
| return ret; |
| |
| default: |
| break; |
| } |
| |
| return NULL_TREE; |
| } |
| |
| /* Return nonzero if VALUE is a valid constant-valued expression |
| for use in initializing a static variable; one that can be an |
| element of a "constant" initializer. |
| |
| Return null_pointer_node if the value is absolute; |
| if it is relocatable, return the variable that determines the relocation. |
| We assume that VALUE has been folded as much as possible; |
| therefore, we do not need to check for such things as |
| arithmetic-combinations of integers. */ |
| tree |
| initializer_constant_valid_p (tree value, tree endtype, bool reverse) |
| { |
| tree reloc = initializer_constant_valid_p_1 (value, endtype, NULL); |
| |
| /* An absolute value is required with reverse storage order. */ |
| if (reloc |
| && reloc != null_pointer_node |
| && reverse |
| && !AGGREGATE_TYPE_P (endtype) |
| && !VECTOR_TYPE_P (endtype)) |
| reloc = NULL_TREE; |
| |
| return reloc; |
| } |
| |
| /* Return true if VALUE is a valid constant-valued expression |
| for use in initializing a static bit-field; one that can be |
| an element of a "constant" initializer. */ |
| |
| bool |
| initializer_constant_valid_for_bitfield_p (const_tree value) |
| { |
| /* For bitfields we support integer constants or possibly nested aggregates |
| of such. */ |
| switch (TREE_CODE (value)) |
| { |
| case CONSTRUCTOR: |
| { |
| unsigned HOST_WIDE_INT idx; |
| const_tree elt; |
| |
| FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (value), idx, elt) |
| if (!initializer_constant_valid_for_bitfield_p (elt)) |
| return false; |
| return true; |
| } |
| |
| case INTEGER_CST: |
| case REAL_CST: |
| return true; |
| |
| case VIEW_CONVERT_EXPR: |
| case NON_LVALUE_EXPR: |
| return |
| initializer_constant_valid_for_bitfield_p (TREE_OPERAND (value, 0)); |
| |
| default: |
| break; |
| } |
| |
| return false; |
| } |
| |
| /* Check if a STRING_CST fits into the field. |
| Tolerate only the case when the NUL termination |
| does not fit into the field. */ |
| |
| static bool |
| check_string_literal (tree string, unsigned HOST_WIDE_INT size) |
| { |
| tree type = TREE_TYPE (string); |
| tree eltype = TREE_TYPE (type); |
| unsigned HOST_WIDE_INT elts = tree_to_uhwi (TYPE_SIZE_UNIT (eltype)); |
| unsigned HOST_WIDE_INT mem_size = tree_to_uhwi (TYPE_SIZE_UNIT (type)); |
| int len = TREE_STRING_LENGTH (string); |
| |
| if (elts != 1 && elts != 2 && elts != 4) |
| return false; |
| if (len < 0 || len % elts != 0) |
| return false; |
| if (size < (unsigned)len) |
| return false; |
| if (mem_size != size) |
| return false; |
| return true; |
| } |
| |
| /* output_constructor outer state of relevance in recursive calls, typically |
| for nested aggregate bitfields. */ |
| |
| struct oc_outer_state { |
| unsigned int bit_offset; /* current position in ... */ |
| int byte; /* ... the outer byte buffer. */ |
| }; |
| |
| static unsigned HOST_WIDE_INT |
| output_constructor (tree, unsigned HOST_WIDE_INT, unsigned int, bool, |
| oc_outer_state *); |
| |
| /* Output assembler code for constant EXP, with no label. |
| This includes the pseudo-op such as ".int" or ".byte", and a newline. |
| Assumes output_addressed_constants has been done on EXP already. |
| |
| Generate at least SIZE bytes of assembler data, padding at the end |
| with zeros if necessary. SIZE must always be specified. The returned |
| value is the actual number of bytes of assembler data generated, which |
| may be bigger than SIZE if the object contains a variable length field. |
| |
| SIZE is important for structure constructors, |
| since trailing members may have been omitted from the constructor. |
| It is also important for initialization of arrays from string constants |
| since the full length of the string constant might not be wanted. |
| It is also needed for initialization of unions, where the initializer's |
| type is just one member, and that may not be as long as the union. |
| |
| There a case in which we would fail to output exactly SIZE bytes: |
| for a structure constructor that wants to produce more than SIZE bytes. |
| But such constructors will never be generated for any possible input. |
| |
| ALIGN is the alignment of the data in bits. |
| |
| If REVERSE is true, EXP is output in reverse storage order. */ |
| |
| static unsigned HOST_WIDE_INT |
| output_constant (tree exp, unsigned HOST_WIDE_INT size, unsigned int align, |
| bool reverse, bool merge_strings) |
| { |
| enum tree_code code; |
| unsigned HOST_WIDE_INT thissize; |
| rtx cst; |
| |
| if (size == 0 || flag_syntax_only) |
| return size; |
| |
| /* See if we're trying to initialize a pointer in a non-default mode |
| to the address of some declaration somewhere. If the target says |
| the mode is valid for pointers, assume the target has a way of |
| resolving it. */ |
| if (TREE_CODE (exp) == NOP_EXPR |
| && POINTER_TYPE_P (TREE_TYPE (exp)) |
| && targetm.addr_space.valid_pointer_mode |
| (SCALAR_INT_TYPE_MODE (TREE_TYPE (exp)), |
| TYPE_ADDR_SPACE (TREE_TYPE (TREE_TYPE (exp))))) |
| { |
| tree saved_type = TREE_TYPE (exp); |
| |
| /* Peel off any intermediate conversions-to-pointer for valid |
| pointer modes. */ |
| while (TREE_CODE (exp) == NOP_EXPR |
| && POINTER_TYPE_P (TREE_TYPE (exp)) |
| && targetm.addr_space.valid_pointer_mode |
| (SCALAR_INT_TYPE_MODE (TREE_TYPE (exp)), |
| TYPE_ADDR_SPACE (TREE_TYPE (TREE_TYPE (exp))))) |
| exp = TREE_OPERAND (exp, 0); |
| |
| /* If what we're left with is the address of something, we can |
| convert the address to the final type and output it that |
| way. */ |
| if (TREE_CODE (exp) == ADDR_EXPR) |
| exp = build1 (ADDR_EXPR, saved_type, TREE_OPERAND (exp, 0)); |
| /* Likewise for constant ints. */ |
| else if (TREE_CODE (exp) == INTEGER_CST) |
| exp = fold_convert (saved_type, exp); |
| |
| } |
| |
| /* Eliminate any conversions since we'll be outputting the underlying |
| constant. */ |
| while (CONVERT_EXPR_P (exp) |
| || TREE_CODE (exp) == NON_LVALUE_EXPR |
| || TREE_CODE (exp) == VIEW_CONVERT_EXPR) |
| { |
| HOST_WIDE_INT type_size = int_size_in_bytes (TREE_TYPE (exp)); |
| HOST_WIDE_INT op_size = int_size_in_bytes (TREE_TYPE (TREE_OPERAND (exp, 0))); |
| |
| /* Make sure eliminating the conversion is really a no-op, except with |
| VIEW_CONVERT_EXPRs to allow for wild Ada unchecked conversions and |
| union types to allow for Ada unchecked unions. */ |
| if (type_size > op_size |
| && TREE_CODE (exp) != VIEW_CONVERT_EXPR |
| && TREE_CODE (TREE_TYPE (exp)) != UNION_TYPE) |
| /* Keep the conversion. */ |
| break; |
| else |
| exp = TREE_OPERAND (exp, 0); |
| } |
| |
| code = TREE_CODE (TREE_TYPE (exp)); |
| thissize = int_size_in_bytes (TREE_TYPE (exp)); |
| |
| /* Allow a constructor with no elements for any data type. |
| This means to fill the space with zeros. */ |
| if (TREE_CODE (exp) == CONSTRUCTOR |
| && vec_safe_is_empty (CONSTRUCTOR_ELTS (exp))) |
| { |
| assemble_zeros (size); |
| return size; |
| } |
| |
| if (TREE_CODE (exp) == FDESC_EXPR) |
| { |
| #ifdef ASM_OUTPUT_FDESC |
| HOST_WIDE_INT part = tree_to_shwi (TREE_OPERAND (exp, 1)); |
| tree decl = TREE_OPERAND (exp, 0); |
| ASM_OUTPUT_FDESC (asm_out_file, decl, part); |
| #else |
| gcc_unreachable (); |
| #endif |
| return size; |
| } |
| |
| /* Now output the underlying data. If we've handling the padding, return. |
| Otherwise, break and ensure SIZE is the size written. */ |
| switch (code) |
| { |
| case BOOLEAN_TYPE: |
| case INTEGER_TYPE: |
| case ENUMERAL_TYPE: |
| case POINTER_TYPE: |
| case REFERENCE_TYPE: |
| case OFFSET_TYPE: |
| case FIXED_POINT_TYPE: |
| case NULLPTR_TYPE: |
| cst = expand_expr (exp, NULL_RTX, VOIDmode, EXPAND_INITIALIZER); |
| if (reverse) |
| cst = flip_storage_order (TYPE_MODE (TREE_TYPE (exp)), cst); |
| if (!assemble_integer (cst, MIN (size, thissize), align, 0)) |
| error ("initializer for integer/fixed-point value is too complicated"); |
| break; |
| |
| case REAL_TYPE: |
| gcc_assert (size == thissize); |
| if (TREE_CODE (exp) != REAL_CST) |
| error ("initializer for floating value is not a floating constant"); |
| else |
| assemble_real (TREE_REAL_CST (exp), |
| SCALAR_FLOAT_TYPE_MODE (TREE_TYPE (exp)), |
| align, reverse); |
| break; |
| |
| case COMPLEX_TYPE: |
| output_constant (TREE_REALPART (exp), thissize / 2, align, |
| reverse, false); |
| output_constant (TREE_IMAGPART (exp), thissize / 2, |
| min_align (align, BITS_PER_UNIT * (thissize / 2)), |
| reverse, false); |
| break; |
| |
| case BITINT_TYPE: |
| if (TREE_CODE (exp) != INTEGER_CST) |
| error ("initializer for %<_BitInt(%d)%> value is not an integer " |
| "constant", TYPE_PRECISION (TREE_TYPE (exp))); |
| else |
| { |
| struct bitint_info info; |
| tree type = TREE_TYPE (exp); |
| bool ok = targetm.c.bitint_type_info (TYPE_PRECISION (type), &info); |
| gcc_assert (ok); |
| scalar_int_mode limb_mode |
| = as_a <scalar_int_mode> (info.abi_limb_mode); |
| if (TYPE_PRECISION (type) <= GET_MODE_PRECISION (limb_mode)) |
| { |
| cst = expand_expr (exp, NULL_RTX, VOIDmode, EXPAND_INITIALIZER); |
| if (reverse) |
| cst = flip_storage_order (TYPE_MODE (TREE_TYPE (exp)), cst); |
| if (!assemble_integer (cst, MIN (size, thissize), align, 0)) |
| error ("initializer for integer/fixed-point value is too " |
| "complicated"); |
| break; |
| } |
| int prec = GET_MODE_PRECISION (limb_mode); |
| int cnt = CEIL (TYPE_PRECISION (type), prec); |
| tree limb_type = build_nonstandard_integer_type (prec, 1); |
| int elt_size = GET_MODE_SIZE (limb_mode); |
| unsigned int nalign = MIN (align, GET_MODE_ALIGNMENT (limb_mode)); |
| thissize = 0; |
| if (prec == HOST_BITS_PER_WIDE_INT) |
| for (int i = 0; i < cnt; i++) |
| { |
| int idx = (info.big_endian ^ reverse) ? cnt - 1 - i : i; |
| tree c; |
| if (idx >= TREE_INT_CST_EXT_NUNITS (exp)) |
| c = build_int_cst (limb_type, |
| tree_int_cst_sgn (exp) < 0 ? -1 : 0); |
| else |
| c = build_int_cst (limb_type, |
| TREE_INT_CST_ELT (exp, idx)); |
| output_constant (c, elt_size, nalign, reverse, false); |
| thissize += elt_size; |
| } |
| else |
| for (int i = 0; i < cnt; i++) |
| { |
| int idx = (info.big_endian ^ reverse) ? cnt - 1 - i : i; |
| wide_int w = wi::rshift (wi::to_wide (exp), idx * prec, |
| TYPE_SIGN (TREE_TYPE (exp))); |
| tree c = wide_int_to_tree (limb_type, |
| wide_int::from (w, prec, UNSIGNED)); |
| output_constant (c, elt_size, nalign, reverse, false); |
| thissize += elt_size; |
| } |
| } |
| break; |
| |
| case ARRAY_TYPE: |
| case VECTOR_TYPE: |
| switch (TREE_CODE (exp)) |
| { |
| case CONSTRUCTOR: |
| return output_constructor (exp, size, align, reverse, NULL); |
| case STRING_CST: |
| thissize = (unsigned HOST_WIDE_INT)TREE_STRING_LENGTH (exp); |
| if (merge_strings |
| && (thissize == 0 |
| || TREE_STRING_POINTER (exp) [thissize - 1] != '\0')) |
| thissize++; |
| gcc_checking_assert (check_string_literal (exp, size)); |
| assemble_string (TREE_STRING_POINTER (exp), thissize); |
| break; |
| case VECTOR_CST: |
| { |
| scalar_mode inner = SCALAR_TYPE_MODE (TREE_TYPE (TREE_TYPE (exp))); |
| unsigned int nalign = MIN (align, GET_MODE_ALIGNMENT (inner)); |
| int elt_size = GET_MODE_SIZE (inner); |
| output_constant (VECTOR_CST_ELT (exp, 0), elt_size, align, |
| reverse, false); |
| thissize = elt_size; |
| /* Static constants must have a fixed size. */ |
| unsigned int nunits = VECTOR_CST_NELTS (exp).to_constant (); |
| for (unsigned int i = 1; i < nunits; i++) |
| { |
| output_constant (VECTOR_CST_ELT (exp, i), elt_size, nalign, |
| reverse, false); |
| thissize += elt_size; |
| } |
| break; |
| } |
| default: |
| gcc_unreachable (); |
| } |
| break; |
| |
| case RECORD_TYPE: |
| case UNION_TYPE: |
| gcc_assert (TREE_CODE (exp) == CONSTRUCTOR); |
| return output_constructor (exp, size, align, reverse, NULL); |
| |
| case ERROR_MARK: |
| return 0; |
| |
| default: |
| gcc_unreachable (); |
| } |
| |
| if (size > thissize) |
| assemble_zeros (size - thissize); |
| |
| return size; |
| } |
| |
| /* Subroutine of output_constructor, used for computing the size of |
| arrays of unspecified length. VAL must be a CONSTRUCTOR of an array |
| type with an unspecified upper bound. */ |
| |
| static unsigned HOST_WIDE_INT |
| array_size_for_constructor (tree val) |
| { |
| tree max_index; |
| unsigned HOST_WIDE_INT cnt; |
| tree index, value, tmp; |
| offset_int i; |
| |
| /* This code used to attempt to handle string constants that are not |
| arrays of single-bytes, but nothing else does, so there's no point in |
| doing it here. */ |
| if (TREE_CODE (val) == STRING_CST) |
| return TREE_STRING_LENGTH (val); |
| |
| max_index = NULL_TREE; |
| FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (val), cnt, index, value) |
| { |
| if (TREE_CODE (index) == RANGE_EXPR) |
| index = TREE_OPERAND (index, 1); |
| if (max_index == NULL_TREE || tree_int_cst_lt (max_index, index)) |
| max_index = index; |
| } |
| |
| if (max_index == NULL_TREE) |
| return 0; |
| |
| /* Compute the total number of array elements. */ |
| tmp = TYPE_MIN_VALUE (TYPE_DOMAIN (TREE_TYPE (val))); |
| i = wi::to_offset (max_index) - wi::to_offset (tmp) + 1; |
| |
| /* Multiply by the array element unit size to find number of bytes. */ |
| i *= wi::to_offset (TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (val)))); |
| |
| gcc_assert (wi::fits_uhwi_p (i)); |
| return i.to_uhwi (); |
| } |
| |
| /* Other datastructures + helpers for output_constructor. */ |
| |
| /* output_constructor local state to support interaction with helpers. */ |
| |
| struct oc_local_state { |
| |
| /* Received arguments. */ |
| tree exp; /* Constructor expression. */ |
| tree type; /* Type of constructor expression. */ |
| unsigned HOST_WIDE_INT size; /* # bytes to output - pad if necessary. */ |
| unsigned int align; /* Known initial alignment. */ |
| tree min_index; /* Lower bound if specified for an array. */ |
| |
| /* Output processing state. */ |
| HOST_WIDE_INT total_bytes; /* # bytes output so far / current position. */ |
| int byte; /* Part of a bitfield byte yet to be output. */ |
| int last_relative_index; /* Implicit or explicit index of the last |
| array element output within a bitfield. */ |
| bool byte_buffer_in_use; /* Whether BYTE is in use. */ |
| bool reverse; /* Whether reverse storage order is in use. */ |
| |
| /* Current element. */ |
| tree field; /* Current field decl in a record. */ |
| tree val; /* Current element value. */ |
| tree index; /* Current element index. */ |
| |
| }; |
| |
| /* Helper for output_constructor. From the current LOCAL state, output a |
| RANGE_EXPR element. */ |
| |
| static void |
| output_constructor_array_range (oc_local_state *local) |
| { |
| /* Perform the index calculation in modulo arithmetic but |
| sign-extend the result because Ada has negative DECL_FIELD_OFFSETs |
| but we are using an unsigned sizetype. */ |
| unsigned prec = TYPE_PRECISION (sizetype); |
| offset_int idx = wi::sext (wi::to_offset (TREE_OPERAND (local->index, 0)) |
| - wi::to_offset (local->min_index), prec); |
| tree valtype = TREE_TYPE (local->val); |
| HOST_WIDE_INT fieldpos |
| = (idx * wi::to_offset (TYPE_SIZE_UNIT (valtype))).to_short_addr (); |
| |
| /* Advance to offset of this element. */ |
| if (fieldpos > local->total_bytes) |
| { |
| assemble_zeros (fieldpos - local->total_bytes); |
| local->total_bytes = fieldpos; |
| } |
| else |
| /* Must not go backwards. */ |
| gcc_assert (fieldpos == local->total_bytes); |
| |
| unsigned HOST_WIDE_INT fieldsize |
| = int_size_in_bytes (TREE_TYPE (local->type)); |
| |
| HOST_WIDE_INT lo_index |
| = tree_to_shwi (TREE_OPERAND (local->index, 0)); |
| HOST_WIDE_INT hi_index |
| = tree_to_shwi (TREE_OPERAND (local->index, 1)); |
| HOST_WIDE_INT index; |
| |
| unsigned int align2 |
| = min_align (local->align, fieldsize * BITS_PER_UNIT); |
| |
| for (index = lo_index; index <= hi_index; index++) |
| { |
| /* Output the element's initial value. */ |
| if (local->val == NULL_TREE) |
| assemble_zeros (fieldsize); |
| else |
| fieldsize = output_constant (local->val, fieldsize, align2, |
| local->reverse, false); |
| |
| /* Count its size. */ |
| local->total_bytes += fieldsize; |
| } |
| } |
| |
| /* Helper for output_constructor. From the current LOCAL state, output a |
| field element that is not true bitfield or part of an outer one. */ |
| |
| static void |
| output_constructor_regular_field (oc_local_state *local) |
| { |
| /* Field size and position. Since this structure is static, we know the |
| positions are constant. */ |
| unsigned HOST_WIDE_INT fieldsize; |
| HOST_WIDE_INT fieldpos; |
| |
| unsigned int align2; |
| |
| /* Output any buffered-up bit-fields preceding this element. */ |
| if (local->byte_buffer_in_use) |
| { |
| assemble_integer (GEN_INT (local->byte), 1, BITS_PER_UNIT, 1); |
| local->total_bytes++; |
| local->byte_buffer_in_use = false; |
| } |
| |
| if (local->index != NULL_TREE) |
| { |
| /* Perform the index calculation in modulo arithmetic but |
| sign-extend the result because Ada has negative DECL_FIELD_OFFSETs |
| but we are using an unsigned sizetype. */ |
| unsigned prec = TYPE_PRECISION (sizetype); |
| offset_int idx = wi::sext (wi::to_offset (local->index) |
| - wi::to_offset (local->min_index), prec); |
| fieldpos = (idx * wi::to_offset (TYPE_SIZE_UNIT (TREE_TYPE (local->val)))) |
| .to_short_addr (); |
| } |
| else if (local->field != NULL_TREE) |
| fieldpos = int_byte_position (local->field); |
| else |
| fieldpos = 0; |
| |
| /* Advance to offset of this element. |
| Note no alignment needed in an array, since that is guaranteed |
| if each element has the proper size. */ |
| if (local->field != NULL_TREE || local->index != NULL_TREE) |
| { |
| if (fieldpos > local->total_bytes) |
| { |
| assemble_zeros (fieldpos - local->total_bytes); |
| local->total_bytes = fieldpos; |
| } |
| else |
| /* Must not go backwards. */ |
| gcc_assert (fieldpos == local->total_bytes); |
| } |
| |
| /* Find the alignment of this element. */ |
| align2 = min_align (local->align, BITS_PER_UNIT * fieldpos); |
| |
| /* Determine size this element should occupy. */ |
| if (local->field) |
| { |
| fieldsize = 0; |
| |
| /* If this is an array with an unspecified upper bound, |
| the initializer determines the size. */ |
| /* ??? This ought to only checked if DECL_SIZE_UNIT is NULL, |
| but we cannot do this until the deprecated support for |
| initializing zero-length array members is removed. */ |
| if (TREE_CODE (TREE_TYPE (local->field)) == ARRAY_TYPE |
| && (!TYPE_DOMAIN (TREE_TYPE (local->field)) |
| || !TYPE_MAX_VALUE (TYPE_DOMAIN (TREE_TYPE (local->field))))) |
| { |
| unsigned HOST_WIDE_INT fldsize |
| = array_size_for_constructor (local->val); |
| fieldsize = int_size_in_bytes (TREE_TYPE (local->val)); |
| /* In most cases fieldsize == fldsize as the size of the initializer |
| determines how many elements the flexible array member has. For |
| C++ fldsize can be smaller though, if the last or several last or |
| all initializers of the flexible array member have side-effects |
| and the FE splits them into dynamic initialization. */ |
| gcc_checking_assert (fieldsize >= fldsize); |
| /* Given a non-empty initialization, this field had better |
| be last. Given a flexible array member, the next field |
| on the chain is a TYPE_DECL of the enclosing struct. */ |
| const_tree next = DECL_CHAIN (local->field); |
| gcc_assert (!fieldsize || !next || TREE_CODE (next) != FIELD_DECL); |
| } |
| else |
| fieldsize = tree_to_uhwi (DECL_SIZE_UNIT (local->field)); |
| } |
| else |
| fieldsize = int_size_in_bytes (TREE_TYPE (local->type)); |
| |
| /* Output the element's initial value. */ |
| if (local->val == NULL_TREE) |
| assemble_zeros (fieldsize); |
| else |
| fieldsize = output_constant (local->val, fieldsize, align2, |
| local->reverse, false); |
| |
| /* Count its size. */ |
| local->total_bytes += fieldsize; |
| } |
| |
| /* Helper for output_constructor. From the LOCAL state, output an element |
| that is a true bitfield or part of an outer one. BIT_OFFSET is the offset |
| from the start of a possibly ongoing outer byte buffer. */ |
| |
| static void |
| output_constructor_bitfield (oc_local_state *local, unsigned int bit_offset) |
| { |
| /* Bit size of this element. */ |
| HOST_WIDE_INT ebitsize |
| = (local->field |
| ? tree_to_uhwi (DECL_SIZE (local->field)) |
| : tree_to_uhwi (TYPE_SIZE (TREE_TYPE (local->type)))); |
| |
| /* Relative index of this element if this is an array component. */ |
| HOST_WIDE_INT relative_index |
| = (local->field |
| ? 0 |
| : (local->index |
| ? tree_to_uhwi (local->index) - tree_to_uhwi (local->min_index) |
| : local->last_relative_index + 1)); |
| |
| /* Bit position of this element from the start of the containing |
| constructor. */ |
| HOST_WIDE_INT constructor_relative_ebitpos |
| = (local->field |
| ? int_bit_position (local->field) |
| : ebitsize * relative_index); |
| |
| /* Bit position of this element from the start of a possibly ongoing |
| outer byte buffer. */ |
| HOST_WIDE_INT byte_relative_ebitpos |
| = bit_offset + constructor_relative_ebitpos; |
| |
| /* From the start of a possibly ongoing outer byte buffer, offsets to |
| the first bit of this element and to the first bit past the end of |
| this element. */ |
| HOST_WIDE_INT next_offset = byte_relative_ebitpos; |
| HOST_WIDE_INT end_offset = byte_relative_ebitpos + ebitsize; |
| |
| local->last_relative_index = relative_index; |
| |
| if (local->val == NULL_TREE) |
| local->val = integer_zero_node; |
| |
| while (TREE_CODE (local->val) == VIEW_CONVERT_EXPR |
| || TREE_CODE (local->val) == NON_LVALUE_EXPR) |
| local->val = TREE_OPERAND (local->val, 0); |
| |
| if (TREE_CODE (local->val) != INTEGER_CST |
| && TREE_CODE (local->val) != CONSTRUCTOR) |
| { |
| error ("invalid initial value for member %qE", DECL_NAME (local->field)); |
| return; |
| } |
| |
| /* If this field does not start in this (or next) byte, skip some bytes. */ |
| if (next_offset / BITS_PER_UNIT != local->total_bytes) |
| { |
| /* Output remnant of any bit field in previous bytes. */ |
| if (local->byte_buffer_in_use) |
| { |
| assemble_integer (GEN_INT (local->byte), 1, BITS_PER_UNIT, 1); |
| local->total_bytes++; |
| local->byte_buffer_in_use = false; |
| } |
| |
| /* If still not at proper byte, advance to there. */ |
| if (next_offset / BITS_PER_UNIT != local->total_bytes) |
| { |
| gcc_assert (next_offset / BITS_PER_UNIT >= local->total_bytes); |
| assemble_zeros (next_offset / BITS_PER_UNIT - local->total_bytes); |
| local->total_bytes = next_offset / BITS_PER_UNIT; |
| } |
| } |
| |
| /* Set up the buffer if necessary. */ |
| if (!local->byte_buffer_in_use) |
| { |
| local->byte = 0; |
| if (ebitsize > 0) |
| local->byte_buffer_in_use = true; |
| } |
| |
| /* If this is nested constructor, recurse passing the bit offset and the |
| pending data, then retrieve the new pending data afterwards. */ |
| if (TREE_CODE (local->val) == CONSTRUCTOR) |
| { |
| oc_outer_state temp_state; |
| temp_state.bit_offset = next_offset % BITS_PER_UNIT; |
| temp_state.byte = local->byte; |
| local->total_bytes |
| += output_constructor (local->val, 0, 0, local->reverse, &temp_state); |
| local->byte = temp_state.byte; |
| return; |
| } |
| |
| /* Otherwise, we must split the element into pieces that fall within |
| separate bytes, and combine each byte with previous or following |
| bit-fields. */ |
| while (next_offset < end_offset) |
| { |
| int this_time; |
| int shift; |
| unsigned HOST_WIDE_INT value; |
| HOST_WIDE_INT next_byte = next_offset / BITS_PER_UNIT; |
| HOST_WIDE_INT next_bit = next_offset % BITS_PER_UNIT; |
| |
| /* Advance from byte to byte within this element when necessary. */ |
| while (next_byte != local->total_bytes) |
| { |
| assemble_integer (GEN_INT (local->byte), 1, BITS_PER_UNIT, 1); |
| local->total_bytes++; |
| local->byte = 0; |
| } |
| |
| /* Number of bits we can process at once (all part of the same byte). */ |
| this_time = MIN (end_offset - next_offset, BITS_PER_UNIT - next_bit); |
| if (local->reverse ? !BYTES_BIG_ENDIAN : BYTES_BIG_ENDIAN) |
| { |
| /* For big-endian data, take the most significant bits (of the |
| bits that are significant) first and put them into bytes from |
| the most significant end. */ |
| shift = end_offset - next_offset - this_time; |
| |
| /* Don't try to take a bunch of bits that cross |
| the word boundary in the INTEGER_CST. We can |
| only select bits from one element. */ |
| if ((shift / HOST_BITS_PER_WIDE_INT) |
| != ((shift + this_time - 1) / HOST_BITS_PER_WIDE_INT)) |
| { |
| const int end = shift + this_time - 1; |
| shift = end & -HOST_BITS_PER_WIDE_INT; |
| this_time = end - shift + 1; |
| } |
| |
| /* Now get the bits we want to insert. */ |
| value = wi::extract_uhwi (wi::to_widest (local->val), |
| shift, this_time); |
| |
| /* Get the result. This works only when: |
| 1 <= this_time <= HOST_BITS_PER_WIDE_INT. */ |
| local->byte |= value << (BITS_PER_UNIT - this_time - next_bit); |
| } |
| else |
| { |
| /* On little-endian machines, take the least significant bits of |
| the value first and pack them starting at the least significant |
| bits of the bytes. */ |
| shift = next_offset - byte_relative_ebitpos; |
| |
| /* Don't try to take a bunch of bits that cross |
| the word boundary in the INTEGER_CST. We can |
| only select bits from one element. */ |
| if ((shift / HOST_BITS_PER_WIDE_INT) |
| != ((shift + this_time - 1) / HOST_BITS_PER_WIDE_INT)) |
| this_time |
| = HOST_BITS_PER_WIDE_INT - (shift & (HOST_BITS_PER_WIDE_INT - 1)); |
| |
| /* Now get the bits we want to insert. */ |
| value = wi::extract_uhwi (wi::to_widest (local->val), |
| shift, this_time); |
| |
| /* Get the result. This works only when: |
| 1 <= this_time <= HOST_BITS_PER_WIDE_INT. */ |
| local->byte |= value << next_bit; |
| } |
| |
| next_offset += this_time; |
| local->byte_buffer_in_use = true; |
| } |
| } |
| |
| /* Subroutine of output_constant, used for CONSTRUCTORs (aggregate constants). |
| Generate at least SIZE bytes, padding if necessary. OUTER designates the |
| caller output state of relevance in recursive invocations. */ |
| |
| static unsigned HOST_WIDE_INT |
| output_constructor (tree exp, unsigned HOST_WIDE_INT size, unsigned int align, |
| bool reverse, oc_outer_state *outer) |
| { |
| unsigned HOST_WIDE_INT cnt; |
| constructor_elt *ce; |
| oc_local_state local; |
| |
| /* Setup our local state to communicate with helpers. */ |
| local.exp = exp; |
| local.type = TREE_TYPE (exp); |
| local.size = size; |
| local.align = align; |
| if (TREE_CODE (local.type) == ARRAY_TYPE && TYPE_DOMAIN (local.type)) |
| local.min_index = TYPE_MIN_VALUE (TYPE_DOMAIN (local.type)); |
| else |
| local.min_index = integer_zero_node; |
| |
| local.total_bytes = 0; |
| local.byte_buffer_in_use = outer != NULL; |
| local.byte = outer ? outer->byte : 0; |
| local.last_relative_index = -1; |
| /* The storage order is specified for every aggregate type. */ |
| if (AGGREGATE_TYPE_P (local.type)) |
| local.reverse = TYPE_REVERSE_STORAGE_ORDER (local.type); |
| else |
| local.reverse = reverse; |
| |
| gcc_assert (HOST_BITS_PER_WIDE_INT >= BITS_PER_UNIT); |
| |
| /* As CE goes through the elements of the constant, FIELD goes through the |
| structure fields if the constant is a structure. If the constant is a |
| union, we override this by getting the field from the TREE_LIST element. |
| But the constant could also be an array. Then FIELD is zero. |
| |
| There is always a maximum of one element in the chain LINK for unions |
| (even if the initializer in a source program incorrectly contains |
| more one). */ |
| |
| if (TREE_CODE (local.type) == RECORD_TYPE) |
| local.field = TYPE_FIELDS (local.type); |
| else |
| local.field = NULL_TREE; |
| |
| for (cnt = 0; |
| vec_safe_iterate (CONSTRUCTOR_ELTS (exp), cnt, &ce); |
| cnt++, local.field = local.field ? DECL_CHAIN (local.field) : 0) |
| { |
| local.val = ce->value; |
| local.index = NULL_TREE; |
| |
| /* The element in a union constructor specifies the proper field |
| or index. */ |
| if (RECORD_OR_UNION_TYPE_P (local.type) && ce->index != NULL_TREE) |
| local.field = ce->index; |
| |
| else if (TREE_CODE (local.type) == ARRAY_TYPE) |
| local.index = ce->index; |
| |
| if (local.field && flag_verbose_asm) |
| fprintf (asm_out_file, "%s %s:\n", |
| ASM_COMMENT_START, |
| DECL_NAME (local.field) |
| ? IDENTIFIER_POINTER (DECL_NAME (local.field)) |
| : "<anonymous>"); |
| |
| /* Eliminate the marker that makes a cast not be an lvalue. */ |
| if (local.val != NULL_TREE) |
| STRIP_NOPS (local.val); |
| |
| /* Output the current element, using the appropriate helper ... */ |
| |
| /* For an array slice not part of an outer bitfield. */ |
| if (!outer |
| && local.index != NULL_TREE |
| && TREE_CODE (local.index) == RANGE_EXPR) |
| output_constructor_array_range (&local); |
| |
| /* For a field that is neither a true bitfield nor part of an outer one, |
| known to be at least byte aligned and multiple-of-bytes long. */ |
| else if (!outer |
| && (local.field == NULL_TREE |
| || !CONSTRUCTOR_BITFIELD_P (local.field))) |
| output_constructor_regular_field (&local); |
| |
| /* For a true bitfield or part of an outer one. Only INTEGER_CSTs are |
| supported for scalar fields, so we may need to convert first. */ |
| else |
| { |
| if (TREE_CODE (local.val) == REAL_CST) |
| local.val |
| = fold_unary (VIEW_CONVERT_EXPR, |
| build_nonstandard_integer_type |
| (TYPE_PRECISION (TREE_TYPE (local.val)), 0), |
| local.val); |
| output_constructor_bitfield (&local, outer ? outer->bit_offset : 0); |
| } |
| } |
| |
| /* If we are not at toplevel, save the pending data for our caller. |
| Otherwise output the pending data and padding zeros as needed. */ |
| if (outer) |
| outer->byte = local.byte; |
| else |
| { |
| if (local.byte_buffer_in_use) |
| { |
| assemble_integer (GEN_INT (local.byte), 1, BITS_PER_UNIT, 1); |
| local.total_bytes++; |
| } |
| |
| if ((unsigned HOST_WIDE_INT)local.total_bytes < local.size) |
| { |
| assemble_zeros (local.size - local.total_bytes); |
| local.total_bytes = local.size; |
| } |
| } |
| |
| return local.total_bytes; |
| } |
| |
| /* Mark DECL as weak. */ |
| |
| static void |
| mark_weak (tree decl) |
| { |
| if (DECL_WEAK (decl)) |
| return; |
| |
| struct symtab_node *n = symtab_node::get (decl); |
| if (n && n->refuse_visibility_changes) |
| error ("%qD declared weak after being used", decl); |
| DECL_WEAK (decl) = 1; |
| |
| if (DECL_RTL_SET_P (decl) |
| && MEM_P (DECL_RTL (decl)) |
| && XEXP (DECL_RTL (decl), 0) |
| && GET_CODE (XEXP (DECL_RTL (decl), 0)) == SYMBOL_REF) |
| SYMBOL_REF_WEAK (XEXP (DECL_RTL (decl), 0)) = 1; |
| } |
| |
| /* Merge weak status between NEWDECL and OLDDECL. */ |
| |
| void |
| merge_weak (tree newdecl, tree olddecl) |
| { |
| if (DECL_WEAK (newdecl) == DECL_WEAK (olddecl)) |
| { |
| if (DECL_WEAK (newdecl) && TARGET_SUPPORTS_WEAK) |
| { |
| tree *pwd; |
| /* We put the NEWDECL on the weak_decls list at some point |
| and OLDDECL as well. Keep just OLDDECL on the list. */ |
| for (pwd = &weak_decls; *pwd; pwd = &TREE_CHAIN (*pwd)) |
| if (TREE_VALUE (*pwd) == newdecl) |
| { |
| *pwd = TREE_CHAIN (*pwd); |
| break; |
| } |
| } |
| return; |
| } |
| |
| if (DECL_WEAK (newdecl)) |
| { |
| tree wd; |
| |
| /* NEWDECL is weak, but OLDDECL is not. */ |
| |
| /* If we already output the OLDDECL, we're in trouble; we can't |
| go back and make it weak. This should never happen in |
| unit-at-a-time compilation. */ |
| gcc_assert (!TREE_ASM_WRITTEN (olddecl)); |
| |
| /* If we've already generated rtl referencing OLDDECL, we may |
| have done so in a way that will not function properly with |
| a weak symbol. Again in unit-at-a-time this should be |
| impossible. */ |
| gcc_assert (!TREE_USED (olddecl) |
| || !TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (olddecl))); |
| |
| /* PR 49899: You cannot convert a static function into a weak, public function. */ |
| if (! TREE_PUBLIC (olddecl) && TREE_PUBLIC (newdecl)) |
| error ("weak declaration of %q+D being applied to a already " |
| "existing, static definition", newdecl); |
| |
| if (TARGET_SUPPORTS_WEAK) |
| { |
| /* We put the NEWDECL on the weak_decls list at some point. |
| Replace it with the OLDDECL. */ |
| for (wd = weak_decls; wd; wd = TREE_CHAIN (wd)) |
| if (TREE_VALUE (wd) == newdecl) |
| { |
| TREE_VALUE (wd) = olddecl; |
| break; |
| } |
| /* We may not find the entry on the list. If NEWDECL is a |
| weak alias, then we will have already called |
| globalize_decl to remove the entry; in that case, we do |
| not need to do anything. */ |
| } |
| |
| /* Make the OLDDECL weak; it's OLDDECL that we'll be keeping. */ |
| mark_weak (olddecl); |
| } |
| else |
| /* OLDDECL was weak, but NEWDECL was not explicitly marked as |
| weak. Just update NEWDECL to indicate that it's weak too. */ |
| mark_weak (newdecl); |
| } |
| |
| /* Declare DECL to be a weak symbol. */ |
| |
| void |
| declare_weak (tree decl) |
| { |
| /* With -fsyntax-only, TREE_ASM_WRITTEN might be set on certain function |
| decls earlier than normally, but as with -fsyntax-only nothing is really |
| emitted, there is no harm in marking it weak later. */ |
| gcc_assert (TREE_CODE (decl) != FUNCTION_DECL |
| || !TREE_ASM_WRITTEN (decl) |
| || flag_syntax_only); |
| if (! TREE_PUBLIC (decl)) |
| { |
| error ("weak declaration of %q+D must be public", decl); |
| return; |
| } |
| else if (!TARGET_SUPPORTS_WEAK) |
| warning (0, "weak declaration of %q+D not supported", decl); |
| |
| mark_weak (decl); |
| if (!lookup_attribute ("weak", DECL_ATTRIBUTES (decl))) |
| DECL_ATTRIBUTES (decl) |
| = tree_cons (get_identifier ("weak"), NULL, DECL_ATTRIBUTES (decl)); |
| } |
| |
| static void |
| weak_finish_1 (tree decl) |
| { |
| #if defined (ASM_WEAKEN_DECL) || defined (ASM_WEAKEN_LABEL) |
| const char *const name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)); |
| #endif |
| |
| if (! TREE_USED (decl)) |
| return; |
| |
| #ifdef ASM_WEAKEN_DECL |
| ASM_WEAKEN_DECL (asm_out_file, decl, name, NULL); |
| #else |
| #ifdef ASM_WEAKEN_LABEL |
| ASM_WEAKEN_LABEL (asm_out_file, name); |
| #else |
| #ifdef ASM_OUTPUT_WEAK_ALIAS |
| { |
| static bool warn_once = 0; |
| if (! warn_once) |
| { |
| warning (0, "only weak aliases are supported in this configuration"); |
| warn_once = 1; |
| } |
| return; |
| } |
| #endif |
| #endif |
| #endif |
| } |
| |
| /* Fiven an assembly name, find the decl it is associated with. */ |
| static tree |
| find_decl (tree target) |
| { |
| symtab_node *node = symtab_node::get_for_asmname (target); |
| if (node) |
| return node->decl; |
| return NULL_TREE; |
| } |
| |
| /* This TREE_LIST contains weakref targets. */ |
| |
| static GTY(()) tree weakref_targets; |
| |
| /* Emit any pending weak declarations. */ |
| |
| void |
| weak_finish (void) |
| { |
| tree t; |
| |
| for (t = weakref_targets; t; t = TREE_CHAIN (t)) |
| { |
| tree alias_decl = TREE_PURPOSE (t); |
| tree target = ultimate_transparent_alias_target (&TREE_VALUE (t)); |
| |
| if (! TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (alias_decl)) |
| || TREE_SYMBOL_REFERENCED (target)) |
| /* Remove alias_decl from the weak list, but leave entries for |
| the target alone. */ |
| target = NULL_TREE; |
| #ifndef ASM_OUTPUT_WEAKREF |
| else if (! TREE_SYMBOL_REFERENCED (target)) |
| { |
| /* Use ASM_WEAKEN_LABEL only if ASM_WEAKEN_DECL is not |
| defined, otherwise we and weak_finish_1 would use |
| different macros. */ |
| # if defined ASM_WEAKEN_LABEL && ! defined ASM_WEAKEN_DECL |
| ASM_WEAKEN_LABEL (asm_out_file, IDENTIFIER_POINTER (target)); |
| # else |
| tree decl = find_decl (target); |
| |
| if (! decl) |
| { |
| decl = build_decl (DECL_SOURCE_LOCATION (alias_decl), |
| TREE_CODE (alias_decl), target, |
| TREE_TYPE (alias_decl)); |
| |
| DECL_EXTERNAL (decl) = 1; |
| TREE_PUBLIC (decl) = 1; |
| DECL_ARTIFICIAL (decl) = 1; |
| TREE_NOTHROW (decl) = TREE_NOTHROW (alias_decl); |
| TREE_USED (decl) = 1; |
| } |
| |
| weak_finish_1 (decl); |
| # endif |
| } |
| #endif |
| |
| { |
| tree *p; |
| tree t2; |
| |
| /* Remove the alias and the target from the pending weak list |
| so that we do not emit any .weak directives for the former, |
| nor multiple .weak directives for the latter. */ |
| for (p = &weak_decls; (t2 = *p) ; ) |
| { |
| if (TREE_VALUE (t2) == alias_decl |
| || target == DECL_ASSEMBLER_NAME (TREE_VALUE (t2))) |
| *p = TREE_CHAIN (t2); |
| else |
| p = &TREE_CHAIN (t2); |
| } |
| |
| /* Remove other weakrefs to the same target, to speed things up. */ |
| for (p = &TREE_CHAIN (t); (t2 = *p) ; ) |
| { |
| if (target == ultimate_transparent_alias_target (&TREE_VALUE (t2))) |
| *p = TREE_CHAIN (t2); |
| else |
| p = &TREE_CHAIN (t2); |
| } |
| } |
| } |
| |
| for (t = weak_decls; t; t = TREE_CHAIN (t)) |
| { |
| tree decl = TREE_VALUE (t); |
| |
| weak_finish_1 (decl); |
| } |
| } |
| |
| /* Emit the assembly bits to indicate that DECL is globally visible. */ |
| |
| static void |
| globalize_decl (tree decl) |
| { |
| |
| #if defined (ASM_WEAKEN_LABEL) || defined (ASM_WEAKEN_DECL) |
| if (DECL_WEAK (decl)) |
| { |
| const char *name = XSTR (XEXP (DECL_RTL (decl), 0), 0); |
| tree *p, t; |
| |
| #ifdef ASM_WEAKEN_DECL |
| ASM_WEAKEN_DECL (asm_out_file, decl, name, 0); |
| #else |
| ASM_WEAKEN_LABEL (asm_out_file, name); |
| #endif |
| |
| /* Remove this function from the pending weak list so that |
| we do not emit multiple .weak directives for it. */ |
| for (p = &weak_decls; (t = *p) ; ) |
| { |
| if (DECL_ASSEMBLER_NAME (decl) == DECL_ASSEMBLER_NAME (TREE_VALUE (t))) |
| *p = TREE_CHAIN (t); |
| else |
| p = &TREE_CHAIN (t); |
| } |
| |
| /* Remove weakrefs to the same target from the pending weakref |
| list, for the same reason. */ |
| for (p = &weakref_targets; (t = *p) ; ) |
| { |
| if (DECL_ASSEMBLER_NAME (decl) |
| == ultimate_transparent_alias_target (&TREE_VALUE (t))) |
| *p = TREE_CHAIN (t); |
| else |
| p = &TREE_CHAIN (t); |
| } |
| |
| return; |
| } |
| #endif |
| |
| targetm.asm_out.globalize_decl_name (asm_out_file, decl); |
| } |
| |
| vec<alias_pair, va_gc> *alias_pairs; |
| |
| /* Output the assembler code for a define (equate) using ASM_OUTPUT_DEF |
| or ASM_OUTPUT_DEF_FROM_DECLS. The function defines the symbol whose |
| tree node is DECL to have the value of the tree node TARGET. */ |
| |
| void |
| do_assemble_alias (tree decl, tree target) |
| { |
| tree id; |
| |
| /* Emulated TLS had better not get this var. */ |
| gcc_assert (!(!targetm.have_tls |
| && VAR_P (decl) |
| && DECL_THREAD_LOCAL_P (decl))); |
| |
| if (TREE_ASM_WRITTEN (decl)) |
| return; |
| |
| id = DECL_ASSEMBLER_NAME (decl); |
| ultimate_transparent_alias_target (&id); |
| ultimate_transparent_alias_target (&target); |
| |
| /* We must force creation of DECL_RTL for debug info generation, even though |
| we don't use it here. */ |
| make_decl_rtl (decl); |
| |
| TREE_ASM_WRITTEN (decl) = 1; |
| TREE_ASM_WRITTEN (DECL_ASSEMBLER_NAME (decl)) = 1; |
| TREE_ASM_WRITTEN (id) = 1; |
| |
| if (lookup_attribute ("weakref", DECL_ATTRIBUTES (decl))) |
| { |
| if (!TREE_SYMBOL_REFERENCED (target)) |
| weakref_targets = tree_cons (decl, target, weakref_targets); |
| |
| #ifdef ASM_OUTPUT_WEAKREF |
| ASM_OUTPUT_WEAKREF (asm_out_file, decl, |
| IDENTIFIER_POINTER (id), |
| IDENTIFIER_POINTER (target)); |
| #else |
| if (!TARGET_SUPPORTS_WEAK) |
| { |
| error_at (DECL_SOURCE_LOCATION (decl), |
| "%qs is not supported in this configuration", "weakref "); |
| return; |
| } |
| #endif |
| return; |
| } |
| |
| #ifdef ASM_OUTPUT_DEF |
| tree orig_decl = decl; |
| |
| /* Make name accessible from other files, if appropriate. */ |
| |
| if (TREE_PUBLIC (decl) || TREE_PUBLIC (orig_decl)) |
| { |
| globalize_decl (decl); |
| maybe_assemble_visibility (decl); |
| } |
| if (TREE_CODE (decl) == FUNCTION_DECL |
| && cgraph_node::get (decl)->ifunc_resolver) |
| { |
| #if defined (ASM_OUTPUT_TYPE_DIRECTIVE) |
| if (targetm.has_ifunc_p ()) |
| ASM_OUTPUT_TYPE_DIRECTIVE |
| (asm_out_file, IDENTIFIER_POINTER (id), |
| IFUNC_ASM_TYPE); |
| else |
| #endif |
| error_at (DECL_SOURCE_LOCATION (decl), |
| "%qs is not supported on this target", "ifunc"); |
| } |
| |
| # ifdef ASM_OUTPUT_DEF_FROM_DECLS |
| ASM_OUTPUT_DEF_FROM_DECLS (asm_out_file, decl, target); |
| # else |
| ASM_OUTPUT_DEF (asm_out_file, |
| IDENTIFIER_POINTER (id), |
| IDENTIFIER_POINTER (target)); |
| # endif |
| /* If symbol aliases aren't actually supported... */ |
| if (!TARGET_SUPPORTS_ALIASES) |
| /* ..., 'ASM_OUTPUT_DEF{,_FROM_DECLS}' better have raised an error. */ |
| gcc_checking_assert (seen_error ()); |
| #elif defined (ASM_OUTPUT_WEAK_ALIAS) || defined (ASM_WEAKEN_DECL) |
| { |
| const char *name; |
| tree *p, t; |
| |
| name = IDENTIFIER_POINTER (id); |
| # ifdef ASM_WEAKEN_DECL |
| ASM_WEAKEN_DECL (asm_out_file, decl, name, IDENTIFIER_POINTER (target)); |
| # else |
| ASM_OUTPUT_WEAK_ALIAS (asm_out_file, name, IDENTIFIER_POINTER (target)); |
| # endif |
| /* Remove this function from the pending weak list so that |
| we do not emit multiple .weak directives for it. */ |
| for (p = &weak_decls; (t = *p) ; ) |
| if (DECL_ASSEMBLER_NAME (decl) == DECL_ASSEMBLER_NAME (TREE_VALUE (t)) |
| || id == DECL_ASSEMBLER_NAME (TREE_VALUE (t))) |
| *p = TREE_CHAIN (t); |
| else |
| p = &TREE_CHAIN (t); |
| |
| /* Remove weakrefs to the same target from the pending weakref |
| list, for the same reason. */ |
| for (p = &weakref_targets; (t = *p) ; ) |
| { |
| if (id == ultimate_transparent_alias_target (&TREE_VALUE (t))) |
| *p = TREE_CHAIN (t); |
| else |
| p = &TREE_CHAIN (t); |
| } |
| } |
| #endif |
| } |
| |
| /* Output .symver directive. */ |
| |
| void |
| do_assemble_symver (tree decl, tree target) |
| { |
| tree id = DECL_ASSEMBLER_NAME (decl); |
| ultimate_transparent_alias_target (&id); |
| ultimate_transparent_alias_target (&target); |
| #ifdef ASM_OUTPUT_SYMVER_DIRECTIVE |
| ASM_OUTPUT_SYMVER_DIRECTIVE (asm_out_file, |
| IDENTIFIER_POINTER (target), |
| IDENTIFIER_POINTER (id)); |
| #else |
| error ("symver is only supported on ELF platforms"); |
| #endif |
| } |
| |
| /* Emit an assembler directive to make the symbol for DECL an alias to |
| the symbol for TARGET. */ |
| |
| void |
| assemble_alias (tree decl, tree target) |
| { |
| tree target_decl; |
| |
| if (lookup_attribute ("weakref", DECL_ATTRIBUTES (decl))) |
| { |
| tree alias = DECL_ASSEMBLER_NAME (decl); |
| |
| ultimate_transparent_alias_target (&target); |
| |
| if (alias == target) |
| error ("%qs symbol %q+D ultimately targets itself", "weakref", decl); |
| if (TREE_PUBLIC (decl)) |
| error ("%qs symbol %q+D must have static linkage", "weakref", decl); |
| } |
| else if (!TARGET_SUPPORTS_ALIASES) |
| { |
| # if !defined(ASM_OUTPUT_WEAK_ALIAS) && !defined (ASM_WEAKEN_DECL) |
| error_at (DECL_SOURCE_LOCATION (decl), |
| "alias definitions not supported in this configuration"); |
| TREE_ASM_WRITTEN (decl) = 1; |
| return; |
| # else |
| if (!DECL_WEAK (decl)) |
| { |
| /* NB: ifunc_resolver isn't set when an error is detected. */ |
| if (TREE_CODE (decl) == FUNCTION_DECL |
| && lookup_attribute ("ifunc", DECL_ATTRIBUTES (decl))) |
| error_at (DECL_SOURCE_LOCATION (decl), |
| "%qs is not supported in this configuration", "ifunc"); |
| else |
| error_at (DECL_SOURCE_LOCATION (decl), |
| "only weak aliases are supported in this configuration"); |
| TREE_ASM_WRITTEN (decl) = 1; |
| return; |
| } |
| # endif |
| gcc_unreachable (); |
| } |
| TREE_USED (decl) = 1; |
| |
| /* Allow aliases to aliases. */ |
| if (TREE_CODE (decl) == FUNCTION_DECL) |
| cgraph_node::get_create (decl)->alias = true; |
| else |
| varpool_node::get_create (decl)->alias = true; |
| |
| /* If the target has already been emitted, we don't have to queue the |
| alias. This saves a tad of memory. */ |
| if (symtab->global_info_ready) |
| target_decl = find_decl (target); |
| else |
| target_decl= NULL; |
| if ((target_decl && TREE_ASM_WRITTEN (target_decl)) |
| || symtab->state >= EXPANSION) |
| do_assemble_alias (decl, target); |
| else |
| { |
| alias_pair p = {decl, target}; |
| vec_safe_push (alias_pairs, p); |
| } |
| } |
| |
| /* Record and output a table of translations from original function |
| to its transaction aware clone. Note that tm_pure functions are |
| considered to be their own clone. */ |
| |
| struct tm_clone_hasher : ggc_cache_ptr_hash<tree_map> |
| { |
| static hashval_t hash (tree_map *m) { return tree_map_hash (m); } |
| static bool equal (tree_map *a, tree_map *b) { return tree_map_eq (a, b); } |
| |
| static int |
| keep_cache_entry (tree_map *&e) |
| { |
| return ggc_marked_p (e->base.from); |
| } |
| }; |
| |
| static GTY((cache)) hash_table<tm_clone_hasher> *tm_clone_hash; |
| |
| void |
| record_tm_clone_pair (tree o, tree n) |
| { |
| struct tree_map **slot, *h; |
| |
| if (tm_clone_hash == NULL) |
| tm_clone_hash = hash_table<tm_clone_hasher>::create_ggc (32); |
| |
| h = ggc_alloc<tree_map> (); |
| h->hash = htab_hash_pointer (o); |
| h->base.from = o; |
| h->to = n; |
| |
| slot = tm_clone_hash->find_slot_with_hash (h, h->hash, INSERT); |
| *slot = h; |
| } |
| |
| tree |
| get_tm_clone_pair (tree o) |
| { |
| if (tm_clone_hash) |
| { |
| struct tree_map *h, in; |
| |
| in.base.from = o; |
| in.hash = htab_hash_pointer (o); |
| h = tm_clone_hash->find_with_hash (&in, in.hash); |
| if (h) |
| return h->to; |
| } |
| return NULL_TREE; |
| } |
| |
| struct tm_alias_pair |
| { |
| unsigned int uid; |
| tree from; |
| tree to; |
| }; |
| |
| |
| /* Dump the actual pairs to the .tm_clone_table section. */ |
| |
| static void |
| dump_tm_clone_pairs (vec<tm_alias_pair> tm_alias_pairs) |
| { |
| unsigned i; |
| tm_alias_pair *p; |
| bool switched = false; |
| |
| FOR_EACH_VEC_ELT (tm_alias_pairs, i, p) |
| { |
| tree src = p->from; |
| tree dst = p->to; |
| struct cgraph_node *src_n = cgraph_node::get (src); |
| struct cgraph_node *dst_n = cgraph_node::get (dst); |
| |
| /* The function ipa_tm_create_version() marks the clone as needed if |
| the original function was needed. But we also mark the clone as |
| needed if we ever called the clone indirectly through |
| TM_GETTMCLONE. If neither of these are true, we didn't generate |
| a clone, and we didn't call it indirectly... no sense keeping it |
| in the clone table. */ |
| if (!dst_n || !dst_n->definition) |
| continue; |
| |
| /* This covers the case where we have optimized the original |
| function away, and only access the transactional clone. */ |
| if (!src_n || !src_n->definition) |
| continue; |
| |
| if (!switched) |
| { |
| switch_to_section (targetm.asm_out.tm_clone_table_section ()); |
| assemble_align (POINTER_SIZE); |
| switched = true; |
| } |
| |
| assemble_integer (XEXP (DECL_RTL (src), 0), |
| POINTER_SIZE_UNITS, POINTER_SIZE, 1); |
| assemble_integer (XEXP (DECL_RTL (dst), 0), |
| POINTER_SIZE_UNITS, POINTER_SIZE, 1); |
| } |
| } |
| |
| /* Provide a default for the tm_clone_table section. */ |
| |
| section * |
| default_clone_table_section (void) |
| { |
| return get_named_section (NULL, ".tm_clone_table", 3); |
| } |
| |
| /* Helper comparison function for qsorting by the DECL_UID stored in |
| alias_pair->emitted_diags. */ |
| |
| static int |
| tm_alias_pair_cmp (const void *x, const void *y) |
| { |
| const tm_alias_pair *p1 = (const tm_alias_pair *) x; |
| const tm_alias_pair *p2 = (const tm_alias_pair *) y; |
| if (p1->uid < p2->uid) |
| return -1; |
| if (p1->uid > p2->uid) |
| return 1; |
| return 0; |
| } |
| |
| void |
| finish_tm_clone_pairs (void) |
| { |
| vec<tm_alias_pair> tm_alias_pairs = vNULL; |
| |
| if (tm_clone_hash == NULL) |
| return; |
| |
| /* We need a determenistic order for the .tm_clone_table, otherwise |
| we will get bootstrap comparison failures, so dump the hash table |
| to a vector, sort it, and dump the vector. */ |
| |
| /* Dump the hashtable to a vector. */ |
| tree_map *map; |
| hash_table<tm_clone_hasher>::iterator iter; |
| FOR_EACH_HASH_TABLE_ELEMENT (*tm_clone_hash, map, tree_map *, iter) |
| { |
| tm_alias_pair p = {DECL_UID (map->base.from), map->base.from, map->to}; |
| tm_alias_pairs.safe_push (p); |
| } |
| /* Sort it. */ |
| tm_alias_pairs.qsort (tm_alias_pair_cmp); |
| |
| /* Dump it. */ |
| dump_tm_clone_pairs (tm_alias_pairs); |
| |
| tm_clone_hash->empty (); |
| tm_clone_hash = NULL; |
| tm_alias_pairs.release (); |
| } |
| |
| |
| /* Emit an assembler directive to set symbol for DECL visibility to |
| the visibility type VIS, which must not be VISIBILITY_DEFAULT. */ |
| |
| void |
| default_assemble_visibility (tree decl ATTRIBUTE_UNUSED, |
| int vis ATTRIBUTE_UNUSED) |
| { |
| #ifdef HAVE_GAS_HIDDEN |
| static const char * const visibility_types[] = { |
| NULL, "protected", "hidden", "internal" |
| }; |
| |
| const char *name, *type; |
| tree id; |
| |
| id = DECL_ASSEMBLER_NAME (decl); |
| ultimate_transparent_alias_target (&id); |
| name = IDENTIFIER_POINTER (id); |
| |
| type = visibility_types[vis]; |
| |
| fprintf (asm_out_file, "\t.%s\t", type); |
| assemble_name (asm_out_file, name); |
| fprintf (asm_out_file, "\n"); |
| #else |
| if (!DECL_ARTIFICIAL (decl)) |
| warning (OPT_Wattributes, "visibility attribute not supported " |
| "in this configuration; ignored"); |
| #endif |
| } |
| |
| /* A helper function to call assemble_visibility when needed for a decl. */ |
| |
| bool |
| maybe_assemble_visibility (tree decl) |
| { |
| enum symbol_visibility vis = DECL_VISIBILITY (decl); |
| if (vis != VISIBILITY_DEFAULT) |
| { |
| targetm.asm_out.assemble_visibility (decl, vis); |
| return true; |
| } |
| else |
| return false; |
| } |
| |
| /* Returns true if the target configuration supports defining public symbols |
| so that one of them will be chosen at link time instead of generating a |
| multiply-defined symbol error, whether through the use of weak symbols or |
| a target-specific mechanism for having duplicates discarded. */ |
| |
| bool |
| supports_one_only (void) |
| { |
| if (SUPPORTS_ONE_ONLY) |
| return true; |
| if (TARGET_SUPPORTS_WEAK) |
| return true; |
| return false; |
| } |
| |
| /* Set up DECL as a public symbol that can be defined in multiple |
| translation units without generating a linker error. */ |
| |
| void |
| make_decl_one_only (tree decl, tree comdat_group) |
| { |
| struct symtab_node *symbol; |
| gcc_assert (VAR_OR_FUNCTION_DECL_P (decl)); |
| |
| TREE_PUBLIC (decl) = 1; |
| |
| if (VAR_P (decl)) |
| symbol = varpool_node::get_create (decl); |
| else |
| symbol = cgraph_node::get_create (decl); |
| |
| if (SUPPORTS_ONE_ONLY) |
| { |
| #ifdef MAKE_DECL_ONE_ONLY |
| MAKE_DECL_ONE_ONLY (decl); |
| #endif |
| symbol->set_comdat_group (comdat_group); |
| } |
| else if (VAR_P (decl) |
| && (DECL_INITIAL (decl) == 0 |
| || (!in_lto_p && DECL_INITIAL (decl) == error_mark_node))) |
| DECL_COMMON (decl) = 1; |
| else |
| { |
| gcc_assert (TARGET_SUPPORTS_WEAK); |
| DECL_WEAK (decl) = 1; |
| } |
| } |
| |
| void |
| init_varasm_once (void) |
| { |
| section_htab = hash_table<section_hasher>::create_ggc (31); |
| object_block_htab = hash_table<object_block_hasher>::create_ggc (31); |
| const_desc_htab = hash_table<tree_descriptor_hasher>::create_ggc (1009); |
| |
| shared_constant_pool = create_constant_pool (); |
| |
| #ifdef TEXT_SECTION_ASM_OP |
| text_section = get_unnamed_section (SECTION_CODE, output_section_asm_op, |
| TEXT_SECTION_ASM_OP); |
| #endif |
| |
| #ifdef DATA_SECTION_ASM_OP |
| data_section = get_unnamed_section (SECTION_WRITE, output_section_asm_op, |
| DATA_SECTION_ASM_OP); |
| #endif |
| |
| #ifdef SDATA_SECTION_ASM_OP |
| sdata_section = get_unnamed_section (SECTION_WRITE, output_section_asm_op, |
| SDATA_SECTION_ASM_OP); |
| #endif |
| |
| #ifdef READONLY_DATA_SECTION_ASM_OP |
| readonly_data_section = get_unnamed_section (0, output_section_asm_op, |
| READONLY_DATA_SECTION_ASM_OP); |
| #endif |
| |
| #ifdef CTORS_SECTION_ASM_OP |
| ctors_section = get_unnamed_section (0, output_section_asm_op, |
| CTORS_SECTION_ASM_OP); |
| #endif |
| |
| #ifdef DTORS_SECTION_ASM_OP |
| dtors_section = get_unnamed_section (0, output_section_asm_op, |
| DTORS_SECTION_ASM_OP); |
| #endif |
| |
| #ifdef BSS_SECTION_ASM_OP |
| bss_section = get_unnamed_section (SECTION_WRITE | SECTION_BSS, |
| output_section_asm_op, |
| BSS_SECTION_ASM_OP); |
| #endif |
| |
| #ifdef SBSS_SECTION_ASM_OP |
| sbss_section = get_unnamed_section (SECTION_WRITE | SECTION_BSS, |
| output_section_asm_op, |
| SBSS_SECTION_ASM_OP); |
| #endif |
| |
| tls_comm_section = get_noswitch_section (SECTION_WRITE | SECTION_BSS |
| | SECTION_COMMON, emit_tls_common); |
| lcomm_section = get_noswitch_section (SECTION_WRITE | SECTION_BSS |
| | SECTION_COMMON, emit_local); |
| comm_section = get_noswitch_section (SECTION_WRITE | SECTION_BSS |
| | SECTION_COMMON, emit_common); |
| |
| #if defined ASM_OUTPUT_ALIGNED_BSS |
| bss_noswitch_section = get_noswitch_section (SECTION_WRITE | SECTION_BSS, |
| emit_bss); |
| #endif |
| |
| targetm.asm_out.init_sections (); |
| |
| if (readonly_data_section == NULL) |
| readonly_data_section = text_section; |
| |
| #ifdef ASM_OUTPUT_EXTERNAL |
| pending_assemble_externals_set = new hash_set<tree>; |
| #endif |
| } |
| |
| /* Determine whether SYMBOL is used in any optimized function. */ |
| |
| static bool |
| have_optimized_refs (struct symtab_node *symbol) |
| { |
| struct ipa_ref *ref; |
| |
| for (int i = 0; symbol->iterate_referring (i, ref); i++) |
| { |
| cgraph_node *cnode = dyn_cast <cgraph_node *> (ref->referring); |
| |
| if (cnode && opt_for_fn (cnode->decl, optimize)) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /* Check if promoting general-dynamic TLS access model to local-dynamic is |
| desirable for DECL. */ |
| |
| static bool |
| optimize_dyn_tls_for_decl_p (const_tree decl) |
| { |
| if (cfun) |
| return optimize; |
| return symtab->state >= IPA && have_optimized_refs (symtab_node::get (decl)); |
| } |
| |
| |
| enum tls_model |
| decl_default_tls_model (const_tree decl) |
| { |
| enum tls_model kind; |
| bool is_local; |
| |
| is_local = targetm.binds_local_p (decl); |
| if (!flag_shlib) |
| { |
| if (is_local) |
| kind = TLS_MODEL_LOCAL_EXEC; |
| else |
| kind = TLS_MODEL_INITIAL_EXEC; |
| } |
| |
| /* Local dynamic is inefficient when we're not combining the |
| parts of the address. */ |
| else if (is_local && optimize_dyn_tls_for_decl_p (decl)) |
| kind = TLS_MODEL_LOCAL_DYNAMIC; |
| else |
| kind = TLS_MODEL_GLOBAL_DYNAMIC; |
| if (kind < flag_tls_default) |
| kind = flag_tls_default; |
| |
| return kind; |
| } |
| |
| /* Select a set of attributes for section NAME based on the properties |
| of DECL and whether or not RELOC indicates that DECL's initializer |
| might contain runtime relocations. |
| |
| We make the section read-only and executable for a function decl, |
| read-only for a const data decl, and writable for a non-const data decl. */ |
| |
| unsigned int |
| default_section_type_flags (tree decl, const char *name, int reloc) |
| { |
| unsigned int flags; |
| |
| if (decl && TREE_CODE (decl) == FUNCTION_DECL) |
| flags = SECTION_CODE; |
| else if (decl) |
| { |
| enum section_category category |
| = categorize_decl_for_section (decl, reloc); |
| if (decl_readonly_section_1 (category)) |
| flags = 0; |
| else if (category == SECCAT_DATA_REL_RO |
| || category == SECCAT_DATA_REL_RO_LOCAL) |
| flags = SECTION_WRITE | SECTION_RELRO; |
| else |
| flags = SECTION_WRITE; |
| } |
| else |
| { |
| flags = SECTION_WRITE; |
| if (strcmp (name, ".data.rel.ro") == 0 |
| || strcmp (name, ".data.rel.ro.local") == 0) |
| flags |= SECTION_RELRO; |
| } |
| |
| if (decl && DECL_P (decl) && DECL_COMDAT_GROUP (decl)) |
| flags |= SECTION_LINKONCE; |
| |
| if (strcmp (name, ".vtable_map_vars") == 0) |
| flags |= SECTION_LINKONCE; |
| |
| if (decl && VAR_P (decl) && DECL_THREAD_LOCAL_P (decl)) |
| flags |= SECTION_TLS | SECTION_WRITE; |
| |
| if (strcmp (name, ".bss") == 0 |
| || startswith (name, ".bss.") |
| || startswith (name, ".gnu.linkonce.b.") |
| || strcmp (name, ".persistent.bss") == 0 |
| || strcmp (name, ".sbss") == 0 |
| || startswith (name, ".sbss.") |
| || startswith (name, ".gnu.linkonce.sb.")) |
| flags |= SECTION_BSS; |
| |
| if (strcmp (name, ".tdata") == 0 |
| || startswith (name, ".tdata.") |
| || startswith (name, ".gnu.linkonce.td.")) |
| flags |= SECTION_TLS; |
| |
| if (strcmp (name, ".tbss") == 0 |
| || startswith (name, ".tbss.") |
| || startswith (name, ".gnu.linkonce.tb.")) |
| flags |= SECTION_TLS | SECTION_BSS; |
| |
| if (strcmp (name, ".noinit") == 0) |
| flags |= SECTION_WRITE | SECTION_BSS | SECTION_NOTYPE; |
| |
| if (strcmp (name, ".persistent") == 0) |
| flags |= SECTION_WRITE | SECTION_NOTYPE; |
| |
| /* Various sections have special ELF types that the assembler will |
| assign by default based on the name. They are neither SHT_PROGBITS |
| nor SHT_NOBITS, so when changing sections we don't want to print a |
| section type (@progbits or @nobits). Rather than duplicating the |
| assembler's knowledge of what those special name patterns are, just |
| let the assembler choose the type if we don't know a specific |
| reason to set it to something other than the default. SHT_PROGBITS |
| is the default for sections whose name is not specially known to |
| the assembler, so it does no harm to leave the choice to the |
| assembler when @progbits is the best thing we know to use. If |
| someone is silly enough to emit code or TLS variables to one of |
| these sections, then don't handle them specially. |
| |
| default_elf_asm_named_section (below) handles the BSS, TLS, ENTSIZE, and |
| LINKONCE cases when NOTYPE is not set, so leave those to its logic. */ |
| if (!(flags & (SECTION_CODE | SECTION_BSS | SECTION_TLS | SECTION_ENTSIZE)) |
| && !(HAVE_COMDAT_GROUP && (flags & SECTION_LINKONCE))) |
| flags |= SECTION_NOTYPE; |
| |
| return flags; |
| } |
| |
| /* Return true if the target supports some form of global BSS, |
| either through bss_noswitch_section, or by selecting a BSS |
| section in TARGET_ASM_SELECT_SECTION. */ |
| |
| bool |
| have_global_bss_p (void) |
| { |
| return bss_noswitch_section || targetm.have_switchable_bss_sections; |
| } |
| |
| /* Output assembly to switch to section NAME with attribute FLAGS. |
| Four variants for common object file formats. */ |
| |
| void |
| default_no_named_section (const char *name ATTRIBUTE_UNUSED, |
| unsigned int flags ATTRIBUTE_UNUSED, |
| tree decl ATTRIBUTE_UNUSED) |
| { |
| /* Some object formats don't support named sections at all. The |
| front-end should already have flagged this as an error. */ |
| gcc_unreachable (); |
| } |
| |
| #ifndef TLS_SECTION_ASM_FLAG |
| #define TLS_SECTION_ASM_FLAG 'T' |
| #endif |
| |
| void |
| default_elf_asm_named_section (const char *name, unsigned int flags, |
| tree decl) |
| { |
| char flagchars[11], *f = flagchars; |
| unsigned int numeric_value = 0; |
| |
| /* If we have already declared this section, we can use an |
| abbreviated form to switch back to it -- unless this section is |
| part of a COMDAT groups or with SHF_GNU_RETAIN or with SHF_LINK_ORDER, |
| in which case GAS requires the full declaration every time. */ |
| if (!(HAVE_COMDAT_GROUP && (flags & SECTION_LINKONCE)) |
| && !(flags & (SECTION_RETAIN | SECTION_LINK_ORDER)) |
| && (flags & SECTION_DECLARED)) |
| { |
| fprintf (asm_out_file, "\t.section\t%s\n", name); |
| return; |
| } |
| |
| /* If we have a machine specific flag, then use the numeric value to pass |
| this on to GAS. */ |
| if (targetm.asm_out.elf_flags_numeric (flags, &numeric_value)) |
| snprintf (f, sizeof (flagchars), "0x%08x", numeric_value); |
| else |
| { |
| if (!(flags & SECTION_DEBUG)) |
| *f++ = 'a'; |
| #if HAVE_GAS_SECTION_EXCLUDE |
| if (flags & SECTION_EXCLUDE) |
| *f++ = 'e'; |
| #endif |
| if (flags & SECTION_WRITE) |
| *f++ = 'w'; |
| if (flags & SECTION_CODE) |
| *f++ = 'x'; |
| if (flags & SECTION_SMALL) |
| *f++ = 's'; |
| if (flags & SECTION_MERGE) |
| *f++ = 'M'; |
| if (flags & SECTION_STRINGS) |
| *f++ = 'S'; |
| if (flags & SECTION_TLS) |
| *f++ = TLS_SECTION_ASM_FLAG; |
| if (HAVE_COMDAT_GROUP && (flags & SECTION_LINKONCE)) |
| *f++ = 'G'; |
| if (flags & SECTION_RETAIN) |
| *f++ = 'R'; |
| if (flags & SECTION_LINK_ORDER) |
| *f++ = 'o'; |
| #ifdef MACH_DEP_SECTION_ASM_FLAG |
| if (flags & SECTION_MACH_DEP) |
| *f++ = MACH_DEP_SECTION_ASM_FLAG; |
| #endif |
| *f = '\0'; |
| } |
| |
| fprintf (asm_out_file, "\t.section\t%s,\"%s\"", name, flagchars); |
| |
| /* default_section_type_flags (above) knows which flags need special |
| handling here, and sets NOTYPE when none of these apply so that the |
| assembler's logic for default types can apply to user-chosen |
| section names. */ |
| if (!(flags & SECTION_NOTYPE)) |
| { |
| const char *type; |
| const char *format; |
| |
| if (flags & SECTION_BSS) |
| type = "nobits"; |
| else |
| type = "progbits"; |
| |
| format = ",@%s"; |
| /* On platforms that use "@" as the assembly comment character, |
| use "%" instead. */ |
| if (strcmp (ASM_COMMENT_START, "@") == 0) |
| format = ",%%%s"; |
| fprintf (asm_out_file, format, type); |
| |
| if (flags & SECTION_ENTSIZE) |
| fprintf (asm_out_file, ",%d", flags & SECTION_ENTSIZE); |
| if (flags & SECTION_LINK_ORDER) |
| { |
| /* For now, only section "__patchable_function_entries" |
| adopts flag SECTION_LINK_ORDER, internal label LPFE* |
| was emitted in default_print_patchable_function_entry, |
| just place it here for linked_to section. */ |
| gcc_assert (!strcmp (name, "__patchable_function_entries")); |
| fprintf (asm_out_file, ","); |
| char buf[256]; |
| ASM_GENERATE_INTERNAL_LABEL (buf, "LPFE", |
| current_function_funcdef_no); |
| assemble_name_raw (asm_out_file, buf); |
| } |
| if (HAVE_COMDAT_GROUP && (flags & SECTION_LINKONCE)) |
| { |
| if (TREE_CODE (decl) == IDENTIFIER_NODE) |
| fprintf (asm_out_file, ",%s,comdat", IDENTIFIER_POINTER (decl)); |
| else |
| fprintf (asm_out_file, ",%s,comdat", |
| IDENTIFIER_POINTER (DECL_COMDAT_GROUP (decl))); |
| } |
| } |
| |
| putc ('\n', asm_out_file); |
| } |
| |
| void |
| default_coff_asm_named_section (const char *name, unsigned int flags, |
| tree decl ATTRIBUTE_UNUSED) |
| { |
| char flagchars[8], *f = flagchars; |
| |
| if (flags & SECTION_WRITE) |
| *f++ = 'w'; |
| if (flags & SECTION_CODE) |
| *f++ = 'x'; |
| *f = '\0'; |
| |
| fprintf (asm_out_file, "\t.section\t%s,\"%s\"\n", name, flagchars); |
| } |
| |
| void |
| default_pe_asm_named_section (const char *name, unsigned int flags, |
| tree decl) |
| { |
| default_coff_asm_named_section (name, flags, decl); |
| |
| if (flags & SECTION_LINKONCE) |
| { |
| /* Functions may have been compiled at various levels of |
| optimization so we can't use `same_size' here. |
| Instead, have the linker pick one. */ |
| fprintf (asm_out_file, "\t.linkonce %s\n", |
| (flags & SECTION_CODE ? "discard" : "same_size")); |
| } |
| } |
| |
| /* The lame default section selector. */ |
| |
| section * |
| default_select_section (tree decl, int reloc, |
| unsigned HOST_WIDE_INT align ATTRIBUTE_UNUSED) |
| { |
| if (DECL_P (decl)) |
| { |
| if (decl_readonly_section (decl, reloc)) |
| return readonly_data_section; |
| } |
| else if (TREE_CODE (decl) == CONSTRUCTOR) |
| { |
| if (! ((flag_pic && reloc) |
| || !TREE_READONLY (decl) |
| || !TREE_CONSTANT (decl))) |
| return readonly_data_section; |
| } |
| else if (TREE_CODE (decl) == STRING_CST) |
| return readonly_data_section; |
| else if (! (flag_pic && reloc)) |
| return readonly_data_section; |
| |
| return data_section; |
| } |
| |
| enum section_category |
| categorize_decl_for_section (const_tree decl, int reloc) |
| { |
| enum section_category ret; |
| |
| if (TREE_CODE (decl) == FUNCTION_DECL) |
| return SECCAT_TEXT; |
| else if (TREE_CODE (decl) == STRING_CST) |
| { |
| if ((flag_sanitize & SANITIZE_ADDRESS) |
| && asan_protect_global (CONST_CAST_TREE (decl))) |
| /* or !flag_merge_constants */ |
| return SECCAT_RODATA; |
| else |
| return SECCAT_RODATA_MERGE_STR; |
| } |
| else if (VAR_P (decl)) |
| { |
| tree d = CONST_CAST_TREE (decl); |
| if (bss_initializer_p (decl)) |
| ret = SECCAT_BSS; |
| else if (! TREE_READONLY (decl) |
| || (DECL_INITIAL (decl) |
| && ! TREE_CONSTANT (DECL_INITIAL (decl)))) |
| { |
| /* Here the reloc_rw_mask is not testing whether the section should |
| be read-only or not, but whether the dynamic link will have to |
| do something. If so, we wish to segregate the data in order to |
| minimize cache misses inside the dynamic linker. */ |
| if (reloc & targetm.asm_out.reloc_rw_mask ()) |
| ret = reloc == 1 ? SECCAT_DATA_REL_LOCAL : SECCAT_DATA_REL; |
| else |
| ret = SECCAT_DATA; |
| } |
| else if (reloc & targetm.asm_out.reloc_rw_mask ()) |
| ret = reloc == 1 ? SECCAT_DATA_REL_RO_LOCAL : SECCAT_DATA_REL_RO; |
| else if (reloc || (flag_merge_constants < 2 && !DECL_MERGEABLE (decl)) |
| || ((flag_sanitize & SANITIZE_ADDRESS) |
| /* PR 81697: for architectures that use section anchors we |
| need to ignore DECL_RTL_SET_P (decl) for string constants |
| inside this asan_protect_global call because otherwise |
| we'll wrongly put them into SECCAT_RODATA_MERGE_CONST |
| section, set DECL_RTL (decl) later on and add DECL to |
| protected globals via successive asan_protect_global |
| calls. In this scenario we'll end up with wrong |
| alignment of these strings at runtime and possible ASan |
| false positives. */ |
| && asan_protect_global (d, use_object_blocks_p () |
| && use_blocks_for_decl_p (d)))) |
| /* C and C++ don't allow different variables to share the same |
| location. -fmerge-all-constants allows even that (at the |
| expense of not conforming). */ |
| ret = SECCAT_RODATA; |
| else if (DECL_INITIAL (decl) |
| && TREE_CODE (DECL_INITIAL (decl)) == STRING_CST) |
| ret = SECCAT_RODATA_MERGE_STR_INIT; |
| else |
| ret = SECCAT_RODATA_MERGE_CONST; |
| } |
| else if (TREE_CODE (decl) == CONSTRUCTOR) |
| { |
| if ((reloc & targetm.asm_out.reloc_rw_mask ()) |
| || ! TREE_CONSTANT (decl)) |
| ret = SECCAT_DATA; |
| else |
| ret = SECCAT_RODATA; |
| } |
| else |
| ret = SECCAT_RODATA; |
| |
| /* There are no read-only thread-local sections. */ |
| if (VAR_P (decl) && DECL_THREAD_LOCAL_P (decl)) |
| { |
| /* Note that this would be *just* SECCAT_BSS, except that there's |
| no concept of a read-only thread-local-data section. */ |
| if (ret == SECCAT_BSS |
| || DECL_INITIAL (decl) == NULL |
| || (flag_zero_initialized_in_bss |
| && initializer_zerop (DECL_INITIAL (decl)))) |
| ret = SECCAT_TBSS; |
| else |
| ret = SECCAT_TDATA; |
| } |
| |
| /* If the target uses small data sections, select it. */ |
| else if (targetm.in_small_data_p (decl)) |
| { |
| if (ret == SECCAT_BSS) |
| ret = SECCAT_SBSS; |
| else if (targetm.have_srodata_section && ret == SECCAT_RODATA) |
| ret = SECCAT_SRODATA; |
| else |
| ret = SECCAT_SDATA; |
| } |
| |
| return ret; |
| } |
| |
| static bool |
| decl_readonly_section_1 (enum section_category category) |
| { |
| switch (category) |
| { |
| case SECCAT_RODATA: |
| case SECCAT_RODATA_MERGE_STR: |
| case SECCAT_RODATA_MERGE_STR_INIT: |
| case SECCAT_RODATA_MERGE_CONST: |
| case SECCAT_SRODATA: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| bool |
| decl_readonly_section (const_tree decl, int reloc) |
| { |
| return decl_readonly_section_1 (categorize_decl_for_section (decl, reloc)); |
| } |
| |
| /* Select a section based on the above categorization. */ |
| |
| section * |
| default_elf_select_section (tree decl, int reloc, |
| unsigned HOST_WIDE_INT align) |
| { |
| const char *sname; |
| |
| switch (categorize_decl_for_section (decl, reloc)) |
| { |
| case SECCAT_TEXT: |
| /* We're not supposed to be called on FUNCTION_DECLs. */ |
| gcc_unreachable (); |
| case SECCAT_RODATA: |
| return readonly_data_section; |
| case SECCAT_RODATA_MERGE_STR: |
| return mergeable_string_section (decl, align, 0); |
| case SECCAT_RODATA_MERGE_STR_INIT: |
| return mergeable_string_section (DECL_INITIAL (decl), align, 0); |
| case SECCAT_RODATA_MERGE_CONST: |
| return mergeable_constant_section (DECL_MODE (decl), align, 0); |
| case SECCAT_SRODATA: |
| sname = ".sdata2"; |
| break; |
| case SECCAT_DATA: |
| if (DECL_P (decl) && DECL_PERSISTENT_P (decl)) |
| { |
| sname = ".persistent"; |
| break; |
| } |
| return data_section; |
| case SECCAT_DATA_REL: |
| sname = ".data.rel"; |
| break; |
| case SECCAT_DATA_REL_LOCAL: |
| sname = ".data.rel.local"; |
| break; |
| case SECCAT_DATA_REL_RO: |
| sname = ".data.rel.ro"; |
| break; |
| case SECCAT_DATA_REL_RO_LOCAL: |
| sname = ".data.rel.ro.local"; |
| break; |
| case SECCAT_SDATA: |
| sname = ".sdata"; |
| break; |
| case SECCAT_TDATA: |
| sname = ".tdata"; |
| break; |
| case SECCAT_BSS: |
| if (DECL_P (decl) && DECL_NOINIT_P (decl)) |
| { |
| sname = ".noinit"; |
| break; |
| } |
| if (bss_section) |
| return bss_section; |
| sname = ".bss"; |
| break; |
| case SECCAT_SBSS: |
| sname = ".sbss"; |
| break; |
| case SECCAT_TBSS: |
| sname = ".tbss"; |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| |
| return get_named_section (decl, sname, reloc); |
| } |
| |
| /* Construct a unique section name based on the decl name and the |
| categorization performed above. */ |
| |
| void |
| default_unique_section (tree decl, int reloc) |
| { |
| /* We only need to use .gnu.linkonce if we don't have COMDAT groups. */ |
| bool one_only = DECL_ONE_ONLY (decl) && !HAVE_COMDAT_GROUP; |
| const char *prefix, *name, *linkonce; |
| char *string; |
| tree id; |
| |
| switch (categorize_decl_for_section (decl, reloc)) |
| { |
| case SECCAT_TEXT: |
| prefix = one_only ? ".t" : ".text"; |
| break; |
| case SECCAT_RODATA: |
| case SECCAT_RODATA_MERGE_STR: |
| case SECCAT_RODATA_MERGE_STR_INIT: |
| case SECCAT_RODATA_MERGE_CONST: |
| prefix = one_only ? ".r" : ".rodata"; |
| break; |
| case SECCAT_SRODATA: |
| prefix = one_only ? ".s2" : ".sdata2"; |
| break; |
| case SECCAT_DATA: |
| prefix = one_only ? ".d" : ".data"; |
| if (DECL_P (decl) && DECL_PERSISTENT_P (decl)) |
| { |
| prefix = one_only ? ".p" : ".persistent"; |
| break; |
| } |
| break; |
| case SECCAT_DATA_REL: |
| prefix = one_only ? ".d.rel" : ".data.rel"; |
| break; |
| case SECCAT_DATA_REL_LOCAL: |
| prefix = one_only ? ".d.rel.local" : ".data.rel.local"; |
| break; |
| case SECCAT_DATA_REL_RO: |
| prefix = one_only ? ".d.rel.ro" : ".data.rel.ro"; |
| break; |
| case SECCAT_DATA_REL_RO_LOCAL: |
| prefix = one_only ? ".d.rel.ro.local" : ".data.rel.ro.local"; |
| break; |
| case SECCAT_SDATA: |
| prefix = one_only ? ".s" : ".sdata"; |
| break; |
| case SECCAT_BSS: |
| if (DECL_P (decl) && DECL_NOINIT_P (decl)) |
| { |
| prefix = one_only ? ".n" : ".noinit"; |
| break; |
| } |
| prefix = one_only ? ".b" : ".bss"; |
| break; |
| case SECCAT_SBSS: |
| prefix = one_only ? ".sb" : ".sbss"; |
| break; |
| case SECCAT_TDATA: |
| prefix = one_only ? ".td" : ".tdata"; |
| break; |
| case SECCAT_TBSS: |
| prefix = one_only ? ".tb" : ".tbss"; |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| |
| id = DECL_ASSEMBLER_NAME (decl); |
| ultimate_transparent_alias_target (&id); |
| name = IDENTIFIER_POINTER (id); |
| name = targetm.strip_name_encoding (name); |
| |
| /* If we're using one_only, then there needs to be a .gnu.linkonce |
| prefix to the section name. */ |
| linkonce = one_only ? ".gnu.linkonce" : ""; |
| |
| string = ACONCAT ((linkonce, prefix, ".", name, NULL)); |
| |
| set_decl_section_name (decl, string); |
| } |
| |
| /* Subroutine of compute_reloc_for_rtx for leaf rtxes. */ |
| |
| static int |
| compute_reloc_for_rtx_1 (const_rtx x) |
| { |
| switch (GET_CODE (x)) |
| { |
| case SYMBOL_REF: |
| return SYMBOL_REF_LOCAL_P (x) ? 1 : 2; |
| case LABEL_REF: |
| return 1; |
| default: |
| return 0; |
| } |
| } |
| |
| /* Like compute_reloc_for_constant, except for an RTX. The return value |
| is a mask for which bit 1 indicates a global relocation, and bit 0 |
| indicates a local relocation. Used by default_select_rtx_section |
| and default_elf_select_rtx_section. */ |
| |
| static int |
| compute_reloc_for_rtx (const_rtx x) |
| { |
| switch (GET_CODE (x)) |
| { |
| case SYMBOL_REF: |
| case LABEL_REF: |
| return compute_reloc_for_rtx_1 (x); |
| |
| case CONST: |
| { |
| int reloc = 0; |
| subrtx_iterator::array_type array; |
| FOR_EACH_SUBRTX (iter, array, x, ALL) |
| reloc |= compute_reloc_for_rtx_1 (*iter); |
| return reloc; |
| } |
| |
| default: |
| return 0; |
| } |
| } |
| |
| section * |
| default_select_rtx_section (machine_mode mode ATTRIBUTE_UNUSED, |
| rtx x, |
| unsigned HOST_WIDE_INT align ATTRIBUTE_UNUSED) |
| { |
| if (compute_reloc_for_rtx (x) & targetm.asm_out.reloc_rw_mask ()) |
| return data_section; |
| else |
| return readonly_data_section; |
| } |
| |
| section * |
| default_elf_select_rtx_section (machine_mode mode, rtx x, |
| unsigned HOST_WIDE_INT align) |
| { |
| int reloc = compute_reloc_for_rtx (x); |
| tree decl = nullptr; |
| const char *prefix = nullptr; |
| int flags = 0; |
| |
| /* If it is a private COMDAT function symbol reference, call |
| function_rodata_section for the read-only or relocated read-only |
| data section associated with function DECL so that the COMDAT |
| section will be used for the private COMDAT function symbol. */ |
| if (HAVE_COMDAT_GROUP) |
| { |
| if (GET_CODE (x) == CONST |
| && GET_CODE (XEXP (x, 0)) == PLUS |
| && CONST_INT_P (XEXP (XEXP (x, 0), 1))) |
| x = XEXP (XEXP (x, 0), 0); |
| |
| if (GET_CODE (x) == SYMBOL_REF) |
| { |
| decl = SYMBOL_REF_DECL (x); |
| if (decl |
| && (TREE_CODE (decl) != FUNCTION_DECL |
| || !DECL_COMDAT_GROUP (decl) |
| || TREE_PUBLIC (decl))) |
| decl = nullptr; |
| } |
| } |
| |
| /* ??? Handle small data here somehow. */ |
| |
| if (reloc & targetm.asm_out.reloc_rw_mask ()) |
| { |
| if (decl) |
| { |
| prefix = reloc == 1 ? ".data.rel.ro.local" : ".data.rel.ro"; |
| flags = SECTION_WRITE | SECTION_RELRO; |
| } |
| else if (reloc == 1) |
| return get_named_section (NULL, ".data.rel.ro.local", 1); |
| else |
| return get_named_section (NULL, ".data.rel.ro", 3); |
| } |
| |
| if (decl) |
| { |
| const char *comdat = IDENTIFIER_POINTER (DECL_COMDAT_GROUP (decl)); |
| if (!prefix) |
| prefix = ".rodata"; |
| size_t prefix_len = strlen (prefix); |
| size_t comdat_len = strlen (comdat); |
| size_t len = prefix_len + sizeof (".pool.") + comdat_len; |
| char *name = XALLOCAVEC (char, len); |
| memcpy (name, prefix, prefix_len); |
| memcpy (name + prefix_len, ".pool.", sizeof (".pool.") - 1); |
| memcpy (name + prefix_len + sizeof (".pool.") - 1, comdat, |
| comdat_len + 1); |
| return get_section (name, flags | SECTION_LINKONCE, decl); |
| } |
| |
| return mergeable_constant_section (mode, align, 0); |
| } |
| |
| /* Set the generally applicable flags on the SYMBOL_REF for EXP. */ |
| |
| void |
| default_encode_section_info (tree decl, rtx rtl, int first ATTRIBUTE_UNUSED) |
| { |
| rtx symbol; |
| int flags; |
| |
| /* Careful not to prod global register variables. */ |
| if (!MEM_P (rtl)) |
| return; |
| symbol = XEXP (rtl, 0); |
| if (GET_CODE (symbol) != SYMBOL_REF) |
| return; |
| |
| flags = SYMBOL_REF_FLAGS (symbol) & SYMBOL_FLAG_HAS_BLOCK_INFO; |
| if (TREE_CODE (decl) == FUNCTION_DECL) |
| flags |= SYMBOL_FLAG_FUNCTION; |
| if (targetm.binds_local_p (decl)) |
| flags |= SYMBOL_FLAG_LOCAL; |
| if (VAR_P (decl) && DECL_THREAD_LOCAL_P (decl)) |
| flags |= DECL_TLS_MODEL (decl) << SYMBOL_FLAG_TLS_SHIFT; |
| else if (targetm.in_small_data_p (decl)) |
| flags |= SYMBOL_FLAG_SMALL; |
| /* ??? Why is DECL_EXTERNAL ever set for non-PUBLIC names? Without |
| being PUBLIC, the thing *must* be defined in this translation unit. |
| Prevent this buglet from being propagated into rtl code as well. */ |
| if (DECL_P (decl) && DECL_EXTERNAL (decl) && TREE_PUBLIC (decl)) |
| flags |= SYMBOL_FLAG_EXTERNAL; |
| |
| SYMBOL_REF_FLAGS (symbol) = flags; |
| } |
| |
| /* By default, we do nothing for encode_section_info, so we need not |
| do anything but discard the '*' marker. */ |
| |
| const char * |
| default_strip_name_encoding (const char *str) |
| { |
| return str + (*str == '*'); |
| } |
| |
| #ifdef ASM_OUTPUT_DEF |
| /* The default implementation of TARGET_ASM_OUTPUT_ANCHOR. Define the |
| anchor relative to ".", the current section position. */ |
| |
| void |
| default_asm_output_anchor (rtx symbol) |
| { |
| gcc_checking_assert (TARGET_SUPPORTS_ALIASES); |
| |
| char buffer[100]; |
| |
| sprintf (buffer, "*. + " HOST_WIDE_INT_PRINT_DEC, |
| SYMBOL_REF_BLOCK_OFFSET (symbol)); |
| ASM_OUTPUT_DEF (asm_out_file, XSTR (symbol, 0), buffer); |
| } |
| #endif |
| |
| /* The default implementation of TARGET_USE_ANCHORS_FOR_SYMBOL_P. */ |
| |
| bool |
| default_use_anchors_for_symbol_p (const_rtx symbol) |
| { |
| tree decl; |
| section *sect = SYMBOL_REF_BLOCK (symbol)->sect; |
| |
| /* This function should only be called with non-zero SYMBOL_REF_BLOCK, |
| furthermore get_block_for_section should not create object blocks |
| for mergeable sections. */ |
| gcc_checking_assert (sect && !(sect->common.flags & SECTION_MERGE)); |
| |
| /* Don't use anchors for small data sections. The small data register |
| acts as an anchor for such sections. */ |
| if (sect->common.flags & SECTION_SMALL) |
| return false; |
| |
| decl = SYMBOL_REF_DECL (symbol); |
| if (decl && DECL_P (decl)) |
| { |
| /* Don't use section anchors for decls that might be defined or |
| usurped by other modules. */ |
| if (TREE_PUBLIC (decl) && !decl_binds_to_current_def_p (decl)) |
| return false; |
| |
| /* Don't use section anchors for decls that will be placed in a |
| small data section. */ |
| /* ??? Ideally, this check would be redundant with the SECTION_SMALL |
| one above. The problem is that we only use SECTION_SMALL for |
| sections that should be marked as small in the section directive. */ |
| if (targetm.in_small_data_p (decl)) |
| return false; |
| |
| /* Don't use section anchors for decls that won't fit inside a single |
| anchor range to reduce the amount of instructions required to refer |
| to the entire declaration. */ |
| if (DECL_SIZE_UNIT (decl) == NULL_TREE |
| || !tree_fits_uhwi_p (DECL_SIZE_UNIT (decl)) |
| || (tree_to_uhwi (DECL_SIZE_UNIT (decl)) |
| >= (unsigned HOST_WIDE_INT) targetm.max_anchor_offset)) |
| return false; |
| |
| } |
| return true; |
| } |
| |
| /* Return true when RESOLUTION indicate that symbol will be bound to the |
| definition provided by current .o file. */ |
| |
| static bool |
| resolution_to_local_definition_p (enum ld_plugin_symbol_resolution resolution) |
| { |
| return (resolution == LDPR_PREVAILING_DEF |
| || resolution == LDPR_PREVAILING_DEF_IRONLY_EXP |
| || resolution == LDPR_PREVAILING_DEF_IRONLY); |
| } |
| |
| /* Return true when RESOLUTION indicate that symbol will be bound locally |
| within current executable or DSO. */ |
| |
| static bool |
| resolution_local_p (enum ld_plugin_symbol_resolution resolution) |
| { |
| return (resolution == LDPR_PREVAILING_DEF |
| || resolution == LDPR_PREVAILING_DEF_IRONLY |
| || resolution == LDPR_PREVAILING_DEF_IRONLY_EXP |
| || resolution == LDPR_PREEMPTED_REG |
| || resolution == LDPR_PREEMPTED_IR |
| || resolution == LDPR_RESOLVED_IR |
| || resolution == LDPR_RESOLVED_EXEC); |
| } |
| |
| /* COMMON_LOCAL_P is true means that the linker can guarantee that an |
| uninitialized common symbol in the executable will still be defined |
| (through COPY relocation) in the executable. */ |
| |
| bool |
| default_binds_local_p_3 (const_tree exp, bool shlib, bool weak_dominate, |
| bool extern_protected_data, bool common_local_p) |
| { |
| /* A non-decl is an entry in the constant pool. */ |
| if (!DECL_P (exp)) |
| return true; |
| |
| /* Weakrefs may not bind locally, even though the weakref itself is always |
| static and therefore local. Similarly, the resolver for ifunc functions |
| might resolve to a non-local function. |
| FIXME: We can resolve the weakref case more curefuly by looking at the |
| weakref alias. */ |
| if (lookup_attribute ("weakref", DECL_ATTRIBUTES (exp)) |
| || (!targetm.ifunc_ref_local_ok () |
| && TREE_CODE (exp) == FUNCTION_DECL |
| && cgraph_node::get (exp) |
| && cgraph_node::get (exp)->ifunc_resolver)) |
| return false; |
| |
| /* Static variables are always local. */ |
| if (! TREE_PUBLIC (exp)) |
| return true; |
| |
| /* With resolution file in hand, take look into resolutions. |
| We can't just return true for resolved_locally symbols, |
| because dynamic linking might overwrite symbols |
| in shared libraries. */ |
| bool resolved_locally = false; |
| |
| bool uninited_common = (DECL_COMMON (exp) |
| && (DECL_INITIAL (exp) == NULL |
| || (!in_lto_p |
| && DECL_INITIAL (exp) == error_mark_node))); |
| |
| /* A non-external variable is defined locally only if it isn't |
| uninitialized COMMON variable or common_local_p is true. */ |
| bool defined_locally = (!DECL_EXTERNAL (exp) |
| && (!uninited_common || common_local_p)); |
| if (symtab_node *node = symtab_node::get (exp)) |
| { |
| if (node->in_other_partition) |
| defined_locally = true; |
| if (node->can_be_discarded_p ()) |
| ; |
| else if (resolution_to_local_definition_p (node->resolution)) |
| defined_locally = resolved_locally = true; |
| else if (resolution_local_p (node->resolution)) |
| resolved_locally = true; |
| } |
| if (defined_locally && weak_dominate && !shlib) |
| resolved_locally = true; |
| |
| /* Undefined weak symbols are never defined locally. */ |
| if (DECL_WEAK (exp) && !defined_locally) |
| return false; |
| |
| /* A symbol is local if the user has said explicitly that it will be, |
| or if we have a definition for the symbol. We cannot infer visibility |
| for undefined symbols. */ |
| if (DECL_VISIBILITY (exp) != VISIBILITY_DEFAULT |
| && (TREE_CODE (exp) == FUNCTION_DECL |
| || !extern_protected_data |
| || DECL_VISIBILITY (exp) != VISIBILITY_PROTECTED) |
| && (DECL_VISIBILITY_SPECIFIED (exp) || defined_locally)) |
| return true; |
| |
| /* If PIC, then assume that any global name can be overridden by |
| symbols resolved from other modules. */ |
| if (shlib) |
| return false; |
| |
| /* Variables defined outside this object might not be local. */ |
| if (DECL_EXTERNAL (exp) && !resolved_locally) |
| return false; |
| |
| /* Non-dominant weak symbols are not defined locally. */ |
| if (DECL_WEAK (exp) && !resolved_locally) |
| return false; |
| |
| /* Uninitialized COMMON variable may be unified with symbols |
| resolved from other modules. */ |
| if (uninited_common && !resolved_locally) |
| return false; |
| |
| /* Otherwise we're left with initialized (or non-common) global data |
| which is of necessity defined locally. */ |
| return true; |
| } |
| |
| /* Assume ELF-ish defaults, since that's pretty much the most liberal |
| wrt cross-module name binding. */ |
| |
| bool |
| default_binds_local_p (const_tree exp) |
| { |
| return default_binds_local_p_3 (exp, flag_shlib != 0, true, false, false); |
| } |
| |
| /* Similar to default_binds_local_p, but common symbol may be local and |
| extern protected data is non-local. */ |
| |
| bool |
| default_binds_local_p_2 (const_tree exp) |
| { |
| return default_binds_local_p_3 (exp, flag_shlib != 0, true, true, |
| !flag_pic); |
| } |
| |
| bool |
| default_binds_local_p_1 (const_tree exp, int shlib) |
| { |
| return default_binds_local_p_3 (exp, shlib != 0, false, false, false); |
| } |
| |
| /* Return true when references to DECL must bind to current definition in |
| final executable. |
| |
| The condition is usually equivalent to whether the function binds to the |
| current module (shared library or executable), that is to binds_local_p. |
| We use this fact to avoid need for another target hook and implement |
| the logic using binds_local_p and just special cases where |
| decl_binds_to_current_def_p is stronger than binds_local_p. In particular |
| the weak definitions (that can be overwritten at linktime by other |
| definition from different object file) and when resolution info is available |
| we simply use the knowledge passed to us by linker plugin. */ |
| bool |
| decl_binds_to_current_def_p (const_tree decl) |
| { |
| gcc_assert (DECL_P (decl)); |
| if (!targetm.binds_local_p (decl)) |
| return false; |
| if (!TREE_PUBLIC (decl)) |
| return true; |
| |
| /* When resolution is available, just use it. */ |
| if (symtab_node *node = symtab_node::get (decl)) |
| { |
| if (node->resolution != LDPR_UNKNOWN |
| && !node->can_be_discarded_p ()) |
| return resolution_to_local_definition_p (node->resolution); |
| } |
| |
| /* Otherwise we have to assume the worst for DECL_WEAK (hidden weaks |
| binds locally but still can be overwritten), DECL_COMMON (can be merged |
| with a non-common definition somewhere in the same module) or |
| DECL_EXTERNAL. |
| This rely on fact that binds_local_p behave as decl_replaceable_p |
| for all other declaration types. */ |
| if (DECL_WEAK (decl)) |
| return false; |
| if (DECL_COMDAT_GROUP (decl)) |
| return false; |
| if (DECL_COMMON (decl) |
| && (DECL_INITIAL (decl) == NULL |
| || (!in_lto_p && DECL_INITIAL (decl) == error_mark_node))) |
| return false; |
| if (DECL_EXTERNAL (decl)) |
| return false; |
| return true; |
| } |
| |
| /* A replaceable function or variable is one which may be replaced |
| at link-time with an entirely different definition, provided that the |
| replacement has the same type. For example, functions declared |
| with __attribute__((weak)) on most systems are replaceable. |
| If SEMANTIC_INTERPOSITION_P is false allow interposition only on |
| symbols explicitly declared weak. |
| |
| COMDAT functions are not replaceable, since all definitions of the |
| function must be equivalent. It is important that COMDAT functions |
| not be treated as replaceable so that use of C++ template |
| instantiations is not penalized. */ |
| |
| bool |
| decl_replaceable_p (tree decl, bool semantic_interposition_p) |
| { |
| gcc_assert (DECL_P (decl)); |
| if (!TREE_PUBLIC (decl) || DECL_COMDAT (decl)) |
| return false; |
| if (!semantic_interposition_p |
| && !DECL_WEAK (decl)) |
| return false; |
| return !decl_binds_to_current_def_p (decl); |
| } |
| |
| /* Default function to output code that will globalize a label. A |
| target must define GLOBAL_ASM_OP or provide its own function to |
| globalize a label. */ |
| #ifdef GLOBAL_ASM_OP |
| void |
| default_globalize_label (FILE * stream, const char *name) |
| { |
| fputs (GLOBAL_ASM_OP, stream); |
| assemble_name (stream, name); |
| putc ('\n', stream); |
| } |
| #endif /* GLOBAL_ASM_OP */ |
| |
| /* Default function to output code that will globalize a declaration. */ |
| void |
| default_globalize_decl_name (FILE * stream, tree decl) |
| { |
| const char *name = XSTR (XEXP (DECL_RTL (decl), 0), 0); |
| targetm.asm_out.globalize_label (stream, name); |
| } |
| |
| /* Default function to output a label for unwind information. The |
| default is to do nothing. A target that needs nonlocal labels for |
| unwind information must provide its own function to do this. */ |
| void |
| default_emit_unwind_label (FILE * stream ATTRIBUTE_UNUSED, |
| tree decl ATTRIBUTE_UNUSED, |
| int for_eh ATTRIBUTE_UNUSED, |
| int empty ATTRIBUTE_UNUSED) |
| { |
| } |
| |
| /* Default function to output a label to divide up the exception table. |
| The default is to do nothing. A target that needs/wants to divide |
| up the table must provide it's own function to do this. */ |
| void |
| default_emit_except_table_label (FILE * stream ATTRIBUTE_UNUSED) |
| { |
| } |
| |
| /* This is how to output an internal numbered label where PREFIX is |
| the class of label and LABELNO is the number within the class. */ |
| |
| void |
| default_generate_internal_label (char *buf, const char *prefix, |
| unsigned long labelno) |
| { |
| ASM_GENERATE_INTERNAL_LABEL (buf, prefix, labelno); |
| } |
| |
| /* This is how to output an internal numbered label where PREFIX is |
| the class of label and LABELNO is the number within the class. */ |
| |
| void |
| default_internal_label (FILE *stream, const char *prefix, |
| unsigned long labelno) |
| { |
| char *const buf = (char *) alloca (40 + strlen (prefix)); |
| ASM_GENERATE_INTERNAL_LABEL (buf, prefix, labelno); |
| ASM_OUTPUT_INTERNAL_LABEL (stream, buf); |
| } |
| |
| |
| /* The default implementation of ASM_DECLARE_CONSTANT_NAME. */ |
| |
| void |
| default_asm_declare_constant_name (FILE *file, const char *name, |
| const_tree exp ATTRIBUTE_UNUSED, |
| HOST_WIDE_INT size ATTRIBUTE_UNUSED) |
| { |
| assemble_label (file, name); |
| } |
| |
| /* This is the default behavior at the beginning of a file. It's |
| controlled by two other target-hook toggles. */ |
| void |
| default_file_start (void) |
| { |
| if (targetm.asm_file_start_app_off |
| && !(flag_verbose_asm || flag_debug_asm || flag_dump_rtl_in_asm)) |
| fputs (ASM_APP_OFF, asm_out_file); |
| |
| if (targetm.asm_file_start_file_directive) |
| { |
| /* LTO produced units have no meaningful main_input_filename. */ |
| if (in_lto_p) |
| output_file_directive (asm_out_file, "<artificial>"); |
| else |
| output_file_directive (asm_out_file, main_input_filename); |
| } |
| } |
| |
| /* This is a generic routine suitable for use as TARGET_ASM_FILE_END |
| which emits a special section directive used to indicate whether or |
| not this object file needs an executable stack. This is primarily |
| a GNU extension to ELF but could be used on other targets. */ |
| |
| int trampolines_created; |
| |
| void |
| file_end_indicate_exec_stack (void) |
| { |
| unsigned int flags = SECTION_DEBUG; |
| if (trampolines_created) |
| flags |= SECTION_CODE; |
| |
| switch_to_section (get_section (".note.GNU-stack", flags, NULL)); |
| } |
| |
| /* Emit a special section directive to indicate that this object file |
| was compiled with -fsplit-stack. This is used to let the linker |
| detect calls between split-stack code and non-split-stack code, so |
| that it can modify the split-stack code to allocate a sufficiently |
| large stack. We emit another special section if there are any |
| functions in this file which have the no_split_stack attribute, to |
| prevent the linker from warning about being unable to convert the |
| functions if they call non-split-stack code. */ |
| |
| void |
| file_end_indicate_split_stack (void) |
| { |
| if (flag_split_stack) |
| { |
| switch_to_section (get_section (".note.GNU-split-stack", SECTION_DEBUG, |
| NULL)); |
| if (saw_no_split_stack) |
| switch_to_section (get_section (".note.GNU-no-split-stack", |
| SECTION_DEBUG, NULL)); |
| } |
| } |
| |
| /* Output DIRECTIVE (a C string) followed by a newline. This is used as |
| a get_unnamed_section callback. */ |
| |
| void |
| output_section_asm_op (const char *directive) |
| { |
| fprintf (asm_out_file, "%s\n", directive); |
| } |
| |
| /* Emit assembly code to switch to section NEW_SECTION. Do nothing if |
| the current section is NEW_SECTION. */ |
| |
| void |
| switch_to_section (section *new_section, tree decl) |
| { |
| bool retain_p; |
| if ((new_section->common.flags & SECTION_NAMED) |
| && decl != nullptr |
| && DECL_P (decl) |
| && ((retain_p = !!lookup_attribute ("retain", |
| DECL_ATTRIBUTES (decl))) |
| != !!(new_section->common.flags & SECTION_RETAIN))) |
| { |
| /* If the SECTION_RETAIN bit doesn't match, switch to a new |
| section. */ |
| tree used_decl, no_used_decl; |
| |
| if (retain_p) |
| { |
| new_section->common.flags |= SECTION_RETAIN; |
| used_decl = decl; |
| no_used_decl = new_section->named.decl; |
| } |
| else |
| { |
| new_section->common.flags &= ~(SECTION_RETAIN |
| | SECTION_DECLARED); |
| used_decl = new_section->named.decl; |
| no_used_decl = decl; |
| } |
| if (no_used_decl != used_decl) |
| { |
| warning (OPT_Wattributes, |
| "%+qD without %<retain%> attribute and %qD with " |
| "%<retain%> attribute are placed in a section with " |
| "the same name", no_used_decl, used_decl); |
| inform (DECL_SOURCE_LOCATION (used_decl), |
| "%qD was declared here", used_decl); |
| } |
| } |
| else if (in_section == new_section) |
| return; |
| |
| in_section = new_section; |
| |
| switch (SECTION_STYLE (new_section)) |
| { |
| case SECTION_NAMED: |
| targetm.asm_out.named_section (new_section->named.name, |
| new_section->named.common.flags, |
| new_section->named.decl); |
| break; |
| |
| case SECTION_UNNAMED: |
| new_section->unnamed.callback (new_section->unnamed.data); |
| break; |
| |
| case SECTION_NOSWITCH: |
| gcc_unreachable (); |
| break; |
| } |
| |
| new_section->common.flags |= SECTION_DECLARED; |
| } |
| |
| /* If block symbol SYMBOL has not yet been assigned an offset, place |
| it at the end of its block. */ |
| |
| void |
| place_block_symbol (rtx symbol) |
| { |
| unsigned HOST_WIDE_INT size, mask, offset; |
| class constant_descriptor_rtx *desc; |
| unsigned int alignment; |
| struct object_block *block; |
| tree decl; |
| |
| gcc_assert (SYMBOL_REF_BLOCK (symbol)); |
| if (SYMBOL_REF_BLOCK_OFFSET (symbol) >= 0) |
| return; |
| |
| /* Work out the symbol's size and alignment. */ |
| if (CONSTANT_POOL_ADDRESS_P (symbol)) |
| { |
| desc = SYMBOL_REF_CONSTANT (symbol); |
| alignment = desc->align; |
| size = GET_MODE_SIZE (desc->mode); |
| } |
| else if (TREE_CONSTANT_POOL_ADDRESS_P (symbol)) |
| { |
| decl = SYMBOL_REF_DECL (symbol); |
| gcc_checking_assert (DECL_IN_CONSTANT_POOL (decl)); |
| alignment = DECL_ALIGN (decl); |
| size = get_constant_size (DECL_INITIAL (decl)); |
| if ((flag_sanitize & SANITIZE_ADDRESS) |
| && TREE_CODE (DECL_INITIAL (decl)) == STRING_CST |
| && asan_protect_global (DECL_INITIAL (decl))) |
| { |
| size += asan_red_zone_size (size); |
| alignment = MAX (alignment, |
| ASAN_RED_ZONE_SIZE * BITS_PER_UNIT); |
| } |
| } |
| else |
| { |
| struct symtab_node *snode; |
| decl = SYMBOL_REF_DECL (symbol); |
| |
| snode = symtab_node::get (decl); |
| if (snode->alias) |
| { |
| rtx target = DECL_RTL (snode->ultimate_alias_target ()->decl); |
| |
| gcc_assert (MEM_P (target) |
| && GET_CODE (XEXP (target, 0)) == SYMBOL_REF |
| && SYMBOL_REF_HAS_BLOCK_INFO_P (XEXP (target, 0))); |
| target = XEXP (target, 0); |
| place_block_symbol (target); |
| SYMBOL_REF_BLOCK_OFFSET (symbol) = SYMBOL_REF_BLOCK_OFFSET (target); |
| return; |
| } |
| alignment = get_variable_align (decl); |
| size = tree_to_uhwi (DECL_SIZE_UNIT (decl)); |
| if ((flag_sanitize & SANITIZE_ADDRESS) |
| && asan_protect_global (decl)) |
| { |
| size += asan_red_zone_size (size); |
| alignment = MAX (alignment, |
| ASAN_RED_ZONE_SIZE * BITS_PER_UNIT); |
| } |
| } |
| |
| /* Calculate the object's offset from the start of the block. */ |
| block = SYMBOL_REF_BLOCK (symbol); |
| mask = alignment / BITS_PER_UNIT - 1; |
| offset = (block->size + mask) & ~mask; |
| SYMBOL_REF_BLOCK_OFFSET (symbol) = offset; |
| |
| /* Record the block's new alignment and size. */ |
| block->alignment = MAX (block->alignment, alignment); |
| block->size = offset + size; |
| |
| vec_safe_push (block->objects, symbol); |
| } |
| |
| /* Return the anchor that should be used to address byte offset OFFSET |
| from the first object in BLOCK. MODEL is the TLS model used |
| to access it. */ |
| |
| rtx |
| get_section_anchor (struct object_block *block, HOST_WIDE_INT offset, |
| enum tls_model model) |
| { |
| char label[100]; |
| unsigned int begin, middle, end; |
| unsigned HOST_WIDE_INT min_offset, max_offset, range, bias, delta; |
| rtx anchor; |
| |
| /* Work out the anchor's offset. Use an offset of 0 for the first |
| anchor so that we don't pessimize the case where we take the address |
| of a variable at the beginning of the block. This is particularly |
| useful when a block has only one variable assigned to it. |
| |
| We try to place anchors RANGE bytes apart, so there can then be |
| anchors at +/-RANGE, +/-2 * RANGE, and so on, up to the limits of |
| a ptr_mode offset. With some target settings, the lowest such |
| anchor might be out of range for the lowest ptr_mode offset; |
| likewise the highest anchor for the highest offset. Use anchors |
| at the extreme ends of the ptr_mode range in such cases. |
| |
| All arithmetic uses unsigned integers in order to avoid |
| signed overflow. */ |
| max_offset = (unsigned HOST_WIDE_INT) targetm.max_anchor_offset; |
| min_offset = (unsigned HOST_WIDE_INT) targetm.min_anchor_offset; |
| range = max_offset - min_offset + 1; |
| if (range == 0) |
| offset = 0; |
| else |
| { |
| bias = HOST_WIDE_INT_1U << (GET_MODE_BITSIZE (ptr_mode) - 1); |
| if (offset < 0) |
| { |
| delta = -(unsigned HOST_WIDE_INT) offset + max_offset; |
| delta -= delta % range; |
| if (delta > bias) |
| delta = bias; |
| offset = (HOST_WIDE_INT) (-delta); |
| } |
| else |
| { |
| delta = (unsigned HOST_WIDE_INT) offset - min_offset; |
| delta -= delta % range; |
| if (delta > bias - 1) |
| delta = bias - 1; |
| offset = (HOST_WIDE_INT) delta; |
| } |
| } |
| |
| /* Do a binary search to see if there's already an anchor we can use. |
| Set BEGIN to the new anchor's index if not. */ |
| begin = 0; |
| end = vec_safe_length (block->anchors); |
| while (begin != end) |
| { |
| middle = (end + begin) / 2; |
| anchor = (*block->anchors)[middle]; |
| if (SYMBOL_REF_BLOCK_OFFSET (anchor) > offset) |
| end = middle; |
| else if (SYMBOL_REF_BLOCK_OFFSET (anchor) < offset) |
| begin = middle + 1; |
| else if (SYMBOL_REF_TLS_MODEL (anchor) > model) |
| end = middle; |
| else if (SYMBOL_REF_TLS_MODEL (anchor) < model) |
| begin = middle + 1; |
| else |
| return anchor; |
| } |
| |
| /* Create a new anchor with a unique label. */ |
| ASM_GENERATE_INTERNAL_LABEL (label, "LANCHOR", anchor_labelno++); |
| anchor = create_block_symbol (ggc_strdup (label), block, offset); |
| SYMBOL_REF_FLAGS (anchor) |= SYMBOL_FLAG_LOCAL | SYMBOL_FLAG_ANCHOR; |
| SYMBOL_REF_FLAGS (anchor) |= model << SYMBOL_FLAG_TLS_SHIFT; |
| |
| /* Insert it at index BEGIN. */ |
| vec_safe_insert (block->anchors, begin, anchor); |
| return anchor; |
| } |
| |
| /* Output the objects in BLOCK. */ |
| |
| static void |
| output_object_block (struct object_block *block) |
| { |
| class constant_descriptor_rtx *desc; |
| unsigned int i; |
| HOST_WIDE_INT offset; |
| tree decl; |
| rtx symbol; |
| |
| if (!block->objects) |
| return; |
| |
| /* Switch to the section and make sure that the first byte is |
| suitably aligned. */ |
| /* Special case VTV comdat sections similar to assemble_variable. */ |
| if (SECTION_STYLE (block->sect) == SECTION_NAMED |
| && block->sect->named.name |
| && (strcmp (block->sect->named.name, ".vtable_map_vars") == 0)) |
| handle_vtv_comdat_section (block->sect, block->sect->named.decl); |
| else |
| switch_to_section (block->sect, SYMBOL_REF_DECL ((*block->objects)[0])); |
| |
| gcc_checking_assert (!(block->sect->common.flags & SECTION_MERGE)); |
| assemble_align (block->alignment); |
| |
| /* Define the values of all anchors relative to the current section |
| position. */ |
| FOR_EACH_VEC_SAFE_ELT (block->anchors, i, symbol) |
| targetm.asm_out.output_anchor (symbol); |
| |
| /* Output the objects themselves. */ |
| offset = 0; |
| FOR_EACH_VEC_ELT (*block->objects, i, symbol) |
| { |
| /* Move to the object's offset, padding with zeros if necessary. */ |
| assemble_zeros (SYMBOL_REF_BLOCK_OFFSET (symbol) - offset); |
| offset = SYMBOL_REF_BLOCK_OFFSET (symbol); |
| if (CONSTANT_POOL_ADDRESS_P (symbol)) |
| { |
| desc = SYMBOL_REF_CONSTANT (symbol); |
| /* Pass 1 for align as we have already laid out everything in the block. |
| So aligning shouldn't be necessary. */ |
| output_constant_pool_1 (desc, 1); |
| offset += GET_MODE_SIZE (desc->mode); |
| } |
| else if (TREE_CONSTANT_POOL_ADDRESS_P (symbol)) |
| { |
| HOST_WIDE_INT size; |
| decl = SYMBOL_REF_DECL (symbol); |
| assemble_constant_contents (DECL_INITIAL (decl), XSTR (symbol, 0), |
| DECL_ALIGN (decl), false); |
| |
| size = get_constant_size (DECL_INITIAL (decl)); |
| offset += size; |
| if ((flag_sanitize & SANITIZE_ADDRESS) |
| && TREE_CODE (DECL_INITIAL (decl)) == STRING_CST |
| && asan_protect_global (DECL_INITIAL (decl))) |
| { |
| size = asan_red_zone_size (size); |
| assemble_zeros (size); |
| offset += size; |
| } |
| } |
| else |
| { |
| HOST_WIDE_INT size; |
| decl = SYMBOL_REF_DECL (symbol); |
| assemble_variable_contents (decl, XSTR (symbol, 0), false, false); |
| size = tree_to_uhwi (DECL_SIZE_UNIT (decl)); |
| offset += size; |
| if ((flag_sanitize & SANITIZE_ADDRESS) |
| && asan_protect_global (decl)) |
| { |
| size = asan_red_zone_size (size); |
| assemble_zeros (size); |
| offset += size; |
| } |
| } |
| } |
| } |
| |
| /* A callback for qsort to compare object_blocks. */ |
| |
| static int |
| output_object_block_compare (const void *x, const void *y) |
| { |
| object_block *p1 = *(object_block * const*)x; |
| object_block *p2 = *(object_block * const*)y; |
| |
| if (p1->sect->common.flags & SECTION_NAMED |
| && !(p2->sect->common.flags & SECTION_NAMED)) |
| return 1; |
| |
| if (!(p1->sect->common.flags & SECTION_NAMED) |
| && p2->sect->common.flags & SECTION_NAMED) |
| return -1; |
| |
| if (p1->sect->common.flags & SECTION_NAMED |
| && p2->sect->common.flags & SECTION_NAMED) |
| return strcmp (p1->sect->named.name, p2->sect->named.name); |
| |
| unsigned f1 = p1->sect->common.flags; |
| unsigned f2 = p2->sect->common.flags; |
| if (f1 == f2) |
| return 0; |
| return f1 < f2 ? -1 : 1; |
| } |
| |
| /* Output the definitions of all object_blocks. */ |
| |
| void |
| output_object_blocks (void) |
| { |
| vec<object_block *, va_heap> v; |
| v.create (object_block_htab->elements ()); |
| object_block *obj; |
| hash_table<object_block_hasher>::iterator hi; |
| |
| FOR_EACH_HASH_TABLE_ELEMENT (*object_block_htab, obj, object_block *, hi) |
| v.quick_push (obj); |
| |
| /* Sort them in order to output them in a deterministic manner, |
| otherwise we may get .rodata sections in different orders with |
| and without -g. */ |
| v.qsort (output_object_block_compare); |
| unsigned i; |
| FOR_EACH_VEC_ELT (v, i, obj) |
| output_object_block (obj); |
| |
| v.release (); |
| } |
| |
| /* This function provides a possible implementation of the |
| TARGET_ASM_RECORD_GCC_SWITCHES target hook for ELF targets. When triggered |
| by -frecord-gcc-switches it creates a new mergeable, string section in the |
| assembler output file called TARGET_ASM_RECORD_GCC_SWITCHES_SECTION which |
| contains the switches in ASCII format. |
| |
| FIXME: This code does not correctly handle double quote characters |
| that appear inside strings, (it strips them rather than preserving them). |
| FIXME: ASM_OUTPUT_ASCII, as defined in config/elfos.h will not emit NUL |
| characters - instead it treats them as sub-string separators. Since |
| we want to emit NUL strings terminators into the object file we have to use |
| ASM_OUTPUT_SKIP. */ |
| |
| void |
| elf_record_gcc_switches (const char *options) |
| { |
| section *sec = get_section (targetm.asm_out.record_gcc_switches_section, |
| SECTION_DEBUG | SECTION_MERGE |
| | SECTION_STRINGS | (SECTION_ENTSIZE & 1), NULL); |
| switch_to_section (sec); |
| ASM_OUTPUT_ASCII (asm_out_file, options, strlen (options) + 1); |
| } |
| |
| /* Emit text to declare externally defined symbols. It is needed to |
| properly support non-default visibility. */ |
| void |
| default_elf_asm_output_external (FILE *file ATTRIBUTE_UNUSED, |
| tree decl, |
| const char *name ATTRIBUTE_UNUSED) |
| { |
| /* We output the name if and only if TREE_SYMBOL_REFERENCED is |
| set in order to avoid putting out names that are never really |
| used. Always output visibility specified in the source. */ |
| if (TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl)) |
| && (DECL_VISIBILITY_SPECIFIED (decl) |
| || targetm.binds_local_p (decl))) |
| maybe_assemble_visibility (decl); |
| } |
| |
| /* The default hook for TARGET_ASM_OUTPUT_SOURCE_FILENAME. */ |
| |
| void |
| default_asm_output_source_filename (FILE *file, const char *name) |
| { |
| #ifdef ASM_OUTPUT_SOURCE_FILENAME |
| ASM_OUTPUT_SOURCE_FILENAME (file, name); |
| #else |
| fprintf (file, "\t.file\t"); |
| output_quoted_string (file, name); |
| putc ('\n', file); |
| #endif |
| } |
| |
| /* Output a file name in the form wanted by System V. */ |
| |
| void |
| output_file_directive (FILE *asm_file, const char *input_name) |
| { |
| int len; |
| const char *na; |
| |
| if (input_name == NULL) |
| input_name = "<stdin>"; |
| else |
| input_name = remap_debug_filename (input_name); |
| |
| len = strlen (input_name); |
| na = input_name + len; |
| |
| /* NA gets INPUT_NAME sans directory names. */ |
| while (na > input_name) |
| { |
| if (IS_DIR_SEPARATOR (na[-1])) |
| break; |
| na--; |
| } |
| |
| targetm.asm_out.output_source_filename (asm_file, na); |
| } |
| |
| /* Create a DEBUG_EXPR_DECL / DEBUG_EXPR pair from RTL expression |
| EXP. */ |
| rtx |
| make_debug_expr_from_rtl (const_rtx exp) |
| { |
| tree ddecl = make_node (DEBUG_EXPR_DECL), type; |
| machine_mode mode = GET_MODE (exp); |
| rtx dval; |
| |
| DECL_ARTIFICIAL (ddecl) = 1; |
| if (REG_P (exp) && REG_EXPR (exp)) |
| type = TREE_TYPE (REG_EXPR (exp)); |
| else if (MEM_P (exp) && MEM_EXPR (exp)) |
| type = TREE_TYPE (MEM_EXPR (exp)); |
| else |
| type = NULL_TREE; |
| if (type && TYPE_MODE (type) == mode) |
| TREE_TYPE (ddecl) = type; |
| else |
| TREE_TYPE (ddecl) = lang_hooks.types.type_for_mode (mode, 1); |
| SET_DECL_MODE (ddecl, mode); |
| dval = gen_rtx_DEBUG_EXPR (mode); |
| DEBUG_EXPR_TREE_DECL (dval) = ddecl; |
| SET_DECL_RTL (ddecl, dval); |
| return dval; |
| } |
| |
| #ifdef ELF_ASCII_ESCAPES |
| /* Default ASM_OUTPUT_LIMITED_STRING for ELF targets. */ |
| |
| void |
| default_elf_asm_output_limited_string (FILE *f, const char *s) |
| { |
| int escape; |
| unsigned char c; |
| |
| fputs (STRING_ASM_OP, f); |
| putc ('"', f); |
| while (*s != '\0') |
| { |
| c = *s; |
| escape = ELF_ASCII_ESCAPES[c]; |
| switch (escape) |
| { |
| case 0: |
| putc (c, f); |
| break; |
| case 1: |
| putc ('\\', f); |
| putc ('0'+((c>>6)&7), f); |
| putc ('0'+((c>>3)&7), f); |
| putc ('0'+(c&7), f); |
| break; |
| default: |
| putc ('\\', f); |
| putc (escape, f); |
| break; |
| } |
| s++; |
| } |
| putc ('\"', f); |
| putc ('\n', f); |
| } |
| |
| /* Default ASM_OUTPUT_ASCII for ELF targets. */ |
| |
| void |
| default_elf_asm_output_ascii (FILE *f, const char *s, unsigned int len) |
| { |
| const char *limit = s + len; |
| const char *last_null = NULL; |
| unsigned bytes_in_chunk = 0; |
| unsigned char c; |
| int escape; |
| |
| for (; s < limit; s++) |
| { |
| const char *p; |
| |
| if (bytes_in_chunk >= 60) |
| { |
| putc ('\"', f); |
| putc ('\n', f); |
| bytes_in_chunk = 0; |
| } |
| |
| if (s > last_null) |
| { |
| for (p = s; p < limit && *p != '\0'; p++) |
| continue; |
| last_null = p; |
| } |
| else |
| p = last_null; |
| |
| if (p < limit && (p - s) <= (long) ELF_STRING_LIMIT) |
| { |
| if (bytes_in_chunk > 0) |
| { |
| putc ('\"', f); |
| putc ('\n', f); |
| bytes_in_chunk = 0; |
| } |
| |
| default_elf_asm_output_limited_string (f, s); |
| s = p; |
| } |
| else |
| { |
| if (bytes_in_chunk == 0) |
| fputs (ASCII_DATA_ASM_OP "\"", f); |
| |
| c = *s; |
| escape = ELF_ASCII_ESCAPES[c]; |
| switch (escape) |
| { |
| case 0: |
| putc (c, f); |
| bytes_in_chunk++; |
| break; |
| case 1: |
| putc ('\\', f); |
| putc ('0'+((c>>6)&7), f); |
| putc ('0'+((c>>3)&7), f); |
| putc ('0'+(c&7), f); |
| bytes_in_chunk += 4; |
| break; |
| default: |
| putc ('\\', f); |
| putc (escape, f); |
| bytes_in_chunk += 2; |
| break; |
| } |
| |
| } |
| } |
| |
| if (bytes_in_chunk > 0) |
| { |
| putc ('\"', f); |
| putc ('\n', f); |
| } |
| } |
| #endif |
| |
| static GTY(()) section *elf_init_array_section; |
| static GTY(()) section *elf_fini_array_section; |
| |
| static section * |
| get_elf_initfini_array_priority_section (int priority, |
| bool constructor_p) |
| { |
| section *sec; |
| if (priority != DEFAULT_INIT_PRIORITY) |
| { |
| char buf[18]; |
| sprintf (buf, "%s.%.5u", |
| constructor_p ? ".init_array" : ".fini_array", |
| priority); |
| sec = get_section (buf, SECTION_WRITE | SECTION_NOTYPE, NULL_TREE); |
| } |
| else |
| { |
| if (constructor_p) |
| { |
| if (elf_init_array_section == NULL) |
| elf_init_array_section |
| = get_section (".init_array", |
| SECTION_WRITE | SECTION_NOTYPE, NULL_TREE); |
| sec = elf_init_array_section; |
| } |
| else |
| { |
| if (elf_fini_array_section == NULL) |
| elf_fini_array_section |
| = get_section (".fini_array", |
| SECTION_WRITE | SECTION_NOTYPE, NULL_TREE); |
| sec = elf_fini_array_section; |
| } |
| } |
| return sec; |
| } |
| |
| /* Use .init_array section for constructors. */ |
| |
| void |
| default_elf_init_array_asm_out_constructor (rtx symbol, int priority) |
| { |
| section *sec = get_elf_initfini_array_priority_section (priority, |
| true); |
| assemble_addr_to_section (symbol, sec); |
| } |
| |
| /* Use .fini_array section for destructors. */ |
| |
| void |
| default_elf_fini_array_asm_out_destructor (rtx symbol, int priority) |
| { |
| section *sec = get_elf_initfini_array_priority_section (priority, |
| false); |
| assemble_addr_to_section (symbol, sec); |
| } |
| |
| /* Default TARGET_ASM_OUTPUT_IDENT hook. |
| |
| This is a bit of a cheat. The real default is a no-op, but this |
| hook is the default for all targets with a .ident directive. */ |
| |
| void |
| default_asm_output_ident_directive (const char *ident_str) |
| { |
| const char *ident_asm_op = "\t.ident\t"; |
| |
| /* If we are still in the front end, do not write out the string |
| to asm_out_file. Instead, add a fake top-level asm statement. |
| This allows the front ends to use this hook without actually |
| writing to asm_out_file, to handle #ident or Pragma Ident. */ |
| if (symtab->state == PARSING) |
| { |
| char *buf = ACONCAT ((ident_asm_op, "\"", ident_str, "\"\n", NULL)); |
| symtab->finalize_toplevel_asm (build_string (strlen (buf), buf)); |
| } |
| else |
| fprintf (asm_out_file, "%s\"%s\"\n", ident_asm_op, ident_str); |
| } |
| |
| /* Switch to a COMDAT section with COMDAT name of decl. |
| |
| FIXME: resolve_unique_section needs to deal better with |
| decls with both DECL_SECTION_NAME and DECL_ONE_ONLY. Once |
| that is fixed, this if-else statement can be replaced with |
| a single call to "switch_to_section (sect)". */ |
| |
| void |
| switch_to_comdat_section (section *sect, tree decl) |
| { |
| #if defined (OBJECT_FORMAT_ELF) |
| targetm.asm_out.named_section (sect->named.name, |
| sect->named.common.flags |
| | SECTION_LINKONCE, |
| decl); |
| in_section = sect; |
| #else |
| /* Neither OBJECT_FORMAT_PE, nor OBJECT_FORMAT_COFF is set here. |
| Therefore the following check is used. |
| In case a the target is PE or COFF a comdat group section |
| is created, e.g. .vtable_map_vars$foo. The linker places |
| everything in .vtable_map_vars at the end. |
| |
| A fix could be made in |
| gcc/config/i386/winnt.cc: i386_pe_unique_section. */ |
| if (TARGET_PECOFF) |
| { |
| char *name; |
| |
| if (TREE_CODE (decl) == IDENTIFIER_NODE) |
| name = ACONCAT ((sect->named.name, "$", |
| IDENTIFIER_POINTER (decl), NULL)); |
| else |
| name = ACONCAT ((sect->named.name, "$", |
| IDENTIFIER_POINTER (DECL_COMDAT_GROUP (decl)), |
| NULL)); |
| |
| targetm.asm_out.named_section (name, |
| sect->named.common.flags |
| | SECTION_LINKONCE, |
| decl); |
| in_section = sect; |
| } |
| else |
| switch_to_section (sect); |
| #endif |
| } |
| |
| /* This function ensures that vtable_map variables are not only |
| in the comdat section, but that each variable has its own unique |
| comdat name. Without this the variables end up in the same section |
| with a single comdat name. */ |
| |
| static void |
| handle_vtv_comdat_section (section *sect, const_tree decl ATTRIBUTE_UNUSED) |
| { |
| switch_to_comdat_section(sect, DECL_NAME (decl)); |
| } |
| |
| void |
| varasm_cc_finalize () |
| { |
| first_global_object_name = nullptr; |
| weak_global_object_name = nullptr; |
| |
| const_labelno = 0; |
| size_directive_output = 0; |
| |
| last_assemble_variable_decl = NULL_TREE; |
| first_function_block_is_cold = false; |
| saw_no_split_stack = false; |
| text_section = nullptr; |
| data_section = nullptr; |
| readonly_data_section = nullptr; |
| sdata_section = nullptr; |
| ctors_section = nullptr; |
| dtors_section = nullptr; |
| bss_section = nullptr; |
| sbss_section = nullptr; |
| tls_comm_section = nullptr; |
| comm_section = nullptr; |
| lcomm_section = nullptr; |
| bss_noswitch_section = nullptr; |
| exception_section = nullptr; |
| eh_frame_section = nullptr; |
| in_section = nullptr; |
| in_cold_section_p = false; |
| cold_function_name = NULL_TREE; |
| unnamed_sections = nullptr; |
| section_htab = nullptr; |
| object_block_htab = nullptr; |
| anchor_labelno = 0; |
| shared_constant_pool = nullptr; |
| pending_assemble_externals = NULL_TREE; |
| pending_libcall_symbols = nullptr; |
| |
| #ifdef ASM_OUTPUT_EXTERNAL |
| pending_assemble_externals_processed = false; |
| delete pending_assemble_externals_set; |
| pending_assemble_externals_set = nullptr; |
| #endif |
| |
| weak_decls = NULL_TREE; |
| initial_trampoline = nullptr; |
| const_desc_htab = nullptr; |
| weakref_targets = NULL_TREE; |
| alias_pairs = nullptr; |
| tm_clone_hash = nullptr; |
| trampolines_created = 0; |
| elf_init_array_section = nullptr; |
| elf_fini_array_section = nullptr; |
| } |
| |
| #include "gt-varasm.h" |