diff options
Diffstat (limited to 'gcc/varasm.c')
-rw-r--r-- | gcc/varasm.c | 262 |
1 files changed, 166 insertions, 96 deletions
diff --git a/gcc/varasm.c b/gcc/varasm.c index 81552fe6fed..b4048b5b4dd 100644 --- a/gcc/varasm.c +++ b/gcc/varasm.c @@ -307,19 +307,22 @@ get_section (const char *name, unsigned int flags, tree decl) return sect; } /* Sanity check user variables for flag changes. */ - if (decl == 0) - decl = sect->named.decl; - gcc_assert (decl); - if (sect->named.decl == NULL) - error ("%+D causes a section type conflict", decl); - else + if (sect->named.decl != NULL + && DECL_P (sect->named.decl) + && decl != sect->named.decl) { - error ("%+D causes a section type conflict with %D", - decl, sect->named.decl); - if (decl != sect->named.decl) - inform (DECL_SOURCE_LOCATION (sect->named.decl), - "%qD was declared here", sect->named.decl); + if (decl != NULL && DECL_P (decl)) + error ("%+D causes a section type conflict with %D", + decl, sect->named.decl); + else + error ("section type conflict with %D", sect->named.decl); + inform (DECL_SOURCE_LOCATION (sect->named.decl), + "%qD was declared here", sect->named.decl); } + else if (decl != NULL && DECL_P (decl)) + error ("%+D causes a section type conflict", decl); + else + error ("section type conflict"); /* Make sure we don't error about one section multiple times. */ sect->common.flags |= SECTION_OVERRIDE; } @@ -409,9 +412,6 @@ get_named_section (tree decl, const char *name, int reloc) } flags = targetm.section_type_flags (decl, name, reloc); - - if (decl && !DECL_P (decl)) - decl = NULL_TREE; return get_section (name, flags, decl); } @@ -966,13 +966,80 @@ align_variable (tree decl, bool dont_output_data) align = MAX_OFILE_ALIGNMENT; } - /* On some machines, it is good to increase alignment sometimes. */ if (! DECL_USER_ALIGN (decl)) { +#ifdef DATA_ABI_ALIGNMENT + unsigned int data_abi_align + = DATA_ABI_ALIGNMENT (TREE_TYPE (decl), align); + /* For backwards compatibility, don't assume the ABI alignment for + TLS variables. */ + if (! DECL_THREAD_LOCAL_P (decl) || data_abi_align <= BITS_PER_WORD) + align = data_abi_align; +#endif + + /* On some machines, it is good to increase alignment sometimes. + But as DECL_ALIGN is used both for actually emitting the variable + and for code accessing the variable as guaranteed alignment, we + can only increase the alignment if it is a performance optimization + if the references to it must bind to the current definition. */ + if (decl_binds_to_current_def_p (decl)) + { +#ifdef DATA_ALIGNMENT + unsigned int data_align = DATA_ALIGNMENT (TREE_TYPE (decl), align); + /* Don't increase alignment too much for TLS variables - TLS space + is too precious. */ + if (! DECL_THREAD_LOCAL_P (decl) || data_align <= BITS_PER_WORD) + align = data_align; +#endif +#ifdef CONSTANT_ALIGNMENT + if (DECL_INITIAL (decl) != 0 + && DECL_INITIAL (decl) != error_mark_node) + { + unsigned int const_align + = CONSTANT_ALIGNMENT (DECL_INITIAL (decl), align); + /* Don't increase alignment too much for TLS variables - TLS + space is too precious. */ + if (! DECL_THREAD_LOCAL_P (decl) || const_align <= BITS_PER_WORD) + align = const_align; + } +#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 DECL_ALIGN (decl), possibly increased for optimization purposes + beyond what align_variable returned. */ + +static unsigned int +get_variable_align (tree decl) +{ + unsigned int align = DECL_ALIGN (decl); + + /* For user aligned vars or static vars align_variable already did + everything. */ + if (DECL_USER_ALIGN (decl) || !TREE_PUBLIC (decl)) + return align; + +#ifdef DATA_ABI_ALIGNMENT + if (DECL_THREAD_LOCAL_P (decl)) + align = DATA_ABI_ALIGNMENT (TREE_TYPE (decl), align); +#endif + + /* For decls that bind to the current definition, align_variable + did also everything, except for not assuming ABI required alignment + of TLS variables. For other vars, increase the alignment here + as an optimization. */ + if (!decl_binds_to_current_def_p (decl)) + { + /* On some machines, it is good to increase alignment sometimes. */ #ifdef DATA_ALIGNMENT unsigned int data_align = DATA_ALIGNMENT (TREE_TYPE (decl), align); /* Don't increase alignment too much for TLS variables - TLS space - is too precious. */ + is too precious. */ if (! DECL_THREAD_LOCAL_P (decl) || data_align <= BITS_PER_WORD) align = data_align; #endif @@ -989,9 +1056,7 @@ align_variable (tree decl, bool dont_output_data) #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 align; } /* Return the section into which the given VAR_DECL or CONST_DECL @@ -1043,7 +1108,8 @@ get_variable_section (tree decl, bool prefer_noswitch_p) return bss_noswitch_section; } - return targetm.asm_out.select_section (decl, reloc, DECL_ALIGN (decl)); + return targetm.asm_out.select_section (decl, reloc, + get_variable_align (decl)); } /* Return the block into which object_block DECL should be placed. */ @@ -1780,7 +1846,8 @@ emit_bss (tree decl ATTRIBUTE_UNUSED, unsigned HOST_WIDE_INT rounded ATTRIBUTE_UNUSED) { #if defined ASM_OUTPUT_ALIGNED_BSS - ASM_OUTPUT_ALIGNED_BSS (asm_out_file, decl, name, size, DECL_ALIGN (decl)); + ASM_OUTPUT_ALIGNED_BSS (asm_out_file, decl, name, size, + get_variable_align (decl)); return true; #endif } @@ -1796,10 +1863,11 @@ emit_common (tree decl ATTRIBUTE_UNUSED, { #if defined ASM_OUTPUT_ALIGNED_DECL_COMMON ASM_OUTPUT_ALIGNED_DECL_COMMON (asm_out_file, decl, name, - size, DECL_ALIGN (decl)); + size, get_variable_align (decl)); return true; #elif defined ASM_OUTPUT_ALIGNED_COMMON - ASM_OUTPUT_ALIGNED_COMMON (asm_out_file, name, size, DECL_ALIGN (decl)); + ASM_OUTPUT_ALIGNED_COMMON (asm_out_file, name, size, + get_variable_align (decl)); return true; #else ASM_OUTPUT_COMMON (asm_out_file, name, size, rounded); @@ -1828,7 +1896,8 @@ emit_tls_common (tree decl ATTRIBUTE_UNUSED, NAME is the name of DECL's SYMBOL_REF. */ static void -assemble_noswitch_variable (tree decl, const char *name, section *sect) +assemble_noswitch_variable (tree decl, const char *name, section *sect, + unsigned int align) { unsigned HOST_WIDE_INT size, rounded; @@ -1850,7 +1919,7 @@ assemble_noswitch_variable (tree decl, const char *name, section *sect) * (BIGGEST_ALIGNMENT / BITS_PER_UNIT)); if (!sect->noswitch.callback (decl, name, size, rounded) - && (unsigned HOST_WIDE_INT) DECL_ALIGN_UNIT (decl) > rounded) + && (unsigned HOST_WIDE_INT) (align / BITS_PER_UNIT) > rounded) warning (0, "requested alignment for %q+D is greater than " "implemented alignment of %wu", decl, rounded); } @@ -1880,7 +1949,7 @@ assemble_variable_contents (tree decl, const char *name, /* Output the actual data. */ output_constant (DECL_INITIAL (decl), tree_low_cst (DECL_SIZE_UNIT (decl), 1), - DECL_ALIGN (decl)); + get_variable_align (decl)); else /* Leave space for it. */ assemble_zeros (tree_low_cst (DECL_SIZE_UNIT (decl), 1)); @@ -1904,6 +1973,7 @@ assemble_variable (tree decl, int top_level ATTRIBUTE_UNUSED, const char *name; rtx decl_rtl, symbol; section *sect; + unsigned int align; bool asan_protected = false; /* This function is supposed to handle VARIABLES. Ensure we have one. */ @@ -2003,6 +2073,8 @@ assemble_variable (tree decl, int top_level ATTRIBUTE_UNUSED, set_mem_align (decl_rtl, DECL_ALIGN (decl)); + align = get_variable_align (decl); + if (TREE_PUBLIC (decl)) maybe_assemble_visibility (decl); @@ -2032,9 +2104,19 @@ assemble_variable (tree decl, int top_level ATTRIBUTE_UNUSED, place_block_symbol (symbol); } else if (SECTION_STYLE (sect) == SECTION_NOSWITCH) - assemble_noswitch_variable (decl, name, sect); + assemble_noswitch_variable (decl, name, sect, align); else { + /* The following bit of code ensures that vtable_map + variables are not only in the comdat section, but that + each variable has its own unique comdat name. If this + code is removed, the variables end up in the same section + with a single comdat name. + + FIXME: resolve_unique_section needs to deal better with + decls with both DECL_SECTION_NAME and DECL_ONE_ONLY. Once + that is fixed, this if-else statement can be replaced with + a single call to "switch_to_section (sect)". */ if (sect->named.name && (strcmp (sect->named.name, ".vtable_map_vars") == 0)) { @@ -2049,9 +2131,9 @@ assemble_variable (tree decl, int top_level ATTRIBUTE_UNUSED, #endif } else - switch_to_section (sect); - if (DECL_ALIGN (decl) > BITS_PER_UNIT) - ASM_OUTPUT_ALIGN (asm_out_file, floor_log2 (DECL_ALIGN_UNIT (decl))); + switch_to_section (sect); + if (align > BITS_PER_UNIT) + ASM_OUTPUT_ALIGN (asm_out_file, floor_log2 (align / BITS_PER_UNIT)); assemble_variable_contents (decl, name, dont_output_data); if (asan_protected) { @@ -4501,7 +4583,7 @@ static unsigned HOST_WIDE_INT output_constructor (tree, unsigned HOST_WIDE_INT, unsigned int, oc_outer_state *); -/* Output assembler code for constant EXP to FILE, with no label. +/* Output assembler code for constant EXP, with no label. This includes the pseudo-op such as ".int" or ".byte", and a newline. Assumes output_addressed_constants has been done on EXP already. @@ -4643,28 +4725,21 @@ output_constant (tree exp, unsigned HOST_WIDE_INT size, unsigned int align) switch (TREE_CODE (exp)) { case CONSTRUCTOR: - output_constructor (exp, size, align, NULL); + output_constructor (exp, size, align, NULL); return; case STRING_CST: - thissize = MIN ((unsigned HOST_WIDE_INT)TREE_STRING_LENGTH (exp), - size); + thissize + = MIN ((unsigned HOST_WIDE_INT)TREE_STRING_LENGTH (exp), size); assemble_string (TREE_STRING_POINTER (exp), thissize); break; - case VECTOR_CST: { - int elt_size; - unsigned int i, nalign; - enum machine_mode inner; - - inner = TYPE_MODE (TREE_TYPE (TREE_TYPE (exp))); - nalign = MIN (align, GET_MODE_ALIGNMENT (inner)); - - elt_size = GET_MODE_SIZE (inner); - + enum machine_mode inner = TYPE_MODE (TREE_TYPE (TREE_TYPE (exp))); + unsigned int nalign = MIN (align, GET_MODE_ALIGNMENT (inner)); + int elt_size = GET_MODE_SIZE (inner); output_constant (VECTOR_CST_ELT (exp, 0), elt_size, align); thissize = elt_size; - for (i = 1; i < VECTOR_CST_NELTS (exp); ++i) + for (unsigned int i = 1; i < VECTOR_CST_NELTS (exp); i++) { output_constant (VECTOR_CST_ELT (exp, i), elt_size, nalign); thissize += elt_size; @@ -4744,25 +4819,22 @@ typedef struct { /* Received arguments. */ tree exp; /* Constructor expression. */ + tree type; /* Type of constructor expression. */ unsigned HOST_WIDE_INT size; /* # bytes to output - pad if necessary. */ unsigned int align; /* Known initial alignment. */ - - /* Constructor expression data. */ - tree type; /* Expression type. */ - tree field; /* Current field decl in a record. */ - tree min_index; /* Lower bound if specified for an array. */ + tree min_index; /* Lower bound if specified for an array. */ /* Output processing state. */ HOST_WIDE_INT total_bytes; /* # bytes output so far / current position. */ - bool byte_buffer_in_use; /* Whether byte ... */ - int byte; /* ... contains part of a bitfield byte yet to - be output. */ - + int byte; /* Part of a bitfield byte yet to be output. */ int last_relative_index; /* Implicit or explicit index of the last array element output within a bitfield. */ + bool byte_buffer_in_use; /* Whether BYTE is in use. */ + /* Current element. */ - tree val; /* Current element value. */ - tree index; /* Current element index. */ + tree field; /* Current field decl in a record. */ + tree val; /* Current element value. */ + tree index; /* Current element index. */ } oc_local_state; @@ -4884,11 +4956,12 @@ output_constructor_regular_field (oc_local_state *local) local->total_bytes += fieldsize; } -/* Helper for output_constructor. From the current LOCAL and OUTER states, - output an element that is a true bitfield or part of an outer one. */ +/* Helper for output_constructor. From the LOCAL state, output an element + that is a true bitfield or part of an outer one. BIT_OFFSET is the offset + from the start of a possibly ongoing outer byte buffer. */ static void -output_constructor_bitfield (oc_local_state *local, oc_outer_state *outer) +output_constructor_bitfield (oc_local_state *local, unsigned int bit_offset) { /* Bit size of this element. */ HOST_WIDE_INT ebitsize @@ -4915,7 +4988,7 @@ output_constructor_bitfield (oc_local_state *local, oc_outer_state *outer) /* Bit position of this element from the start of a possibly ongoing outer byte buffer. */ HOST_WIDE_INT byte_relative_ebitpos - = ((outer ? outer->bit_offset : 0) + constructor_relative_ebitpos); + = bit_offset + constructor_relative_ebitpos; /* From the start of a possibly ongoing outer byte buffer, offsets to the first bit of this element and to the first bit past the end of @@ -4939,8 +5012,7 @@ output_constructor_bitfield (oc_local_state *local, oc_outer_state *outer) return; } - /* If this field does not start in this (or, next) byte, - skip some bytes. */ + /* If this field does not start in this (or next) byte, skip some bytes. */ if (next_offset / BITS_PER_UNIT != local->total_bytes) { /* Output remnant of any bit field in previous bytes. */ @@ -4972,13 +5044,12 @@ output_constructor_bitfield (oc_local_state *local, oc_outer_state *outer) pending data, then retrieve the new pending data afterwards. */ if (TREE_CODE (local->val) == CONSTRUCTOR) { - oc_outer_state output_state; - - output_state.bit_offset = next_offset % BITS_PER_UNIT; - output_state.byte = local->byte; + oc_outer_state temp_state; + temp_state.bit_offset = next_offset % BITS_PER_UNIT; + temp_state.byte = local->byte; local->total_bytes - += output_constructor (local->val, 0, 0, &output_state); - local->byte = output_state.byte; + += output_constructor (local->val, 0, 0, &temp_state); + local->byte = temp_state.byte; return; } @@ -4993,8 +5064,7 @@ output_constructor_bitfield (oc_local_state *local, oc_outer_state *outer) HOST_WIDE_INT next_byte = next_offset / BITS_PER_UNIT; HOST_WIDE_INT next_bit = next_offset % BITS_PER_UNIT; - /* Advance from byte to byte - within this element when necessary. */ + /* Advance from byte to byte within this element when necessary. */ while (next_byte != local->total_bytes) { assemble_integer (GEN_INT (local->byte), 1, BITS_PER_UNIT, 1); @@ -5002,10 +5072,8 @@ output_constructor_bitfield (oc_local_state *local, oc_outer_state *outer) local->byte = 0; } - /* Number of bits we can process at once - (all part of the same byte). */ - this_time = MIN (end_offset - next_offset, - BITS_PER_UNIT - next_bit); + /* Number of bits we can process at once (all part of the same byte). */ + this_time = MIN (end_offset - next_offset, BITS_PER_UNIT - next_bit); if (BYTES_BIG_ENDIAN) { /* On big-endian machine, take the most significant bits @@ -5084,7 +5152,7 @@ output_constructor_bitfield (oc_local_state *local, oc_outer_state *outer) static unsigned HOST_WIDE_INT output_constructor (tree exp, unsigned HOST_WIDE_INT size, - unsigned int align, oc_outer_state * outer) + unsigned int align, oc_outer_state *outer) { unsigned HOST_WIDE_INT cnt; constructor_elt *ce; @@ -5093,22 +5161,20 @@ output_constructor (tree exp, unsigned HOST_WIDE_INT size, /* Setup our local state to communicate with helpers. */ local.exp = exp; + local.type = TREE_TYPE (exp); local.size = size; local.align = align; + if (TREE_CODE (local.type) == ARRAY_TYPE && TYPE_DOMAIN (local.type)) + local.min_index = TYPE_MIN_VALUE (TYPE_DOMAIN (local.type)); + else + local.min_index = NULL_TREE; local.total_bytes = 0; local.byte_buffer_in_use = outer != NULL; local.byte = outer ? outer->byte : 0; - local.type = TREE_TYPE (exp); - local.last_relative_index = -1; - local.min_index = NULL_TREE; - if (TREE_CODE (local.type) == ARRAY_TYPE - && TYPE_DOMAIN (local.type) != NULL_TREE) - local.min_index = TYPE_MIN_VALUE (TYPE_DOMAIN (local.type)); - gcc_assert (HOST_BITS_PER_WIDE_INT >= BITS_PER_UNIT); /* As CE goes through the elements of the constant, FIELD goes through the @@ -5120,9 +5186,10 @@ output_constructor (tree exp, unsigned HOST_WIDE_INT size, (even if the initializer in a source program incorrectly contains more one). */ - local.field = NULL_TREE; if (TREE_CODE (local.type) == RECORD_TYPE) local.field = TYPE_FIELDS (local.type); + else + local.field = NULL_TREE; for (cnt = 0; vec_safe_iterate (CONSTRUCTOR_ELTS (exp), cnt, &ce); @@ -5175,7 +5242,7 @@ output_constructor (tree exp, unsigned HOST_WIDE_INT size, build_nonstandard_integer_type (TYPE_PRECISION (TREE_TYPE (local.val)), 0), local.val); - output_constructor_bitfield (&local, outer); + output_constructor_bitfield (&local, outer ? outer->bit_offset : 0); } } @@ -5591,13 +5658,6 @@ assemble_alias (tree decl, tree 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 - } if (TREE_PUBLIC (decl)) error ("weakref %q+D must have static linkage", decl); } @@ -6764,10 +6824,10 @@ bool decl_binds_to_current_def_p (tree decl) { gcc_assert (DECL_P (decl)); - if (!TREE_PUBLIC (decl)) - return true; if (!targetm.binds_local_p (decl)) return false; + if (!TREE_PUBLIC (decl)) + return true; /* When resolution is available, just use it. */ if (TREE_CODE (decl) == VAR_DECL && (TREE_STATIC (decl) || DECL_EXTERNAL (decl))) @@ -6785,10 +6845,20 @@ decl_binds_to_current_def_p (tree decl) return resolution_to_local_definition_p (node->symbol.resolution); } /* Otherwise we have to assume the worst for DECL_WEAK (hidden weaks - binds locally but still can be overwritten). + binds locally but still can be overwritten), DECL_COMMON (can be merged + with a non-common definition somewhere in the same module) or + DECL_EXTERNAL. This rely on fact that binds_local_p behave as decl_replaceable_p for all other declaration types. */ - return !DECL_WEAK (decl); + if (DECL_WEAK (decl)) + return false; + if (DECL_COMMON (decl) + && (DECL_INITIAL (decl) == NULL + || DECL_INITIAL (decl) == error_mark_node)) + return false; + if (DECL_EXTERNAL (decl)) + return false; + return true; } /* A replaceable function or variable is one which may be replaced @@ -7014,7 +7084,7 @@ place_block_symbol (rtx symbol) else { decl = SYMBOL_REF_DECL (symbol); - alignment = DECL_ALIGN (decl); + alignment = get_variable_align (decl); size = tree_low_cst (DECL_SIZE_UNIT (decl), 1); if (flag_asan && asan_protect_global (decl)) { |