#include "fold-const.h"
#include "stor-layout.h"
#include "varasm.h"
+#include "version.h"
#include "flags.h"
#include "stmt.h"
#include "expr.h"
#include "asan.h"
#include "rtl-iter.h"
#include "file-prefix-map.h" /* remap_debug_filename() */
+#include "alloc-pool.h"
+#include "toplev.h"
+#include "opts.h"
#ifdef XCOFF_DEBUGGING_INFO
#include "xcoffout.h" /* Needed for external data declarations. */
static hashval_t const_hash_1 (const tree);
static int compare_constant (const tree, const tree);
static void output_constant_def_contents (rtx);
-static void output_addressed_constants (tree);
+static void output_addressed_constants (tree, int);
static unsigned HOST_WIDE_INT output_constant (tree, unsigned HOST_WIDE_INT,
unsigned int, bool, bool);
static void globalize_decl (tree);
}
/* Return the named section structure associated with NAME. Create
- a new section with the given fields if no such structure exists. */
+ a new section with the given fields if no such structure exists.
+ When NOT_EXISTING, then fail if the section already exists. */
section *
-get_section (const char *name, unsigned int flags, tree decl)
+get_section (const char *name, unsigned int flags, tree decl,
+ bool not_existing)
{
section *sect, **slot;
slot = section_htab->find_slot_with_hash (name, htab_hash_string (name),
INSERT);
flags |= SECTION_NAMED;
+ if (HAVE_GAS_SHF_GNU_RETAIN
+ && decl != nullptr
+ && DECL_PRESERVE_P (decl))
+ flags |= SECTION_RETAIN;
if (*slot == NULL)
{
sect = ggc_alloc<section> ();
}
else
{
+ if (not_existing)
+ internal_error ("Section already exists: %qs", name);
+
sect = *slot;
/* It is fine if one of the sections has SECTION_NOTYPE as long as
the other has none of the contrary flags (see the logic at the end
if (DECL_SECTION_NAME (decl) == NULL
&& targetm_common.have_named_sections
&& (flag_function_or_data_sections
+ || (HAVE_GAS_SHF_GNU_RETAIN && DECL_PRESERVE_P (decl))
|| DECL_COMDAT_GROUP (decl)))
{
targetm.asm_out.unique_section (decl, reloc);
switch_to_section (current_function_section ());
}
-/* Return the read-only data section associated with function DECL. */
+/* Return the read-only or relocated read-only data section
+ associated with function DECL. */
section *
-default_function_rodata_section (tree decl)
+default_function_rodata_section (tree decl, bool relocatable)
{
- if (decl != NULL_TREE && DECL_SECTION_NAME (decl))
+ const char* sname;
+ unsigned int flags;
+
+ flags = 0;
+
+ if (relocatable)
+ {
+ sname = ".data.rel.ro.local";
+ flags = (SECTION_WRITE | SECTION_RELRO);
+ }
+ else
+ sname = ".rodata";
+
+ if (decl && DECL_SECTION_NAME (decl))
{
const char *name = DECL_SECTION_NAME (decl);
dot = strchr (name + 1, '.');
if (!dot)
dot = name;
- len = strlen (dot) + 8;
+ len = strlen (dot) + strlen (sname) + 1;
rname = (char *) alloca (len);
- strcpy (rname, ".rodata");
+ strcpy (rname, sname);
strcat (rname, dot);
- return get_section (rname, SECTION_LINKONCE, decl);
+ return get_section (rname, (SECTION_LINKONCE | flags), decl);
}
- /* For .gnu.linkonce.t.foo we want to use .gnu.linkonce.r.foo. */
+ /* For .gnu.linkonce.t.foo we want to use .gnu.linkonce.r.foo or
+ .gnu.linkonce.d.rel.ro.local.foo if the jump table is relocatable. */
else if (DECL_COMDAT_GROUP (decl)
&& strncmp (name, ".gnu.linkonce.t.", 16) == 0)
{
- size_t len = strlen (name) + 1;
- char *rname = (char *) alloca (len);
+ size_t len;
+ char *rname;
+
+ if (relocatable)
+ {
+ len = strlen (name) + strlen (".rel.ro.local") + 1;
+ rname = (char *) alloca (len);
- memcpy (rname, name, len);
- rname[14] = 'r';
- return get_section (rname, SECTION_LINKONCE, decl);
+ strcpy (rname, ".gnu.linkonce.d.rel.ro.local");
+ strcat (rname, name + 15);
+ }
+ else
+ {
+ len = strlen (name) + 1;
+ rname = (char *) alloca (len);
+
+ memcpy (rname, name, len);
+ rname[14] = 'r';
+ }
+ return get_section (rname, (SECTION_LINKONCE | flags), decl);
}
/* For .text.foo we want to use .rodata.foo. */
else if (flag_function_sections && flag_data_sections
&& strncmp (name, ".text.", 6) == 0)
{
size_t len = strlen (name) + 1;
- char *rname = (char *) alloca (len + 2);
+ char *rname = (char *) alloca (len + strlen (sname) - 5);
- memcpy (rname, ".rodata", 7);
- memcpy (rname + 7, name + 5, len - 5);
- return get_section (rname, 0, decl);
+ memcpy (rname, sname, strlen (sname));
+ memcpy (rname + strlen (sname), name + 5, len - 5);
+ return get_section (rname, flags, decl);
}
}
- return readonly_data_section;
+ if (relocatable)
+ return get_section (sname, flags, decl);
+ else
+ return readonly_data_section;
}
/* Return the read-only data section associated with function DECL
readonly data section. */
section *
-default_no_function_rodata_section (tree decl ATTRIBUTE_UNUSED)
+default_no_function_rodata_section (tree, bool)
{
return readonly_data_section;
}
static const char *
function_mergeable_rodata_prefix (void)
{
- section *s = targetm.asm_out.function_rodata_section (current_function_decl);
+ section *s = targetm.asm_out.function_rodata_section (current_function_decl,
+ false);
if (SECTION_STYLE (s) == SECTION_NAMED)
return s->named.name;
else
|| (DECL_INITIAL (decl) == error_mark_node
&& !in_lto_p)
|| (flag_zero_initialized_in_bss
- && initializer_zerop (DECL_INITIAL (decl)))));
+ && initializer_zerop (DECL_INITIAL (decl))
+ /* A decl with the "persistent" attribute applied and
+ explicitly initialized to 0 should not be treated as a BSS
+ variable. */
+ && !DECL_PERSISTENT_P (decl))));
}
/* Compute the alignment of variable specified by DECL.
if (vnode)
vnode->get_constructor ();
- if (DECL_COMMON (decl))
+ if (DECL_COMMON (decl)
+ && !(HAVE_GAS_SHF_GNU_RETAIN && DECL_PRESERVE_P (decl)))
{
/* If the decl has been given an explicit section name, or it resides
in a non-generic address space, then it isn't common, and shouldn't
if (ADDR_SPACE_GENERIC_P (as)
&& !DECL_THREAD_LOCAL_P (decl)
+ && !DECL_NOINIT_P (decl)
&& !(prefer_noswitch_p && targetm.have_switchable_bss_sections)
&& bss_initializer_p (decl))
{
/* 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));
+ output_addressed_constants (DECL_INITIAL (decl), 0);
/* dbxout.c needs to know this. */
if (sect && (sect->common.flags & SECTION_CODE) != 0)
already have labels. */
static constant_descriptor_tree *
-add_constant_to_table (tree exp)
+add_constant_to_table (tree exp, int defer)
{
/* The hash table methods may call output_constant_def for addressed
constants, so handle them first. */
- output_addressed_constants (exp);
+ output_addressed_constants (exp, defer);
/* Sanity check to catch recursive insertion. */
static bool inserting;
rtx
output_constant_def (tree exp, int defer)
{
- struct constant_descriptor_tree *desc = add_constant_to_table (exp);
+ struct constant_descriptor_tree *desc = add_constant_to_table (exp, defer);
maybe_output_constant_def_contents (desc, defer);
return desc->rtl;
}
/* Make sure any other constants whose addresses appear in EXP
are assigned label numbers. */
- output_addressed_constants (exp);
+ output_addressed_constants (exp, 0);
/* We are no longer deferring this constant. */
TREE_ASM_WRITTEN (decl) = TREE_ASM_WRITTEN (exp) = 1;
tree
tree_output_constant_def (tree exp)
{
- struct constant_descriptor_tree *desc = add_constant_to_table (exp);
+ struct constant_descriptor_tree *desc = add_constant_to_table (exp, 1);
tree decl = SYMBOL_REF_DECL (XEXP (desc->rtl, 0));
varpool_node::finalize_decl (decl);
return decl;
class constant_descriptor_rtx *desc;
for (desc = pool->first; desc ; desc = desc->next)
- if (desc->mark)
+ if (desc->mark < 0)
+ {
+#ifdef ASM_OUTPUT_DEF
+ const char *name = targetm.strip_name_encoding (XSTR (desc->sym, 0));
+ char label[256];
+ char buffer[256 + 32];
+ const char *p;
+
+ ASM_GENERATE_INTERNAL_LABEL (label, "LC", ~desc->mark);
+ p = targetm.strip_name_encoding (label);
+ if (desc->offset)
+ {
+ sprintf (buffer, "%s+%ld", p, (long) (desc->offset));
+ p = buffer;
+ }
+ ASM_OUTPUT_DEF (asm_out_file, name, p);
+#else
+ gcc_unreachable ();
+#endif
+ }
+ else if (desc->mark)
{
/* If the constant is part of an object_block, make sure that
the constant has been positioned within its block, but do not
}
}
+struct constant_descriptor_rtx_data {
+ constant_descriptor_rtx *desc;
+ target_unit *bytes;
+ unsigned short size;
+ unsigned short offset;
+ unsigned int hash;
+};
+
+/* qsort callback to sort constant_descriptor_rtx_data * vector by
+ decreasing size. */
+
+static int
+constant_descriptor_rtx_data_cmp (const void *p1, const void *p2)
+{
+ constant_descriptor_rtx_data *const data1
+ = *(constant_descriptor_rtx_data * const *) p1;
+ constant_descriptor_rtx_data *const data2
+ = *(constant_descriptor_rtx_data * const *) p2;
+ if (data1->size > data2->size)
+ return -1;
+ if (data1->size < data2->size)
+ return 1;
+ if (data1->hash < data2->hash)
+ return -1;
+ gcc_assert (data1->hash > data2->hash);
+ return 1;
+}
+
+struct const_rtx_data_hasher : nofree_ptr_hash<constant_descriptor_rtx_data>
+{
+ static hashval_t hash (constant_descriptor_rtx_data *);
+ static bool equal (constant_descriptor_rtx_data *,
+ constant_descriptor_rtx_data *);
+};
+
+/* Hash and compare functions for const_rtx_data_htab. */
+
+hashval_t
+const_rtx_data_hasher::hash (constant_descriptor_rtx_data *data)
+{
+ return data->hash;
+}
+
+bool
+const_rtx_data_hasher::equal (constant_descriptor_rtx_data *x,
+ constant_descriptor_rtx_data *y)
+{
+ if (x->hash != y->hash || x->size != y->size)
+ return 0;
+ unsigned int align1 = x->desc->align;
+ unsigned int align2 = y->desc->align;
+ unsigned int offset1 = (x->offset * BITS_PER_UNIT) & (align1 - 1);
+ unsigned int offset2 = (y->offset * BITS_PER_UNIT) & (align2 - 1);
+ if (offset1)
+ align1 = least_bit_hwi (offset1);
+ if (offset2)
+ align2 = least_bit_hwi (offset2);
+ if (align2 > align1)
+ return 0;
+ if (memcmp (x->bytes, y->bytes, x->size * sizeof (target_unit)) != 0)
+ return 0;
+ return 1;
+}
+
+/* Attempt to optimize constant pool POOL. If it contains both CONST_VECTOR
+ constants and scalar constants with the values of CONST_VECTOR elements,
+ try to alias the scalar constants with the CONST_VECTOR elements. */
+
+static void
+optimize_constant_pool (struct rtx_constant_pool *pool)
+{
+ auto_vec<target_unit, 128> buffer;
+ auto_vec<constant_descriptor_rtx_data *, 128> vec;
+ object_allocator<constant_descriptor_rtx_data>
+ data_pool ("constant_descriptor_rtx_data_pool");
+ int idx = 0;
+ size_t size = 0;
+ for (constant_descriptor_rtx *desc = pool->first; desc; desc = desc->next)
+ if (desc->mark > 0
+ && ! (SYMBOL_REF_HAS_BLOCK_INFO_P (desc->sym)
+ && SYMBOL_REF_BLOCK (desc->sym)))
+ {
+ buffer.truncate (0);
+ buffer.reserve (GET_MODE_SIZE (desc->mode));
+ if (native_encode_rtx (desc->mode, desc->constant, buffer, 0,
+ GET_MODE_SIZE (desc->mode)))
+ {
+ constant_descriptor_rtx_data *data = data_pool.allocate ();
+ data->desc = desc;
+ data->bytes = NULL;
+ data->size = GET_MODE_SIZE (desc->mode);
+ data->offset = 0;
+ data->hash = idx++;
+ size += data->size;
+ vec.safe_push (data);
+ }
+ }
+ if (idx)
+ {
+ vec.qsort (constant_descriptor_rtx_data_cmp);
+ unsigned min_size = vec.last ()->size;
+ target_unit *bytes = XNEWVEC (target_unit, size);
+ unsigned int i;
+ constant_descriptor_rtx_data *data;
+ hash_table<const_rtx_data_hasher> * htab
+ = new hash_table<const_rtx_data_hasher> (31);
+ size = 0;
+ FOR_EACH_VEC_ELT (vec, i, data)
+ {
+ buffer.truncate (0);
+ native_encode_rtx (data->desc->mode, data->desc->constant,
+ buffer, 0, data->size);
+ memcpy (bytes + size, buffer.address (), data->size);
+ data->bytes = bytes + size;
+ data->hash = iterative_hash (data->bytes,
+ data->size * sizeof (target_unit), 0);
+ size += data->size;
+ constant_descriptor_rtx_data **slot
+ = htab->find_slot_with_hash (data, data->hash, INSERT);
+ if (*slot)
+ {
+ data->desc->mark = ~(*slot)->desc->labelno;
+ data->desc->offset = (*slot)->offset;
+ }
+ else
+ {
+ unsigned int sz = 1 << floor_log2 (data->size);
+
+ *slot = data;
+ for (sz >>= 1; sz >= min_size; sz >>= 1)
+ for (unsigned off = 0; off + sz <= data->size; off += sz)
+ {
+ constant_descriptor_rtx_data tmp;
+ tmp.desc = data->desc;
+ tmp.bytes = data->bytes + off;
+ tmp.size = sz;
+ tmp.offset = off;
+ tmp.hash = iterative_hash (tmp.bytes,
+ sz * sizeof (target_unit), 0);
+ slot = htab->find_slot_with_hash (&tmp, tmp.hash, INSERT);
+ if (*slot == NULL)
+ {
+ *slot = data_pool.allocate ();
+ **slot = tmp;
+ }
+ }
+ }
+ }
+ delete htab;
+ XDELETE (bytes);
+ }
+ data_pool.release ();
+}
+
/* Mark all constants that are used in the current function, then write
out the function's private constant pool. */
void
output_shared_constant_pool (void)
{
+ if (optimize
+ && TARGET_SUPPORTS_ALIASES)
+ optimize_constant_pool (shared_constant_pool);
+
output_constant_pool_contents (shared_constant_pool);
}
\f
Indicate whether an ADDR_EXPR has been encountered. */
static void
-output_addressed_constants (tree exp)
+output_addressed_constants (tree exp, int defer)
{
tree tem;
tem = DECL_INITIAL (tem);
if (CONSTANT_CLASS_P (tem) || TREE_CODE (tem) == CONSTRUCTOR)
- output_constant_def (tem, 0);
+ output_constant_def (tem, defer);
if (TREE_CODE (tem) == MEM_REF)
- output_addressed_constants (TREE_OPERAND (tem, 0));
+ output_addressed_constants (TREE_OPERAND (tem, 0), defer);
break;
case PLUS_EXPR:
case POINTER_PLUS_EXPR:
case MINUS_EXPR:
- output_addressed_constants (TREE_OPERAND (exp, 1));
+ output_addressed_constants (TREE_OPERAND (exp, 1), defer);
gcc_fallthrough ();
CASE_CONVERT:
case VIEW_CONVERT_EXPR:
- output_addressed_constants (TREE_OPERAND (exp, 0));
+ output_addressed_constants (TREE_OPERAND (exp, 0), defer);
break;
case CONSTRUCTOR:
unsigned HOST_WIDE_INT idx;
FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (exp), idx, tem)
if (tem != 0)
- output_addressed_constants (tem);
+ output_addressed_constants (tem, defer);
}
break;
if (strcmp (name, ".noinit") == 0)
flags |= SECTION_WRITE | SECTION_BSS | SECTION_NOTYPE;
+ if (strcmp (name, ".persistent") == 0)
+ flags |= SECTION_WRITE | SECTION_NOTYPE;
+
/* Various sections have special ELF types that the assembler will
assign by default based on the name. They are neither SHT_PROGBITS
nor SHT_NOBITS, so when changing sections we don't want to print a
/* 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. */
+ part of a COMDAT groups or with SHF_GNU_RETAIN, in which case GAS
+ requires the full declaration every time. */
if (!(HAVE_COMDAT_GROUP && (flags & SECTION_LINKONCE))
+ && !(flags & SECTION_RETAIN)
&& (flags & SECTION_DECLARED))
{
fprintf (asm_out_file, "\t.section\t%s\n", name);
*f++ = TLS_SECTION_ASM_FLAG;
if (HAVE_COMDAT_GROUP && (flags & SECTION_LINKONCE))
*f++ = 'G';
+ if (flags & SECTION_RETAIN)
+ *f++ = 'R';
+ if (flags & SECTION_LINK_ORDER)
+ *f++ = 'o';
#ifdef MACH_DEP_SECTION_ASM_FLAG
if (flags & SECTION_MACH_DEP)
*f++ = MACH_DEP_SECTION_ASM_FLAG;
if (flags & SECTION_ENTSIZE)
fprintf (asm_out_file, ",%d", flags & SECTION_ENTSIZE);
+ if (flags & SECTION_LINK_ORDER)
+ {
+ tree id = DECL_ASSEMBLER_NAME (decl);
+ ultimate_transparent_alias_target (&id);
+ const char *name = IDENTIFIER_POINTER (id);
+ name = targetm.strip_name_encoding (name);
+ fprintf (asm_out_file, ",%s", name);
+ }
if (HAVE_COMDAT_GROUP && (flags & SECTION_LINKONCE))
{
if (TREE_CODE (decl) == IDENTIFIER_NODE)
sname = ".sdata2";
break;
case SECCAT_DATA:
+ if (DECL_P (decl) && DECL_PERSISTENT_P (decl))
+ {
+ sname = ".persistent";
+ break;
+ }
return data_section;
case SECCAT_DATA_REL:
sname = ".data.rel";
sname = ".tdata";
break;
case SECCAT_BSS:
- if (DECL_P (decl)
- && lookup_attribute ("noinit", DECL_ATTRIBUTES (decl)) != NULL_TREE)
+ if (DECL_P (decl) && DECL_NOINIT_P (decl))
{
sname = ".noinit";
break;
}
-
if (bss_section)
return bss_section;
sname = ".bss";
break;
case SECCAT_DATA:
prefix = one_only ? ".d" : ".data";
+ if (DECL_P (decl) && DECL_PERSISTENT_P (decl))
+ {
+ prefix = one_only ? ".p" : ".persistent";
+ break;
+ }
break;
case SECCAT_DATA_REL:
prefix = one_only ? ".d.rel" : ".data.rel";
prefix = one_only ? ".s" : ".sdata";
break;
case SECCAT_BSS:
+ if (DECL_P (decl) && DECL_NOINIT_P (decl))
+ {
+ prefix = one_only ? ".n" : ".noinit";
+ break;
+ }
prefix = one_only ? ".b" : ".bss";
break;
case SECCAT_SBSS:
we want to emit NUL strings terminators into the object file we have to use
ASM_OUTPUT_SKIP. */
-int
-elf_record_gcc_switches (print_switch_type type, const char * name)
+void
+elf_record_gcc_switches (const char *options)
{
- switch (type)
- {
- case SWITCH_TYPE_PASSED:
- ASM_OUTPUT_ASCII (asm_out_file, name, strlen (name));
- ASM_OUTPUT_SKIP (asm_out_file, HOST_WIDE_INT_1U);
- break;
-
- case SWITCH_TYPE_DESCRIPTIVE:
- if (name == NULL)
- {
- /* Distinguish between invocations where name is NULL. */
- static bool started = false;
-
- if (!started)
- {
- section * sec;
-
- sec = get_section (targetm.asm_out.record_gcc_switches_section,
- SECTION_DEBUG
- | SECTION_MERGE
- | SECTION_STRINGS
- | (SECTION_ENTSIZE & 1),
- NULL);
- switch_to_section (sec);
- started = true;
- }
- }
-
- default:
- break;
- }
-
- /* The return value is currently ignored by the caller, but must be 0.
- For -fverbose-asm the return value would be the number of characters
- emitted into the assembler file. */
- return 0;
+ section *sec = get_section (targetm.asm_out.record_gcc_switches_section,
+ SECTION_DEBUG | SECTION_MERGE
+ | SECTION_STRINGS | (SECTION_ENTSIZE & 1), NULL);
+ switch_to_section (sec);
+ ASM_OUTPUT_ASCII (asm_out_file, options, strlen (options) + 1);
}
/* Emit text to declare externally defined symbols. It is needed to