diff options
author | kan <kan@FreeBSD.org> | 2007-05-19 01:19:51 +0000 |
---|---|---|
committer | kan <kan@FreeBSD.org> | 2007-05-19 01:19:51 +0000 |
commit | 1f9ea4d0a40cca64d60cf4dab152349da7b9dddf (patch) | |
tree | 0cb530c9c38af219e6dda2994c078b6b2b9ad853 /contrib/gcc/varasm.c | |
parent | 4895159b2b4f648051c1f139faa7b6dc50c2bfcb (diff) | |
download | FreeBSD-src-1f9ea4d0a40cca64d60cf4dab152349da7b9dddf.zip FreeBSD-src-1f9ea4d0a40cca64d60cf4dab152349da7b9dddf.tar.gz |
GCC 4.2.0 release.
Diffstat (limited to 'contrib/gcc/varasm.c')
-rw-r--r-- | contrib/gcc/varasm.c | 4725 |
1 files changed, 2826 insertions, 1899 deletions
diff --git a/contrib/gcc/varasm.c b/contrib/gcc/varasm.c index 33307e5..60a17fc 100644 --- a/contrib/gcc/varasm.c +++ b/contrib/gcc/varasm.c @@ -1,6 +1,7 @@ /* Output variables, constants and external declarations, for GNU compiler. Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997, - 1998, 1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc. + 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 + Free Software Foundation, Inc. This file is part of GCC. @@ -16,8 +17,8 @@ for more details. You should have received a copy of the GNU General Public License along with GCC; see the file COPYING. If not, write to the Free -Software Foundation, 59 Temple Place - Suite 330, Boston, MA -02111-1307, USA. */ +Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301, USA. */ /* This file handles generation of all the assembler code @@ -48,65 +49,37 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA #include "tm_p.h" #include "debug.h" #include "target.h" +#include "tree-mudflap.h" #include "cgraph.h" +#include "cfglayout.h" +#include "basic-block.h" #ifdef XCOFF_DEBUGGING_INFO #include "xcoffout.h" /* Needed for external data declarations for e.g. AIX 4.x. */ #endif -#ifndef TRAMPOLINE_ALIGNMENT -#define TRAMPOLINE_ALIGNMENT FUNCTION_BOUNDARY -#endif - -#ifndef ASM_STABS_OP -#define ASM_STABS_OP "\t.stabs\t" -#endif - /* 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; struct addr_const; struct constant_descriptor_rtx; -struct rtx_const; -struct pool_constant; - -#define MAX_RTX_HASH_TABLE 61 +struct rtx_constant_pool; struct varasm_status GTY(()) { - /* 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. - - This pool of constants is reinitialized for each function - so each function gets its own constants-pool that comes right before - it. */ - struct constant_descriptor_rtx ** GTY ((length ("MAX_RTX_HASH_TABLE"))) - x_const_rtx_hash_table; - struct pool_constant ** GTY ((length ("MAX_RTX_HASH_TABLE"))) - x_const_rtx_sym_hash_table; - - /* Pointers to first and last constant in pool. */ - struct pool_constant *x_first_pool; - struct pool_constant *x_last_pool; - - /* Current offset in constant pool (does not include any machine-specific - header). */ - HOST_WIDE_INT x_pool_offset; + /* If we're using a per-function constant pool, this is it. */ + struct rtx_constant_pool *pool; /* Number of tree-constants deferred during the expansion of this function. */ unsigned int deferred_constants; }; -#define const_rtx_hash_table (cfun->varasm->x_const_rtx_hash_table) -#define const_rtx_sym_hash_table (cfun->varasm->x_const_rtx_sym_hash_table) -#define first_pool (cfun->varasm->x_first_pool) -#define last_pool (cfun->varasm->x_last_pool) -#define pool_offset (cfun->varasm->x_pool_offset) #define n_deferred_constants (cfun->varasm->deferred_constants) /* Number for making the label on the next @@ -126,12 +99,13 @@ int size_directive_output; tree last_assemble_variable_decl; -/* RTX_UNCHANGING_P in a MEM can mean it is stored into, for initialization. - So giving constant the alias set for the type will allow such - initializations to appear to conflict with the load of the constant. We - avoid this by giving all constants an alias set for just constants. - Since there will be no stores to that alias set, nothing will ever - conflict with them. */ +/* 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; + +/* We give all constants their own alias set. Perhaps redundant with + MEM_READONLY_P, but pre-dates it. */ static HOST_WIDE_INT const_alias_set; @@ -147,58 +121,65 @@ static hashval_t const_hash_1 (const tree); static int compare_constant (const tree, const tree); static tree copy_constant (tree); static void output_constant_def_contents (rtx); -static void decode_rtx_const (enum machine_mode, rtx, struct rtx_const *); -static unsigned int const_hash_rtx (enum machine_mode, rtx); -static int compare_constant_rtx (enum machine_mode, rtx, - struct constant_descriptor_rtx *); -static struct constant_descriptor_rtx * record_constant_rtx - (enum machine_mode, rtx); -static struct pool_constant *find_pool_constant (struct function *, rtx); -static void mark_constant_pool (void); -static void mark_constants (rtx); -static int mark_constant (rtx *current_rtx, void *data); static void output_addressed_constants (tree); static unsigned HOST_WIDE_INT array_size_for_constructor (tree); static unsigned min_align (unsigned, unsigned); static void output_constructor (tree, unsigned HOST_WIDE_INT, unsigned int); static void globalize_decl (tree); static void maybe_assemble_visibility (tree); -static int in_named_entry_eq (const void *, const void *); -static hashval_t in_named_entry_hash (const void *); +#ifdef BSS_SECTION_ASM_OP #ifdef ASM_OUTPUT_BSS static void asm_output_bss (FILE *, tree, const char *, unsigned HOST_WIDE_INT, unsigned HOST_WIDE_INT); #endif -#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 bool asm_emit_uninitialised (tree, const char*, - unsigned HOST_WIDE_INT, - unsigned HOST_WIDE_INT); static void mark_weak (tree); +static void output_constant_pool (const char *, tree); -enum in_section { no_section, in_text, in_data, in_named -#ifdef BSS_SECTION_ASM_OP - , in_bss -#endif -#ifdef CTORS_SECTION_ASM_OP - , in_ctors -#endif -#ifdef DTORS_SECTION_ASM_OP - , in_dtors -#endif -#ifdef READONLY_DATA_SECTION_ASM_OP - , in_readonly_data -#endif -#ifdef EXTRA_SECTIONS - , EXTRA_SECTIONS -#endif -}; -static GTY(()) enum in_section in_section = no_section; +/* 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; + +/* A linked list of all the unnamed sections. */ +static GTY(()) section *unnamed_sections; /* Return a nonzero value if DECL has a section attribute. */ #ifndef IN_NAMED_SECTION @@ -207,234 +188,278 @@ static GTY(()) enum in_section in_section = no_section; && DECL_SECTION_NAME (DECL) != NULL_TREE) #endif -/* Text of section name when in_section == in_named. */ -static GTY(()) const char *in_named_name; +/* Hash table of named sections. */ +static GTY((param_is (section))) htab_t section_htab; -/* Hash table of flags that have been used for a particular named section. */ +/* A table of object_blocks, indexed by section. */ +static GTY((param_is (struct object_block))) htab_t object_block_htab; -struct in_named_entry GTY(()) -{ - const char *name; - unsigned int flags; - bool declared; -}; - -static GTY((param_is (struct in_named_entry))) htab_t in_named_htab; +/* The next number to use for internal anchor labels. */ +static GTY(()) int anchor_labelno; -/* Define functions like text_section for any extra sections. */ -#ifdef EXTRA_SECTION_FUNCTIONS -EXTRA_SECTION_FUNCTIONS -#endif +/* A pool of constants that can be shared between functions. */ +static GTY(()) struct rtx_constant_pool *shared_constant_pool; -/* Tell assembler to switch to text section. */ +/* Helper routines for maintaining section_htab. */ -void -text_section (void) +static int +section_entry_eq (const void *p1, const void *p2) { - if (in_section != in_text) - { - in_section = in_text; - fprintf (asm_out_file, "%s\n", TEXT_SECTION_ASM_OP); - } -} + const section *old = p1; + const char *new = p2; -/* Tell assembler to switch to data section. */ + return strcmp (old->named.name, new) == 0; +} -void -data_section (void) +static hashval_t +section_entry_hash (const void *p) { - if (in_section != in_data) - { - in_section = in_data; - if (flag_shared_data) - { -#ifdef SHARED_SECTION_ASM_OP - fprintf (asm_out_file, "%s\n", SHARED_SECTION_ASM_OP); -#else - fprintf (asm_out_file, "%s\n", DATA_SECTION_ASM_OP); -#endif - } - else - fprintf (asm_out_file, "%s\n", DATA_SECTION_ASM_OP); - } + const section *old = p; + return htab_hash_string (old->named.name); } -/* Tell assembler to switch to read-only data section. This is normally - the text section. */ +/* Return a hash value for section SECT. */ -void -readonly_data_section (void) +static hashval_t +hash_section (section *sect) { -#ifdef READONLY_DATA_SECTION - READONLY_DATA_SECTION (); /* Note this can call data_section. */ -#else -#ifdef READONLY_DATA_SECTION_ASM_OP - if (in_section != in_readonly_data) - { - in_section = in_readonly_data; - fputs (READONLY_DATA_SECTION_ASM_OP, asm_out_file); - fputc ('\n', asm_out_file); - } -#else - text_section (); -#endif -#endif + if (sect->common.flags & SECTION_NAMED) + return htab_hash_string (sect->named.name); + return sect->common.flags; } -/* Determine if we're in the text section. */ +/* Helper routines for maintaining object_block_htab. */ -int -in_text_section (void) +static int +object_block_entry_eq (const void *p1, const void *p2) { - return in_section == in_text; -} + const struct object_block *old = p1; + const section *new = p2; -/* Determine if we're in the data section. */ + return old->sect == new; +} -int -in_data_section (void) +static hashval_t +object_block_entry_hash (const void *p) { - return in_section == in_data; + const struct object_block *old = p; + return hash_section (old->sect); } -/* Helper routines for maintaining in_named_htab. */ +/* Return a new unnamed section with the given fields. */ -static int -in_named_entry_eq (const void *p1, const void *p2) +section * +get_unnamed_section (unsigned int flags, void (*callback) (const void *), + const void *data) { - const struct in_named_entry *old = p1; - const char *new = p2; + section *sect; - return strcmp (old->name, new) == 0; -} + sect = ggc_alloc (sizeof (struct unnamed_section)); + sect->unnamed.common.flags = flags | SECTION_UNNAMED; + sect->unnamed.callback = callback; + sect->unnamed.data = data; + sect->unnamed.next = unnamed_sections; -static hashval_t -in_named_entry_hash (const void *p) -{ - const struct in_named_entry *old = p; - return htab_hash_string (old->name); + unnamed_sections = sect; + return sect; } -/* If SECTION has been seen before as a named section, return the flags - that were used. Otherwise, return 0. Note, that 0 is a perfectly valid - set of flags for a section to have, so 0 does not mean that the section - has not been seen. */ +/* Return a SECTION_NOSWITCH section with the given fields. */ -unsigned int -get_named_section_flags (const char *section) +static section * +get_noswitch_section (unsigned int flags, noswitch_section_callback callback) { - struct in_named_entry **slot; + section *sect; - slot = (struct in_named_entry **) - htab_find_slot_with_hash (in_named_htab, section, - htab_hash_string (section), NO_INSERT); + sect = ggc_alloc (sizeof (struct unnamed_section)); + sect->noswitch.common.flags = flags | SECTION_NOSWITCH; + sect->noswitch.callback = callback; - return slot ? (*slot)->flags : 0; + return sect; } -/* Returns true if the section has been declared before. Sets internal - flag on this section in in_named_hash so subsequent calls on this - section will return false. */ +/* Return the named section structure associated with NAME. Create + a new section with the given fields if no such structure exists. */ -bool -named_section_first_declaration (const char *name) +section * +get_section (const char *name, unsigned int flags, tree decl) { - struct in_named_entry **slot; + section *sect, **slot; - slot = (struct in_named_entry **) - htab_find_slot_with_hash (in_named_htab, name, - htab_hash_string (name), NO_INSERT); - if (! (*slot)->declared) + slot = (section **) + htab_find_slot_with_hash (section_htab, name, + htab_hash_string (name), INSERT); + flags |= SECTION_NAMED; + if (*slot == NULL) { - (*slot)->declared = true; - return true; + sect = ggc_alloc (sizeof (struct named_section)); + sect->named.common.flags = flags; + sect->named.name = ggc_strdup (name); + sect->named.decl = decl; + *slot = sect; } else { - return false; + sect = *slot; + if ((sect->common.flags & ~SECTION_DECLARED) != flags + && ((sect->common.flags | flags) & SECTION_OVERRIDE) == 0) + { + /* Sanity check user variables for flag changes. */ + if (decl == 0) + decl = sect->named.decl; + gcc_assert (decl); + error ("%+D causes a section type conflict", decl); + } } + return sect; } +/* Return true if the current compilation mode benefits from having + objects grouped into blocks. */ -/* Record FLAGS for SECTION. If SECTION was previously recorded with a - different set of flags, return false. */ +static bool +use_object_blocks_p (void) +{ + return flag_section_anchors; +} -bool -set_named_section_flags (const char *section, unsigned int flags) +/* 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. */ + +static struct object_block * +get_block_for_section (section *sect) { - struct in_named_entry **slot, *entry; + struct object_block *block; + void **slot; - slot = (struct in_named_entry **) - htab_find_slot_with_hash (in_named_htab, section, - htab_hash_string (section), INSERT); - entry = *slot; + if (sect == NULL) + return NULL; - if (!entry) + slot = htab_find_slot_with_hash (object_block_htab, sect, + hash_section (sect), INSERT); + block = (struct object_block *) *slot; + if (block == NULL) { - entry = ggc_alloc (sizeof (*entry)); - *slot = entry; - entry->name = ggc_strdup (section); - entry->flags = flags; - entry->declared = false; + block = (struct object_block *) + ggc_alloc_cleared (sizeof (struct object_block)); + block->sect = sect; + *slot = block; } - else if (entry->flags != flags) - return false; - - return true; + return block; } -/* Tell assembler to change to section NAME with attributes FLAGS. */ +/* 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. */ -void -named_section_flags (const char *name, unsigned int flags) +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 = ggc_alloc_zone (size, &rtl_zone); + + /* 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; +} + +static void +initialize_cold_section_name (void) { - if (in_section != in_named || strcmp (name, in_named_name) != 0) + const char *stripped_name; + char *name, *buffer; + tree dsn; + + gcc_assert (cfun && current_function_decl); + if (cfun->unlikely_text_section_name) + return; + + dsn = DECL_SECTION_NAME (current_function_decl); + if (flag_function_sections && dsn) { - if (! set_named_section_flags (name, flags)) - abort (); + name = alloca (TREE_STRING_LENGTH (dsn) + 1); + memcpy (name, TREE_STRING_POINTER (dsn), TREE_STRING_LENGTH (dsn) + 1); - (*targetm.asm_out.named_section) (name, flags); + stripped_name = targetm.strip_name_encoding (name); - if (flags & SECTION_FORGET) - in_section = no_section; - else - { - in_named_name = ggc_strdup (name); - in_section = in_named; - } + buffer = ACONCAT ((stripped_name, "_unlikely", NULL)); + cfun->unlikely_text_section_name = ggc_strdup (buffer); } + else + cfun->unlikely_text_section_name = UNLIKELY_EXECUTED_TEXT_SECTION_NAME; } -/* Tell assembler to change to section NAME for DECL. - If DECL is NULL, just switch to section NAME. - If NAME is NULL, get the name from DECL. - If RELOC is 1, the initializer for DECL contains relocs. */ +/* Tell assembler to switch to unlikely-to-be-executed text section. */ -void -named_section (tree decl, const char *name, int reloc) +section * +unlikely_text_section (void) +{ + if (cfun) + { + if (!cfun->unlikely_text_section_name) + initialize_cold_section_name (); + + return get_named_section (NULL, cfun->unlikely_text_section_name, 0); + } + else + return get_named_section (NULL, UNLIKELY_EXECUTED_TEXT_SECTION_NAME, 0); +} + +/* 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) +{ + const char *name; + + if (cfun) + name = cfun->unlikely_text_section_name; + else + name = UNLIKELY_EXECUTED_TEXT_SECTION_NAME; + + return (name + && sect + && SECTION_STYLE (sect) == SECTION_NAMED + && strcmp (name, sect->named.name) == 0); +} + +/* 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 (decl != NULL_TREE && !DECL_P (decl)) - abort (); + gcc_assert (!decl || DECL_P (decl)); if (name == NULL) name = TREE_STRING_POINTER (DECL_SECTION_NAME (decl)); - flags = (* targetm.section_type_flags) (decl, name, reloc); - - /* Sanity check user variables for flag changes. Non-user - section flag changes will abort in named_section_flags. - However, don't complain if SECTION_OVERRIDE is set. - We trust that the setter knows that it is safe to ignore - the default flags for this decl. */ - if (decl && ! set_named_section_flags (name, flags)) - { - flags = get_named_section_flags (name); - if ((flags & SECTION_OVERRIDE) == 0) - error ("%J%D causes a section type conflict", decl, decl); - } + flags = targetm.section_type_flags (decl, name, reloc); - named_section_flags (name, flags); + return get_section (name, flags, decl); } /* If required, set DECL_SECTION_NAME to a unique name. */ @@ -447,23 +472,11 @@ resolve_unique_section (tree decl, int reloc ATTRIBUTE_UNUSED, && targetm.have_named_sections && (flag_function_or_data_sections || DECL_ONE_ONLY (decl))) - (*targetm.asm_out.unique_section) (decl, reloc); + targetm.asm_out.unique_section (decl, reloc); } #ifdef BSS_SECTION_ASM_OP -/* Tell the assembler to switch to the bss section. */ - -void -bss_section (void) -{ - if (in_section != in_bss) - { - fprintf (asm_out_file, "%s\n", BSS_SECTION_ASM_OP); - in_section = in_bss; - } -} - #ifdef ASM_OUTPUT_BSS /* Utility function for ASM_OUTPUT_BSS for targets to use if @@ -477,8 +490,8 @@ asm_output_bss (FILE *file, tree decl ATTRIBUTE_UNUSED, unsigned HOST_WIDE_INT size ATTRIBUTE_UNUSED, unsigned HOST_WIDE_INT rounded) { - (*targetm.asm_out.globalize_label) (file, name); - bss_section (); + targetm.asm_out.globalize_label (file, name); + switch_to_section (bss_section); #ifdef ASM_DECLARE_OBJECT_NAME last_assemble_variable_decl = decl; ASM_DECLARE_OBJECT_NAME (file, name, decl); @@ -503,7 +516,7 @@ asm_output_aligned_bss (FILE *file, tree decl ATTRIBUTE_UNUSED, const char *name, unsigned HOST_WIDE_INT size, int align) { - bss_section (); + 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; @@ -519,51 +532,143 @@ asm_output_aligned_bss (FILE *file, tree decl ATTRIBUTE_UNUSED, #endif /* BSS_SECTION_ASM_OP */ -/* Switch to the section for function DECL. +#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_TREE + && targetm.have_named_sections) + return get_named_section (decl, NULL, 0); + else + return text_section; +} +#endif + +/* Return the section for function DECL. - If DECL is NULL_TREE, switch to the text section. - ??? It's not clear that we will ever be passed NULL_TREE, but it's - safer to handle it. */ + If DECL is NULL_TREE, return the text section. We can be passed + NULL_TREE under some circumstances by dbxout.c at least. */ -void +section * function_section (tree decl) { + int reloc = 0; + + if (first_function_block_is_cold) + reloc = 1; + +#ifdef USE_SELECT_SECTION_FOR_FUNCTIONS if (decl != NULL_TREE && DECL_SECTION_NAME (decl) != NULL_TREE) - named_section (decl, (char *) 0, 0); + return reloc ? unlikely_text_section () + : get_named_section (decl, NULL, 0); else - text_section (); + return targetm.asm_out.select_section (decl, reloc, DECL_ALIGN (decl)); +#else + return reloc ? unlikely_text_section () : hot_function_section (decl); +#endif } -/* Switch to section for variable DECL. RELOC is the same as the - argument to SELECT_SECTION. */ - -void -variable_section (tree decl, int reloc) +section * +current_function_section (void) { - if (IN_NAMED_SECTION (decl)) - named_section (decl, NULL, reloc); +#ifdef USE_SELECT_SECTION_FOR_FUNCTIONS + if (current_function_decl != NULL_TREE + && DECL_SECTION_NAME (current_function_decl) != NULL_TREE) + return in_cold_section_p ? unlikely_text_section () + : get_named_section (current_function_decl, + NULL, 0); else - (*targetm.asm_out.select_section) (decl, reloc, DECL_ALIGN (decl)); + return targetm.asm_out.select_section (current_function_decl, + in_cold_section_p, + DECL_ALIGN (current_function_decl)); +#else + return (in_cold_section_p + ? unlikely_text_section () + : hot_function_section (current_function_decl)); +#endif } -/* Tell assembler to switch to the section for string merging. */ +/* Return the read-only data section associated with function DECL. */ -void +section * +default_function_rodata_section (tree decl) +{ + if (decl != NULL_TREE && DECL_SECTION_NAME (decl)) + { + const char *name = TREE_STRING_POINTER (DECL_SECTION_NAME (decl)); + + if (DECL_ONE_ONLY (decl) && HAVE_COMDAT_GROUP) + { + size_t len = strlen (name) + 3; + char* rname = alloca (len); + + strcpy (rname, ".rodata"); + strcat (rname, name + 5); + return get_section (rname, SECTION_LINKONCE, decl); + } + /* For .gnu.linkonce.t.foo we want to use .gnu.linkonce.r.foo. */ + else if (DECL_ONE_ONLY (decl) + && strncmp (name, ".gnu.linkonce.t.", 16) == 0) + { + size_t len = strlen (name) + 1; + char *rname = alloca (len); + + memcpy (rname, name, len); + rname[14] = 'r'; + return get_section (rname, SECTION_LINKONCE, decl); + } + /* For .text.foo we want to use .rodata.foo. */ + else if (flag_function_sections && flag_data_sections + && strncmp (name, ".text.", 6) == 0) + { + size_t len = strlen (name) + 1; + char *rname = alloca (len + 2); + + memcpy (rname, ".rodata", 7); + memcpy (rname + 7, name + 5, len - 5); + return get_section (rname, 0, decl); + } + } + + 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 decl ATTRIBUTE_UNUSED) +{ + return readonly_data_section; +} + +/* 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 - && TREE_STRING_LENGTH (decl) >= int_size_in_bytes (TREE_TYPE (decl))) + && (len = int_size_in_bytes (TREE_TYPE (decl))) > 0 + && TREE_STRING_LENGTH (decl) >= len) { enum machine_mode mode; unsigned int modesize; const char *str; - int i, j, len, unit; + HOST_WIDE_INT i; + int j, unit; char name[30]; mode = TYPE_MODE (TREE_TYPE (TREE_TYPE (decl))); @@ -575,7 +680,6 @@ mergeable_string_section (tree decl ATTRIBUTE_UNUSED, align = modesize; str = TREE_STRING_POINTER (decl); - len = TREE_STRING_LENGTH (decl); unit = GET_MODE_SIZE (mode); /* Check for embedded NUL characters. */ @@ -592,37 +696,17 @@ mergeable_string_section (tree decl ATTRIBUTE_UNUSED, sprintf (name, ".rodata.str%d.%d", modesize / 8, (int) (align / 8)); flags |= (modesize / 8) | SECTION_MERGE | SECTION_STRINGS; - if (!i && modesize < align) - { - /* A "" string with requested alignment greater than - character size might cause a problem: - if some other string required even bigger - alignment than "", then linker might think the - "" is just part of padding after some other string - and not put it into the hash table initially. - But this means "" could have smaller alignment - than requested. */ -#ifdef ASM_OUTPUT_SECTION_START - named_section_flags (name, flags); - ASM_OUTPUT_SECTION_START (asm_out_file); -#else - readonly_data_section (); -#endif - return; - } - - named_section_flags (name, flags); - return; + return get_section (name, flags, NULL); } } } - readonly_data_section (); + return readonly_data_section; } -/* Tell assembler to switch to the section for constant merging. */ +/* Return the section to use for constant merging. */ -void +section * mergeable_constant_section (enum machine_mode mode ATTRIBUTE_UNUSED, unsigned HOST_WIDE_INT align ATTRIBUTE_UNUSED, unsigned int flags ATTRIBUTE_UNUSED) @@ -641,11 +725,9 @@ mergeable_constant_section (enum machine_mode mode ATTRIBUTE_UNUSED, sprintf (name, ".rodata.cst%d", (int) (align / 8)); flags |= (align / 8) | SECTION_MERGE; - named_section_flags (name, flags); - return; + return get_section (name, flags, NULL); } - - readonly_data_section (); + return readonly_data_section; } /* Given NAME, a putative register name, discard any customary prefixes. */ @@ -662,6 +744,19 @@ strip_reg_name (const char *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 = alloca (strlen (name) + 2); + starred[0] = '*'; + strcpy (starred + 1, name); + 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, @@ -704,7 +799,8 @@ decode_reg_name (const char *asmspec) = ADDITIONAL_REGISTER_NAMES; for (i = 0; i < (int) ARRAY_SIZE (table); i++) - if (! strcmp (asmspec, table[i].name)) + if (table[i].name[0] + && ! strcmp (asmspec, table[i].name)) return table[i].number; } #endif /* ADDITIONAL_REGISTER_NAMES */ @@ -721,6 +817,177 @@ decode_reg_name (const char *asmspec) return -1; } +/* Return true if DECL's initializer is suitable for a BSS section. */ + +static bool +bss_initializer_p (tree decl) +{ + return (DECL_INITIAL (decl) == NULL + || DECL_INITIAL (decl) == error_mark_node + || (flag_zero_initialized_in_bss + /* Leave constant zeroes in .rodata so they + can be shared. */ + && !TREE_READONLY (decl) + && initializer_zerop (DECL_INITIAL (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) + { + warning (0, "alignment of %q+D is greater than maximum object " + "file alignment. Using %d", decl, + MAX_OFILE_ALIGNMENT/BITS_PER_UNIT); + align = MAX_OFILE_ALIGNMENT; + } + + /* On some machines, it is good to increase alignment sometimes. */ + if (! DECL_USER_ALIGN (decl)) + { +#ifdef DATA_ALIGNMENT + align = DATA_ALIGNMENT (TREE_TYPE (decl), align); +#endif +#ifdef CONSTANT_ALIGNMENT + if (DECL_INITIAL (decl) != 0 && DECL_INITIAL (decl) != error_mark_node) + align = CONSTANT_ALIGNMENT (DECL_INITIAL (decl), align); +#endif + } + + /* Reset the alignment in case we have made it tighter, so we can benefit + from it in get_pointer_alignment. */ + DECL_ALIGN (decl) = align; +} + +/* 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. */ + +static section * +get_variable_section (tree decl, bool prefer_noswitch_p) +{ + int reloc; + + /* If the decl has been given an explicit section name, then it + isn't common, and shouldn't be handled as such. */ + if (DECL_COMMON (decl) && DECL_SECTION_NAME (decl) == NULL) + { + if (DECL_THREAD_LOCAL_P (decl)) + return tls_comm_section; + if (TREE_PUBLIC (decl) && bss_initializer_p (decl)) + return comm_section; + } + + 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; + + resolve_unique_section (decl, reloc, flag_data_sections); + if (IN_NAMED_SECTION (decl)) + return get_named_section (decl, NULL, reloc); + + if (!DECL_THREAD_LOCAL_P (decl) + && !(prefer_noswitch_p && targetm.have_switchable_bss_sections) + && bss_initializer_p (decl)) + { + if (!TREE_PUBLIC (decl)) + return lcomm_section; + if (bss_noswitch_section) + return bss_noswitch_section; + } + + return targetm.asm_out.select_section (decl, reloc, DECL_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 (TREE_CODE (decl) == VAR_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_ONE_ONLY (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 (!host_integerp (DECL_SIZE_UNIT (decl), 1)) + return NULL; + + /* Find out which section should contain DECL. We cannot put it into + an object block if it requires a standalone definition. */ + if (TREE_CODE (decl) == VAR_DECL) + align_variable (decl, 0); + sect = get_variable_section (decl, true); + if (SECTION_STYLE (sect) == SECTION_NOSWITCH) + 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) +{ + /* Only data DECLs can be placed into object blocks. */ + if (TREE_CODE (decl) != VAR_DECL && TREE_CODE (decl) != CONST_DECL) + return false; + + /* Detect decls created by dw2_force_const_mem. Such decls are + special because DECL_INITIAL doesn't specify the decl's true value. + dw2_output_indirect_constants will instead call assemble_variable + with dont_output_data set to 1 and then print the contents itself. */ + if (DECL_INITIAL (decl) == decl) + return false; + + /* If this decl is an alias, then we don't want to emit a definition. */ + if (lookup_attribute ("alias", DECL_ATTRIBUTES (decl))) + return false; + + return true; +} + /* 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. @@ -728,41 +995,41 @@ decode_reg_name (const char *asmspec) There is, however, one exception: this function handles variables explicitly placed in a particular register by the user. - ASMSPEC, if not 0, is the string which the user specified as the - assembler symbol name. - This is never called for PARM_DECL nodes. */ void -make_decl_rtl (tree decl, const char *asmspec) +make_decl_rtl (tree decl) { const char *name = 0; int reg_number; 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. */ - if (TREE_CODE (decl) == PARM_DECL - || TREE_CODE (decl) == RESULT_DECL - || (TREE_CODE (decl) == VAR_DECL - && !TREE_STATIC (decl) - && !TREE_PUBLIC (decl) - && !DECL_EXTERNAL (decl) - && !DECL_REGISTER (decl))) - abort (); + gcc_assert (TREE_CODE (decl) != VAR_DECL + || TREE_STATIC (decl) + || TREE_PUBLIC (decl) + || DECL_EXTERNAL (decl) + || DECL_REGISTER (decl)); + /* And that we were not given a type or a label. */ - else if (TREE_CODE (decl) == TYPE_DECL - || TREE_CODE (decl) == LABEL_DECL) - abort (); + 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. */ - if (GET_MODE (DECL_RTL (decl)) != DECL_MODE (decl)) - SET_DECL_RTL (decl, adjust_address_nv (DECL_RTL (decl), - DECL_MODE (decl), 0)); + 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 @@ -772,36 +1039,44 @@ make_decl_rtl (tree decl, const char *asmspec) /* 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); - return; - } + targetm.encode_section_info (decl, DECL_RTL (decl), false); - reg_number = decode_reg_name (asmspec); - if (reg_number == -2) - { - /* ASMSPEC is given, and not the name of a register. Mark the - name with a star so assemble_name won't munge it. */ - char *starred = alloca (strlen (asmspec) + 2); - starred[0] = '*'; - strcpy (starred + 1, asmspec); - change_decl_assembler_name (decl, get_identifier (starred)); + /* 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)); + + /* Make this function static known to the mudflap runtime. */ + if (flag_mudflap && TREE_CODE (decl) == VAR_DECL) + mudflap_enqueue_decl (decl); + + return; } name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)); - if (TREE_CODE (decl) != FUNCTION_DECL && DECL_REGISTER (decl)) + 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; + reg_number = decode_reg_name (asmspec); /* First detect errors in declaring global registers. */ if (reg_number == -1) - error ("%Jregister name not specified for '%D'", decl, decl); + error ("register name not specified for %q+D", decl); else if (reg_number < 0) - error ("%Jinvalid register name for '%D'", decl, decl); + error ("invalid register name for %q+D", decl); else if (TYPE_MODE (TREE_TYPE (decl)) == BLKmode) - error ("%Jdata type of '%D' isn't suitable for a register", - decl, decl); + error ("data type of %q+D isn%'t suitable for a register", + decl); else if (! HARD_REGNO_MODE_OK (reg_number, TYPE_MODE (TREE_TYPE (decl)))) - error ("%Jregister specified for '%D' isn't suitable for data type", - decl, decl); + error ("register specified for %q+D isn%'t suitable for data type", + decl); /* Now handle properly declared static register variables. */ else { @@ -813,7 +1088,9 @@ make_decl_rtl (tree decl, const char *asmspec) error ("global register variable has initial value"); } if (TREE_THIS_VOLATILE (decl)) - warning ("volatile register variables don't work as you might wish"); + 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 @@ -829,9 +1106,10 @@ make_decl_rtl (tree decl, const char *asmspec) /* 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, DECL_MODE (decl)); + nregs = hard_regno_nregs[reg_number][DECL_MODE (decl)]; while (nregs > 0) globalize_reg (reg_number + --nregs); } @@ -840,12 +1118,19 @@ make_decl_rtl (tree decl, const char *asmspec) return; } } - /* Now handle ordinary static variables and functions (in memory). Also handle vars declared register invalidly. */ - - if (reg_number >= 0 || reg_number == -3) - error ("%Jregister name given for non-register variable '%D'", decl, decl); + 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. */ @@ -859,9 +1144,12 @@ make_decl_rtl (tree decl, const char *asmspec) if (TREE_CODE (decl) == VAR_DECL && DECL_WEAK (decl)) DECL_COMMON (decl) = 0; - x = gen_rtx_SYMBOL_REF (Pmode, name); + if (use_object_blocks_p () && use_blocks_for_decl_p (decl)) + x = create_block_symbol (name, get_block_for_decl (decl), -1); + else + x = gen_rtx_SYMBOL_REF (Pmode, name); SYMBOL_REF_WEAK (x) = DECL_WEAK (decl); - SYMBOL_REF_DECL (x) = decl; + SET_SYMBOL_REF_DECL (x, decl); x = gen_rtx_MEM (DECL_MODE (decl), x); if (TREE_CODE (decl) != FUNCTION_DECL) @@ -872,19 +1160,11 @@ make_decl_rtl (tree decl, const char *asmspec) 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); -} - -/* Make the rtl for variable VAR be volatile. - Use this only for static variables. */ + targetm.encode_section_info (decl, DECL_RTL (decl), true); -void -make_var_volatile (tree var) -{ - if (GET_CODE (DECL_RTL (var)) != MEM) - abort (); - - MEM_VOLATILE_P (DECL_RTL (var)) = 1; + /* Make this function static known to the mudflap runtime. */ + if (flag_mudflap && TREE_CODE (decl) == VAR_DECL) + mudflap_enqueue_decl (decl); } /* Output a string of literal assembler code @@ -906,14 +1186,18 @@ assemble_asm (tree string) between 0 and MAX_INIT_PRIORITY. */ void -default_stabs_asm_out_destructor (rtx symbol, int priority ATTRIBUTE_UNUSED) +default_stabs_asm_out_destructor (rtx symbol ATTRIBUTE_UNUSED, + int priority ATTRIBUTE_UNUSED) { +#if defined DBX_DEBUGGING_INFO || defined XCOFF_DEBUGGING_INFO /* Tell GNU LD that this is part of the static destructor set. This will work for any system that uses stabs, most usefully aout systems. */ - fprintf (asm_out_file, "%s\"___DTOR_LIST__\",22,0,0,", ASM_STABS_OP); - assemble_name (asm_out_file, XSTR (symbol, 0)); - fputc ('\n', asm_out_file); + dbxout_begin_simple_stabs ("___DTOR_LIST__", 22 /* N_SETT */); + dbxout_stab_value_label (XSTR (symbol, 0)); +#else + sorry ("global destructors not supported on this target"); +#endif } void @@ -933,28 +1217,17 @@ default_named_section_asm_out_destructor (rtx symbol, int priority) section = buf; } - named_section_flags (section, SECTION_WRITE); + switch_to_section (get_section (section, SECTION_WRITE, NULL)); assemble_align (POINTER_SIZE); assemble_integer (symbol, POINTER_SIZE / BITS_PER_UNIT, POINTER_SIZE, 1); } #ifdef DTORS_SECTION_ASM_OP void -dtors_section (void) -{ - if (in_section != in_dtors) - { - in_section = in_dtors; - fputs (DTORS_SECTION_ASM_OP, asm_out_file); - fputc ('\n', asm_out_file); - } -} - -void default_dtor_section_asm_out_destructor (rtx symbol, int priority ATTRIBUTE_UNUSED) { - dtors_section (); + switch_to_section (dtors_section); assemble_align (POINTER_SIZE); assemble_integer (symbol, POINTER_SIZE / BITS_PER_UNIT, POINTER_SIZE, 1); } @@ -963,14 +1236,18 @@ default_dtor_section_asm_out_destructor (rtx symbol, /* Likewise for global constructors. */ void -default_stabs_asm_out_constructor (rtx symbol, int priority ATTRIBUTE_UNUSED) +default_stabs_asm_out_constructor (rtx symbol ATTRIBUTE_UNUSED, + int priority ATTRIBUTE_UNUSED) { +#if defined DBX_DEBUGGING_INFO || defined XCOFF_DEBUGGING_INFO /* Tell GNU LD that this is part of the static destructor set. This will work for any system that uses stabs, most usefully aout systems. */ - fprintf (asm_out_file, "%s\"___CTOR_LIST__\",22,0,0,", ASM_STABS_OP); - assemble_name (asm_out_file, XSTR (symbol, 0)); - fputc ('\n', asm_out_file); + dbxout_begin_simple_stabs ("___CTOR_LIST__", 22 /* N_SETT */); + dbxout_stab_value_label (XSTR (symbol, 0)); +#else + sorry ("global constructors not supported on this target"); +#endif } void @@ -990,28 +1267,17 @@ default_named_section_asm_out_constructor (rtx symbol, int priority) section = buf; } - named_section_flags (section, SECTION_WRITE); + switch_to_section (get_section (section, SECTION_WRITE, NULL)); assemble_align (POINTER_SIZE); assemble_integer (symbol, POINTER_SIZE / BITS_PER_UNIT, POINTER_SIZE, 1); } #ifdef CTORS_SECTION_ASM_OP void -ctors_section (void) -{ - if (in_section != in_ctors) - { - in_section = in_ctors; - fputs (CTORS_SECTION_ASM_OP, asm_out_file); - fputc ('\n', asm_out_file); - } -} - -void default_ctor_section_asm_out_constructor (rtx symbol, int priority ATTRIBUTE_UNUSED) { - ctors_section (); + switch_to_section (ctors_section); assemble_align (POINTER_SIZE); assemble_integer (symbol, POINTER_SIZE / BITS_PER_UNIT, POINTER_SIZE, 1); } @@ -1037,17 +1303,18 @@ notice_global_symbol (tree decl) const char **type = &first_global_object_name; if (first_global_object_name - || !TREE_PUBLIC (decl) || DECL_EXTERNAL (decl) + || !TREE_PUBLIC (decl) + || DECL_EXTERNAL (decl) || !DECL_NAME (decl) || (TREE_CODE (decl) != FUNCTION_DECL && (TREE_CODE (decl) != VAR_DECL || (DECL_COMMON (decl) && (DECL_INITIAL (decl) == 0 || DECL_INITIAL (decl) == error_mark_node)))) - || GET_CODE (DECL_RTL (decl)) != MEM) + || !MEM_P (DECL_RTL (decl))) return; - /* We win when global object is found, but it is usefull to know about weak + /* 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)) type = &weak_global_object_name; @@ -1055,11 +1322,11 @@ notice_global_symbol (tree decl) if (!*type) { const char *p; - char *name; + const char *name; rtx decl_rtl = DECL_RTL (decl); - p = (* targetm.strip_name_encoding) (XSTR (XEXP (decl_rtl, 0), 0)); - name = xstrdup (p); + p = targetm.strip_name_encoding (XSTR (XEXP (decl_rtl, 0), 0)); + name = ggc_strdup (p); *type = name; } @@ -1074,6 +1341,31 @@ void assemble_start_function (tree decl, const char *fnname) { int align; + char tmp_label[100]; + bool hot_label_written = false; + + cfun->unlikely_text_section_name = NULL; + + first_function_block_is_cold = false; + if (flag_reorder_blocks_and_partition) + { + ASM_GENERATE_INTERNAL_LABEL (tmp_label, "LHOTB", const_labelno); + cfun->hot_section_label = ggc_strdup (tmp_label); + ASM_GENERATE_INTERNAL_LABEL (tmp_label, "LCOLDB", const_labelno); + cfun->cold_section_label = ggc_strdup (tmp_label); + ASM_GENERATE_INTERNAL_LABEL (tmp_label, "LHOTE", const_labelno); + cfun->hot_section_end_label = ggc_strdup (tmp_label); + ASM_GENERATE_INTERNAL_LABEL (tmp_label, "LCOLDE", const_labelno); + cfun->cold_section_end_label = ggc_strdup (tmp_label); + const_labelno++; + } + else + { + cfun->hot_section_label = NULL; + cfun->cold_section_label = NULL; + cfun->hot_section_end_label = NULL; + cfun->cold_section_end_label = NULL; + } /* The following code does not need preprocessing in the assembler. */ @@ -1083,7 +1375,54 @@ assemble_start_function (tree decl, const char *fnname) output_constant_pool (fnname, decl); resolve_unique_section (decl, 0, flag_function_sections); - function_section (decl); + + /* 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 (flag_reorder_blocks_and_partition) + { + switch_to_section (unlikely_text_section ()); + assemble_align (FUNCTION_BOUNDARY); + ASM_OUTPUT_LABEL (asm_out_file, cfun->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 (!current_function_is_thunk + && BB_PARTITION (ENTRY_BLOCK_PTR->next_bb) == BB_COLD_PARTITION) + { + switch_to_section (text_section); + assemble_align (FUNCTION_BOUNDARY); + ASM_OUTPUT_LABEL (asm_out_file, cfun->hot_section_label); + hot_label_written = true; + first_function_block_is_cold = true; + } + } + else if (DECL_SECTION_NAME (decl)) + { + /* Calls to function_section rely on first_function_block_is_cold + being accurate. The first block may be cold even if we aren't + doing partitioning, if the entire function was decided by + choose_function_section (predict.c) to be cold. */ + + initialize_cold_section_name (); + + if (cfun->unlikely_text_section_name + && strcmp (TREE_STRING_POINTER (DECL_SECTION_NAME (decl)), + cfun->unlikely_text_section_name) == 0) + 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)); + if (flag_reorder_blocks_and_partition + && !hot_label_written) + ASM_OUTPUT_LABEL (asm_out_file, cfun->hot_section_label); /* Tell assembler to move to target machine's alignment for functions. */ align = floor_log2 (FUNCTION_BOUNDARY / BITS_PER_UNIT); @@ -1125,6 +1464,9 @@ assemble_start_function (tree decl, const char *fnname) maybe_assemble_visibility (decl); } + if (DECL_PRESERVE_P (decl)) + targetm.asm_out.mark_decl_preserved (fnname); + /* 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); @@ -1138,15 +1480,34 @@ assemble_start_function (tree decl, const char *fnname) function. DECL describes the function. NAME is the function's name. */ void -assemble_end_function (tree decl, const char *fnname) +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 (flag_reorder_blocks_and_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); - function_section (decl); /* need to switch back */ + 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 (flag_reorder_blocks_and_partition) + { + section *save_text_section; + + save_text_section = in_section; + switch_to_section (unlikely_text_section ()); + ASM_OUTPUT_LABEL (asm_out_file, cfun->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, cfun->hot_section_end_label); + switch_to_section (save_text_section); } } @@ -1162,7 +1523,7 @@ assemble_zeros (unsigned HOST_WIDE_INT size) #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_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++) @@ -1209,117 +1570,142 @@ assemble_string (const char *p, int size) } -#if defined ASM_OUTPUT_ALIGNED_DECL_LOCAL -#define ASM_EMIT_LOCAL(decl, name, size, rounded) \ - ASM_OUTPUT_ALIGNED_DECL_LOCAL (asm_out_file, decl, name, size, DECL_ALIGN (decl)) -#else -#if defined ASM_OUTPUT_ALIGNED_LOCAL -#define ASM_EMIT_LOCAL(decl, name, size, rounded) \ - ASM_OUTPUT_ALIGNED_LOCAL (asm_out_file, name, size, DECL_ALIGN (decl)) +/* 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 + ASM_OUTPUT_ALIGNED_DECL_LOCAL (asm_out_file, decl, name, + size, DECL_ALIGN (decl)); + return true; +#elif defined ASM_OUTPUT_ALIGNED_LOCAL + ASM_OUTPUT_ALIGNED_LOCAL (asm_out_file, name, size, DECL_ALIGN (decl)); + return true; #else -#define ASM_EMIT_LOCAL(decl, name, size, rounded) \ - ASM_OUTPUT_LOCAL (asm_out_file, name, size, rounded) -#endif + 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 || defined ASM_OUTPUT_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) +{ #if defined ASM_OUTPUT_ALIGNED_BSS -#define ASM_EMIT_BSS(decl, name, size, rounded) \ - ASM_OUTPUT_ALIGNED_BSS (asm_out_file, decl, name, size, DECL_ALIGN (decl)) -#else -#if defined ASM_OUTPUT_BSS -#define ASM_EMIT_BSS(decl, name, size, rounded) \ - ASM_OUTPUT_BSS (asm_out_file, decl, name, size, rounded) + ASM_OUTPUT_ALIGNED_BSS (asm_out_file, decl, name, size, DECL_ALIGN (decl)); + return true; #else -#undef ASM_EMIT_BSS + ASM_OUTPUT_BSS (asm_out_file, decl, name, size, rounded); + return false; #endif +} #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 -#define ASM_EMIT_COMMON(decl, name, size, rounded) \ - ASM_OUTPUT_ALIGNED_DECL_COMMON (asm_out_file, decl, name, size, DECL_ALIGN (decl)) -#else -#if defined ASM_OUTPUT_ALIGNED_COMMON -#define ASM_EMIT_COMMON(decl, name, size, rounded) \ - ASM_OUTPUT_ALIGNED_COMMON (asm_out_file, name, size, DECL_ALIGN (decl)) + ASM_OUTPUT_ALIGNED_DECL_COMMON (asm_out_file, decl, name, + size, DECL_ALIGN (decl)); + return true; +#elif defined ASM_OUTPUT_ALIGNED_COMMON + ASM_OUTPUT_ALIGNED_COMMON (asm_out_file, name, size, DECL_ALIGN (decl)); + return true; #else -#define ASM_EMIT_COMMON(decl, name, size, rounded) \ - ASM_OUTPUT_COMMON (asm_out_file, name, size, rounded) -#endif + ASM_OUTPUT_COMMON (asm_out_file, name, size, rounded); + return false; #endif +} + +/* A noswitch_section_callback for tls_comm_section. */ static bool -asm_emit_uninitialised (tree decl, const char *name, - unsigned HOST_WIDE_INT size ATTRIBUTE_UNUSED, - unsigned HOST_WIDE_INT rounded ATTRIBUTE_UNUSED) +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) { - enum - { - asm_dest_common, - asm_dest_bss, - asm_dest_local - } - destination = asm_dest_local; - - /* ??? We should handle .bss via select_section mechanisms rather than - via special target hooks. That would eliminate this special case. */ - if (TREE_PUBLIC (decl)) - { - if (!DECL_COMMON (decl)) -#ifdef ASM_EMIT_BSS - destination = asm_dest_bss; +#ifdef ASM_OUTPUT_TLS_COMMON + ASM_OUTPUT_TLS_COMMON (asm_out_file, decl, name, size); + return true; #else - return false; + sorry ("thread-local COMMON data not implemented"); + return true; #endif - else - destination = asm_dest_common; - } +} - if (destination == asm_dest_bss) - globalize_decl (decl); - resolve_unique_section (decl, 0, flag_data_sections); +/* Assemble DECL given that it belongs in SECTION_NOSWITCH section SECT. + NAME is the name of DECL's SYMBOL_REF. */ - if (flag_shared_data) - { - switch (destination) - { -#ifdef ASM_OUTPUT_SHARED_BSS - case asm_dest_bss: - ASM_OUTPUT_SHARED_BSS (asm_out_file, decl, name, size, rounded); - return; -#endif -#ifdef ASM_OUTPUT_SHARED_COMMON - case asm_dest_common: - ASM_OUTPUT_SHARED_COMMON (asm_out_file, name, size, rounded); - return; -#endif -#ifdef ASM_OUTPUT_SHARED_LOCAL - case asm_dest_local: - ASM_OUTPUT_SHARED_LOCAL (asm_out_file, name, size, rounded); - return; -#endif - default: - break; - } - } +static void +assemble_noswitch_variable (tree decl, const char *name, section *sect) +{ + unsigned HOST_WIDE_INT size, rounded; - switch (destination) + size = tree_low_cst (DECL_SIZE_UNIT (decl), 1); + rounded = 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) DECL_ALIGN_UNIT (decl) > rounded) + warning (0, "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) +{ + /* 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) { -#ifdef ASM_EMIT_BSS - case asm_dest_bss: - ASM_EMIT_BSS (decl, name, size, rounded); - break; -#endif - case asm_dest_common: - ASM_EMIT_COMMON (decl, name, size, rounded); - break; - case asm_dest_local: - ASM_EMIT_LOCAL (decl, name, size, rounded); - break; - default: - abort (); + 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_low_cst (DECL_SIZE_UNIT (decl), 1), + DECL_ALIGN (decl)); + else + /* Leave space for it. */ + assemble_zeros (tree_low_cst (DECL_SIZE_UNIT (decl), 1)); } - - return true; } /* Assemble everything that is needed for a variable or function declaration. @@ -1337,12 +1723,11 @@ assemble_variable (tree decl, int top_level ATTRIBUTE_UNUSED, int at_end ATTRIBUTE_UNUSED, int dont_output_data) { const char *name; - unsigned int align; - int reloc = 0; - rtx decl_rtl; + rtx decl_rtl, symbol; + section *sect; if (lang_hooks.decls.prepare_assemble_variable) - (*lang_hooks.decls.prepare_assemble_variable) (decl); + lang_hooks.decls.prepare_assemble_variable (decl); last_assemble_variable_decl = 0; @@ -1360,7 +1745,7 @@ assemble_variable (tree decl, int top_level ATTRIBUTE_UNUSED, return; /* Do nothing for global register variables. */ - if (DECL_RTL_SET_P (decl) && GET_CODE (DECL_RTL (decl)) == REG) + if (DECL_RTL_SET_P (decl) && REG_P (DECL_RTL (decl))) { TREE_ASM_WRITTEN (decl) = 1; return; @@ -1377,7 +1762,7 @@ assemble_variable (tree decl, int top_level ATTRIBUTE_UNUSED, if (!dont_output_data && DECL_SIZE (decl) == 0) { - error ("%Jstorage size of `%D' isn't known", decl, decl); + error ("storage size of %q+D isn%'t known", decl); TREE_ASM_WRITTEN (decl) = 1; return; } @@ -1405,154 +1790,59 @@ assemble_variable (tree decl, int top_level ATTRIBUTE_UNUSED, if (! dont_output_data && ! host_integerp (DECL_SIZE_UNIT (decl), 1)) { - error ("%Jsize of variable '%D' is too large", decl, decl); + error ("size of variable %q+D is too large", decl); return; } - name = XSTR (XEXP (decl_rtl, 0), 0); + gcc_assert (MEM_P (decl_rtl)); + gcc_assert (GET_CODE (XEXP (decl_rtl, 0)) == SYMBOL_REF); + symbol = XEXP (decl_rtl, 0); + name = XSTR (symbol, 0); if (TREE_PUBLIC (decl) && DECL_NAME (decl)) notice_global_symbol (decl); /* Compute the alignment of this data. */ - 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. */ -#ifndef MAX_OFILE_ALIGNMENT -#define MAX_OFILE_ALIGNMENT BIGGEST_ALIGNMENT -#endif - if (align > MAX_OFILE_ALIGNMENT) - { - warning ("%Jalignment of '%D' is greater than maximum object " - "file alignment. Using %d", decl, decl, - MAX_OFILE_ALIGNMENT/BITS_PER_UNIT); - align = MAX_OFILE_ALIGNMENT; - } - - /* On some machines, it is good to increase alignment sometimes. */ - if (! DECL_USER_ALIGN (decl)) - { -#ifdef DATA_ALIGNMENT - align = DATA_ALIGNMENT (TREE_TYPE (decl), align); -#endif -#ifdef CONSTANT_ALIGNMENT - if (DECL_INITIAL (decl) != 0 && DECL_INITIAL (decl) != error_mark_node) - align = CONSTANT_ALIGNMENT (DECL_INITIAL (decl), align); -#endif - } - - /* Reset the alignment in case we have made it tighter, so we can benefit - from it in get_pointer_alignment. */ - DECL_ALIGN (decl) = align; - set_mem_align (decl_rtl, align); + align_variable (decl, dont_output_data); + set_mem_align (decl_rtl, DECL_ALIGN (decl)); if (TREE_PUBLIC (decl)) maybe_assemble_visibility (decl); - /* Output any data that we will need to use the address of. */ - 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)); - output_addressed_constants (DECL_INITIAL (decl)); - } - resolve_unique_section (decl, reloc, flag_data_sections); - - /* Handle uninitialized definitions. */ - - /* If the decl has been given an explicit section name, then it - isn't common, and shouldn't be handled as such. */ - if (DECL_SECTION_NAME (decl) || dont_output_data) - ; - /* We don't implement common thread-local data at present. */ - else if (DECL_THREAD_LOCAL (decl)) - { - if (DECL_COMMON (decl)) - sorry ("thread-local COMMON data not implemented"); - } - else if (DECL_INITIAL (decl) == 0 - || DECL_INITIAL (decl) == error_mark_node - || (flag_zero_initialized_in_bss - /* Leave constant zeroes in .rodata so they can be shared. */ - && !TREE_READONLY (decl) - && initializer_zerop (DECL_INITIAL (decl)))) - { - unsigned HOST_WIDE_INT size = tree_low_cst (DECL_SIZE_UNIT (decl), 1); - unsigned HOST_WIDE_INT rounded = 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 !defined(ASM_OUTPUT_ALIGNED_COMMON) && !defined(ASM_OUTPUT_ALIGNED_DECL_COMMON) && !defined(ASM_OUTPUT_ALIGNED_BSS) - if ((unsigned HOST_WIDE_INT) DECL_ALIGN (decl) / BITS_PER_UNIT > rounded) - warning ("%Jrequested alignment for '%D' is greater than " - "implemented alignment of %d", decl, decl, rounded); -#endif - - /* If the target cannot output uninitialized but not common global data - in .bss, then we have to use .data, so fall through. */ - if (asm_emit_uninitialised (decl, name, size, rounded)) - return; - } - - /* Handle initialized definitions. - Also handle uninitialized global definitions if -fno-common and the - target doesn't support ASM_OUTPUT_BSS. */ + if (DECL_PRESERVE_P (decl)) + targetm.asm_out.mark_decl_preserved (name); /* First make the assembler name(s) global if appropriate. */ - if (TREE_PUBLIC (decl) && DECL_NAME (decl)) + sect = get_variable_section (decl, false); + if (TREE_PUBLIC (decl) + && DECL_NAME (decl) + && (sect->common.flags & SECTION_COMMON) == 0) globalize_decl (decl); - /* Switch to the appropriate section. */ - variable_section (decl, reloc); + /* 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)); /* dbxout.c needs to know this. */ - if (in_text_section ()) + if (sect && (sect->common.flags & SECTION_CODE) != 0) DECL_IN_TEXT_SECTION (decl) = 1; - /* Output the alignment of this data. */ - if (align > BITS_PER_UNIT) + /* 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)) { - ASM_OUTPUT_ALIGN (asm_out_file, - floor_log2 (DECL_ALIGN (decl) / BITS_PER_UNIT)); + gcc_assert (!dont_output_data); + place_block_symbol (symbol); } - - /* 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) + else if (SECTION_STYLE (sect) == SECTION_NOSWITCH) + assemble_noswitch_variable (decl, name, sect); + else { - if (DECL_INITIAL (decl) && DECL_INITIAL (decl) != error_mark_node) - /* Output the actual data. */ - output_constant (DECL_INITIAL (decl), - tree_low_cst (DECL_SIZE_UNIT (decl), 1), - align); - else - /* Leave space for it. */ - assemble_zeros (tree_low_cst (DECL_SIZE_UNIT (decl), 1)); + switch_to_section (sect); + if (DECL_ALIGN (decl) > BITS_PER_UNIT) + ASM_OUTPUT_ALIGN (asm_out_file, floor_log2 (DECL_ALIGN_UNIT (decl))); + assemble_variable_contents (decl, name, dont_output_data); } } @@ -1592,6 +1882,12 @@ contains_pointers_p (tree type) } } +/* In unit-at-a-time mode, 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; + #ifdef ASM_OUTPUT_EXTERNAL /* 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. */ @@ -1613,7 +1909,36 @@ incorporeal_function_p (tree decl) } 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)); + + pending_assemble_externals = 0; #endif +} /* Output something to declare an external symbol to the assembler. (Most assemblers don't need this, so we normally output nothing.) @@ -1626,23 +1951,17 @@ assemble_external (tree decl ATTRIBUTE_UNUSED) main body of this code is only rarely exercised. To provide some testing, on all platforms, we make sure that the ASM_OUT_FILE is open. If it's not, we should not be calling this function. */ - if (!asm_out_file) - abort (); + gcc_assert (asm_out_file); #ifdef ASM_OUTPUT_EXTERNAL - if (DECL_P (decl) && DECL_EXTERNAL (decl) && TREE_PUBLIC (decl)) - { - rtx rtl = DECL_RTL (decl); + if (!DECL_P (decl) || !DECL_EXTERNAL (decl) || !TREE_PUBLIC (decl)) + return; - if (GET_CODE (rtl) == MEM && 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)); - } - } + if (flag_unit_at_a_time) + pending_assemble_externals = tree_cons (0, decl, + pending_assemble_externals); + else + assemble_external_real (decl); #endif } @@ -1655,7 +1974,7 @@ assemble_external_libcall (rtx fun) if (! SYMBOL_REF_USED (fun)) { SYMBOL_REF_USED (fun) = 1; - (*targetm.asm_out.external_libcall) (fun); + targetm.asm_out.external_libcall (fun); } } @@ -1667,34 +1986,84 @@ assemble_label (const char *name) ASM_OUTPUT_LABEL (asm_out_file, name); } -/* Set the symbol_referenced flag for ID and notify callgraph code. */ +/* Set the symbol_referenced flag for ID. */ void mark_referenced (tree id) { - if (!TREE_SYMBOL_REFERENCED (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 (decl); + if (!DECL_EXTERNAL (decl) + && (!node->local.vtable_method || !cgraph_global_info_ready + || !node->local.finalized)) + cgraph_mark_needed_node (node); + } + else if (TREE_CODE (decl) == VAR_DECL) { - struct cgraph_node *node; - struct cgraph_varpool_node *vnode; + struct cgraph_varpool_node *node = cgraph_varpool_node (decl); + cgraph_varpool_mark_needed_node (node); + /* 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. */ +} - if (!cgraph_global_info_ready) - { - node = cgraph_node_for_identifier (id); - if (node) - cgraph_mark_needed_node (node); - } - vnode = cgraph_varpool_node_for_identifier (id); - if (vnode) - cgraph_varpool_mark_needed_node (vnode); +/* 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; } - TREE_SYMBOL_REFERENCED (id) = 1; + + return target; } -/* Output to FILE a reference to the assembler name of a C-level name NAME. - If NAME starts with a *, the rest of NAME is output verbatim. - Otherwise NAME is transformed in an implementation-defined way - (usually by the addition of an underscore). - Many macros in the tm file are defined to call this function. */ +/* 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); +} + +/* 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) @@ -1702,16 +2071,21 @@ assemble_name (FILE *file, const char *name) const char *real_name; tree id; - real_name = (* targetm.strip_name_encoding) (name); + real_name = targetm.strip_name_encoding (name); id = maybe_get_identifier (real_name); if (id) - mark_referenced (id); + { + tree id_orig = id; - if (name[0] == '*') - fputs (&name[1], file); - else - ASM_OUTPUT_LABELREF (file, name); + mark_referenced (id); + ultimate_transparent_alias_target (&id); + if (id != id_orig) + name = IDENTIFIER_POINTER (id); + gcc_assert (! TREE_CHAIN (id)); + } + + assemble_name_raw (file, name); } /* Allocate SIZE bytes writable static space with a gensym name @@ -1724,11 +2098,6 @@ assemble_static_space (unsigned HOST_WIDE_INT size) const char *namestring; rtx x; -#if 0 - if (flag_shared_data) - data_section (); -#endif - ASM_GENERATE_INTERNAL_LABEL (name, "LF", const_labelno); ++const_labelno; namestring = ggc_strdup (name); @@ -1762,6 +2131,8 @@ assemble_static_space (unsigned HOST_WIDE_INT size) This is done at most once per compilation. Returns an RTX for the address of the template. */ +static GTY(()) rtx initial_trampoline; + #ifdef TRAMPOLINE_TEMPLATE rtx assemble_trampoline_template (void) @@ -1771,12 +2142,15 @@ assemble_trampoline_template (void) int align; rtx symbol; + if (initial_trampoline) + return initial_trampoline; + /* By default, put trampoline templates in read-only data section. */ #ifdef TRAMPOLINE_SECTION - TRAMPOLINE_SECTION (); + switch_to_section (TRAMPOLINE_SECTION); #else - readonly_data_section (); + switch_to_section (readonly_data_section); #endif /* Write the assembler code to define one. */ @@ -1786,7 +2160,7 @@ assemble_trampoline_template (void) ASM_OUTPUT_ALIGN (asm_out_file, align); } - (*targetm.asm_out.internal_label) (asm_out_file, "LTRAMP", 0); + targetm.asm_out.internal_label (asm_out_file, "LTRAMP", 0); TRAMPOLINE_TEMPLATE (asm_out_file); /* Record the rtl to refer to it. */ @@ -1795,7 +2169,10 @@ assemble_trampoline_template (void) symbol = gen_rtx_SYMBOL_REF (Pmode, name); SYMBOL_REF_FLAGS (symbol) = SYMBOL_FLAG_LOCAL; - return symbol; + initial_trampoline = gen_rtx_MEM (BLKmode, symbol); + set_mem_align (initial_trampoline, TRAMPOLINE_ALIGNMENT); + + return initial_trampoline; } #endif @@ -1862,13 +2239,17 @@ default_assemble_integer (rtx x 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 / BITS_PER_UNIT) + 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. If FORCE is nonzero, abort if we can't output - the constant. */ + 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) @@ -1878,7 +2259,7 @@ assemble_integer (rtx x, unsigned int size, unsigned int align, int force) 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)) + if (targetm.asm_out.integer (x, size, aligned_p)) return true; /* If the object is a multi-byte one, try splitting it up. Split @@ -1905,12 +2286,10 @@ assemble_integer (rtx x, unsigned int size, unsigned int align, int force) /* If we've printed some of it, but not all of it, there's no going back now. */ - if (i > 0) - abort (); + gcc_assert (!i); } - if (force) - abort (); + gcc_assert (!force); return false; } @@ -1918,7 +2297,7 @@ assemble_integer (rtx x, unsigned int size, unsigned int align, int force) void assemble_real (REAL_VALUE_TYPE d, enum machine_mode mode, unsigned int align) { - long data[4]; + long data[4] = {0, 0, 0, 0}; int i; int bitsize, nelts, nunits, units_per; @@ -1958,7 +2337,7 @@ assemble_real (REAL_VALUE_TYPE d, enum machine_mode mode, unsigned int align) /* 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. - Abort if EXP does not reduce. */ + EXP must be reducible. */ struct addr_const GTY(()) { @@ -2002,7 +2381,7 @@ decode_addr_const (tree exp, struct addr_const *value) case LABEL_DECL: x = gen_rtx_MEM (FUNCTION_MODE, - gen_rtx_LABEL_REF (VOIDmode, force_label_rtx (target))); + gen_rtx_LABEL_REF (Pmode, force_label_rtx (target))); break; case REAL_CST: @@ -2014,47 +2393,16 @@ decode_addr_const (tree exp, struct addr_const *value) break; default: - abort (); + gcc_unreachable (); } - if (GET_CODE (x) != MEM) - abort (); + gcc_assert (MEM_P (x)); x = XEXP (x, 0); value->base = x; value->offset = offset; } -/* We do RTX_UNSPEC + XINT (blah), so nothing can go after RTX_UNSPEC. */ -enum kind { RTX_UNKNOWN, RTX_DOUBLE, RTX_VECTOR, RTX_INT, RTX_UNSPEC }; -struct rtx_const GTY(()) -{ - ENUM_BITFIELD(kind) kind : 16; - ENUM_BITFIELD(machine_mode) mode : 16; - union rtx_const_un { - REAL_VALUE_TYPE GTY ((tag ("4"))) du; - struct rtx_const_u_addr { - rtx base; - const char *symbol; - HOST_WIDE_INT offset; - } GTY ((tag ("1"))) addr; - struct rtx_const_u_di { - HOST_WIDE_INT high; - HOST_WIDE_INT low; - } GTY ((tag ("0"))) di; - - /* The max vector size we have is 16 wide; two variants for - integral and floating point vectors. */ - struct rtx_const_int_vec { - HOST_WIDE_INT high; - HOST_WIDE_INT low; - } GTY ((tag ("2"))) int_vec[16]; - - REAL_VALUE_TYPE GTY ((tag ("3"))) fp_vec[8]; - - } GTY ((desc ("%1.kind >= RTX_INT"), descbits ("1"))) un; -}; - /* Uniquize all constants that appear in memory. Each constant in memory thus far output is recorded in `const_desc_table'. */ @@ -2066,6 +2414,11 @@ struct constant_descriptor_tree GTY(()) /* The value of the constant. */ tree value; + + /* Hash of value. Computing the hash from value each time + hashfn is called can't work properly, as that means recursive + use of the hash table during hash table expansion. */ + hashval_t hash; }; static GTY((param_is (struct constant_descriptor_tree))) @@ -2079,7 +2432,7 @@ static void maybe_output_constant_def_contents (struct constant_descriptor_tree static hashval_t const_desc_hash (const void *ptr) { - return const_hash_1 (((struct constant_descriptor_tree *)ptr)->value); + return ((struct constant_descriptor_tree *)ptr)->hash; } static hashval_t @@ -2104,16 +2457,8 @@ const_hash_1 (const tree exp) return real_hash (TREE_REAL_CST_PTR (exp)); case STRING_CST: - if (flag_writable_strings) - { - p = (char *) &exp; - len = sizeof exp; - } - else - { - p = TREE_STRING_POINTER (exp); - len = TREE_STRING_LENGTH (exp); - } + p = TREE_STRING_POINTER (exp); + len = TREE_STRING_LENGTH (exp); break; case COMPLEX_CST: @@ -2121,28 +2466,18 @@ const_hash_1 (const tree exp) + const_hash_1 (TREE_IMAGPART (exp))); case CONSTRUCTOR: - if (TREE_CODE (TREE_TYPE (exp)) == SET_TYPE) - { - char *tmp; - - len = int_size_in_bytes (TREE_TYPE (exp)); - tmp = alloca (len); - get_set_constructor_bytes (exp, (unsigned char *) tmp, len); - p = tmp; - break; - } - else - { - tree link; + { + unsigned HOST_WIDE_INT idx; + tree value; - hi = 5 + int_size_in_bytes (TREE_TYPE (exp)); + hi = 5 + int_size_in_bytes (TREE_TYPE (exp)); - for (link = CONSTRUCTOR_ELTS (exp); link; link = TREE_CHAIN (link)) - if (TREE_VALUE (link)) - hi = hi * 603 + const_hash_1 (TREE_VALUE (link)); + FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (exp), idx, value) + if (value) + hi = hi * 603 + const_hash_1 (value); - return hi; - } + return hi; + } case ADDR_EXPR: case FDESC_EXPR: @@ -2150,19 +2485,24 @@ const_hash_1 (const tree exp) struct addr_const value; decode_addr_const (exp, &value); - if (GET_CODE (value.base) == SYMBOL_REF) + 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; 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 + CODE_LABEL_NUMBER (XEXP (value.base, 0)) * 13; + break; + + default: + gcc_unreachable (); } - else if (GET_CODE (value.base) == LABEL_REF) - hi = value.offset + CODE_LABEL_NUMBER (XEXP (value.base, 0)) * 13; - else - abort (); } return hi; @@ -2193,8 +2533,11 @@ const_hash_1 (const tree exp) static int const_desc_eq (const void *p1, const void *p2) { - return compare_constant (((struct constant_descriptor_tree *)p1)->value, - ((struct constant_descriptor_tree *)p2)->value); + const struct constant_descriptor_tree *c1 = p1; + const struct constant_descriptor_tree *c2 = p2; + if (c1->hash != c2->hash) + return 0; + return compare_constant (c1->value, c2->value); } /* Compare t1 and t2, and return 1 only if they are known to result in @@ -2219,6 +2562,8 @@ compare_constant (const tree t1, const tree t2) /* Integer constants are the same only if the same width of type. */ if (TYPE_PRECISION (TREE_TYPE (t1)) != TYPE_PRECISION (TREE_TYPE (t2))) return 0; + if (TYPE_MODE (TREE_TYPE (t1)) != TYPE_MODE (TREE_TYPE (t2))) + return 0; return tree_int_cst_equal (t1, t2); case REAL_CST: @@ -2229,9 +2574,6 @@ compare_constant (const tree t1, const tree t2) return REAL_VALUES_IDENTICAL (TREE_REAL_CST (t1), TREE_REAL_CST (t2)); case STRING_CST: - if (flag_writable_strings) - return t1 == t2; - if (TYPE_MODE (TREE_TYPE (t1)) != TYPE_MODE (TREE_TYPE (t2))) return 0; @@ -2244,72 +2586,60 @@ compare_constant (const tree t1, const tree t2) && compare_constant (TREE_IMAGPART (t1), TREE_IMAGPART (t2))); case CONSTRUCTOR: - typecode = TREE_CODE (TREE_TYPE (t1)); - if (typecode != TREE_CODE (TREE_TYPE (t2))) - return 0; - - if (typecode == SET_TYPE) - { - int len = int_size_in_bytes (TREE_TYPE (t2)); - unsigned char *tmp1, *tmp2; + { + VEC(constructor_elt, gc) *v1, *v2; + unsigned HOST_WIDE_INT idx; - if (int_size_in_bytes (TREE_TYPE (t1)) != len) - return 0; + typecode = TREE_CODE (TREE_TYPE (t1)); + if (typecode != TREE_CODE (TREE_TYPE (t2))) + return 0; - tmp1 = alloca (len); - tmp2 = alloca (len); + if (typecode == ARRAY_TYPE) + { + HOST_WIDE_INT size_1 = int_size_in_bytes (TREE_TYPE (t1)); + /* For arrays, check that the sizes all match. */ + if (TYPE_MODE (TREE_TYPE (t1)) != TYPE_MODE (TREE_TYPE (t2)) + || size_1 == -1 + || size_1 != int_size_in_bytes (TREE_TYPE (t2))) + return 0; + } + else + { + /* For record and union constructors, require exact type + equality. */ + if (TREE_TYPE (t1) != TREE_TYPE (t2)) + return 0; + } - if (get_set_constructor_bytes (t1, tmp1, len) != NULL_TREE) - return 0; - if (get_set_constructor_bytes (t2, tmp2, len) != NULL_TREE) + v1 = CONSTRUCTOR_ELTS (t1); + v2 = CONSTRUCTOR_ELTS (t2); + if (VEC_length (constructor_elt, v1) + != VEC_length (constructor_elt, v2)) return 0; - return memcmp (tmp1, tmp2, len) == 0; - } - else - { - tree l1, l2; - - if (typecode == ARRAY_TYPE) - { - HOST_WIDE_INT size_1 = int_size_in_bytes (TREE_TYPE (t1)); - /* For arrays, check that the sizes all match. */ - if (TYPE_MODE (TREE_TYPE (t1)) != TYPE_MODE (TREE_TYPE (t2)) - || size_1 == -1 - || size_1 != int_size_in_bytes (TREE_TYPE (t2))) - return 0; - } - else - { - /* For record and union constructors, require exact type - equality. */ - if (TREE_TYPE (t1) != TREE_TYPE (t2)) - return 0; - } - - for (l1 = CONSTRUCTOR_ELTS (t1), l2 = CONSTRUCTOR_ELTS (t2); - l1 && l2; - l1 = TREE_CHAIN (l1), l2 = TREE_CHAIN (l2)) - { - /* Check that each value is the same... */ - if (! compare_constant (TREE_VALUE (l1), TREE_VALUE (l2))) - return 0; - /* ... and that they apply to the same fields! */ - if (typecode == ARRAY_TYPE) - { - if (! compare_constant (TREE_PURPOSE (l1), - TREE_PURPOSE (l2))) - return 0; - } - else - { - if (TREE_PURPOSE (l1) != TREE_PURPOSE (l2)) - return 0; - } - } + for (idx = 0; idx < VEC_length (constructor_elt, v1); ++idx) + { + constructor_elt *c1 = VEC_index (constructor_elt, v1, idx); + constructor_elt *c2 = VEC_index (constructor_elt, v2, idx); + + /* Check that each value is the same... */ + if (!compare_constant (c1->value, c2->value)) + return 0; + /* ... and that they apply to the same fields! */ + if (typecode == ARRAY_TYPE) + { + if (!compare_constant (c1->index, c2->index)) + return 0; + } + else + { + if (c1->index != c2->index) + return 0; + } + } - return l1 == NULL_TREE && l2 == NULL_TREE; - } + return 1; + } case ADDR_EXPR: case FDESC_EXPR: @@ -2331,13 +2661,14 @@ compare_constant (const tree t1, const tree t2) case NOP_EXPR: case CONVERT_EXPR: case NON_LVALUE_EXPR: + case VIEW_CONVERT_EXPR: return compare_constant (TREE_OPERAND (t1, 0), TREE_OPERAND (t2, 0)); default: { tree nt1, nt2; - nt1 = (*lang_hooks.expand_constant) (t1); - nt2 = (*lang_hooks.expand_constant) (t2); + nt1 = lang_hooks.expand_constant (t1); + nt2 = lang_hooks.expand_constant (t2); if (nt1 != t1 || nt2 != t2) return compare_constant (nt1, nt2); else @@ -2345,13 +2676,11 @@ compare_constant (const tree t1, const tree t2) } } - /* Should not get here. */ - abort (); + gcc_unreachable (); } /* Make a copy of the whole tree structure for a constant. This - handles the same types of nodes that compare_constant handles. - Writable string constants are never copied. */ + handles the same types of nodes that compare_constant handles. */ static tree copy_constant (tree exp) @@ -2361,18 +2690,15 @@ copy_constant (tree exp) case ADDR_EXPR: /* For ADDR_EXPR, we do not want to copy the decl whose address is requested. We do want to copy constants though. */ - if (TREE_CODE_CLASS (TREE_CODE (TREE_OPERAND (exp, 0))) == 'c') + if (CONSTANT_CLASS_P (TREE_OPERAND (exp, 0))) return build1 (TREE_CODE (exp), TREE_TYPE (exp), copy_constant (TREE_OPERAND (exp, 0))); else return copy_node (exp); - case STRING_CST: - if (flag_writable_strings) - return exp; - /* FALLTHROUGH */ case INTEGER_CST: case REAL_CST: + case STRING_CST: return copy_node (exp); case COMPLEX_CST: @@ -2382,9 +2708,9 @@ copy_constant (tree exp) case PLUS_EXPR: case MINUS_EXPR: - return build (TREE_CODE (exp), TREE_TYPE (exp), - copy_constant (TREE_OPERAND (exp, 0)), - copy_constant (TREE_OPERAND (exp, 1))); + return build2 (TREE_CODE (exp), TREE_TYPE (exp), + copy_constant (TREE_OPERAND (exp, 0)), + copy_constant (TREE_OPERAND (exp, 1))); case NOP_EXPR: case CONVERT_EXPR: @@ -2396,31 +2722,72 @@ copy_constant (tree exp) case CONSTRUCTOR: { tree copy = copy_node (exp); - tree list = copy_list (CONSTRUCTOR_ELTS (exp)); - tree tail; - - CONSTRUCTOR_ELTS (copy) = list; - for (tail = list; tail; tail = TREE_CHAIN (tail)) - TREE_VALUE (tail) = copy_constant (TREE_VALUE (tail)); - if (TREE_CODE (TREE_TYPE (exp)) == SET_TYPE) - for (tail = list; tail; tail = TREE_CHAIN (tail)) - TREE_PURPOSE (tail) = copy_constant (TREE_PURPOSE (tail)); + VEC(constructor_elt, gc) *v; + unsigned HOST_WIDE_INT idx; + tree purpose, value; + v = VEC_alloc(constructor_elt, gc, VEC_length(constructor_elt, + CONSTRUCTOR_ELTS (exp))); + FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (exp), idx, purpose, value) + { + constructor_elt *ce = VEC_quick_push (constructor_elt, v, NULL); + ce->index = purpose; + ce->value = copy_constant (value); + } + CONSTRUCTOR_ELTS (copy) = v; return copy; } default: { - tree t; - t = (*lang_hooks.expand_constant) (exp); - if (t != exp) - return copy_constant (t); - else - abort (); + tree t = lang_hooks.expand_constant (exp); + + gcc_assert (t != exp); + return copy_constant (t); } } } +/* Return the alignment of constant EXP in bits. */ + +static unsigned int +get_constant_alignment (tree exp) +{ + unsigned int align; + + align = TYPE_ALIGN (TREE_TYPE (exp)); +#ifdef CONSTANT_ALIGNMENT + align = CONSTANT_ALIGNMENT (exp, align); +#endif + return align; +} + +/* Return the section into which constant EXP should be placed. */ + +static section * +get_constant_section (tree exp) +{ + if (IN_NAMED_SECTION (exp)) + return get_named_section (exp, NULL, compute_reloc_for_constant (exp)); + else + return targetm.asm_out.select_section (exp, + compute_reloc_for_constant (exp), + get_constant_alignment (exp)); +} + +/* 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)); + if (TREE_CODE (exp) == STRING_CST) + size = MAX (TREE_STRING_LENGTH (exp), size); + 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. @@ -2440,14 +2807,25 @@ build_constant_desc (tree exp) desc = ggc_alloc (sizeof (*desc)); desc->value = copy_constant (exp); + /* Propagate marked-ness to copied constant. */ + if (flag_mudflap && mf_marked_p (exp)) + mf_mark (desc->value); + /* Create a string containing the label name, in LABEL. */ labelno = const_labelno++; ASM_GENERATE_INTERNAL_LABEL (label, "LC", labelno); /* We have a symbol name; construct the SYMBOL_REF and the MEM. */ - symbol = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (label)); - SYMBOL_REF_FLAGS (symbol) = SYMBOL_FLAG_LOCAL; - SYMBOL_REF_DECL (symbol) = desc->value; + if (use_object_blocks_p ()) + { + section *sect = get_constant_section (exp); + 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, desc->value); TREE_CONSTANT_POOL_ADDRESS_P (symbol) = 1; rtl = gen_rtx_MEM (TYPE_MODE (TREE_TYPE (exp)), symbol); @@ -2461,7 +2839,7 @@ build_constant_desc (tree exp) information. This call might invalidate our local variable SYMBOL; we can't use it afterward. */ - (*targetm.encode_section_info) (exp, rtl, true); + targetm.encode_section_info (exp, rtl, true); desc->rtl = rtl; @@ -2491,12 +2869,14 @@ output_constant_def (tree exp, int defer) /* Look up EXP in the table of constant descriptors. If we didn't find it, create a new one. */ key.value = exp; - loc = htab_find_slot (const_desc_htab, &key, INSERT); + key.hash = const_hash_1 (exp); + loc = htab_find_slot_with_hash (const_desc_htab, &key, key.hash, INSERT); desc = *loc; if (desc == 0) { desc = build_constant_desc (exp); + desc->hash = key.hash; *loc = desc; } @@ -2520,9 +2900,9 @@ maybe_output_constant_def_contents (struct constant_descriptor_tree *desc, /* Already output; don't do it again. */ return; - /* The only constants that cannot safely be deferred, assuming the - context allows it, are strings under flag_writable_strings. */ - if (defer && (TREE_CODE (exp) != STRING_CST || !flag_writable_strings)) + /* 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 @@ -2537,43 +2917,16 @@ maybe_output_constant_def_contents (struct constant_descriptor_tree *desc, output_constant_def_contents (symbol); } -/* We must output the constant data referred to by SYMBOL; do so. */ +/* 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 -output_constant_def_contents (rtx symbol) +assemble_constant_contents (tree exp, const char *label, unsigned int align) { - tree exp = SYMBOL_REF_DECL (symbol); - const char *label = XSTR (symbol, 0); HOST_WIDE_INT size; - /* Make sure any other constants whose addresses appear in EXP - are assigned label numbers. */ - int reloc = compute_reloc_for_constant (exp); - - /* Align the location counter as required by EXP's data type. */ - int align = TYPE_ALIGN (TREE_TYPE (exp)); -#ifdef CONSTANT_ALIGNMENT - align = CONSTANT_ALIGNMENT (exp, align); -#endif - - output_addressed_constants (exp); - - /* We are no longer deferring this constant. */ - TREE_ASM_WRITTEN (exp) = 1; - - if (IN_NAMED_SECTION (exp)) - named_section (exp, NULL, reloc); - else - (*targetm.asm_out.select_section) (exp, reloc, align); - - if (align > BITS_PER_UNIT) - { - ASM_OUTPUT_ALIGN (asm_out_file, floor_log2 (align / BITS_PER_UNIT)); - } - - size = int_size_in_bytes (TREE_TYPE (exp)); - if (TREE_CODE (exp) == STRING_CST) - size = MAX (TREE_STRING_LENGTH (exp), size); + size = get_constant_size (exp); /* Do any machine/system dependent processing of the constant. */ #ifdef ASM_DECLARE_CONSTANT_NAME @@ -2587,13 +2940,36 @@ output_constant_def_contents (rtx symbol) output_constant (exp, size, align); } -/* A constant which was deferred in its original location has been - inserted by the RTL inliner into a different function. The - current function's deferred constant count must be incremented. */ -void -notice_rtl_inlining_of_deferred_constant (void) +/* We must output the constant data referred to by SYMBOL; do so. */ + +static void +output_constant_def_contents (rtx symbol) { - n_deferred_constants++; + tree exp = SYMBOL_REF_DECL (symbol); + unsigned int align; + + /* Make sure any other constants whose addresses appear in EXP + are assigned label numbers. */ + output_addressed_constants (exp); + + /* We are no longer deferring this constant. */ + TREE_ASM_WRITTEN (exp) = 1; + + /* 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 + { + switch_to_section (get_constant_section (exp)); + align = get_constant_alignment (exp); + 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); + } + if (flag_mudflap) + mudflap_enqueue_constant (exp); } /* Look up EXP in the table of constant descriptors. Return the rtl @@ -2606,321 +2982,194 @@ lookup_constant_def (tree exp) struct constant_descriptor_tree key; key.value = exp; - desc = htab_find (const_desc_htab, &key); + key.hash = const_hash_1 (exp); + desc = htab_find_with_hash (const_desc_htab, &key, key.hash); return (desc ? desc->rtl : NULL_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; there seems - to be no reason for the difference. */ + 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 constant_descriptor_rtx GTY(()) +struct rtx_constant_pool GTY(()) { - /* More constant_descriptors with the same hash code. */ - struct constant_descriptor_rtx *next; + /* Pointers to first and last constant in pool, as ordered by offset. */ + struct constant_descriptor_rtx *first; + struct constant_descriptor_rtx *last; - /* A MEM for the constant. */ - rtx rtl; + /* 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. */ + htab_t GTY((param_is (struct constant_descriptor_rtx))) const_rtx_htab; - /* The value of the constant. */ - struct rtx_const value; + /* Current offset in constant pool (does not include any + machine-specific header). */ + HOST_WIDE_INT offset; }; -/* Structure to represent sufficient information about a constant so that - it can be output when the constant pool is output, so that function - integration can be done, and to simplify handling on machines that reference - constant pool as base+displacement. */ - -struct pool_constant GTY(()) +struct constant_descriptor_rtx GTY((chain_next ("%h.next"))) { - struct constant_descriptor_rtx *desc; - struct pool_constant *next; - struct pool_constant *next_sym; + struct constant_descriptor_rtx *next; + rtx mem; + rtx sym; rtx constant; + HOST_WIDE_INT offset; + hashval_t hash; enum machine_mode mode; - int labelno; unsigned int align; - HOST_WIDE_INT offset; + int labelno; int mark; }; -/* Hash code for a SYMBOL_REF with CONSTANT_POOL_ADDRESS_P true. - The argument is XSTR (... , 0) */ +/* Hash and compare functions for const_rtx_htab. */ -#define SYMHASH(LABEL) (((unsigned long) (LABEL)) % MAX_RTX_HASH_TABLE) - -/* Initialize constant pool hashing for a new function. */ +static hashval_t +const_desc_rtx_hash (const void *ptr) +{ + const struct constant_descriptor_rtx *desc = ptr; + return desc->hash; +} -void -init_varasm_status (struct function *f) +static int +const_desc_rtx_eq (const void *a, const void *b) { - struct varasm_status *p; - p = ggc_alloc (sizeof (struct varasm_status)); - f->varasm = p; - p->x_const_rtx_hash_table - = ggc_alloc_cleared (MAX_RTX_HASH_TABLE - * sizeof (struct constant_descriptor_rtx *)); - p->x_const_rtx_sym_hash_table - = ggc_alloc_cleared (MAX_RTX_HASH_TABLE - * sizeof (struct pool_constant *)); - - p->x_first_pool = p->x_last_pool = 0; - p->x_pool_offset = 0; - p->deferred_constants = 0; + const struct constant_descriptor_rtx *x = a; + const struct constant_descriptor_rtx *y = b; + + if (x->mode != y->mode) + return 0; + return rtx_equal_p (x->constant, y->constant); } - -/* Express an rtx for a constant integer (perhaps symbolic) - as the sum of a symbol or label plus an explicit integer. - They are stored into VALUE. */ +/* This is the worker function for const_rtx_hash, called via for_each_rtx. */ -static void -decode_rtx_const (enum machine_mode mode, rtx x, struct rtx_const *value) +static int +const_rtx_hash_1 (rtx *xp, void *data) { - /* Clear the whole structure, including any gaps. */ - memset (value, 0, sizeof (struct rtx_const)); + unsigned HOST_WIDE_INT hwi; + enum machine_mode mode; + enum rtx_code code; + hashval_t h, *hp; + rtx x; - value->kind = RTX_INT; /* Most usual kind. */ - value->mode = mode; + x = *xp; + code = GET_CODE (x); + mode = GET_MODE (x); + h = (hashval_t) code * 1048573 + mode; - switch (GET_CODE (x)) + switch (code) { - case CONST_DOUBLE: - value->kind = RTX_DOUBLE; - if (GET_MODE (x) != VOIDmode) - { - const REAL_VALUE_TYPE *r = CONST_DOUBLE_REAL_VALUE (x); + case CONST_INT: + hwi = INTVAL (x); + fold_hwi: + { + const int shift = sizeof (hashval_t) * CHAR_BIT; + const int n = sizeof (HOST_WIDE_INT) / sizeof (hashval_t); + int i; - value->mode = GET_MODE (x); + h ^= (hashval_t) hwi; + for (i = 1; i < n; ++i) + { + hwi >>= shift; + h ^= (hashval_t) hwi; + } + } + break; - /* Copy the REAL_VALUE_TYPE by members so that we don't - copy garbage from the original structure into our - carefully cleaned hashing structure. */ - value->un.du.class = r->class; - value->un.du.sign = r->sign; - switch (r->class) - { - case rvc_zero: - case rvc_inf: - break; - case rvc_normal: - value->un.du.exp = r->exp; - /* Fall through. */ - case rvc_nan: - memcpy (value->un.du.sig, r->sig, sizeof (r->sig)); - break; - default: - abort (); - } - } - else + case CONST_DOUBLE: + if (mode == VOIDmode) { - value->un.di.low = CONST_DOUBLE_LOW (x); - value->un.di.high = CONST_DOUBLE_HIGH (x); + hwi = CONST_DOUBLE_LOW (x) ^ CONST_DOUBLE_HIGH (x); + goto fold_hwi; } + else + h ^= real_hash (CONST_DOUBLE_REAL_VALUE (x)); break; case CONST_VECTOR: { - int units, i; - - units = CONST_VECTOR_NUNITS (x); - value->kind = RTX_VECTOR; - value->mode = mode; - - if (GET_MODE_CLASS (mode) == MODE_VECTOR_INT) - { - for (i = 0; i < units; ++i) - { - rtx elt = CONST_VECTOR_ELT (x, i); - if (GET_CODE (elt) == CONST_INT) - { - value->un.int_vec[i].low = INTVAL (elt); - value->un.int_vec[i].high = 0; - } - else - { - value->un.int_vec[i].low = CONST_DOUBLE_LOW (elt); - value->un.int_vec[i].high = CONST_DOUBLE_HIGH (elt); - } - } - } - else if (GET_MODE_CLASS (mode) == MODE_VECTOR_FLOAT) - { - for (i = 0; i < units; ++i) - { - const REAL_VALUE_TYPE *r - = CONST_DOUBLE_REAL_VALUE (CONST_VECTOR_ELT (x, i)); - REAL_VALUE_TYPE *d = &value->un.fp_vec[i]; - - /* Copy the REAL_VALUE_TYPE by members so that we don't - copy garbage from the original structure into our - carefully cleaned hashing structure. */ - d->class = r->class; - d->sign = r->sign; - switch (r->class) - { - case rvc_zero: - case rvc_inf: - break; - case rvc_normal: - d->exp = r->exp; - /* Fall through. */ - case rvc_nan: - memcpy (d->sig, r->sig, sizeof (r->sig)); - break; - default: - abort (); - } - } - } - else - abort (); + int i; + for (i = XVECLEN (x, 0); i-- > 0; ) + h = h * 251 + const_rtx_hash_1 (&XVECEXP (x, 0, i), data); } break; - case CONST_INT: - value->un.addr.offset = INTVAL (x); + case SYMBOL_REF: + h ^= htab_hash_string (XSTR (x, 0)); break; - case SYMBOL_REF: case LABEL_REF: - case PC: - value->un.addr.base = x; + h = h * 251 + CODE_LABEL_NUMBER (XEXP (x, 0)); break; - case CONST: - x = XEXP (x, 0); - if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 1)) == CONST_INT) - { - value->un.addr.base = XEXP (x, 0); - value->un.addr.offset = INTVAL (XEXP (x, 1)); - } - else if (GET_CODE (x) == MINUS && GET_CODE (XEXP (x, 1)) == CONST_INT) - { - value->un.addr.base = XEXP (x, 0); - value->un.addr.offset = - INTVAL (XEXP (x, 1)); - } - else - { - value->un.addr.base = x; - value->un.addr.offset = 0; - } + case UNSPEC: + case UNSPEC_VOLATILE: + h = h * 251 + XINT (x, 1); break; default: - value->kind = RTX_UNKNOWN; break; } - if (value->kind == RTX_INT && value->un.addr.base != 0 - && GET_CODE (value->un.addr.base) == UNSPEC) - { - /* For a simple UNSPEC, the base is set to the - operand, the kind field is set to the index of - the unspec expression. - Together with the code below, in case that - the operand is a SYMBOL_REF or LABEL_REF, - the address of the string or the code_label - is taken as base. */ - if (XVECLEN (value->un.addr.base, 0) == 1) - { - value->kind = RTX_UNSPEC + XINT (value->un.addr.base, 1); - value->un.addr.base = XVECEXP (value->un.addr.base, 0, 0); - } - } - - if (value->kind >= RTX_INT && value->un.addr.base != 0) - switch (GET_CODE (value->un.addr.base)) - { - case SYMBOL_REF: - /* Use the string's address, not the SYMBOL_REF's address, - for the sake of addresses of library routines. */ - value->un.addr.symbol = XSTR (value->un.addr.base, 0); - value->un.addr.base = NULL_RTX; - break; - - case LABEL_REF: - /* For a LABEL_REF, compare labels. */ - value->un.addr.base = XEXP (value->un.addr.base, 0); - - default: - break; - } + hp = data; + *hp = *hp * 509 + h; + return 0; } -/* Given a MINUS expression, simplify it if both sides - include the same symbol. */ +/* Compute a hash value for X, which should be a constant. */ -rtx -simplify_subtraction (rtx x) +static hashval_t +const_rtx_hash (rtx x) { - struct rtx_const val0, val1; - - decode_rtx_const (GET_MODE (x), XEXP (x, 0), &val0); - decode_rtx_const (GET_MODE (x), XEXP (x, 1), &val1); - - if (val0.kind >= RTX_INT - && val0.kind == val1.kind - && val0.un.addr.base == val1.un.addr.base - && val0.un.addr.symbol == val1.un.addr.symbol) - return GEN_INT (val0.un.addr.offset - val1.un.addr.offset); - - return x; + hashval_t h = 0; + for_each_rtx (&x, const_rtx_hash_1, &h); + return h; } -/* Compute a hash code for a constant RTL expression. */ + +/* Create and return a new rtx constant pool. */ -static unsigned int -const_hash_rtx (enum machine_mode mode, rtx x) +static struct rtx_constant_pool * +create_constant_pool (void) { - union { - struct rtx_const value; - unsigned int data[sizeof(struct rtx_const) / sizeof (unsigned int)]; - } u; - - unsigned int hi; - size_t i; + struct rtx_constant_pool *pool; - decode_rtx_const (mode, x, &u.value); - - /* Compute hashing function. */ - hi = 0; - for (i = 0; i < ARRAY_SIZE (u.data); i++) - hi = hi * 613 + u.data[i]; - - return hi % MAX_RTX_HASH_TABLE; + pool = ggc_alloc (sizeof (struct rtx_constant_pool)); + pool->const_rtx_htab = htab_create_ggc (31, const_desc_rtx_hash, + const_desc_rtx_eq, NULL); + pool->first = NULL; + pool->last = NULL; + pool->offset = 0; + return pool; } -/* Compare a constant rtl object X with a constant-descriptor DESC. - Return 1 if DESC describes a constant with the same value as X. */ +/* Initialize constant pool hashing for a new function. */ -static int -compare_constant_rtx (enum machine_mode mode, rtx x, - struct constant_descriptor_rtx *desc) +void +init_varasm_status (struct function *f) { - struct rtx_const value; + struct varasm_status *p; - decode_rtx_const (mode, x, &value); + p = ggc_alloc (sizeof (struct varasm_status)); + f->varasm = p; - /* Compare constant contents. */ - return memcmp (&value, &desc->value, sizeof (struct rtx_const)) == 0; + p->pool = create_constant_pool (); + p->deferred_constants = 0; } + +/* Given a MINUS expression, simplify it if both sides + include the same symbol. */ -/* Construct a constant descriptor for the rtl-expression X. - It is up to the caller to enter the descriptor in the hash table. */ - -static struct constant_descriptor_rtx * -record_constant_rtx (enum machine_mode mode, rtx x) +rtx +simplify_subtraction (rtx x) { - struct constant_descriptor_rtx *ptr; - - ptr = ggc_alloc (sizeof (*ptr)); - decode_rtx_const (mode, x, &ptr->value); - - return ptr; + rtx r = simplify_rtx (x); + return r ? r : x; } /* Given a constant rtx X, make (or find) a memory constant for its value @@ -2929,116 +3178,108 @@ record_constant_rtx (enum machine_mode mode, rtx x) rtx force_const_mem (enum machine_mode mode, rtx x) { - int hash; - struct constant_descriptor_rtx *desc; + struct constant_descriptor_rtx *desc, tmp; + struct rtx_constant_pool *pool; char label[256]; rtx def, symbol; - struct pool_constant *pool; + hashval_t hash; unsigned int align; + void **slot; /* If we're not allowed to drop X into the constant pool, don't. */ - if ((*targetm.cannot_force_const_mem) (x)) + if (targetm.cannot_force_const_mem (x)) return NULL_RTX; - /* Compute hash code of X. Search the descriptors for that hash code - to see if any of them describes X. If yes, we have an rtx to use. */ - hash = const_hash_rtx (mode, x); - for (desc = const_rtx_hash_table[hash]; desc; desc = desc->next) - if (compare_constant_rtx (mode, x, desc)) - return copy_rtx (desc->rtl); + /* Record that this function has used a constant pool entry. */ + current_function_uses_const_pool = 1; + + /* Decide which pool to use. */ + pool = (targetm.use_blocks_for_constant_p (mode, x) + ? shared_constant_pool + : cfun->varasm->pool); + + /* Lookup the value in the hashtable. */ + tmp.constant = x; + tmp.mode = mode; + hash = const_rtx_hash (x); + slot = htab_find_slot_with_hash (pool->const_rtx_htab, &tmp, hash, INSERT); + desc = *slot; + + /* If the constant was already present, return its memory. */ + if (desc) + return copy_rtx (desc->mem); - /* No constant equal to X is known to have been output. - Make a constant descriptor to enter X in the hash table - and make a MEM for it. */ - desc = record_constant_rtx (mode, x); - desc->next = const_rtx_hash_table[hash]; - const_rtx_hash_table[hash] = desc; + /* Otherwise, create a new descriptor. */ + desc = ggc_alloc (sizeof (*desc)); + *slot = desc; /* Align the location counter as required by EXP's data type. */ align = GET_MODE_ALIGNMENT (mode == VOIDmode ? word_mode : mode); #ifdef CONSTANT_ALIGNMENT { - tree type = (*lang_hooks.types.type_for_mode) (mode, 0); + tree type = lang_hooks.types.type_for_mode (mode, 0); if (type != NULL_TREE) align = CONSTANT_ALIGNMENT (make_tree (type, x), align); } #endif - pool_offset += (align / BITS_PER_UNIT) - 1; - pool_offset &= ~ ((align / BITS_PER_UNIT) - 1); - - if (GET_CODE (x) == LABEL_REF) - LABEL_PRESERVE_P (XEXP (x, 0)) = 1; - - /* Allocate a pool constant descriptor, fill it in, and chain it in. */ - pool = ggc_alloc (sizeof (struct pool_constant)); - pool->desc = desc; - pool->constant = x; - pool->mode = mode; - pool->labelno = const_labelno; - pool->align = align; - pool->offset = pool_offset; - pool->mark = 1; - pool->next = 0; - - if (last_pool == 0) - first_pool = pool; + pool->offset += (align / BITS_PER_UNIT) - 1; + pool->offset &= ~ ((align / BITS_PER_UNIT) - 1); + + desc->next = NULL; + desc->constant = 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 - last_pool->next = pool; - - last_pool = pool; - pool_offset += GET_MODE_SIZE (mode); + 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 and the MEM. */ - - symbol = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (label)); - SYMBOL_REF_FLAGS (symbol) = SYMBOL_FLAG_LOCAL; - - pool->desc->rtl = def = gen_rtx_MEM (mode, symbol); - set_mem_attributes (def, (*lang_hooks.types.type_for_mode) (mode, 0), 1); - RTX_UNCHANGING_P (def) = 1; + /* 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); - /* Add label to symbol hash table. */ - hash = SYMHASH (XSTR (symbol, 0)); - pool->next_sym = const_rtx_sym_hash_table[hash]; - const_rtx_sym_hash_table[hash] = pool; + /* Construct the MEM. */ + desc->mem = def = gen_const_mem (mode, symbol); + set_mem_attributes (def, lang_hooks.types.type_for_mode (mode, 0), 1); + set_mem_align (def, align); - /* Mark the symbol_ref as belonging to this constants pool. */ - CONSTANT_POOL_ADDRESS_P (symbol) = 1; - SYMBOL_REF_FLAGS (symbol) = SYMBOL_FLAG_LOCAL; - current_function_uses_const_pool = 1; + /* 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 SYMBOL_REF with CONSTANT_POOL_ADDRESS_P true, return a pointer to - the corresponding pool_constant structure. */ - -static struct pool_constant * -find_pool_constant (struct function *f, rtx addr) -{ - struct pool_constant *pool; - const char *label = XSTR (addr, 0); - - for (pool = f->varasm->x_const_rtx_sym_hash_table[SYMHASH (label)]; pool; - pool = pool->next_sym) - if (XSTR (XEXP (pool->desc->rtl, 0), 0) == label) - return pool; - - abort (); -} - /* Given a constant pool SYMBOL_REF, return the corresponding constant. */ rtx get_pool_constant (rtx addr) { - return (find_pool_constant (cfun, addr))->constant; + return SYMBOL_REF_CONSTANT (addr)->constant; } /* Given a constant pool SYMBOL_REF, return the corresponding constant @@ -3047,17 +3288,11 @@ get_pool_constant (rtx addr) rtx get_pool_constant_mark (rtx addr, bool *pmarked) { - struct pool_constant *pool = find_pool_constant (cfun, addr); - *pmarked = (pool->mark != 0); - return pool->constant; -} - -/* Likewise, but for the constant pool of a specific function. */ + struct constant_descriptor_rtx *desc; -rtx -get_pool_constant_for_function (struct function *f, rtx addr) -{ - return (find_pool_constant (f, addr))->constant; + desc = SYMBOL_REF_CONSTANT (addr); + *pmarked = (desc->mark != 0); + return desc->constant; } /* Similar, return the mode. */ @@ -3065,21 +3300,7 @@ get_pool_constant_for_function (struct function *f, rtx addr) enum machine_mode get_pool_mode (rtx addr) { - return (find_pool_constant (cfun, addr))->mode; -} - -enum machine_mode -get_pool_mode_for_function (struct function *f, rtx addr) -{ - return (find_pool_constant (f, addr))->mode; -} - -/* Similar, return the offset in the constant pool. */ - -int -get_pool_offset (rtx addr) -{ - return (find_pool_constant (cfun, addr))->offset; + return SYMBOL_REF_CONSTANT (addr)->mode; } /* Return the size of the constant pool. */ @@ -3087,295 +3308,264 @@ get_pool_offset (rtx addr) int get_pool_size (void) { - return pool_offset; + return cfun->varasm->pool->offset; } -/* Write all the constants in the constant pool. */ +/* Worker function for output_constant_pool_1. Emit assembly for X + in MODE with known alignment ALIGN. */ -void -output_constant_pool (const char *fnname ATTRIBUTE_UNUSED, - tree fndecl ATTRIBUTE_UNUSED) +static void +output_constant_pool_2 (enum machine_mode mode, rtx x, unsigned int align) { - struct pool_constant *pool; - rtx x; - REAL_VALUE_TYPE r; - - /* 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 (); + switch (GET_MODE_CLASS (mode)) + { + case MODE_FLOAT: + case MODE_DECIMAL_FLOAT: + { + REAL_VALUE_TYPE r; -#ifdef ASM_OUTPUT_POOL_PROLOGUE - ASM_OUTPUT_POOL_PROLOGUE (asm_out_file, fnname, fndecl, pool_offset); -#endif + gcc_assert (GET_CODE (x) == CONST_DOUBLE); + REAL_VALUE_FROM_CONST_DOUBLE (r, x); + assemble_real (r, mode, align); + break; + } - for (pool = first_pool; pool; pool = pool->next) - { - rtx tmp; + case MODE_INT: + case MODE_PARTIAL_INT: + assemble_integer (x, GET_MODE_SIZE (mode), align, 1); + break; - x = pool->constant; + case MODE_VECTOR_FLOAT: + case MODE_VECTOR_INT: + { + int i, units; + enum machine_mode submode = GET_MODE_INNER (mode); + unsigned int subalign = MIN (align, GET_MODE_BITSIZE (submode)); - if (! pool->mark) - continue; + gcc_assert (GET_CODE (x) == CONST_VECTOR); + units = CONST_VECTOR_NUNITS (x); - /* 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 INSN_DELETED_P and friends. */ + for (i = 0; i < units; i++) + { + rtx elt = CONST_VECTOR_ELT (x, i); + output_constant_pool_2 (submode, elt, i ? subalign : align); + } + } + break; - tmp = x; - switch (GET_CODE (x)) - { - case CONST: - if (GET_CODE (XEXP (x, 0)) != PLUS - || GET_CODE (XEXP (XEXP (x, 0), 0)) != LABEL_REF) - break; - tmp = XEXP (XEXP (x, 0), 0); - /* Fall through. */ - - case LABEL_REF: - tmp = XEXP (x, 0); - if (INSN_DELETED_P (tmp) - || (GET_CODE (tmp) == NOTE - && NOTE_LINE_NUMBER (tmp) == NOTE_INSN_DELETED)) - { - abort (); - x = const0_rtx; - } - break; + default: + gcc_unreachable (); + } +} - default: - break; - } +/* Worker function for output_constant_pool. Emit constant DESC, + giving it ALIGN bits of alignment. */ - /* First switch to correct section. */ - (*targetm.asm_out.select_rtx_section) (pool->mode, x, pool->align); +static void +output_constant_pool_1 (struct constant_descriptor_rtx *desc, + unsigned int align) +{ + rtx x, tmp; -#ifdef ASM_OUTPUT_SPECIAL_POOL_ENTRY - ASM_OUTPUT_SPECIAL_POOL_ENTRY (asm_out_file, x, pool->mode, - pool->align, pool->labelno, done); -#endif + x = desc->constant; - assemble_align (pool->align); + /* 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 INSN_DELETED_P and friends. */ - /* Output the label. */ - (*targetm.asm_out.internal_label) (asm_out_file, "LC", pool->labelno); + tmp = x; + switch (GET_CODE (x)) + { + case CONST: + if (GET_CODE (XEXP (x, 0)) != PLUS + || GET_CODE (XEXP (XEXP (x, 0), 0)) != LABEL_REF) + break; + tmp = XEXP (XEXP (x, 0), 0); + /* FALLTHRU */ - /* Output the value of the constant itself. */ - switch (GET_MODE_CLASS (pool->mode)) - { - case MODE_FLOAT: - if (GET_CODE (x) != CONST_DOUBLE) - abort (); + case LABEL_REF: + tmp = XEXP (x, 0); + gcc_assert (!INSN_DELETED_P (tmp)); + gcc_assert (!NOTE_P (tmp) + || NOTE_LINE_NUMBER (tmp) != NOTE_INSN_DELETED); + break; - REAL_VALUE_FROM_CONST_DOUBLE (r, x); - assemble_real (r, pool->mode, pool->align); - break; + default: + break; + } - case MODE_INT: - case MODE_PARTIAL_INT: - assemble_integer (x, GET_MODE_SIZE (pool->mode), pool->align, 1); - break; +#ifdef ASM_OUTPUT_SPECIAL_POOL_ENTRY + ASM_OUTPUT_SPECIAL_POOL_ENTRY (asm_out_file, x, desc->mode, + align, desc->labelno, done); +#endif - case MODE_VECTOR_FLOAT: - { - int i, units; - rtx elt; + assemble_align (align); - if (GET_CODE (x) != CONST_VECTOR) - abort (); + /* Output the label. */ + targetm.asm_out.internal_label (asm_out_file, "LC", desc->labelno); - units = CONST_VECTOR_NUNITS (x); + /* Output the data. */ + output_constant_pool_2 (desc->mode, x, align); - for (i = 0; i < units; i++) - { - elt = CONST_VECTOR_ELT (x, i); - REAL_VALUE_FROM_CONST_DOUBLE (r, elt); - assemble_real (r, GET_MODE_INNER (pool->mode), pool->align); - } - } - break; + /* 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); - case MODE_VECTOR_INT: - { - int i, units; - rtx elt; +#ifdef ASM_OUTPUT_SPECIAL_POOL_ENTRY + done: +#endif + return; +} - if (GET_CODE (x) != CONST_VECTOR) - abort (); +/* Given a SYMBOL_REF CURRENT_RTX, mark it and all constants it refers + to as used. Emit referenced deferred strings. This function can + be used with for_each_rtx to mark all SYMBOL_REFs in an rtx. */ - units = CONST_VECTOR_NUNITS (x); +static int +mark_constant (rtx *current_rtx, void *data ATTRIBUTE_UNUSED) +{ + rtx x = *current_rtx; - for (i = 0; i < units; i++) - { - elt = CONST_VECTOR_ELT (x, i); - assemble_integer (elt, GET_MODE_UNIT_SIZE (pool->mode), - pool->align, 1); - } - } - break; + if (x == NULL_RTX || GET_CODE (x) != SYMBOL_REF) + return 0; - default: - abort (); + if (CONSTANT_POOL_ADDRESS_P (x)) + { + struct constant_descriptor_rtx *desc = SYMBOL_REF_CONSTANT (x); + if (desc->mark == 0) + { + desc->mark = 1; + for_each_rtx (&desc->constant, mark_constant, NULL); } + } + else if (TREE_CONSTANT_POOL_ADDRESS_P (x)) + { + tree exp = SYMBOL_REF_DECL (x); + if (!TREE_ASM_WRITTEN (exp)) + { + n_deferred_constants--; + output_constant_def_contents (x); + } + } - /* Make sure all constants in SECTION_MERGE and not SECTION_STRINGS - sections have proper size. */ - if (pool->align > GET_MODE_BITSIZE (pool->mode) - && in_section == in_named - && get_named_section_flags (in_named_name) & SECTION_MERGE) - assemble_align (pool->align); + return -1; +} -#ifdef ASM_OUTPUT_SPECIAL_POOL_ENTRY - done: ; -#endif - } +/* 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. */ -#ifdef ASM_OUTPUT_POOL_EPILOGUE - ASM_OUTPUT_POOL_EPILOGUE (asm_out_file, fnname, fndecl, pool_offset); -#endif +static void +mark_constants (rtx insn) +{ + if (!INSN_P (insn)) + return; - /* Done with this pool. */ - first_pool = last_pool = 0; + /* 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 (GET_CODE (PATTERN (insn)) == SEQUENCE) + { + rtx seq = PATTERN (insn); + int i, n = XVECLEN (seq, 0); + for (i = 0; i < n; ++i) + { + rtx subinsn = XVECEXP (seq, 0, i); + if (INSN_P (subinsn)) + for_each_rtx (&PATTERN (subinsn), mark_constant, NULL); + } + } + else + for_each_rtx (&PATTERN (insn), mark_constant, NULL); } /* Look through the instructions for this function, and mark all the - entries in the constant pool which are actually being used. Emit - deferred constants which have indeed been used. */ + entries in POOL which are actually being used. Emit deferred constants + which have indeed been used. */ static void mark_constant_pool (void) { - rtx insn; - rtx link; - struct pool_constant *pool; + rtx insn, link; - if (first_pool == 0 && n_deferred_constants == 0) + if (!current_function_uses_const_pool && n_deferred_constants == 0) return; - for (pool = first_pool; pool; pool = pool->next) - pool->mark = 0; - for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) - if (INSN_P (insn)) - mark_constants (PATTERN (insn)); + mark_constants (insn); for (link = current_function_epilogue_delay_list; link; link = XEXP (link, 1)) - { - insn = XEXP (link, 0); - - if (INSN_P (insn)) - mark_constants (PATTERN (insn)); - } + mark_constants (XEXP (link, 0)); } -/* Look through appropriate parts of X, 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. */ +/* Write all the constants in POOL. */ static void -mark_constants (rtx x) +output_constant_pool_contents (struct rtx_constant_pool *pool) { - int i; - const char *format_ptr; - - if (x == 0) - return; - - if (GET_CODE (x) == SYMBOL_REF) - { - mark_constant (&x, NULL); - return; - } + struct constant_descriptor_rtx *desc; - /* 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 (INSN_P (x)) - { - mark_constants (PATTERN (x)); - return; - } + for (desc = pool->first; desc ; desc = desc->next) + 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); + } + } +} - format_ptr = GET_RTX_FORMAT (GET_CODE (x)); +/* Mark all constants that are used in the current function, then write + out the function's private constant pool. */ - for (i = 0; i < GET_RTX_LENGTH (GET_CODE (x)); i++) - { - switch (*format_ptr++) - { - case 'e': - mark_constants (XEXP (x, i)); - break; +static void +output_constant_pool (const char *fnname ATTRIBUTE_UNUSED, + tree fndecl ATTRIBUTE_UNUSED) +{ + struct rtx_constant_pool *pool = cfun->varasm->pool; - case 'E': - if (XVEC (x, i) != 0) - { - int j; + /* 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 (); - for (j = 0; j < XVECLEN (x, i); j++) - mark_constants (XVECEXP (x, i, j)); - } - break; +#ifdef ASM_OUTPUT_POOL_PROLOGUE + ASM_OUTPUT_POOL_PROLOGUE (asm_out_file, fnname, fndecl, pool->offset); +#endif - case 'S': - case 's': - case '0': - case 'i': - case 'w': - case 'n': - case 'u': - case 'B': - break; + output_constant_pool_contents (pool); - default: - abort (); - } - } +#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. */ -/* Given a SYMBOL_REF CURRENT_RTX, mark it and all constants it refers - to as used. Emit referenced deferred strings. This function can - be used with for_each_rtx to mark all SYMBOL_REFs in an rtx. */ - -static int -mark_constant (rtx *current_rtx, void *data ATTRIBUTE_UNUSED) +void +output_shared_constant_pool (void) { - rtx x = *current_rtx; - - if (x == NULL_RTX) - return 0; - - else if (GET_CODE (x) == SYMBOL_REF) - { - if (CONSTANT_POOL_ADDRESS_P (x)) - { - struct pool_constant *pool = find_pool_constant (cfun, x); - if (pool->mark == 0) - { - pool->mark = 1; - for_each_rtx (&(pool->constant), &mark_constant, NULL); - } - else - return -1; - } - else if (TREE_CONSTANT_POOL_ADDRESS_P (x)) - { - tree exp = SYMBOL_REF_DECL (x); - if (!TREE_ASM_WRITTEN (exp)) - { - n_deferred_constants--; - output_constant_def_contents (x); - } - } - } - return 0; + output_constant_pool_contents (shared_constant_pool); } /* Determine what kind of relocations EXP may need. */ @@ -3388,7 +3578,7 @@ compute_reloc_for_constant (tree exp) /* Give the front-end a chance to convert VALUE to something that looks more like a constant to the back-end. */ - exp = (*lang_hooks.expand_constant) (exp); + exp = lang_hooks.expand_constant (exp); switch (TREE_CODE (exp)) { @@ -3425,14 +3615,17 @@ compute_reloc_for_constant (tree exp) case NOP_EXPR: case CONVERT_EXPR: case NON_LVALUE_EXPR: + case VIEW_CONVERT_EXPR: reloc = compute_reloc_for_constant (TREE_OPERAND (exp, 0)); break; case CONSTRUCTOR: - for (tem = CONSTRUCTOR_ELTS (exp); tem; tem = TREE_CHAIN (tem)) - if (TREE_VALUE (tem) != 0) - reloc |= compute_reloc_for_constant (TREE_VALUE (tem)); - + { + 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: @@ -3452,7 +3645,7 @@ output_addressed_constants (tree exp) /* Give the front-end a chance to convert VALUE to something that looks more like a constant to the back-end. */ - exp = (*lang_hooks.expand_constant) (exp); + exp = lang_hooks.expand_constant (exp); switch (TREE_CODE (exp)) { @@ -3465,8 +3658,11 @@ output_addressed_constants (tree exp) tem = TREE_OPERAND (tem, 0)) ; - if (TREE_CODE_CLASS (TREE_CODE (tem)) == 'c' - || TREE_CODE (tem) == CONSTRUCTOR) + /* 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, 0); break; @@ -3478,14 +3674,17 @@ output_addressed_constants (tree exp) case NOP_EXPR: case CONVERT_EXPR: case NON_LVALUE_EXPR: + case VIEW_CONVERT_EXPR: output_addressed_constants (TREE_OPERAND (exp, 0)); break; case CONSTRUCTOR: - for (tem = CONSTRUCTOR_ELTS (exp); tem; tem = TREE_CHAIN (tem)) - if (TREE_VALUE (tem) != 0) - output_addressed_constants (TREE_VALUE (tem)); - + { + unsigned HOST_WIDE_INT idx; + FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (exp), idx, tem) + if (tem != 0) + output_addressed_constants (tem); + } break; default: @@ -3493,6 +3692,20 @@ output_addressed_constants (tree exp) } } +/* 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 (tree ctor) +{ + return (TREE_CONSTANT (ctor) + && (TREE_CODE (TREE_TYPE (ctor)) == UNION_TYPE + || TREE_CODE (TREE_TYPE (ctor)) == RECORD_TYPE) + && !VEC_empty (constructor_elt, CONSTRUCTOR_ELTS (ctor))); +} + /* 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. @@ -3508,24 +3721,21 @@ initializer_constant_valid_p (tree value, tree endtype) { /* Give the front-end a chance to convert VALUE to something that looks more like a constant to the back-end. */ - value = (*lang_hooks.expand_constant) (value); + value = lang_hooks.expand_constant (value); switch (TREE_CODE (value)) { case CONSTRUCTOR: - if ((TREE_CODE (TREE_TYPE (value)) == UNION_TYPE - || TREE_CODE (TREE_TYPE (value)) == RECORD_TYPE) - && TREE_CONSTANT (value) - && CONSTRUCTOR_ELTS (value)) + if (constructor_static_from_elts_p (value)) { + unsigned HOST_WIDE_INT idx; tree elt; bool absolute = true; - for (elt = CONSTRUCTOR_ELTS (value); elt; elt = TREE_CHAIN (elt)) + FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (value), idx, elt) { tree reloc; - value = TREE_VALUE (elt); - reloc = initializer_constant_valid_p (value, TREE_TYPE (value)); + reloc = initializer_constant_valid_p (elt, TREE_TYPE (elt)); if (!reloc) return NULL_TREE; if (reloc != null_pointer_node) @@ -3548,7 +3758,26 @@ initializer_constant_valid_p (tree value, tree endtype) case ADDR_EXPR: case FDESC_EXPR: - return staticp (TREE_OPERAND (value, 0)) ? TREE_OPERAND (value, 0) : 0; + value = staticp (TREE_OPERAND (value, 0)); + if (value) + { + /* "&(*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 (value) == INDIRECT_REF + && TREE_CONSTANT (TREE_OPERAND (value, 0))) + return null_pointer_node; + /* Taking the address of a nested function involves a trampoline. */ + if (TREE_CODE (value) == FUNCTION_DECL + && ((decl_function_context (value) + && !DECL_NO_STATIC_CHAIN (value)) + || DECL_DLLIMPORT_P (value))) + return NULL_TREE; + /* "&{...}" requires a temporary to hold the constructed + object. */ + if (TREE_CODE (value) == CONSTRUCTOR) + return NULL_TREE; + } + return value; case VIEW_CONVERT_EXPR: case NON_LVALUE_EXPR: @@ -3599,6 +3828,9 @@ initializer_constant_valid_p (tree value, tree endtype) || 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)) @@ -3648,16 +3880,17 @@ initializer_constant_valid_p (tree value, tree endtype) /* Since GCC guarantees that string constants are unique in the generated code, a subtraction between two copies of the same constant string is absolute. */ - if (valid0 && TREE_CODE (valid0) == STRING_CST && - valid1 && TREE_CODE (valid1) == STRING_CST && - TREE_STRING_POINTER (valid0) == TREE_STRING_POINTER (valid1)) + if (valid0 && TREE_CODE (valid0) == STRING_CST + && valid1 && TREE_CODE (valid1) == STRING_CST + && operand_equal_p (valid0, valid1, 1)) return null_pointer_node; } - /* Support differences between labels. */ + /* Support narrowing differences. */ if (INTEGRAL_TYPE_P (endtype)) { tree op0, op1; + op0 = TREE_OPERAND (value, 0); op1 = TREE_OPERAND (value, 1); @@ -3692,11 +3925,25 @@ initializer_constant_valid_p (tree value, tree endtype) op1 = inner; } - if (TREE_CODE (op0) == ADDR_EXPR - && TREE_CODE (TREE_OPERAND (op0, 0)) == LABEL_DECL - && TREE_CODE (op1) == ADDR_EXPR - && TREE_CODE (TREE_OPERAND (op1, 0)) == LABEL_DECL) - return null_pointer_node; + op0 = initializer_constant_valid_p (op0, endtype); + op1 = initializer_constant_valid_p (op1, endtype); + + /* Both initializers must be known. */ + if (op0 && op1) + { + if (op0 == op1) + 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; + } } break; @@ -3736,24 +3983,71 @@ output_constant (tree exp, unsigned HOST_WIDE_INT size, unsigned int align) /* Some front-ends use constants other than the standard language-independent varieties, but which may still be output directly. Give the front-end a chance to convert EXP to a language-independent representation. */ - exp = (*lang_hooks.expand_constant) (exp); + exp = lang_hooks.expand_constant (exp); if (size == 0 || flag_syntax_only) return; + /* 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.valid_pointer_mode (TYPE_MODE (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.valid_pointer_mode (TYPE_MODE (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 = build_int_cst_wide (saved_type, TREE_INT_CST_LOW (exp), + TREE_INT_CST_HIGH (exp)); + + } + /* Eliminate any conversions since we'll be outputting the underlying constant. */ while (TREE_CODE (exp) == NOP_EXPR || TREE_CODE (exp) == CONVERT_EXPR || TREE_CODE (exp) == NON_LVALUE_EXPR || TREE_CODE (exp) == VIEW_CONVERT_EXPR) - exp = TREE_OPERAND (exp, 0); + { + 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)); + /* Give the front end another chance to expand constants. */ + exp = lang_hooks.expand_constant (exp); + /* Allow a constructor with no elements for any data type. This means to fill the space with zeros. */ - if (TREE_CODE (exp) == CONSTRUCTOR && CONSTRUCTOR_ELTS (exp) == 0) + if (TREE_CODE (exp) == CONSTRUCTOR + && VEC_empty (constructor_elt, CONSTRUCTOR_ELTS (exp))) { assemble_zeros (size); return; @@ -3766,16 +4060,15 @@ output_constant (tree exp, unsigned HOST_WIDE_INT size, unsigned int align) tree decl = TREE_OPERAND (exp, 0); ASM_OUTPUT_FDESC (asm_out_file, decl, part); #else - abort (); + gcc_unreachable (); #endif return; } /* Now output the underlying data. If we've handling the padding, return. - Otherwise, break and ensure THISSIZE is the size written. */ + Otherwise, break and ensure SIZE is the size written. */ switch (code) { - case CHAR_TYPE: case BOOLEAN_TYPE: case INTEGER_TYPE: case ENUMERAL_TYPE: @@ -3784,7 +4077,7 @@ output_constant (tree exp, unsigned HOST_WIDE_INT size, unsigned int align) case OFFSET_TYPE: if (! assemble_integer (expand_expr (exp, NULL_RTX, VOIDmode, EXPAND_INITIALIZER), - size, align, 0)) + MIN (size, thissize), align, 0)) error ("initializer for integer value is too complicated"); break; @@ -3803,67 +4096,55 @@ output_constant (tree exp, unsigned HOST_WIDE_INT size, unsigned int align) case ARRAY_TYPE: case VECTOR_TYPE: - if (TREE_CODE (exp) == CONSTRUCTOR) + switch (TREE_CODE (exp)) { + case CONSTRUCTOR: output_constructor (exp, size, align); return; - } - else if (TREE_CODE (exp) == STRING_CST) - { + case STRING_CST: thissize = MIN ((unsigned HOST_WIDE_INT)TREE_STRING_LENGTH (exp), size); assemble_string (TREE_STRING_POINTER (exp), thissize); - } - else if (TREE_CODE (exp) == VECTOR_CST) - { - int elt_size; - tree link; - unsigned int nalign; - enum machine_mode inner; + break; - inner = GET_MODE_INNER (TYPE_MODE (TREE_TYPE (exp))); - nalign = MIN (align, GET_MODE_ALIGNMENT (inner)); + case VECTOR_CST: + { + int elt_size; + tree link; + unsigned int nalign; + enum machine_mode inner; + + inner = TYPE_MODE (TREE_TYPE (TREE_TYPE (exp))); + nalign = MIN (align, GET_MODE_ALIGNMENT (inner)); - elt_size = GET_MODE_UNIT_SIZE (TYPE_MODE (TREE_TYPE (exp))); + elt_size = GET_MODE_SIZE (inner); - link = TREE_VECTOR_CST_ELTS (exp); - output_constant (TREE_VALUE (link), elt_size, align); - while ((link = TREE_CHAIN (link)) != NULL) - output_constant (TREE_VALUE (link), elt_size, nalign); + link = TREE_VECTOR_CST_ELTS (exp); + output_constant (TREE_VALUE (link), elt_size, align); + thissize = elt_size; + while ((link = TREE_CHAIN (link)) != NULL) + { + output_constant (TREE_VALUE (link), elt_size, nalign); + thissize += elt_size; + } + break; + } + default: + gcc_unreachable (); } - else - abort (); break; case RECORD_TYPE: case UNION_TYPE: - if (TREE_CODE (exp) == CONSTRUCTOR) - output_constructor (exp, size, align); - else - abort (); - return; - - case SET_TYPE: - if (TREE_CODE (exp) == INTEGER_CST) - assemble_integer (expand_expr (exp, NULL_RTX, - VOIDmode, EXPAND_INITIALIZER), - thissize, align, 1); - else if (TREE_CODE (exp) == CONSTRUCTOR) - { - unsigned char *buffer = alloca (thissize); - if (get_set_constructor_bytes (exp, buffer, thissize)) - abort (); - assemble_string ((char *) buffer, thissize); - } - else - error ("unknown set constructor type"); + gcc_assert (TREE_CODE (exp) == CONSTRUCTOR); + output_constructor (exp, size, align); return; case ERROR_MARK: return; default: - abort (); + gcc_unreachable (); } if (size > thissize) @@ -3879,6 +4160,8 @@ static unsigned HOST_WIDE_INT array_size_for_constructor (tree val) { tree max_index, i; + unsigned HOST_WIDE_INT cnt; + tree index, value, tmp; /* 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 @@ -3887,10 +4170,8 @@ array_size_for_constructor (tree val) return TREE_STRING_LENGTH (val); max_index = NULL_TREE; - for (i = CONSTRUCTOR_ELTS (val); i; i = TREE_CHAIN (i)) + FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (val), cnt, index, value) { - tree index = TREE_PURPOSE (i); - if (TREE_CODE (index) == RANGE_EXPR) index = TREE_OPERAND (index, 1); if (max_index == NULL_TREE || tree_int_cst_lt (max_index, index)) @@ -3901,10 +4182,10 @@ array_size_for_constructor (tree val) return 0; /* Compute the total number of array elements. */ - i = size_binop (MINUS_EXPR, convert (sizetype, max_index), - convert (sizetype, - TYPE_MIN_VALUE (TYPE_DOMAIN (TREE_TYPE (val))))); - i = size_binop (PLUS_EXPR, i, convert (sizetype, integer_one_node)); + tmp = TYPE_MIN_VALUE (TYPE_DOMAIN (TREE_TYPE (val))); + i = size_binop (MINUS_EXPR, fold_convert (sizetype, max_index), + fold_convert (sizetype, tmp)); + i = size_binop (PLUS_EXPR, i, build_int_cst (sizetype, 1)); /* Multiply by the array element unit size to find number of bytes. */ i = size_binop (MULT_EXPR, i, TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (val)))); @@ -3920,7 +4201,7 @@ output_constructor (tree exp, unsigned HOST_WIDE_INT size, unsigned int align) { tree type = TREE_TYPE (exp); - tree link, field = 0; + tree field = 0; tree min_index = 0; /* Number of bytes output or skipped so far. In other words, current position within the constructor. */ @@ -3928,9 +4209,10 @@ output_constructor (tree exp, unsigned HOST_WIDE_INT size, /* Nonzero means BYTE contains part of a byte, to be output. */ int byte_buffer_in_use = 0; int byte = 0; + unsigned HOST_WIDE_INT cnt; + constructor_elt *ce; - if (HOST_BITS_PER_WIDE_INT < BITS_PER_UNIT) - abort (); + gcc_assert (HOST_BITS_PER_WIDE_INT >= BITS_PER_UNIT); if (TREE_CODE (type) == RECORD_TYPE) field = TYPE_FIELDS (type); @@ -3948,29 +4230,28 @@ output_constructor (tree exp, unsigned HOST_WIDE_INT size, 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). */ - for (link = CONSTRUCTOR_ELTS (exp); - link; - link = TREE_CHAIN (link), - field = field ? TREE_CHAIN (field) : 0) + for (cnt = 0; + VEC_iterate (constructor_elt, CONSTRUCTOR_ELTS (exp), cnt, ce); + cnt++, field = field ? TREE_CHAIN (field) : 0) { - tree val = TREE_VALUE (link); + tree val = ce->value; tree index = 0; /* The element in a union constructor specifies the proper field or index. */ if ((TREE_CODE (type) == RECORD_TYPE || TREE_CODE (type) == UNION_TYPE || TREE_CODE (type) == QUAL_UNION_TYPE) - && TREE_PURPOSE (link) != 0) - field = TREE_PURPOSE (link); + && ce->index != 0) + field = ce->index; else if (TREE_CODE (type) == ARRAY_TYPE) - index = TREE_PURPOSE (link); + index = ce->index; #ifdef ASM_COMMENT_START if (field && flag_verbose_asm) fprintf (asm_out_file, "%s %s:\n", - ASM_COMMENT_START, - DECL_NAME (field) + ASM_COMMENT_START, + DECL_NAME (field) ? IDENTIFIER_POINTER (DECL_NAME (field)) : "<anonymous>"); #endif @@ -4027,6 +4308,7 @@ output_constructor (tree exp, unsigned HOST_WIDE_INT size, if each element has the proper size. */ if ((field != 0 || index != 0) && pos != total_bytes) { + gcc_assert (pos >= total_bytes); assemble_zeros (pos - total_bytes); total_bytes = pos; } @@ -4051,8 +4333,7 @@ output_constructor (tree exp, unsigned HOST_WIDE_INT size, fieldsize = array_size_for_constructor (val); /* Given a non-empty initialization, this field had better be last. */ - if (fieldsize != 0 && TREE_CHAIN (field) != NULL_TREE) - abort (); + gcc_assert (!fieldsize || !TREE_CHAIN (field)); } else if (DECL_SIZE_UNIT (field)) { @@ -4075,7 +4356,7 @@ output_constructor (tree exp, unsigned HOST_WIDE_INT size, total_bytes += fieldsize; } else if (val != 0 && TREE_CODE (val) != INTEGER_CST) - error ("invalid initial value for member `%s'", + error ("invalid initial value for member %qs", IDENTIFIER_POINTER (DECL_NAME (field))); else { @@ -4103,6 +4384,7 @@ output_constructor (tree exp, unsigned HOST_WIDE_INT size, /* If still not at proper byte, advance to there. */ if (next_offset / BITS_PER_UNIT != total_bytes) { + gcc_assert (next_offset / BITS_PER_UNIT >= total_bytes); assemble_zeros (next_offset / BITS_PER_UNIT - total_bytes); total_bytes = next_offset / BITS_PER_UNIT; } @@ -4161,13 +4443,12 @@ output_constructor (tree exp, unsigned HOST_WIDE_INT size, /* Now get the bits from the appropriate constant word. */ if (shift < HOST_BITS_PER_WIDE_INT) value = TREE_INT_CST_LOW (val); - else if (shift < 2 * HOST_BITS_PER_WIDE_INT) + else { + gcc_assert (shift < 2 * HOST_BITS_PER_WIDE_INT); value = TREE_INT_CST_HIGH (val); shift -= HOST_BITS_PER_WIDE_INT; } - else - abort (); /* Get the result. This works only when: 1 <= this_time <= HOST_BITS_PER_WIDE_INT. */ @@ -4194,13 +4475,12 @@ output_constructor (tree exp, unsigned HOST_WIDE_INT size, /* Now get the bits from the appropriate constant word. */ if (shift < HOST_BITS_PER_WIDE_INT) value = TREE_INT_CST_LOW (val); - else if (shift < 2 * HOST_BITS_PER_WIDE_INT) + else { + gcc_assert (shift < 2 * HOST_BITS_PER_WIDE_INT); value = TREE_INT_CST_HIGH (val); shift -= HOST_BITS_PER_WIDE_INT; } - else - abort (); /* Get the result. This works only when: 1 <= this_time <= HOST_BITS_PER_WIDE_INT. */ @@ -4237,7 +4517,7 @@ mark_weak (tree decl) DECL_WEAK (decl) = 1; if (DECL_RTL_SET_P (decl) - && GET_CODE (DECL_RTL (decl)) == MEM + && 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; @@ -4249,7 +4529,21 @@ void merge_weak (tree newdecl, tree olddecl) { if (DECL_WEAK (newdecl) == DECL_WEAK (olddecl)) - return; + { + if (DECL_WEAK (newdecl) && 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)) { @@ -4262,16 +4556,16 @@ merge_weak (tree newdecl, tree olddecl) declare_weak because the NEWDECL and OLDDECL was not yet been merged; therefore, TREE_ASM_WRITTEN was not set. */ if (TREE_ASM_WRITTEN (olddecl)) - error ("%Jweak declaration of '%D' must precede definition", - newdecl, newdecl); + error ("weak declaration of %q+D must precede definition", + newdecl); /* 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. */ else if (TREE_USED (olddecl) && TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (olddecl))) - warning ("%Jweak declaration of '%D' after first use results " - "in unspecified behavior", newdecl, newdecl); + warning (0, "weak declaration of %q+D after first use results " + "in unspecified behavior", newdecl); if (SUPPORTS_WEAK) { @@ -4304,49 +4598,134 @@ void declare_weak (tree decl) { if (! TREE_PUBLIC (decl)) - error ("%Jweak declaration of '%D' must be public", decl, decl); + error ("weak declaration of %q+D must be public", decl); else if (TREE_CODE (decl) == FUNCTION_DECL && TREE_ASM_WRITTEN (decl)) - error ("%Jweak declaration of '%D' must precede definition", decl, decl); + error ("weak declaration of %q+D must precede definition", decl); else if (SUPPORTS_WEAK) { if (! DECL_WEAK (decl)) weak_decls = tree_cons (NULL, decl, weak_decls); } else - warning ("%Jweak declaration of '%D' not supported", decl, decl); + warning (0, "weak declaration of %q+D not supported", decl); mark_weak (decl); } -/* Emit any pending weak declarations. */ - -void -weak_finish (void) +static void +weak_finish_1 (tree decl) { - tree t; - - for (t = weak_decls; t; t = TREE_CHAIN (t)) - { - tree decl = TREE_VALUE (t); #if defined (ASM_WEAKEN_DECL) || defined (ASM_WEAKEN_LABEL) - const char *const name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)); + const char *const name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)); #endif - if (! TREE_USED (decl)) - continue; + if (! TREE_USED (decl)) + return; #ifdef ASM_WEAKEN_DECL - ASM_WEAKEN_DECL (asm_out_file, decl, name, NULL); + ASM_WEAKEN_DECL (asm_out_file, decl, name, NULL); #else #ifdef ASM_WEAKEN_LABEL - ASM_WEAKEN_LABEL (asm_out_file, name); + ASM_WEAKEN_LABEL (asm_out_file, name); #else #ifdef ASM_OUTPUT_WEAK_ALIAS - warning ("only weak aliases are supported in this configuration"); - return; + { + 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 +} + +/* This TREE_LIST contains weakref targets. */ + +static GTY(()) tree weakref_targets; + +/* Forward declaration. */ +static tree find_decl_and_mark_needed (tree decl, tree target); + +/* 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))) + /* 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 a + 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_and_mark_needed (alias_decl, target); + + if (! decl) + { + decl = build_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); } } @@ -4377,133 +4756,285 @@ globalize_decl (tree decl) 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; } +#elif defined(ASM_MAKE_LABEL_LINKONCE) + if (DECL_ONE_ONLY (decl)) + ASM_MAKE_LABEL_LINKONCE (asm_out_file, name); #endif - (*targetm.asm_out.globalize_label) (asm_out_file, name); + targetm.asm_out.globalize_label (asm_out_file, name); } -/* Some targets do not allow a forward or undefined reference in a - ASM_OUTPUT_DEF. Thus, a mechanism is needed to defer the output - of this assembler code. The output_def_pair struct holds the - declaration and target for a deferred output define. */ -struct output_def_pair GTY(()) +/* We have to be able to tell cgraph about the needed-ness of the target + of an alias. This requires that the decl have been defined. Aliases + that precede their definition have to be queued for later processing. */ + +typedef struct alias_pair GTY(()) { tree decl; tree target; -}; -typedef struct output_def_pair *output_def_pair; +} alias_pair; -/* Variable array of deferred output defines. */ -static GTY ((param_is (struct output_def_pair))) varray_type output_defs; +/* Define gc'd vector type. */ +DEF_VEC_O(alias_pair); +DEF_VEC_ALLOC_O(alias_pair,gc); + +static GTY(()) VEC(alias_pair,gc) *alias_pairs; + +/* Given an assembly name, find the decl it is associated with. At the + same time, mark it needed for cgraph. */ + +static tree +find_decl_and_mark_needed (tree decl, tree target) +{ + struct cgraph_node *fnode = NULL; + struct cgraph_varpool_node *vnode = NULL; + + if (TREE_CODE (decl) == FUNCTION_DECL) + { + fnode = cgraph_node_for_asm (target); + if (fnode == NULL) + vnode = cgraph_varpool_node_for_asm (target); + } + else + { + vnode = cgraph_varpool_node_for_asm (target); + if (vnode == NULL) + fnode = cgraph_node_for_asm (target); + } + + if (fnode) + { + /* We can't mark function nodes as used after cgraph global info + is finished. This wouldn't generally be necessary, but C++ + virtual table thunks are introduced late in the game and + might seem like they need marking, although in fact they + don't. */ + if (! cgraph_global_info_ready) + cgraph_mark_needed_node (fnode); + return fnode->decl; + } + else if (vnode) + { + cgraph_varpool_mark_needed_node (vnode); + return vnode->decl; + } + else + return NULL_TREE; +} -#ifdef ASM_OUTPUT_DEF /* 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. */ static void -assemble_output_def (tree decl ATTRIBUTE_UNUSED, tree target ATTRIBUTE_UNUSED) +do_assemble_alias (tree decl, tree target) { -#ifdef ASM_OUTPUT_DEF_FROM_DECLS - ASM_OUTPUT_DEF_FROM_DECLS (asm_out_file, decl, target); + if (TREE_ASM_WRITTEN (decl)) + return; + + TREE_ASM_WRITTEN (decl) = 1; + TREE_ASM_WRITTEN (DECL_ASSEMBLER_NAME (decl)) = 1; + + if (lookup_attribute ("weakref", DECL_ATTRIBUTES (decl))) + { + ultimate_transparent_alias_target (&target); + + 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 (DECL_ASSEMBLER_NAME (decl)), + IDENTIFIER_POINTER (target)); #else + if (!SUPPORTS_WEAK) + { + error ("%Jweakref is not supported in this configuration", decl); + return; + } +#endif + return; + } + +#ifdef ASM_OUTPUT_DEF + /* Make name accessible from other files, if appropriate. */ + + if (TREE_PUBLIC (decl)) + { + globalize_decl (decl); + maybe_assemble_visibility (decl); + } + +# 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 (DECL_ASSEMBLER_NAME (decl)), IDENTIFIER_POINTER (target)); +# endif +#elif defined (ASM_OUTPUT_WEAK_ALIAS) || defined (ASM_WEAKEN_DECL) + { + const char *name; + tree *p, t; + + name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)); +# 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))) + *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); + } + } #endif } -#endif -/* Process the varray of pending assembler defines. */ +/* First pass of completing pending aliases. Make sure that cgraph knows + which symbols will be required. */ void -process_pending_assemble_output_defs (void) +finish_aliases_1 (void) { -#ifdef ASM_OUTPUT_DEF - size_t i; - output_def_pair p; - - if (!output_defs) - return; + unsigned i; + alias_pair *p; - for (i = 0; i < VARRAY_ACTIVE_SIZE (output_defs); i++) + for (i = 0; VEC_iterate (alias_pair, alias_pairs, i, p); i++) { - p = VARRAY_GENERIC_PTR (output_defs, i); - assemble_output_def (p->decl, p->target); + tree target_decl; + + target_decl = find_decl_and_mark_needed (p->decl, p->target); + if (target_decl == NULL) + { + if (! lookup_attribute ("weakref", DECL_ATTRIBUTES (p->decl))) + error ("%q+D aliased to undefined symbol %qs", + p->decl, IDENTIFIER_POINTER (p->target)); + } + else if (DECL_EXTERNAL (target_decl) + && ! lookup_attribute ("weakref", DECL_ATTRIBUTES (p->decl))) + error ("%q+D aliased to external symbol %qs", + p->decl, IDENTIFIER_POINTER (p->target)); } +} - output_defs = NULL; -#endif +/* Second pass of completing pending aliases. Emit the actual assembly. + This happens at the end of compilation and thus it is assured that the + target symbol has been emitted. */ + +void +finish_aliases_2 (void) +{ + unsigned i; + alias_pair *p; + + for (i = 0; VEC_iterate (alias_pair, alias_pairs, i, p); i++) + do_assemble_alias (p->decl, p->target); + + VEC_truncate (alias_pair, alias_pairs, 0); } /* Emit an assembler directive to make the symbol for DECL an alias to the symbol for TARGET. */ void -assemble_alias (tree decl, tree target ATTRIBUTE_UNUSED) +assemble_alias (tree decl, tree target) { - /* We must force creation of DECL_RTL for debug info generation, even though - we don't use it here. */ - make_decl_rtl (decl, NULL); - -#ifdef ASM_OUTPUT_DEF - /* Make name accessible from other files, if appropriate. */ + tree target_decl; + bool is_weakref = false; - if (TREE_PUBLIC (decl)) + if (lookup_attribute ("weakref", DECL_ATTRIBUTES (decl))) { - globalize_decl (decl); - maybe_assemble_visibility (decl); - } + tree alias = DECL_ASSEMBLER_NAME (decl); - if (TARGET_DEFERRED_OUTPUT_DEFS (decl, target)) - { - output_def_pair p; + is_weakref = true; - if (!output_defs) - VARRAY_GENERIC_PTR_INIT (output_defs, 10, "output defs"); - - p = ggc_alloc (sizeof (struct output_def_pair)); - p->decl = decl; - p->target = target; - VARRAY_PUSH_GENERIC_PTR (output_defs, p); - } - else - assemble_output_def (decl, target); -#else /* !ASM_OUTPUT_DEF */ -#if defined (ASM_OUTPUT_WEAK_ALIAS) || defined (ASM_WEAKEN_DECL) - if (DECL_WEAK (decl)) - { - const char *name; - tree *p, t; + ultimate_transparent_alias_target (&target); - name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)); -#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)); + if (alias == target) + error ("weakref %q+D ultimately targets itself", decl); + else + { +#ifndef ASM_OUTPUT_WEAKREF + IDENTIFIER_TRANSPARENT_ALIAS (alias) = 1; + TREE_CHAIN (alias) = 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))) - *p = TREE_CHAIN (t); - else - p = &TREE_CHAIN (t); + } + if (TREE_PUBLIC (decl)) + error ("weakref %q+D must have static linkage", decl); } else - warning ("only weak aliases are supported in this configuration"); - -#else - warning ("alias definitions not supported in this configuration; ignored"); -#endif + { +#if !defined (ASM_OUTPUT_DEF) +# if !defined(ASM_OUTPUT_WEAK_ALIAS) && !defined (ASM_WEAKEN_DECL) + error ("%Jalias definitions not supported in this configuration", decl); + return; +# else + if (!DECL_WEAK (decl)) + { + error ("%Jonly weak aliases are supported in this configuration", decl); + return; + } +# endif #endif + } + /* We must force creation of DECL_RTL for debug info generation, even though + we don't use it here. */ + make_decl_rtl (decl); TREE_USED (decl) = 1; - TREE_ASM_WRITTEN (decl) = 1; - TREE_ASM_WRITTEN (DECL_ASSEMBLER_NAME (decl)) = 1; + + /* A quirk of the initial implementation of aliases required that the user + add "extern" to all of them. Which is silly, but now historical. Do + note that the symbol is in fact locally defined. */ + if (! is_weakref) + DECL_EXTERNAL (decl) = 0; + + /* Allow aliases to aliases. */ + if (TREE_CODE (decl) == FUNCTION_DECL) + cgraph_node (decl)->alias = true; + else + cgraph_varpool_node (decl)->alias = true; + + /* If the target has already been emitted, we don't have to queue the + alias. This saves a tad o memory. */ + target_decl = find_decl_and_mark_needed (decl, target); + if (target_decl && TREE_ASM_WRITTEN (target_decl)) + do_assemble_alias (decl, target); + else + { + alias_pair *p = VEC_safe_push (alias_pair, gc, alias_pairs, NULL); + p->decl = decl; + p->target = target; + } } /* Emit an assembler directive to set symbol for DECL visibility to @@ -4513,12 +5044,12 @@ void default_assemble_visibility (tree decl, int vis) { static const char * const visibility_types[] = { - NULL, "internal", "hidden", "protected" + NULL, "protected", "hidden", "internal" }; const char *name, *type; - name = (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl))); + name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)); type = visibility_types[vis]; #ifdef HAVE_GAS_HIDDEN @@ -4526,7 +5057,8 @@ default_assemble_visibility (tree decl, int vis) assemble_name (asm_out_file, name); fprintf (asm_out_file, "\n"); #else - warning ("visibility attribute not supported in this configuration; ignored"); + warning (OPT_Wattributes, "visibility attribute not supported " + "in this configuration; ignored"); #endif } @@ -4538,7 +5070,7 @@ maybe_assemble_visibility (tree decl) enum symbol_visibility vis = DECL_VISIBILITY (decl); if (vis != VISIBILITY_DEFAULT) - (* targetm.asm_out.visibility) (decl, vis); + targetm.asm_out.visibility (decl, vis); } /* Returns 1 if the target configuration supports defining public symbols @@ -4560,8 +5092,8 @@ supports_one_only (void) void make_decl_one_only (tree decl) { - if (TREE_CODE (decl) != VAR_DECL && TREE_CODE (decl) != FUNCTION_DECL) - abort (); + gcc_assert (TREE_CODE (decl) == VAR_DECL + || TREE_CODE (decl) == FUNCTION_DECL); TREE_PUBLIC (decl) = 1; @@ -4575,56 +5107,101 @@ make_decl_one_only (tree decl) else if (TREE_CODE (decl) == VAR_DECL && (DECL_INITIAL (decl) == 0 || DECL_INITIAL (decl) == error_mark_node)) DECL_COMMON (decl) = 1; - else if (SUPPORTS_WEAK) - DECL_WEAK (decl) = 1; else - abort (); + { + gcc_assert (SUPPORTS_WEAK); + DECL_WEAK (decl) = 1; + } } void init_varasm_once (void) { - in_named_htab = htab_create_ggc (31, in_named_entry_hash, - in_named_entry_eq, NULL); + section_htab = htab_create_ggc (31, section_entry_hash, + section_entry_eq, NULL); + object_block_htab = htab_create_ggc (31, object_block_entry_hash, + object_block_entry_eq, NULL); const_desc_htab = htab_create_ggc (1009, const_desc_hash, const_desc_eq, NULL); const_alias_set = new_alias_set (); + 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 || defined ASM_OUTPUT_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; } enum tls_model -decl_tls_model (tree decl) +decl_default_tls_model (tree decl) { enum tls_model kind; - tree attr = lookup_attribute ("tls_model", DECL_ATTRIBUTES (decl)); bool is_local; - if (attr) - { - attr = TREE_VALUE (TREE_VALUE (attr)); - if (TREE_CODE (attr) != STRING_CST) - abort (); - if (!strcmp (TREE_STRING_POINTER (attr), "local-exec")) - kind = TLS_MODEL_LOCAL_EXEC; - else if (!strcmp (TREE_STRING_POINTER (attr), "initial-exec")) - kind = TLS_MODEL_INITIAL_EXEC; - else if (!strcmp (TREE_STRING_POINTER (attr), "local-dynamic")) - kind = optimize ? TLS_MODEL_LOCAL_DYNAMIC : TLS_MODEL_GLOBAL_DYNAMIC; - else if (!strcmp (TREE_STRING_POINTER (attr), "global-dynamic")) - kind = TLS_MODEL_GLOBAL_DYNAMIC; - else - abort (); - return kind; - } - - is_local = (*targetm.binds_local_p) (decl); - if (!flag_pic) + 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 (optimize && is_local) @@ -4647,26 +5224,28 @@ decl_tls_model (tree decl) unsigned int default_section_type_flags (tree decl, const char *name, int reloc) { - return default_section_type_flags_1 (decl, name, reloc, flag_pic); -} - -unsigned int -default_section_type_flags_1 (tree decl, const char *name, int reloc, - int shlib) -{ unsigned int flags; if (decl && TREE_CODE (decl) == FUNCTION_DECL) flags = SECTION_CODE; - else if (decl && decl_readonly_section_1 (decl, reloc, shlib)) + else if (decl && decl_readonly_section (decl, reloc)) flags = 0; + else if (current_function_decl + && cfun + && cfun->unlikely_text_section_name + && strcmp (name, cfun->unlikely_text_section_name) == 0) + flags = SECTION_CODE; + else if (!decl + && (!current_function_decl || !cfun) + && strcmp (name, UNLIKELY_EXECUTED_TEXT_SECTION_NAME) == 0) + flags = SECTION_CODE; else flags = SECTION_WRITE; if (decl && DECL_ONE_ONLY (decl)) flags |= SECTION_LINKONCE; - if (decl && TREE_CODE (decl) == VAR_DECL && DECL_THREAD_LOCAL (decl)) + if (decl && TREE_CODE (decl) == VAR_DECL && DECL_THREAD_LOCAL_P (decl)) flags |= SECTION_TLS | SECTION_WRITE; if (strcmp (name, ".bss") == 0 @@ -4674,17 +5253,19 @@ default_section_type_flags_1 (tree decl, const char *name, int reloc, || strncmp (name, ".gnu.linkonce.b.", 16) == 0 || strcmp (name, ".sbss") == 0 || strncmp (name, ".sbss.", 6) == 0 - || strncmp (name, ".gnu.linkonce.sb.", 17) == 0 - || strcmp (name, ".tbss") == 0 - || strncmp (name, ".gnu.linkonce.tb.", 17) == 0) + || strncmp (name, ".gnu.linkonce.sb.", 17) == 0) flags |= SECTION_BSS; if (strcmp (name, ".tdata") == 0 - || strcmp (name, ".tbss") == 0 - || strncmp (name, ".gnu.linkonce.td.", 17) == 0 - || strncmp (name, ".gnu.linkonce.tb.", 17) == 0) + || strncmp (name, ".tdata.", 7) == 0 + || strncmp (name, ".gnu.linkonce.td.", 17) == 0) flags |= SECTION_TLS; + if (strcmp (name, ".tbss") == 0 + || strncmp (name, ".tbss.", 6) == 0 + || strncmp (name, ".gnu.linkonce.tb.", 17) == 0) + flags |= SECTION_TLS | SECTION_BSS; + /* These three sections have special ELF types. They are neither SHT_PROGBITS nor SHT_NOBITS, so when changing sections we don't want to print a section type (@progbits or @nobits). If someone @@ -4699,24 +5280,41 @@ default_section_type_flags_1 (tree decl, const char *name, int reloc, 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) + 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. */ - abort (); + gcc_unreachable (); } void -default_elf_asm_named_section (const char *name, unsigned int flags) +default_elf_asm_named_section (const char *name, unsigned int flags, + tree decl ATTRIBUTE_UNUSED) { char flagchars[10], *f = flagchars; - if (! named_section_first_declaration (name)) + /* 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, in which case GAS requires the full + declaration every time. */ + if (!(HAVE_COMDAT_GROUP && (flags & SECTION_LINKONCE)) + && (flags & SECTION_DECLARED)) { fprintf (asm_out_file, "\t.section\t%s\n", name); return; @@ -4736,6 +5334,8 @@ default_elf_asm_named_section (const char *name, unsigned int flags) *f++ = 'S'; if (flags & SECTION_TLS) *f++ = 'T'; + if (HAVE_COMDAT_GROUP && (flags & SECTION_LINKONCE)) + *f++ = 'G'; *f = '\0'; fprintf (asm_out_file, "\t.section\t%s,\"%s\"", name, flagchars); @@ -4743,23 +5343,35 @@ default_elf_asm_named_section (const char *name, unsigned int flags) if (!(flags & SECTION_NOTYPE)) { const char *type; + const char *format; if (flags & SECTION_BSS) type = "nobits"; else type = "progbits"; - fprintf (asm_out_file, ",@%s", type); + format = ",@%s"; +#ifdef ASM_COMMENT_START + /* On platforms that use "@" as the assembly comment character, + use "%" instead. */ + if (strcmp (ASM_COMMENT_START, "@") == 0) + format = ",%%%s"; +#endif + fprintf (asm_out_file, format, type); if (flags & SECTION_ENTSIZE) fprintf (asm_out_file, ",%d", flags & SECTION_ENTSIZE); + if (HAVE_COMDAT_GROUP && (flags & SECTION_LINKONCE)) + fprintf (asm_out_file, ",%s,comdat", + lang_hooks.decls.comdat_group (decl)); } putc ('\n', asm_out_file); } void -default_coff_asm_named_section (const char *name, unsigned int flags) +default_coff_asm_named_section (const char *name, unsigned int flags, + tree decl ATTRIBUTE_UNUSED) { char flagchars[8], *f = flagchars; @@ -4773,9 +5385,10 @@ default_coff_asm_named_section (const char *name, unsigned int flags) } void -default_pe_asm_named_section (const char *name, unsigned int flags) +default_pe_asm_named_section (const char *name, unsigned int flags, + tree decl) { - default_coff_asm_named_section (name, flags); + default_coff_asm_named_section (name, flags, decl); if (flags & SECTION_LINKONCE) { @@ -4789,16 +5402,14 @@ default_pe_asm_named_section (const char *name, unsigned int flags) /* The lame default section selector. */ -void +section * default_select_section (tree decl, int reloc, unsigned HOST_WIDE_INT align ATTRIBUTE_UNUSED) { - bool readonly = false; - if (DECL_P (decl)) { if (decl_readonly_section (decl, reloc)) - readonly = true; + return readonly_data_section; } else if (TREE_CODE (decl) == CONSTRUCTOR) { @@ -4806,61 +5417,18 @@ default_select_section (tree decl, int reloc, || !TREE_READONLY (decl) || TREE_SIDE_EFFECTS (decl) || !TREE_CONSTANT (decl))) - readonly = true; + return readonly_data_section; } else if (TREE_CODE (decl) == STRING_CST) - readonly = !flag_writable_strings; + return readonly_data_section; else if (! (flag_pic && reloc)) - readonly = true; + return readonly_data_section; - if (readonly) - readonly_data_section (); - else - data_section (); + return data_section; } -/* A helper function for default_elf_select_section and - default_elf_unique_section. Categorizes the DECL. */ - enum section_category -{ - SECCAT_TEXT, - - SECCAT_RODATA, - SECCAT_RODATA_MERGE_STR, - SECCAT_RODATA_MERGE_STR_INIT, - SECCAT_RODATA_MERGE_CONST, - SECCAT_SRODATA, - - SECCAT_DATA, - - /* To optimize loading of shared programs, define following subsections - of data section: - _REL Contains data that has relocations, so they get grouped - together and dynamic linker will visit fewer pages in memory. - _RO Contains data that is otherwise read-only. This is useful - with prelinking as most relocations won't be dynamically - linked and thus stay read only. - _LOCAL Marks data containing relocations only to local objects. - These relocations will get fully resolved by prelinking. */ - SECCAT_DATA_REL, - SECCAT_DATA_REL_LOCAL, - SECCAT_DATA_REL_RO, - SECCAT_DATA_REL_RO_LOCAL, - - SECCAT_SDATA, - SECCAT_TDATA, - - SECCAT_BSS, - SECCAT_SBSS, - SECCAT_TBSS -}; - -static enum section_category -categorize_decl_for_section (tree, int, int); - -static enum section_category -categorize_decl_for_section (tree decl, int reloc, int shlib) +categorize_decl_for_section (tree decl, int reloc) { enum section_category ret; @@ -4868,31 +5436,30 @@ categorize_decl_for_section (tree decl, int reloc, int shlib) return SECCAT_TEXT; else if (TREE_CODE (decl) == STRING_CST) { - if (flag_writable_strings) - return SECCAT_DATA; + if (flag_mudflap) /* or !flag_merge_constants */ + return SECCAT_RODATA; else return SECCAT_RODATA_MERGE_STR; } else if (TREE_CODE (decl) == VAR_DECL) { - if (DECL_INITIAL (decl) == NULL - || DECL_INITIAL (decl) == error_mark_node) + if (bss_initializer_p (decl)) ret = SECCAT_BSS; else if (! TREE_READONLY (decl) || TREE_SIDE_EFFECTS (decl) || ! TREE_CONSTANT (DECL_INITIAL (decl))) { - if (shlib && (reloc & 2)) - ret = SECCAT_DATA_REL; - else if (shlib && reloc) - ret = SECCAT_DATA_REL_LOCAL; + /* 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 (shlib && (reloc & 2)) - ret = SECCAT_DATA_REL_RO; - else if (shlib && reloc) - ret = SECCAT_DATA_REL_RO_LOCAL; + 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) /* C and C++ don't allow different variables to share the same location. -fmerge-all-constants allows even that (at the @@ -4905,7 +5472,7 @@ categorize_decl_for_section (tree decl, int reloc, int shlib) } else if (TREE_CODE (decl) == CONSTRUCTOR) { - if ((shlib && reloc) + if ((reloc & targetm.asm_out.reloc_rw_mask ()) || TREE_SIDE_EFFECTS (decl) || ! TREE_CONSTANT (decl)) ret = SECCAT_DATA; @@ -4916,16 +5483,20 @@ categorize_decl_for_section (tree decl, int reloc, int shlib) ret = SECCAT_RODATA; /* There are no read-only thread-local sections. */ - if (TREE_CODE (decl) == VAR_DECL && DECL_THREAD_LOCAL (decl)) + if (TREE_CODE (decl) == VAR_DECL && DECL_THREAD_LOCAL_P (decl)) { - if (ret == SECCAT_BSS) + /* 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 + || (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)) + else if (targetm.in_small_data_p (decl)) { if (ret == SECCAT_BSS) ret = SECCAT_SBSS; @@ -4941,13 +5512,7 @@ categorize_decl_for_section (tree decl, int reloc, int shlib) bool decl_readonly_section (tree decl, int reloc) { - return decl_readonly_section_1 (decl, reloc, flag_pic); -} - -bool -decl_readonly_section_1 (tree decl, int reloc, int shlib) -{ - switch (categorize_decl_for_section (decl, reloc, shlib)) + switch (categorize_decl_for_section (decl, reloc)) { case SECCAT_RODATA: case SECCAT_RODATA_MERGE_STR: @@ -4964,74 +5529,65 @@ decl_readonly_section_1 (tree decl, int reloc, int shlib) /* Select a section based on the above categorization. */ -void +section * default_elf_select_section (tree decl, int reloc, unsigned HOST_WIDE_INT align) { - default_elf_select_section_1 (decl, reloc, align, flag_pic); -} - -void -default_elf_select_section_1 (tree decl, int reloc, - unsigned HOST_WIDE_INT align, int shlib) -{ - switch (categorize_decl_for_section (decl, reloc, shlib)) + const char *sname; + switch (categorize_decl_for_section (decl, reloc)) { case SECCAT_TEXT: /* We're not supposed to be called on FUNCTION_DECLs. */ - abort (); + gcc_unreachable (); case SECCAT_RODATA: - readonly_data_section (); - break; + return readonly_data_section; case SECCAT_RODATA_MERGE_STR: - mergeable_string_section (decl, align, 0); - break; + return mergeable_string_section (decl, align, 0); case SECCAT_RODATA_MERGE_STR_INIT: - mergeable_string_section (DECL_INITIAL (decl), align, 0); - break; + return mergeable_string_section (DECL_INITIAL (decl), align, 0); case SECCAT_RODATA_MERGE_CONST: - mergeable_constant_section (DECL_MODE (decl), align, 0); - break; + return mergeable_constant_section (DECL_MODE (decl), align, 0); case SECCAT_SRODATA: - named_section (NULL_TREE, ".sdata2", reloc); + sname = ".sdata2"; break; case SECCAT_DATA: - data_section (); - break; + return data_section; case SECCAT_DATA_REL: - named_section (NULL_TREE, ".data.rel", reloc); + sname = ".data.rel"; break; case SECCAT_DATA_REL_LOCAL: - named_section (NULL_TREE, ".data.rel.local", reloc); + sname = ".data.rel.local"; break; case SECCAT_DATA_REL_RO: - named_section (NULL_TREE, ".data.rel.ro", reloc); + sname = ".data.rel.ro"; break; case SECCAT_DATA_REL_RO_LOCAL: - named_section (NULL_TREE, ".data.rel.ro.local", reloc); + sname = ".data.rel.ro.local"; break; case SECCAT_SDATA: - named_section (NULL_TREE, ".sdata", reloc); + sname = ".sdata"; break; case SECCAT_TDATA: - named_section (NULL_TREE, ".tdata", reloc); + sname = ".tdata"; break; case SECCAT_BSS: -#ifdef BSS_SECTION_ASM_OP - bss_section (); -#else - named_section (NULL_TREE, ".bss", reloc); -#endif + if (bss_section) + return bss_section; + sname = ".bss"; break; case SECCAT_SBSS: - named_section (NULL_TREE, ".sbss", reloc); + sname = ".sbss"; break; case SECCAT_TBSS: - named_section (NULL_TREE, ".tbss", reloc); + sname = ".tbss"; break; default: - abort (); + gcc_unreachable (); } + + if (!DECL_P (decl)) + decl = NULL_TREE; + return get_named_section (decl, sname, reloc); } /* Construct a unique section name based on the decl name and the @@ -5040,18 +5596,13 @@ default_elf_select_section_1 (tree decl, int reloc, void default_unique_section (tree decl, int reloc) { - default_unique_section_1 (decl, reloc, flag_pic); -} - -void -default_unique_section_1 (tree decl, int reloc, int shlib) -{ - bool one_only = DECL_ONE_ONLY (decl); + /* 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; size_t nlen, plen; char *string; - switch (categorize_decl_for_section (decl, reloc, shlib)) + switch (categorize_decl_for_section (decl, reloc)) { case SECCAT_TEXT: prefix = one_only ? ".gnu.linkonce.t." : ".text."; @@ -5066,11 +5617,20 @@ default_unique_section_1 (tree decl, int reloc, int shlib) prefix = one_only ? ".gnu.linkonce.s2." : ".sdata2."; break; case SECCAT_DATA: + prefix = one_only ? ".gnu.linkonce.d." : ".data."; + break; case SECCAT_DATA_REL: + prefix = one_only ? ".gnu.linkonce.d.rel." : ".data.rel."; + break; case SECCAT_DATA_REL_LOCAL: + prefix = one_only ? ".gnu.linkonce.d.rel.local." : ".data.rel.local."; + break; case SECCAT_DATA_REL_RO: + prefix = one_only ? ".gnu.linkonce.d.rel.ro." : ".data.rel.ro."; + break; case SECCAT_DATA_REL_RO_LOCAL: - prefix = one_only ? ".gnu.linkonce.d." : ".data."; + prefix = one_only ? ".gnu.linkonce.d.rel.ro.local." + : ".data.rel.ro.local."; break; case SECCAT_SDATA: prefix = one_only ? ".gnu.linkonce.s." : ".sdata."; @@ -5088,12 +5648,12 @@ default_unique_section_1 (tree decl, int reloc, int shlib) prefix = one_only ? ".gnu.linkonce.tb." : ".tbss."; break; default: - abort (); + gcc_unreachable (); } plen = strlen (prefix); name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)); - name = (* targetm.strip_name_encoding) (name); + name = targetm.strip_name_encoding (name); nlen = strlen (name); string = alloca (nlen + plen + 1); @@ -5103,50 +5663,78 @@ default_unique_section_1 (tree decl, int reloc, int shlib) DECL_SECTION_NAME (decl) = build_string (nlen + plen, string); } -void +/* 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. */ + +static int +compute_reloc_for_rtx_1 (rtx *xp, void *data) +{ + int *preloc = data; + rtx x = *xp; + + switch (GET_CODE (x)) + { + case SYMBOL_REF: + *preloc |= SYMBOL_REF_LOCAL_P (x) ? 1 : 2; + break; + case LABEL_REF: + *preloc |= 1; + break; + default: + break; + } + + return 0; +} + +static int +compute_reloc_for_rtx (rtx x) +{ + int reloc; + + switch (GET_CODE (x)) + { + case CONST: + case SYMBOL_REF: + case LABEL_REF: + reloc = 0; + for_each_rtx (&x, compute_reloc_for_rtx_1, &reloc); + return reloc; + + default: + return 0; + } +} + +section * default_select_rtx_section (enum machine_mode mode ATTRIBUTE_UNUSED, rtx x, unsigned HOST_WIDE_INT align ATTRIBUTE_UNUSED) { - if (flag_pic) - switch (GET_CODE (x)) - { - case CONST: - case SYMBOL_REF: - case LABEL_REF: - data_section (); - return; - - default: - break; - } - - readonly_data_section (); + if (compute_reloc_for_rtx (x) & targetm.asm_out.reloc_rw_mask ()) + return data_section; + else + return readonly_data_section; } -void +section * default_elf_select_rtx_section (enum machine_mode mode, rtx x, unsigned HOST_WIDE_INT align) { - /* ??? Handle small data here somehow. */ - - if (flag_pic) - switch (GET_CODE (x)) - { - case CONST: - case SYMBOL_REF: - named_section (NULL_TREE, ".data.rel.ro", 3); - return; + int reloc = compute_reloc_for_rtx (x); - case LABEL_REF: - named_section (NULL_TREE, ".data.rel.ro.local", 1); - return; + /* ??? Handle small data here somehow. */ - default: - break; - } + if (reloc & targetm.asm_out.reloc_rw_mask ()) + { + if (reloc == 1) + return get_named_section (NULL, ".data.rel.ro.local", 1); + else + return get_named_section (NULL, ".data.rel.ro", 3); + } - mergeable_constant_section (mode, align, 0); + return mergeable_constant_section (mode, align, 0); } /* Set the generally applicable flags on the SYMBOL_REF for EXP. */ @@ -5158,21 +5746,21 @@ default_encode_section_info (tree decl, rtx rtl, int first ATTRIBUTE_UNUSED) int flags; /* Careful not to prod global register variables. */ - if (GET_CODE (rtl) != MEM) + if (!MEM_P (rtl)) return; symbol = XEXP (rtl, 0); if (GET_CODE (symbol) != SYMBOL_REF) return; - flags = 0; + 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)) + if (targetm.binds_local_p (decl)) flags |= SYMBOL_FLAG_LOCAL; - if ((*targetm.in_small_data_p) (decl)) + if (TREE_CODE (decl) == VAR_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; - if (TREE_CODE (decl) == VAR_DECL && DECL_THREAD_LOCAL (decl)) - flags |= decl_tls_model (decl) << SYMBOL_FLAG_TLS_SHIFT; /* ??? 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. */ @@ -5191,6 +5779,59 @@ 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) +{ + 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 (rtx symbol) +{ + section *sect; + tree decl; + + /* Don't use anchors for mergeable sections. The linker might move + the objects around. */ + sect = SYMBOL_REF_BLOCK (symbol)->sect; + if (sect->common.flags & SECTION_MERGE) + return false; + + /* 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 by + other modules. */ + if (!targetm.binds_local_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; + } + return true; +} + /* Assume ELF-ish defaults, since that's pretty much the most liberal wrt cross-module name binding. */ @@ -5208,17 +5849,28 @@ default_binds_local_p_1 (tree exp, int shlib) /* A non-decl is an entry in the constant pool. */ if (!DECL_P (exp)) local_p = true; + /* Weakrefs may not bind locally, even though the weakref itself is + always static and therefore local. */ + else if (lookup_attribute ("weakref", DECL_ATTRIBUTES (exp))) + local_p = false; /* Static variables are always local. */ else if (! TREE_PUBLIC (exp)) local_p = true; - /* A variable is local if the user tells us so. */ - else if (DECL_VISIBILITY (exp) != VISIBILITY_DEFAULT) + /* A variable is local if the user has said explicitly that it will + be. */ + else if (DECL_VISIBILITY_SPECIFIED (exp) + && DECL_VISIBILITY (exp) != VISIBILITY_DEFAULT) local_p = true; - /* Otherwise, variables defined outside this object may not be local. */ + /* Variables defined outside this object might not be local. */ else if (DECL_EXTERNAL (exp)) local_p = false; - /* Linkonce and weak data are never local. */ - else if (DECL_ONE_ONLY (exp) || DECL_WEAK (exp)) + /* If defined in this object and visibility is not default, must be + local. */ + else if (DECL_VISIBILITY (exp) != VISIBILITY_DEFAULT) + local_p = true; + /* Default visibility weak data can be overridden by a strong symbol + in another module and so are not local. */ + else if (DECL_WEAK (exp)) local_p = false; /* If PIC, then assume that any global name can be overridden by symbols resolved from other modules. */ @@ -5247,7 +5899,7 @@ default_valid_pointer_mode (enum machine_mode mode) } /* Default function to output code that will globalize a label. A - target must define GLOBAL_ASM_OP or provide it's own function to + target must define GLOBAL_ASM_OP or provide its own function to globalize a label. */ #ifdef GLOBAL_ASM_OP void @@ -5259,6 +5911,25 @@ default_globalize_label (FILE * stream, const char *name) } #endif /* GLOBAL_ASM_OP */ +/* 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. */ @@ -5268,7 +5939,7 @@ default_internal_label (FILE *stream, const char *prefix, { char *const buf = alloca (40 + strlen (prefix)); ASM_GENERATE_INTERNAL_LABEL (buf, prefix, labelno); - ASM_OUTPUT_LABEL (stream, buf); + ASM_OUTPUT_INTERNAL_LABEL (stream, buf); } /* This is the default behavior at the beginning of a file. It's @@ -5287,6 +5958,9 @@ default_file_start (void) 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) { @@ -5294,7 +5968,260 @@ file_end_indicate_exec_stack (void) if (trampolines_created) flags |= SECTION_CODE; - named_section_flags (".note.GNU-stack", flags); + switch_to_section (get_section (".note.GNU-stack", flags, 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 void *directive) +{ + fprintf (asm_out_file, "%s\n", (const char *) 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) +{ + if (in_section == new_section) + return; + + if (new_section->common.flags & SECTION_FORGET) + in_section = NULL; + else + in_section = new_section; + + switch (SECTION_STYLE (new_section)) + { + case SECTION_NAMED: + if (cfun + && !cfun->unlikely_text_section_name + && strcmp (new_section->named.name, + UNLIKELY_EXECUTED_TEXT_SECTION_NAME) == 0) + cfun->unlikely_text_section_name = UNLIKELY_EXECUTED_TEXT_SECTION_NAME; + + 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; + struct 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); + alignment = get_constant_alignment (decl); + size = get_constant_size (decl); + } + else + { + decl = SYMBOL_REF_DECL (symbol); + alignment = DECL_ALIGN (decl); + size = tree_low_cst (DECL_SIZE_UNIT (decl), 1); + } + + /* 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 (rtx, gc, 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 = 1 << (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_length (rtx, block->anchors); + while (begin != end) + { + middle = (end + begin) / 2; + anchor = VEC_index (rtx, 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 (rtx, gc, block->anchors, begin, anchor); + return anchor; +} + +/* Output the objects in BLOCK. */ + +static void +output_object_block (struct object_block *block) +{ + struct constant_descriptor_rtx *desc; + unsigned int i; + HOST_WIDE_INT offset; + tree decl; + rtx symbol; + + if (block->objects == NULL) + return; + + /* Switch to the section and make sure that the first byte is + suitably aligned. */ + switch_to_section (block->sect); + assemble_align (block->alignment); + + /* Define the values of all anchors relative to the current section + position. */ + for (i = 0; VEC_iterate (rtx, block->anchors, i, symbol); i++) + targetm.asm_out.output_anchor (symbol); + + /* Output the objects themselves. */ + offset = 0; + for (i = 0; VEC_iterate (rtx, block->objects, i, symbol); i++) + { + /* 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); + output_constant_pool_1 (desc, 1); + offset += GET_MODE_SIZE (desc->mode); + } + else if (TREE_CONSTANT_POOL_ADDRESS_P (symbol)) + { + decl = SYMBOL_REF_DECL (symbol); + assemble_constant_contents (decl, XSTR (symbol, 0), + get_constant_alignment (decl)); + offset += get_constant_size (decl); + } + else + { + decl = SYMBOL_REF_DECL (symbol); + assemble_variable_contents (decl, XSTR (symbol, 0), false); + offset += tree_low_cst (DECL_SIZE_UNIT (decl), 1); + } + } +} + +/* A htab_traverse callback used to call output_object_block for + each member of object_block_htab. */ + +static int +output_object_block_htab (void **slot, void *data ATTRIBUTE_UNUSED) +{ + output_object_block ((struct object_block *) (*slot)); + return 1; +} + +/* Output the definitions of all object_blocks. */ + +void +output_object_blocks (void) +{ + htab_traverse (object_block_htab, output_object_block_htab, NULL); } #include "gt-varasm.h" |