blob: 1747605310d78d97a86f8a1c4fe72c190e7713eb [file] [log] [blame]
/* Output variables, constants and external declarations, for GNU compiler.
Copyright (C) 1987-2015 Free Software Foundation, Inc.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 3, or (at your option) any later
version.
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
/* This file handles generation of all the assembler code
*except* the instructions of a function.
This includes declarations of variables and their initial values.
We also output the assembler code for constants stored in memory
and are responsible for combining constants with the same value. */
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "rtl.h"
#include "hash-set.h"
#include "machmode.h"
#include "vec.h"
#include "double-int.h"
#include "input.h"
#include "alias.h"
#include "symtab.h"
#include "wide-int.h"
#include "inchash.h"
#include "tree.h"
#include "fold-const.h"
#include "stor-layout.h"
#include "stringpool.h"
#include "varasm.h"
#include "flags.h"
#include "hard-reg-set.h"
#include "function.h"
#include "hashtab.h"
#include "statistics.h"
#include "real.h"
#include "fixed-value.h"
#include "insn-config.h"
#include "expmed.h"
#include "dojump.h"
#include "explow.h"
#include "calls.h"
#include "emit-rtl.h"
#include "stmt.h"
#include "expr.h"
#include "regs.h"
#include "output.h"
#include "diagnostic-core.h"
#include "ggc.h"
#include "langhooks.h"
#include "tm_p.h"
#include "debug.h"
#include "target.h"
#include "common/common-target.h"
#include "targhooks.h"
#include "predict.h"
#include "dominance.h"
#include "cfg.h"
#include "basic-block.h"
#include "hash-map.h"
#include "is-a.h"
#include "plugin-api.h"
#include "ipa-ref.h"
#include "cgraph.h"
#include "asan.h"
#include "rtl-iter.h"
#include "tree-chkp.h"
#ifdef XCOFF_DEBUGGING_INFO
#include "xcoffout.h" /* Needed for external data
declarations for e.g. AIX 4.x. */
#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_constant_pool;
#define n_deferred_constants (crtl->varasm.deferred_constants)
/* Number for making the label on the next
constant that is stored in memory. */
static GTY(()) int const_labelno;
/* Carry information from ASM_DECLARE_OBJECT_NAME
to ASM_FINISH_DECLARE_OBJECT. */
int size_directive_output;
/* The last decl for which assemble_variable was called,
if it did ASM_DECLARE_OBJECT_NAME.
If the last call to assemble_variable didn't do that,
this holds 0. */
tree last_assemble_variable_decl;
/* The following global variable indicates if the first basic block
in a function belongs to the cold partition or not. */
bool first_function_block_is_cold;
/* Whether we saw any functions with no_split_stack. */
static bool saw_no_split_stack;
static const char *strip_reg_name (const char *);
static int contains_pointers_p (tree);
#ifdef ASM_OUTPUT_EXTERNAL
static bool incorporeal_function_p (tree);
#endif
static void decode_addr_const (tree, struct addr_const *);
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 unsigned HOST_WIDE_INT output_constant (tree, unsigned HOST_WIDE_INT,
unsigned int);
static void globalize_decl (tree);
static bool decl_readonly_section_1 (enum section_category);
#ifdef BSS_SECTION_ASM_OP
#ifdef ASM_OUTPUT_ALIGNED_BSS
static void asm_output_aligned_bss (FILE *, tree, const char *,
unsigned HOST_WIDE_INT, int)
ATTRIBUTE_UNUSED;
#endif
#endif /* BSS_SECTION_ASM_OP */
static void mark_weak (tree);
static void output_constant_pool (const char *, tree);
/* 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. */
#define IN_NAMED_SECTION(DECL) \
((TREE_CODE (DECL) == FUNCTION_DECL || TREE_CODE (DECL) == VAR_DECL) \
&& DECL_SECTION_NAME (DECL) != NULL)
struct section_hasher : ggc_hasher<section *>
{
typedef const char *compare_type;
static hashval_t hash (section *);
static bool equal (section *, const char *);
};
/* Hash table of named sections. */
static GTY(()) hash_table<section_hasher> *section_htab;
struct object_block_hasher : ggc_hasher<object_block *>
{
typedef const section *compare_type;
static hashval_t hash (object_block *);
static bool equal (object_block *, const section *);
};
/* A table of object_blocks, indexed by section. */
static GTY(()) hash_table<object_block_hasher> *object_block_htab;
/* The next number to use for internal anchor labels. */
static GTY(()) int anchor_labelno;
/* A pool of constants that can be shared between functions. */
static GTY(()) struct rtx_constant_pool *shared_constant_pool;
/* Helper routines for maintaining section_htab. */
bool
section_hasher::equal (section *old, const char *new_name)
{
return strcmp (old->named.name, new_name) == 0;
}
hashval_t
section_hasher::hash (section *old)
{
return htab_hash_string (old->named.name);
}
/* Return a hash value for section SECT. */
static hashval_t
hash_section (section *sect)
{
if (sect->common.flags & SECTION_NAMED)
return htab_hash_string (sect->named.name);
return sect->common.flags;
}
/* Helper routines for maintaining object_block_htab. */
inline bool
object_block_hasher::equal (object_block *old, const section *new_section)
{
return old->sect == new_section;
}
hashval_t
object_block_hasher::hash (object_block *old)
{
return hash_section (old->sect);
}
/* Return a new unnamed section with the given fields. */
section *
get_unnamed_section (unsigned int flags, void (*callback) (const void *),
const void *data)
{
section *sect;
sect = ggc_alloc<section> ();
sect->unnamed.common.flags = flags | SECTION_UNNAMED;
sect->unnamed.callback = callback;
sect->unnamed.data = data;
sect->unnamed.next = unnamed_sections;
unnamed_sections = sect;
return sect;
}
/* Return a SECTION_NOSWITCH section with the given fields. */
static section *
get_noswitch_section (unsigned int flags, noswitch_section_callback callback)
{
section *sect;
sect = ggc_alloc<section> ();
sect->noswitch.common.flags = flags | SECTION_NOSWITCH;
sect->noswitch.callback = callback;
return sect;
}
/* Return the named section structure associated with NAME. Create
a new section with the given fields if no such structure exists. */
section *
get_section (const char *name, unsigned int flags, tree decl)
{
section *sect, **slot;
slot = section_htab->find_slot_with_hash (name, htab_hash_string (name),
INSERT);
flags |= SECTION_NAMED;
if (*slot == NULL)
{
sect = ggc_alloc<section> ();
sect->named.common.flags = flags;
sect->named.name = ggc_strdup (name);
sect->named.decl = decl;
*slot = sect;
}
else
{
sect = *slot;
if ((sect->common.flags & ~SECTION_DECLARED) != flags
&& ((sect->common.flags | flags) & SECTION_OVERRIDE) == 0)
{
/* It is fine if one of the section flags is
SECTION_WRITE | SECTION_RELRO and the other has none of these
flags (i.e. read-only) in named sections and either the
section hasn't been declared yet or has been declared as writable.
In that case just make sure the resulting flags are
SECTION_WRITE | SECTION_RELRO, ie. writable only because of
relocations. */
if (((sect->common.flags ^ flags) & (SECTION_WRITE | SECTION_RELRO))
== (SECTION_WRITE | SECTION_RELRO)
&& (sect->common.flags
& ~(SECTION_DECLARED | SECTION_WRITE | SECTION_RELRO))
== (flags & ~(SECTION_WRITE | SECTION_RELRO))
&& ((sect->common.flags & SECTION_DECLARED) == 0
|| (sect->common.flags & SECTION_WRITE)))
{
sect->common.flags |= (SECTION_WRITE | SECTION_RELRO);
return sect;
}
/* Sanity check user variables for flag changes. */
if (sect->named.decl != NULL
&& DECL_P (sect->named.decl)
&& decl != sect->named.decl)
{
if (decl != NULL && DECL_P (decl))
error ("%+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;
}
}
return sect;
}
/* Return true if the current compilation mode benefits from having
objects grouped into blocks. */
static bool
use_object_blocks_p (void)
{
return flag_section_anchors;
}
/* Return the object_block structure for section SECT. Create a new
structure if we haven't created one already. Return null if SECT
itself is null. */
static struct object_block *
get_block_for_section (section *sect)
{
struct object_block *block;
if (sect == NULL)
return NULL;
object_block **slot
= object_block_htab->find_slot_with_hash (sect, hash_section (sect),
INSERT);
block = *slot;
if (block == NULL)
{
block = ggc_cleared_alloc<object_block> ();
block->sect = sect;
*slot = block;
}
return block;
}
/* Create a symbol with label LABEL and place it at byte offset
OFFSET in BLOCK. OFFSET can be negative if the symbol's offset
is not yet known. LABEL must be a garbage-collected string. */
static rtx
create_block_symbol (const char *label, struct object_block *block,
HOST_WIDE_INT offset)
{
rtx symbol;
unsigned int size;
/* Create the extended SYMBOL_REF. */
size = RTX_HDR_SIZE + sizeof (struct block_symbol);
symbol = (rtx) ggc_internal_alloc (size);
/* Initialize the normal SYMBOL_REF fields. */
memset (symbol, 0, size);
PUT_CODE (symbol, SYMBOL_REF);
PUT_MODE (symbol, Pmode);
XSTR (symbol, 0) = label;
SYMBOL_REF_FLAGS (symbol) = SYMBOL_FLAG_HAS_BLOCK_INFO;
/* Initialize the block_symbol stuff. */
SYMBOL_REF_BLOCK (symbol) = block;
SYMBOL_REF_BLOCK_OFFSET (symbol) = offset;
return symbol;
}
/* Return a section with a particular name and with whatever SECTION_*
flags section_type_flags deems appropriate. The name of the section
is taken from NAME if nonnull, otherwise it is taken from DECL's
DECL_SECTION_NAME. DECL is the decl associated with the section
(see the section comment for details) and RELOC is as for
section_type_flags. */
section *
get_named_section (tree decl, const char *name, int reloc)
{
unsigned int flags;
if (name == NULL)
{
gcc_assert (decl && DECL_P (decl) && DECL_SECTION_NAME (decl));
name = DECL_SECTION_NAME (decl);
}
flags = targetm.section_type_flags (decl, name, reloc);
return get_section (name, flags, decl);
}
/* Worker for resolve_unique_section. */
static bool
set_implicit_section (struct symtab_node *n, void *data ATTRIBUTE_UNUSED)
{
n->implicit_section = true;
return false;
}
/* If required, set DECL_SECTION_NAME to a unique name. */
void
resolve_unique_section (tree decl, int reloc ATTRIBUTE_UNUSED,
int flag_function_or_data_sections)
{
if (DECL_SECTION_NAME (decl) == NULL
&& targetm_common.have_named_sections
&& (flag_function_or_data_sections
|| DECL_COMDAT_GROUP (decl)))
{
targetm.asm_out.unique_section (decl, reloc);
if (DECL_SECTION_NAME (decl))
symtab_node::get (decl)->call_for_symbol_and_aliases
(set_implicit_section, NULL, true);
}
}
#ifdef BSS_SECTION_ASM_OP
#ifdef ASM_OUTPUT_ALIGNED_BSS
/* Utility function for targets to use in implementing
ASM_OUTPUT_ALIGNED_BSS.
??? It is believed that this function will work in most cases so such
support is localized here. */
static void
asm_output_aligned_bss (FILE *file, tree decl ATTRIBUTE_UNUSED,
const char *name, unsigned HOST_WIDE_INT size,
int align)
{
switch_to_section (bss_section);
ASM_OUTPUT_ALIGN (file, floor_log2 (align / BITS_PER_UNIT));
#ifdef ASM_DECLARE_OBJECT_NAME
last_assemble_variable_decl = decl;
ASM_DECLARE_OBJECT_NAME (file, name, decl);
#else
/* Standard thing is just output label for the object. */
ASM_OUTPUT_LABEL (file, name);
#endif /* ASM_DECLARE_OBJECT_NAME */
ASM_OUTPUT_SKIP (file, size ? size : 1);
}
#endif
#endif /* BSS_SECTION_ASM_OP */
#ifndef USE_SELECT_SECTION_FOR_FUNCTIONS
/* Return the hot section for function DECL. Return text_section for
null DECLs. */
static section *
hot_function_section (tree decl)
{
if (decl != NULL_TREE
&& DECL_SECTION_NAME (decl) != NULL
&& targetm_common.have_named_sections)
return get_named_section (decl, NULL, 0);
else
return text_section;
}
#endif
/* Return section for TEXT_SECTION_NAME if DECL or DECL_SECTION_NAME (DECL)
is NULL.
When DECL_SECTION_NAME is non-NULL and it is implicit section and
NAMED_SECTION_SUFFIX is non-NULL, then produce section called
concatenate the name with NAMED_SECTION_SUFFIX.
Otherwise produce "TEXT_SECTION_NAME.IMPLICIT_NAME". */
section *
get_named_text_section (tree decl,
const char *text_section_name,
const char *named_section_suffix)
{
if (decl && DECL_SECTION_NAME (decl))
{
if (named_section_suffix)
{
const char *dsn = DECL_SECTION_NAME (decl);
const char *stripped_name;
char *name, *buffer;
name = (char *) alloca (strlen (dsn) + 1);
memcpy (name, dsn,
strlen (dsn) + 1);
stripped_name = targetm.strip_name_encoding (name);
buffer = ACONCAT ((stripped_name, named_section_suffix, NULL));
return get_named_section (decl, buffer, 0);
}
else if (symtab_node::get (decl)->implicit_section)
{
const char *name;
/* Do not try to split gnu_linkonce functions. This gets somewhat
slipperly. */
if (DECL_COMDAT_GROUP (decl) && !HAVE_COMDAT_GROUP)
return NULL;
name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl));
name = targetm.strip_name_encoding (name);
return get_named_section (decl, ACONCAT ((text_section_name, ".",
name, NULL)), 0);
}
else
return NULL;
}
return get_named_section (decl, text_section_name, 0);
}
/* Choose named function section based on its frequency. */
section *
default_function_section (tree decl, enum node_frequency freq,
bool startup, bool exit)
{
#if defined HAVE_LD_EH_GC_SECTIONS && defined HAVE_LD_EH_GC_SECTIONS_BUG
/* Old GNU linkers have buggy --gc-section support, which sometimes
results in .gcc_except_table* sections being garbage collected. */
if (decl
&& symtab_node::get (decl)->implicit_section)
return NULL;
#endif
if (!flag_reorder_functions
|| !targetm_common.have_named_sections)
return NULL;
/* Startup code should go to startup subsection unless it is
unlikely executed (this happens especially with function splitting
where we can split away unnecessary parts of static constructors. */
if (startup && freq != NODE_FREQUENCY_UNLIKELY_EXECUTED)
{
/* If we do have a profile or(and) LTO phase is executed, we do not need
these ELF section. */
if (!in_lto_p || !flag_profile_values)
return get_named_text_section (decl, ".text.startup", NULL);
else
return NULL;
}
/* Similarly for exit. */
if (exit && freq != NODE_FREQUENCY_UNLIKELY_EXECUTED)
return get_named_text_section (decl, ".text.exit", NULL);
/* Group cold functions together, similarly for hot code. */
switch (freq)
{
case NODE_FREQUENCY_UNLIKELY_EXECUTED:
return get_named_text_section (decl, ".text.unlikely", NULL);
case NODE_FREQUENCY_HOT:
/* If we do have a profile or(and) LTO phase is executed, we do not need
these ELF section. */
if (!in_lto_p || !flag_profile_values)
return get_named_text_section (decl, ".text.hot", NULL);
default:
return NULL;
}
}
/* Return the section for function DECL.
If DECL is NULL_TREE, return the text section. We can be passed
NULL_TREE under some circumstances by dbxout.c at least.
If FORCE_COLD is true, return cold function section ignoring
the frequency info of cgraph_node. */
static section *
function_section_1 (tree decl, bool force_cold)
{
section *section = NULL;
enum node_frequency freq = NODE_FREQUENCY_NORMAL;
bool startup = false, exit = false;
if (decl)
{
struct cgraph_node *node = cgraph_node::get (decl);
if (node)
{
freq = node->frequency;
startup = node->only_called_at_startup;
exit = node->only_called_at_exit;
}
}
if (force_cold)
freq = NODE_FREQUENCY_UNLIKELY_EXECUTED;
#ifdef USE_SELECT_SECTION_FOR_FUNCTIONS
if (decl != NULL_TREE
&& DECL_SECTION_NAME (decl) != NULL)
{
if (targetm.asm_out.function_section)
section = targetm.asm_out.function_section (decl, freq,
startup, exit);
if (section)
return section;
return get_named_section (decl, NULL, 0);
}
else
return targetm.asm_out.select_section
(decl, freq == NODE_FREQUENCY_UNLIKELY_EXECUTED,
symtab_node::get (decl)->definition_alignment ());
#else
if (targetm.asm_out.function_section)
section = targetm.asm_out.function_section (decl, freq, startup, exit);
if (section)
return section;
return hot_function_section (decl);
#endif
}
/* Return the section for function DECL.
If DECL is NULL_TREE, return the text section. We can be passed
NULL_TREE under some circumstances by dbxout.c at least. */
section *
function_section (tree decl)
{
/* Handle cases where function splitting code decides
to put function entry point into unlikely executed section
despite the fact that the function itself is not cold
(i.e. it is called rarely but contains a hot loop that is
better to live in hot subsection for the code locality). */
return function_section_1 (decl,
first_function_block_is_cold);
}
/* Return the section for the current function, take IN_COLD_SECTION_P
into account. */
section *
current_function_section (void)
{
return function_section_1 (current_function_decl, in_cold_section_p);
}
/* Tell assembler to switch to unlikely-to-be-executed text section. */
section *
unlikely_text_section (void)
{
return function_section_1 (current_function_decl, true);
}
/* When called within a function context, return true if the function
has been assigned a cold text section and if SECT is that section.
When called outside a function context, return true if SECT is the
default cold section. */
bool
unlikely_text_section_p (section *sect)
{
return sect == function_section_1 (current_function_decl, true);
}
/* Return the read-only data section associated with function DECL. */
section *
default_function_rodata_section (tree decl)
{
if (decl != NULL_TREE && DECL_SECTION_NAME (decl))
{
const char *name = DECL_SECTION_NAME (decl);
if (DECL_COMDAT_GROUP (decl) && HAVE_COMDAT_GROUP)
{
const char *dot;
size_t len;
char* rname;
dot = strchr (name + 1, '.');
if (!dot)
dot = name;
len = strlen (dot) + 8;
rname = (char *) alloca (len);
strcpy (rname, ".rodata");
strcat (rname, dot);
return get_section (rname, SECTION_LINKONCE, decl);
}
/* For .gnu.linkonce.t.foo we want to use .gnu.linkonce.r.foo. */
else if (DECL_COMDAT_GROUP (decl)
&& strncmp (name, ".gnu.linkonce.t.", 16) == 0)
{
size_t len = strlen (name) + 1;
char *rname = (char *) 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 = (char *) 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
&& (len = int_size_in_bytes (TREE_TYPE (decl))) > 0
&& TREE_STRING_LENGTH (decl) >= len)
{
machine_mode mode;
unsigned int modesize;
const char *str;
HOST_WIDE_INT i;
int j, unit;
const char *prefix = targetm.asm_out.mergeable_rodata_prefix;
char *name = (char *) alloca (strlen (prefix) + 30);
mode = TYPE_MODE (TREE_TYPE (TREE_TYPE (decl)));
modesize = GET_MODE_BITSIZE (mode);
if (modesize >= 8 && modesize <= 256
&& (modesize & (modesize - 1)) == 0)
{
if (align < modesize)
align = modesize;
str = TREE_STRING_POINTER (decl);
unit = GET_MODE_SIZE (mode);
/* Check for embedded NUL characters. */
for (i = 0; i < len; i += unit)
{
for (j = 0; j < unit; j++)
if (str[i + j] != '\0')
break;
if (j == unit)
break;
}
if (i == len - unit)
{
sprintf (name, "%s.str%d.%d", prefix,
modesize / 8, (int) (align / 8));
flags |= (modesize / 8) | SECTION_MERGE | SECTION_STRINGS;
return get_section (name, flags, NULL);
}
}
}
return readonly_data_section;
}
/* Return the section to use for constant merging. */
section *
mergeable_constant_section (machine_mode mode ATTRIBUTE_UNUSED,
unsigned HOST_WIDE_INT align ATTRIBUTE_UNUSED,
unsigned int flags ATTRIBUTE_UNUSED)
{
unsigned int modesize = GET_MODE_BITSIZE (mode);
if (HAVE_GAS_SHF_MERGE && flag_merge_constants
&& mode != VOIDmode
&& mode != BLKmode
&& modesize <= align
&& align >= 8
&& align <= 256
&& (align & (align - 1)) == 0)
{
const char *prefix = targetm.asm_out.mergeable_rodata_prefix;
char *name = (char *) alloca (strlen (prefix) + 30);
sprintf (name, "%s.cst%d", prefix, (int) (align / 8));
flags |= (align / 8) | SECTION_MERGE;
return get_section (name, flags, NULL);
}
return readonly_data_section;
}
/* Given NAME, a putative register name, discard any customary prefixes. */
static const char *
strip_reg_name (const char *name)
{
#ifdef REGISTER_PREFIX
if (!strncmp (name, REGISTER_PREFIX, strlen (REGISTER_PREFIX)))
name += strlen (REGISTER_PREFIX);
#endif
if (name[0] == '%' || name[0] == '#')
name++;
return name;
}
/* The user has asked for a DECL to have a particular name. Set (or
change) it in such a way that we don't prefix an underscore to
it. */
void
set_user_assembler_name (tree decl, const char *name)
{
char *starred = (char *) alloca (strlen (name) + 2);
starred[0] = '*';
strcpy (starred + 1, name);
symtab->change_decl_assembler_name (decl, get_identifier (starred));
SET_DECL_RTL (decl, NULL_RTX);
}
/* Decode an `asm' spec for a declaration as a register name.
Return the register number, or -1 if nothing specified,
or -2 if the ASMSPEC is not `cc' or `memory' and is not recognized,
or -3 if ASMSPEC is `cc' and is not recognized,
or -4 if ASMSPEC is `memory' and is not recognized.
Accept an exact spelling or a decimal number.
Prefixes such as % are optional. */
int
decode_reg_name_and_count (const char *asmspec, int *pnregs)
{
/* Presume just one register is clobbered. */
*pnregs = 1;
if (asmspec != 0)
{
int i;
/* Get rid of confusing prefixes. */
asmspec = strip_reg_name (asmspec);
/* Allow a decimal number as a "register name". */
for (i = strlen (asmspec) - 1; i >= 0; i--)
if (! ISDIGIT (asmspec[i]))
break;
if (asmspec[0] != 0 && i < 0)
{
i = atoi (asmspec);
if (i < FIRST_PSEUDO_REGISTER && i >= 0 && reg_names[i][0])
return i;
else
return -2;
}
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
if (reg_names[i][0]
&& ! strcmp (asmspec, strip_reg_name (reg_names[i])))
return i;
#ifdef OVERLAPPING_REGISTER_NAMES
{
static const struct
{
const char *const name;
const int number;
const int nregs;
} table[] = OVERLAPPING_REGISTER_NAMES;
for (i = 0; i < (int) ARRAY_SIZE (table); i++)
if (table[i].name[0]
&& ! strcmp (asmspec, table[i].name))
{
*pnregs = table[i].nregs;
return table[i].number;
}
}
#endif /* OVERLAPPING_REGISTER_NAMES */
#ifdef ADDITIONAL_REGISTER_NAMES
{
static const struct { const char *const name; const int number; } table[]
= ADDITIONAL_REGISTER_NAMES;
for (i = 0; i < (int) ARRAY_SIZE (table); i++)
if (table[i].name[0]
&& ! strcmp (asmspec, table[i].name)
&& reg_names[table[i].number][0])
return table[i].number;
}
#endif /* ADDITIONAL_REGISTER_NAMES */
if (!strcmp (asmspec, "memory"))
return -4;
if (!strcmp (asmspec, "cc"))
return -3;
return -2;
}
return -1;
}
int
decode_reg_name (const char *name)
{
int count;
return decode_reg_name_and_count (name, &count);
}
/* Return true if DECL's initializer is suitable for a BSS section. */
bool
bss_initializer_p (const_tree decl)
{
return (DECL_INITIAL (decl) == NULL
/* In LTO we have no errors in program; error_mark_node is used
to mark offlined constructors. */
|| (DECL_INITIAL (decl) == error_mark_node
&& !in_lto_p)
|| (flag_zero_initialized_in_bss
/* 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)
{
error ("alignment of %q+D is greater than maximum object "
"file alignment %d", decl,
MAX_OFILE_ALIGNMENT/BITS_PER_UNIT);
align = MAX_OFILE_ALIGNMENT;
}
if (! DECL_USER_ALIGN (decl))
{
#ifdef DATA_ABI_ALIGNMENT
unsigned int data_abi_align
= DATA_ABI_ALIGNMENT (TREE_TYPE (decl), align);
/* For backwards compatibility, don't assume the ABI alignment for
TLS variables. */
if (! DECL_THREAD_LOCAL_P (decl) || data_abi_align <= BITS_PER_WORD)
align = data_abi_align;
#endif
/* On some machines, it is good to increase alignment sometimes.
But as DECL_ALIGN is used both for actually emitting the variable
and for code accessing the variable as guaranteed alignment, we
can only increase the alignment if it is a performance optimization
if the references to it must bind to the current definition. */
if (decl_binds_to_current_def_p (decl)
&& !DECL_VIRTUAL_P (decl))
{
#ifdef DATA_ALIGNMENT
unsigned int data_align = DATA_ALIGNMENT (TREE_TYPE (decl), align);
/* Don't increase alignment too much for TLS variables - TLS space
is too precious. */
if (! DECL_THREAD_LOCAL_P (decl) || data_align <= BITS_PER_WORD)
align = data_align;
#endif
#ifdef CONSTANT_ALIGNMENT
if (DECL_INITIAL (decl) != 0
/* In LTO we have no errors in program; error_mark_node is used
to mark offlined constructors. */
&& (in_lto_p || DECL_INITIAL (decl) != error_mark_node))
{
unsigned int const_align
= 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. */
if (! DECL_THREAD_LOCAL_P (decl) || data_align <= BITS_PER_WORD)
align = data_align;
#endif
#ifdef CONSTANT_ALIGNMENT
if (DECL_INITIAL (decl) != 0
/* In LTO we have no errors in program; error_mark_node is used
to mark offlined constructors. */
&& (in_lto_p || DECL_INITIAL (decl) != error_mark_node))
{
unsigned int const_align = 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
}
return 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. */
section *
get_variable_section (tree decl, bool prefer_noswitch_p)
{
addr_space_t as = ADDR_SPACE_GENERIC;
int reloc;
varpool_node *vnode = varpool_node::get (decl);
if (vnode)
{
vnode = vnode->ultimate_alias_target ();
decl = vnode->decl;
}
if (TREE_TYPE (decl) != error_mark_node)
as = TYPE_ADDR_SPACE (TREE_TYPE (decl));
/* We need the constructor to figure out reloc flag. */
if (vnode)
vnode->get_constructor ();
if (DECL_COMMON (decl))
{
/* If the decl has been given an explicit section name, or it resides
in a non-generic address space, then it isn't common, and shouldn't
be handled as such. */
gcc_assert (DECL_SECTION_NAME (decl) == NULL
&& ADDR_SPACE_GENERIC_P (as));
if (DECL_THREAD_LOCAL_P (decl))
return tls_comm_section;
else if (TREE_PUBLIC (decl) && bss_initializer_p (decl))
return comm_section;
}
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 (ADDR_SPACE_GENERIC_P (as)
&& !DECL_THREAD_LOCAL_P (decl)
&& !(prefer_noswitch_p && targetm.have_switchable_bss_sections)
&& bss_initializer_p (decl))
{
if (!TREE_PUBLIC (decl)
&& !((flag_sanitize & SANITIZE_ADDRESS)
&& asan_protect_global (decl)))
return lcomm_section;
if (bss_noswitch_section)
return bss_noswitch_section;
}
return targetm.asm_out.select_section (decl, reloc,
get_variable_align (decl));
}
/* Return the block into which object_block DECL should be placed. */
static struct object_block *
get_block_for_decl (tree decl)
{
section *sect;
if (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_COMDAT_GROUP (decl))
return NULL;
}
/* We can only calculate block offsets if the decl has a known
constant size. */
if (DECL_SIZE_UNIT (decl) == NULL)
return NULL;
if (!tree_fits_uhwi_p (DECL_SIZE_UNIT (decl)))
return NULL;
/* Find out which section should contain DECL. We cannot put it into
an object block if it requires a standalone definition. */
if (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)
{
struct symtab_node *snode;
/* 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 (TREE_CODE (decl) == VAR_DECL
&& (snode = symtab_node::get (decl)) != NULL
&& snode->alias)
return false;
return targetm.use_blocks_for_decl_p (decl);
}
/* Follow the IDENTIFIER_TRANSPARENT_ALIAS chain starting at *ALIAS
until we find an identifier that is not itself a transparent alias.
Modify the alias passed to it by reference (and all aliases on the
way to the ultimate target), such that they do not have to be
followed again, and return the ultimate target of the alias
chain. */
static inline tree
ultimate_transparent_alias_target (tree *alias)
{
tree target = *alias;
if (IDENTIFIER_TRANSPARENT_ALIAS (target))
{
gcc_assert (TREE_CHAIN (target));
target = ultimate_transparent_alias_target (&TREE_CHAIN (target));
gcc_assert (! IDENTIFIER_TRANSPARENT_ALIAS (target)
&& ! TREE_CHAIN (target));
*alias = target;
}
return target;
}
/* Create the DECL_RTL for a VAR_DECL or FUNCTION_DECL. DECL should
have static storage duration. In other words, it should not be an
automatic variable, including PARM_DECLs.
There is, however, one exception: this function handles variables
explicitly placed in a particular register by the user.
This is never called for PARM_DECL nodes. */
void
make_decl_rtl (tree decl)
{
const char *name = 0;
int reg_number;
tree id;
rtx x;
/* Check that we are not being given an automatic variable. */
gcc_assert (TREE_CODE (decl) != PARM_DECL
&& TREE_CODE (decl) != RESULT_DECL);
/* A weak alias has TREE_PUBLIC set but not the other bits. */
gcc_assert (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. */
gcc_assert (TREE_CODE (decl) != TYPE_DECL
&& TREE_CODE (decl) != LABEL_DECL);
/* For a duplicate declaration, we can be called twice on the
same DECL node. Don't discard the RTL already made. */
if (DECL_RTL_SET_P (decl))
{
/* If the old RTL had the wrong mode, fix the mode. */
x = DECL_RTL (decl);
if (GET_MODE (x) != DECL_MODE (decl))
SET_DECL_RTL (decl, adjust_address_nv (x, DECL_MODE (decl), 0));
if (TREE_CODE (decl) != FUNCTION_DECL && DECL_REGISTER (decl))
return;
/* ??? Another way to do this would be to maintain a hashed
table of such critters. Instead of adding stuff to a DECL
to give certain attributes to it, we could use an external
hash map from DECL to set of attributes. */
/* Let the target reassign the RTL if it wants.
This is necessary, for example, when one machine specific
decl attribute overrides another. */
targetm.encode_section_info (decl, DECL_RTL (decl), false);
/* If the symbol has a SYMBOL_REF_BLOCK field, update it based
on the new decl information. */
if (MEM_P (x)
&& GET_CODE (XEXP (x, 0)) == SYMBOL_REF
&& SYMBOL_REF_HAS_BLOCK_INFO_P (XEXP (x, 0)))
change_symbol_block (XEXP (x, 0), get_block_for_decl (decl));
return;
}
/* If this variable belongs to the global constant pool, retrieve the
pre-computed RTL or recompute it in LTO mode. */
if (TREE_CODE (decl) == VAR_DECL && DECL_IN_CONSTANT_POOL (decl))
{
SET_DECL_RTL (decl, output_constant_def (DECL_INITIAL (decl), 1));
return;
}
id = DECL_ASSEMBLER_NAME (decl);
if (TREE_CODE (decl) == FUNCTION_DECL
&& cgraph_node::get (decl)
&& cgraph_node::get (decl)->instrumentation_clone)
ultimate_transparent_alias_target (&id);
name = IDENTIFIER_POINTER (id);
if (name[0] != '*' && TREE_CODE (decl) != FUNCTION_DECL
&& DECL_REGISTER (decl))
{
error ("register name not specified for %q+D", decl);
}
else if (TREE_CODE (decl) != FUNCTION_DECL && DECL_REGISTER (decl))
{
const char *asmspec = name+1;
machine_mode mode = DECL_MODE (decl);
reg_number = decode_reg_name (asmspec);
/* First detect errors in declaring global registers. */
if (reg_number == -1)
error ("register name not specified for %q+D", decl);
else if (reg_number < 0)
error ("invalid register name for %q+D", decl);
else if (mode == BLKmode)
error ("data type of %q+D isn%'t suitable for a register",
decl);
else if (!in_hard_reg_set_p (accessible_reg_set, mode, reg_number))
error ("the register specified for %q+D cannot be accessed"
" by the current target", decl);
else if (!in_hard_reg_set_p (operand_reg_set, mode, reg_number))
error ("the register specified for %q+D is not general enough"
" to be used as a register variable", decl);
else if (!HARD_REGNO_MODE_OK (reg_number, mode))
error ("register specified for %q+D isn%'t suitable for data type",
decl);
/* Now handle properly declared static register variables. */
else
{
int nregs;
if (DECL_INITIAL (decl) != 0 && TREE_STATIC (decl))
{
DECL_INITIAL (decl) = 0;
error ("global register variable has initial value");
}
if (TREE_THIS_VOLATILE (decl))
warning (OPT_Wvolatile_register_var,
"optimization may eliminate reads and/or "
"writes to register variables");
/* If the user specified one of the eliminables registers here,
e.g., FRAME_POINTER_REGNUM, we don't want to get this variable
confused with that register and be eliminated. This usage is
somewhat suspect... */
SET_DECL_RTL (decl, gen_rtx_raw_REG (mode, reg_number));
ORIGINAL_REGNO (DECL_RTL (decl)) = reg_number;
REG_USERVAR_P (DECL_RTL (decl)) = 1;
if (TREE_STATIC (decl))
{
/* Make this register global, so not usable for anything
else. */
#ifdef ASM_DECLARE_REGISTER_GLOBAL
name = IDENTIFIER_POINTER (DECL_NAME (decl));
ASM_DECLARE_REGISTER_GLOBAL (asm_out_file, decl, reg_number, name);
#endif
nregs = hard_regno_nregs[reg_number][mode];
while (nregs > 0)
globalize_reg (decl, reg_number + --nregs);
}
/* As a register variable, it has no section. */
return;
}
/* Avoid internal errors from invalid register
specifications. */
SET_DECL_ASSEMBLER_NAME (decl, NULL_TREE);
DECL_HARD_REGISTER (decl) = 0;
/* Also avoid SSA inconsistencies by pretending this is an external
decl now. */
DECL_EXTERNAL (decl) = 1;
return;
}
/* Now handle ordinary static variables and functions (in memory).
Also handle vars declared register invalidly. */
else if (name[0] == '*')
{
#ifdef REGISTER_PREFIX
if (strlen (REGISTER_PREFIX) != 0)
{
reg_number = decode_reg_name (name);
if (reg_number >= 0 || reg_number == -3)
error ("register name given for non-register variable %q+D", decl);
}
#endif
}
/* Specifying a section attribute on a variable forces it into a
non-.bss section, and thus it cannot be common. */
/* FIXME: In general this code should not be necessary because
visibility pass is doing the same work. But notice_global_symbol
is called early and it needs to make DECL_RTL to get the name.
we take care of recomputing the DECL_RTL after visibility is changed. */
if (TREE_CODE (decl) == VAR_DECL
&& (TREE_STATIC (decl) || DECL_EXTERNAL (decl))
&& DECL_SECTION_NAME (decl) != NULL
&& DECL_INITIAL (decl) == NULL_TREE
&& DECL_COMMON (decl))
DECL_COMMON (decl) = 0;
/* Variables can't be both common and weak. */
if (TREE_CODE (decl) == VAR_DECL && DECL_WEAK (decl))
DECL_COMMON (decl) = 0;
if (use_object_blocks_p () && use_blocks_for_decl_p (decl))
x = create_block_symbol (name, get_block_for_decl (decl), -1);
else
{
machine_mode address_mode = Pmode;
if (TREE_TYPE (decl) != error_mark_node)
{
addr_space_t as = TYPE_ADDR_SPACE (TREE_TYPE (decl));
address_mode = targetm.addr_space.address_mode (as);
}
x = gen_rtx_SYMBOL_REF (address_mode, name);
}
SYMBOL_REF_WEAK (x) = DECL_WEAK (decl);
SET_SYMBOL_REF_DECL (x, decl);
x = gen_rtx_MEM (DECL_MODE (decl), x);
if (TREE_CODE (decl) != FUNCTION_DECL)
set_mem_attributes (x, decl, 1);
SET_DECL_RTL (decl, x);
/* Optionally set flags or add text to the name to record information
such as that it is a function name.
If the name is changed, the macro ASM_OUTPUT_LABELREF
will have to know how to strip this information. */
targetm.encode_section_info (decl, DECL_RTL (decl), true);
}
/* Like make_decl_rtl, but inhibit creation of new alias sets when
calling make_decl_rtl. Also, reset DECL_RTL before returning the
rtl. */
rtx
make_decl_rtl_for_debug (tree decl)
{
unsigned int save_aliasing_flag;
rtx rtl;
if (DECL_RTL_SET_P (decl))
return DECL_RTL (decl);
/* Kludge alert! Somewhere down the call chain, make_decl_rtl will
call new_alias_set. If running with -fcompare-debug, sometimes
we do not want to create alias sets that will throw the alias
numbers off in the comparison dumps. So... clearing
flag_strict_aliasing will keep new_alias_set() from creating a
new set. */
save_aliasing_flag = flag_strict_aliasing;
flag_strict_aliasing = 0;
rtl = DECL_RTL (decl);
/* Reset DECL_RTL back, as various parts of the compiler expects
DECL_RTL set meaning it is actually going to be output. */
SET_DECL_RTL (decl, NULL);
flag_strict_aliasing = save_aliasing_flag;
return rtl;
}
/* Output a string of literal assembler code
for an `asm' keyword used between functions. */
void
assemble_asm (tree string)
{
const char *p;
app_enable ();
if (TREE_CODE (string) == ADDR_EXPR)
string = TREE_OPERAND (string, 0);
p = TREE_STRING_POINTER (string);
fprintf (asm_out_file, "%s%s\n", p[0] == '\t' ? "" : "\t", p);
}
/* Write the address of the entity given by SYMBOL to SEC. */
void
assemble_addr_to_section (rtx symbol, section *sec)
{
switch_to_section (sec);
assemble_align (POINTER_SIZE);
assemble_integer (symbol, POINTER_SIZE_UNITS, POINTER_SIZE, 1);
}
/* Return the numbered .ctors.N (if CONSTRUCTOR_P) or .dtors.N (if
not) section for PRIORITY. */
section *
get_cdtor_priority_section (int priority, bool constructor_p)
{
char buf[16];
/* ??? This only works reliably with the GNU linker. */
sprintf (buf, "%s.%.5u",
constructor_p ? ".ctors" : ".dtors",
/* Invert the numbering so the linker puts us in the proper
order; constructors are run from right to left, and the
linker sorts in increasing order. */
MAX_INIT_PRIORITY - priority);
return get_section (buf, SECTION_WRITE, NULL);
}
void
default_named_section_asm_out_destructor (rtx symbol, int priority)
{
section *sec;
if (priority != DEFAULT_INIT_PRIORITY)
sec = get_cdtor_priority_section (priority,
/*constructor_p=*/false);
else
sec = get_section (".dtors", SECTION_WRITE, NULL);
assemble_addr_to_section (symbol, sec);
}
#ifdef DTORS_SECTION_ASM_OP
void
default_dtor_section_asm_out_destructor (rtx symbol,
int priority ATTRIBUTE_UNUSED)
{
assemble_addr_to_section (symbol, dtors_section);
}
#endif
void
default_named_section_asm_out_constructor (rtx symbol, int priority)
{
section *sec;
if (priority != DEFAULT_INIT_PRIORITY)
sec = get_cdtor_priority_section (priority,
/*constructor_p=*/true);
else
sec = get_section (".ctors", SECTION_WRITE, NULL);
assemble_addr_to_section (symbol, sec);
}
#ifdef CTORS_SECTION_ASM_OP
void
default_ctor_section_asm_out_constructor (rtx symbol,
int priority ATTRIBUTE_UNUSED)
{
assemble_addr_to_section (symbol, ctors_section);
}
#endif
/* CONSTANT_POOL_BEFORE_FUNCTION may be defined as an expression with
a nonzero value if the constant pool should be output before the
start of the function, or a zero value if the pool should output
after the end of the function. The default is to put it before the
start. */
#ifndef CONSTANT_POOL_BEFORE_FUNCTION
#define CONSTANT_POOL_BEFORE_FUNCTION 1
#endif
/* DECL is an object (either VAR_DECL or FUNCTION_DECL) which is going
to be output to assembler.
Set first_global_object_name and weak_global_object_name as appropriate. */
void
notice_global_symbol (tree decl)
{
const char **t = &first_global_object_name;
if (first_global_object_name
|| !TREE_PUBLIC (decl)
|| DECL_EXTERNAL (decl)
|| !DECL_NAME (decl)
|| (TREE_CODE (decl) == VAR_DECL && DECL_HARD_REGISTER (decl))
|| (TREE_CODE (decl) != FUNCTION_DECL
&& (TREE_CODE (decl) != VAR_DECL
|| (DECL_COMMON (decl)
&& (DECL_INITIAL (decl) == 0
|| DECL_INITIAL (decl) == error_mark_node)))))
return;
/* We win when global object is found, but it is useful to know about weak
symbol as well so we can produce nicer unique names. */
if (DECL_WEAK (decl) || DECL_ONE_ONLY (decl) || flag_shlib)
t = &weak_global_object_name;
if (!*t)
{
tree id = DECL_ASSEMBLER_NAME (decl);
ultimate_transparent_alias_target (&id);
*t = ggc_strdup (targetm.strip_name_encoding (IDENTIFIER_POINTER (id)));
}
}
/* If not using flag_reorder_blocks_and_partition, decide early whether the
current function goes into the cold section, so that targets can use
current_function_section during RTL expansion. DECL describes the
function. */
void
decide_function_section (tree decl)
{
first_function_block_is_cold = false;
if (flag_reorder_blocks_and_partition)
/* We will decide in assemble_start_function. */
return;
if (DECL_SECTION_NAME (decl))
{
struct cgraph_node *node = cgraph_node::get (current_function_decl);
/* Calls to function_section rely on first_function_block_is_cold
being accurate. */
first_function_block_is_cold = (node
&& node->frequency
== NODE_FREQUENCY_UNLIKELY_EXECUTED);
}
in_cold_section_p = first_function_block_is_cold;
}
/* Get the function's name, as described by its RTL. This may be
different from the DECL_NAME name used in the source file. */
const char *
get_fnname_from_decl (tree decl)
{
rtx x = DECL_RTL (decl);
gcc_assert (MEM_P (x));
x = XEXP (x, 0);
gcc_assert (GET_CODE (x) == SYMBOL_REF);
return XSTR (x, 0);
}
/* Output assembler code for the constant pool of a function and associated
with defining the name of the function. DECL describes the function.
NAME is the function's name. For the constant pool, we use the current
constant pool data. */
void
assemble_start_function (tree decl, const char *fnname)
{
int align;
char tmp_label[100];
bool hot_label_written = false;
if (flag_reorder_blocks_and_partition)
{
ASM_GENERATE_INTERNAL_LABEL (tmp_label, "LHOTB", const_labelno);
crtl->subsections.hot_section_label = ggc_strdup (tmp_label);
ASM_GENERATE_INTERNAL_LABEL (tmp_label, "LCOLDB", const_labelno);
crtl->subsections.cold_section_label = ggc_strdup (tmp_label);
ASM_GENERATE_INTERNAL_LABEL (tmp_label, "LHOTE", const_labelno);
crtl->subsections.hot_section_end_label = ggc_strdup (tmp_label);
ASM_GENERATE_INTERNAL_LABEL (tmp_label, "LCOLDE", const_labelno);
crtl->subsections.cold_section_end_label = ggc_strdup (tmp_label);
const_labelno++;
}
else
{
crtl->subsections.hot_section_label = NULL;
crtl->subsections.cold_section_label = NULL;
crtl->subsections.hot_section_end_label = NULL;
crtl->subsections.cold_section_end_label = NULL;
}
/* The following code does not need preprocessing in the assembler. */
app_disable ();
if (CONSTANT_POOL_BEFORE_FUNCTION)
output_constant_pool (fnname, decl);
align = symtab_node::get (decl)->definition_alignment ();
/* Make sure the not and cold text (code) sections are properly
aligned. This is necessary here in the case where the function
has both hot and cold sections, because we don't want to re-set
the alignment when the section switch happens mid-function. */
if (flag_reorder_blocks_and_partition)
{
first_function_block_is_cold = false;
switch_to_section (unlikely_text_section ());
assemble_align (align);
ASM_OUTPUT_LABEL (asm_out_file, crtl->subsections.cold_section_label);
/* When the function starts with a cold section, we need to explicitly
align the hot section and write out the hot section label.
But if the current function is a thunk, we do not have a CFG. */
if (!cfun->is_thunk
&& BB_PARTITION (ENTRY_BLOCK_PTR_FOR_FN (cfun)->next_bb) == BB_COLD_PARTITION)
{
switch_to_section (text_section);
assemble_align (align);
ASM_OUTPUT_LABEL (asm_out_file, crtl->subsections.hot_section_label);
hot_label_written = true;
first_function_block_is_cold = true;
}
in_cold_section_p = first_function_block_is_cold;
}
/* Switch to the correct text section for the start of the function. */
switch_to_section (function_section (decl));
if (flag_reorder_blocks_and_partition
&& !hot_label_written)
ASM_OUTPUT_LABEL (asm_out_file, crtl->subsections.hot_section_label);
/* Tell assembler to move to target machine's alignment for functions. */
align = floor_log2 (align / BITS_PER_UNIT);
if (align > 0)
{
ASM_OUTPUT_ALIGN (asm_out_file, align);
}
/* Handle a user-specified function alignment.
Note that we still need to align to DECL_ALIGN, as above,
because ASM_OUTPUT_MAX_SKIP_ALIGN might not do any alignment at all. */
if (! DECL_USER_ALIGN (decl)
&& align_functions_log > align
&& optimize_function_for_speed_p (cfun))
{
#ifdef ASM_OUTPUT_MAX_SKIP_ALIGN
ASM_OUTPUT_MAX_SKIP_ALIGN (asm_out_file,
align_functions_log, align_functions - 1);
#else
ASM_OUTPUT_ALIGN (asm_out_file, align_functions_log);
#endif
}
#ifdef ASM_OUTPUT_FUNCTION_PREFIX
ASM_OUTPUT_FUNCTION_PREFIX (asm_out_file, fnname);
#endif
if (!DECL_IGNORED_P (decl))
(*debug_hooks->begin_function) (decl);
/* Make function name accessible from other files, if appropriate. */
if (TREE_PUBLIC (decl)
|| (cgraph_node::get (decl)->instrumentation_clone
&& cgraph_node::get (decl)->instrumented_version
&& TREE_PUBLIC (cgraph_node::get (decl)->instrumented_version->decl)))
{
notice_global_symbol (decl);
globalize_decl (decl);
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);
#else
/* Standard thing is just output label for the function. */
ASM_OUTPUT_FUNCTION_LABEL (asm_out_file, fnname, current_function_decl);
#endif /* ASM_DECLARE_FUNCTION_NAME */
if (lookup_attribute ("no_split_stack", DECL_ATTRIBUTES (decl)))
saw_no_split_stack = true;
}
/* Output assembler code associated with defining the size of the
function. DECL describes the function. NAME is the function's name. */
void
assemble_end_function (tree decl, const char *fnname ATTRIBUTE_UNUSED)
{
#ifdef ASM_DECLARE_FUNCTION_SIZE
/* We could have switched section in the middle of the function. */
if (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);
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, crtl->subsections.cold_section_end_label);
if (first_function_block_is_cold)
switch_to_section (text_section);
else
switch_to_section (function_section (decl));
ASM_OUTPUT_LABEL (asm_out_file, crtl->subsections.hot_section_end_label);
switch_to_section (save_text_section);
}
}
/* Assemble code to leave SIZE bytes of zeros. */
void
assemble_zeros (unsigned HOST_WIDE_INT size)
{
/* Do no output if -fsyntax-only. */
if (flag_syntax_only)
return;
#ifdef ASM_NO_SKIP_IN_TEXT
/* The `space' pseudo in the text section outputs nop insns rather than 0s,
so we must output 0s explicitly in the text section. */
if (ASM_NO_SKIP_IN_TEXT && (in_section->common.flags & SECTION_CODE) != 0)
{
unsigned HOST_WIDE_INT i;
for (i = 0; i < size; i++)
assemble_integer (const0_rtx, 1, BITS_PER_UNIT, 1);
}
else
#endif
if (size > 0)
ASM_OUTPUT_SKIP (asm_out_file, size);
}
/* Assemble an alignment pseudo op for an ALIGN-bit boundary. */
void
assemble_align (int align)
{
if (align > BITS_PER_UNIT)
{
ASM_OUTPUT_ALIGN (asm_out_file, floor_log2 (align / BITS_PER_UNIT));
}
}
/* Assemble a string constant with the specified C string as contents. */
void
assemble_string (const char *p, int size)
{
int pos = 0;
int maximum = 2000;
/* If the string is very long, split it up. */
while (pos < size)
{
int thissize = size - pos;
if (thissize > maximum)
thissize = maximum;
ASM_OUTPUT_ASCII (asm_out_file, p, thissize);
pos += thissize;
p += thissize;
}
}
/* A noswitch_section_callback for lcomm_section. */
static bool
emit_local (tree decl ATTRIBUTE_UNUSED,
const char *name ATTRIBUTE_UNUSED,
unsigned HOST_WIDE_INT size ATTRIBUTE_UNUSED,
unsigned HOST_WIDE_INT rounded ATTRIBUTE_UNUSED)
{
#if defined ASM_OUTPUT_ALIGNED_DECL_LOCAL
unsigned int align = symtab_node::get (decl)->definition_alignment ();
ASM_OUTPUT_ALIGNED_DECL_LOCAL (asm_out_file, decl, name,
size, align);
return true;
#elif defined ASM_OUTPUT_ALIGNED_LOCAL
unsigned int align = symtab_node::get (decl)->definition_alignment ();
ASM_OUTPUT_ALIGNED_LOCAL (asm_out_file, name, size, align);
return true;
#else
ASM_OUTPUT_LOCAL (asm_out_file, name, size, rounded);
return false;
#endif
}
/* A noswitch_section_callback for bss_noswitch_section. */
#if defined ASM_OUTPUT_ALIGNED_BSS
static bool
emit_bss (tree decl ATTRIBUTE_UNUSED,
const char *name ATTRIBUTE_UNUSED,
unsigned HOST_WIDE_INT size ATTRIBUTE_UNUSED,
unsigned HOST_WIDE_INT rounded ATTRIBUTE_UNUSED)
{
#if defined ASM_OUTPUT_ALIGNED_BSS
ASM_OUTPUT_ALIGNED_BSS (asm_out_file, decl, name, size,
get_variable_align (decl));
return true;
#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
ASM_OUTPUT_ALIGNED_DECL_COMMON (asm_out_file, decl, name,
size, get_variable_align (decl));
return true;
#elif defined ASM_OUTPUT_ALIGNED_COMMON
ASM_OUTPUT_ALIGNED_COMMON (asm_out_file, name, size,
get_variable_align (decl));
return true;
#else
ASM_OUTPUT_COMMON (asm_out_file, name, size, rounded);
return false;
#endif
}
/* A noswitch_section_callback for tls_comm_section. */
static bool
emit_tls_common (tree decl ATTRIBUTE_UNUSED,
const char *name ATTRIBUTE_UNUSED,
unsigned HOST_WIDE_INT size ATTRIBUTE_UNUSED,
unsigned HOST_WIDE_INT rounded ATTRIBUTE_UNUSED)
{
#ifdef ASM_OUTPUT_TLS_COMMON
ASM_OUTPUT_TLS_COMMON (asm_out_file, decl, name, size);
return true;
#else
sorry ("thread-local COMMON data not implemented");
return true;
#endif
}
/* Assemble DECL given that it belongs in SECTION_NOSWITCH section SECT.
NAME is the name of DECL's SYMBOL_REF. */
static void
assemble_noswitch_variable (tree decl, const char *name, section *sect,
unsigned int align)
{
unsigned HOST_WIDE_INT size, rounded;
size = tree_to_uhwi (DECL_SIZE_UNIT (decl));
rounded = size;
if ((flag_sanitize & SANITIZE_ADDRESS) && asan_protect_global (decl))
size += asan_red_zone_size (size);
/* Don't allocate zero bytes of common,
since that means "undefined external" in the linker. */
if (size == 0)
rounded = 1;
/* Round size up to multiple of BIGGEST_ALIGNMENT bits
so that each uninitialized object starts on such a boundary. */
rounded += (BIGGEST_ALIGNMENT / BITS_PER_UNIT) - 1;
rounded = (rounded / (BIGGEST_ALIGNMENT / BITS_PER_UNIT)
* (BIGGEST_ALIGNMENT / BITS_PER_UNIT));
if (!sect->noswitch.callback (decl, name, size, rounded)
&& (unsigned HOST_WIDE_INT) (align / BITS_PER_UNIT) > rounded)
error ("requested alignment for %q+D is greater than "
"implemented alignment of %wu", decl, rounded);
}
/* A subroutine of assemble_variable. Output the label and contents of
DECL, whose address is a SYMBOL_REF with name NAME. DONT_OUTPUT_DATA
is as for assemble_variable. */
static void
assemble_variable_contents (tree decl, const char *name,
bool dont_output_data)
{
/* Do any machine/system dependent processing of the object. */
#ifdef ASM_DECLARE_OBJECT_NAME
last_assemble_variable_decl = decl;
ASM_DECLARE_OBJECT_NAME (asm_out_file, name, decl);
#else
/* Standard thing is just output label for the object. */
ASM_OUTPUT_LABEL (asm_out_file, name);
#endif /* ASM_DECLARE_OBJECT_NAME */
if (!dont_output_data)
{
/* Caller is supposed to use varpool_get_constructor when it wants
to output the body. */
gcc_assert (!in_lto_p || DECL_INITIAL (decl) != error_mark_node);
if (DECL_INITIAL (decl)
&& DECL_INITIAL (decl) != error_mark_node
&& !initializer_zerop (DECL_INITIAL (decl)))
/* Output the actual data. */
output_constant (DECL_INITIAL (decl),
tree_to_uhwi (DECL_SIZE_UNIT (decl)),
get_variable_align (decl));
else
/* Leave space for it. */
assemble_zeros (tree_to_uhwi (DECL_SIZE_UNIT (decl)));
targetm.asm_out.decl_end ();
}
}
/* Write out assembly for the variable DECL, which is not defined in
the current translation unit. */
void
assemble_undefined_decl (tree decl)
{
const char *name = XSTR (XEXP (DECL_RTL (decl), 0), 0);
targetm.asm_out.assemble_undefined_decl (asm_out_file, name, decl);
}
/* Assemble everything that is needed for a variable or function declaration.
Not used for automatic variables, and not used for function definitions.
Should not be called for variables of incomplete structure type.
TOP_LEVEL is nonzero if this variable has file scope.
AT_END is nonzero if this is the special handling, at end of compilation,
to define things that have had only tentative definitions.
DONT_OUTPUT_DATA if nonzero means don't actually output the
initial value (that will be done by the caller). */
void
assemble_variable (tree decl, int top_level ATTRIBUTE_UNUSED,
int at_end ATTRIBUTE_UNUSED, int dont_output_data)
{
const char *name;
rtx decl_rtl, symbol;
section *sect;
unsigned int align;
bool asan_protected = false;
/* This function is supposed to handle VARIABLES. Ensure we have one. */
gcc_assert (TREE_CODE (decl) == VAR_DECL);
/* Emulated TLS had better not get this far. */
gcc_checking_assert (targetm.have_tls || !DECL_THREAD_LOCAL_P (decl));
last_assemble_variable_decl = 0;
/* Normally no need to say anything here for external references,
since assemble_external is called by the language-specific code
when a declaration is first seen. */
if (DECL_EXTERNAL (decl))
return;
/* Do nothing for global register variables. */
if (DECL_RTL_SET_P (decl) && REG_P (DECL_RTL (decl)))
{
TREE_ASM_WRITTEN (decl) = 1;
return;
}
/* If type was incomplete when the variable was declared,
see if it is complete now. */
if (DECL_SIZE (decl) == 0)
layout_decl (decl, 0);
/* Still incomplete => don't allocate it; treat the tentative defn
(which is what it must have been) as an `extern' reference. */
if (!dont_output_data && DECL_SIZE (decl) == 0)
{
error ("storage size of %q+D isn%'t known", decl);
TREE_ASM_WRITTEN (decl) = 1;
return;
}
/* The first declaration of a variable that comes through this function
decides whether it is global (in C, has external linkage)
or local (in C, has internal linkage). So do nothing more
if this function has already run. */
if (TREE_ASM_WRITTEN (decl))
return;
/* Make sure targetm.encode_section_info is invoked before we set
ASM_WRITTEN. */
decl_rtl = DECL_RTL (decl);
TREE_ASM_WRITTEN (decl) = 1;
/* Do no output if -fsyntax-only. */
if (flag_syntax_only)
return;
if (! dont_output_data
&& ! valid_constant_size_p (DECL_SIZE_UNIT (decl)))
{
error ("size of variable %q+D is too large", decl);
return;
}
gcc_assert (MEM_P (decl_rtl));
gcc_assert (GET_CODE (XEXP (decl_rtl, 0)) == SYMBOL_REF);
symbol = XEXP (decl_rtl, 0);
/* If this symbol belongs to the tree constant pool, output the constant
if it hasn't already been written. */
if (TREE_CONSTANT_POOL_ADDRESS_P (symbol))
{
tree decl = SYMBOL_REF_DECL (symbol);
if (!TREE_ASM_WRITTEN (DECL_INITIAL (decl)))
output_constant_def_contents (symbol);
return;
}
app_disable ();
name = XSTR (symbol, 0);
if (TREE_PUBLIC (decl) && DECL_NAME (decl))
notice_global_symbol (decl);
/* Compute the alignment of this data. */
align_variable (decl, dont_output_data);
if ((flag_sanitize & SANITIZE_ADDRESS)
&& asan_protect_global (decl))
{
asan_protected = true;
DECL_ALIGN (decl) = MAX (DECL_ALIGN (decl),
ASAN_RED_ZONE_SIZE * BITS_PER_UNIT);
}
set_mem_align (decl_rtl, DECL_ALIGN (decl));
align = get_variable_align (decl);
if (TREE_PUBLIC (decl))
maybe_assemble_visibility (decl);
if (DECL_PRESERVE_P (decl))
targetm.asm_out.mark_decl_preserved (name);
/* First make the assembler name(s) global if appropriate. */
sect = get_variable_section (decl, false);
if (TREE_PUBLIC (decl)
&& (sect->common.flags & SECTION_COMMON) == 0)
globalize_decl (decl);
/* Output any data that we will need to use the address of. */
if (DECL_INITIAL (decl) && DECL_INITIAL (decl) != error_mark_node)
output_addressed_constants (DECL_INITIAL (decl));
/* dbxout.c needs to know this. */
if (sect && (sect->common.flags & SECTION_CODE) != 0)
DECL_IN_TEXT_SECTION (decl) = 1;
/* If the decl is part of an object_block, make sure that the decl
has been positioned within its block, but do not write out its
definition yet. output_object_blocks will do that later. */
if (SYMBOL_REF_HAS_BLOCK_INFO_P (symbol) && SYMBOL_REF_BLOCK (symbol))
{
gcc_assert (!dont_output_data);
place_block_symbol (symbol);
}
else if (SECTION_STYLE (sect) == SECTION_NOSWITCH)
assemble_noswitch_variable (decl, name, sect, align);
else
{
/* 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))
{
#if defined (OBJECT_FORMAT_ELF)
targetm.asm_out.named_section (sect->named.name,
sect->named.common.flags
| SECTION_LINKONCE,
DECL_NAME (decl));
in_section = sect;
#elif defined (TARGET_PECOFF)
/* Neither OBJECT_FORMAT_PE, nor OBJECT_FORMAT_COFF is set here.
Therefore the following check is used.
In case a the target is PE or COFF a comdat group section
is created, e.g. .vtable_map_vars$foo. The linker places
everything in .vtable_map_vars at the end.
A fix could be made in
gcc/config/i386/winnt.c: i386_pe_unique_section. */
if (TARGET_PECOFF)
{
char *name;
if (TREE_CODE (DECL_NAME (decl)) == IDENTIFIER_NODE)
name = ACONCAT ((sect->named.name, "$",
IDENTIFIER_POINTER (DECL_NAME (decl)), NULL));
else
name = ACONCAT ((sect->named.name, "$",
IDENTIFIER_POINTER (DECL_COMDAT_GROUP (DECL_NAME (decl))),
NULL));
targetm.asm_out.named_section (name,
sect->named.common.flags
| SECTION_LINKONCE,
DECL_NAME (decl));
in_section = sect;
}
#else
switch_to_section (sect);
#endif
}
else
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)
{
unsigned HOST_WIDE_INT int size
= tree_to_uhwi (DECL_SIZE_UNIT (decl));
assemble_zeros (asan_red_zone_size (size));
}
}
}
/* Given a function declaration (FN_DECL), this function assembles the
function into the .preinit_array section. */
void
assemble_vtv_preinit_initializer (tree fn_decl)
{
section *sect;
unsigned flags = SECTION_WRITE;
rtx symbol = XEXP (DECL_RTL (fn_decl), 0);
flags |= SECTION_NOTYPE;
sect = get_section (".preinit_array", flags, fn_decl);
switch_to_section (sect);
assemble_addr_to_section (symbol, sect);
}
/* Return 1 if type TYPE contains any pointers. */
static int
contains_pointers_p (tree type)
{
switch (TREE_CODE (type))
{
case POINTER_TYPE:
case REFERENCE_TYPE:
/* I'm not sure whether OFFSET_TYPE needs this treatment,
so I'll play safe and return 1. */
case OFFSET_TYPE:
return 1;
case RECORD_TYPE:
case UNION_TYPE:
case QUAL_UNION_TYPE:
{
tree fields;
/* For a type that has fields, see if the fields have pointers. */
for (fields = TYPE_FIELDS (type); fields; fields = DECL_CHAIN (fields))
if (TREE_CODE (fields) == FIELD_DECL
&& contains_pointers_p (TREE_TYPE (fields)))
return 1;
return 0;
}
case ARRAY_TYPE:
/* An array type contains pointers if its element type does. */
return contains_pointers_p (TREE_TYPE (type));
default:
return 0;
}
}
/* 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
/* Some targets delay some output to final using TARGET_ASM_FILE_END.
As a result, assemble_external can be called after the list of externals
is processed and the pointer set destroyed. */
static bool pending_assemble_externals_processed;
/* Avoid O(external_decls**2) lookups in the pending_assemble_externals
TREE_LIST in assemble_external. */
static hash_set<tree> *pending_assemble_externals_set;
/* True if DECL is a function decl for which no out-of-line copy exists.
It is assumed that DECL's assembler name has been set. */
static bool
incorporeal_function_p (tree decl)
{
if (TREE_CODE (decl) == FUNCTION_DECL && DECL_BUILT_IN (decl))
{
const char *name;
if (DECL_BUILT_IN_CLASS (decl) == BUILT_IN_NORMAL
&& (DECL_FUNCTION_CODE (decl) == BUILT_IN_ALLOCA
|| DECL_FUNCTION_CODE (decl) == BUILT_IN_ALLOCA_WITH_ALIGN))
return true;
name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl));
/* Atomic or sync builtins which have survived this far will be
resolved externally and therefore are not incorporeal. */
if (strncmp (name, "__builtin_", 10) == 0)
return true;
}
return false;
}
/* Actually do the tests to determine if this is necessary, and invoke
ASM_OUTPUT_EXTERNAL. */
static void
assemble_external_real (tree decl)
{
rtx rtl = DECL_RTL (decl);
if (MEM_P (rtl) && GET_CODE (XEXP (rtl, 0)) == SYMBOL_REF
&& !SYMBOL_REF_USED (XEXP (rtl, 0))
&& !incorporeal_function_p (decl))
{
/* Some systems do require some output. */
SYMBOL_REF_USED (XEXP (rtl, 0)) = 1;
ASM_OUTPUT_EXTERNAL (asm_out_file, decl, XSTR (XEXP (rtl, 0), 0));
}
}
#endif
void
process_pending_assemble_externals (void)
{
#ifdef ASM_OUTPUT_EXTERNAL
tree list;
for (list = pending_assemble_externals; list; list = TREE_CHAIN (list))
assemble_external_real (TREE_VALUE (list));
pending_assemble_externals = 0;
pending_assemble_externals_processed = true;
delete pending_assemble_externals_set;
#endif
}
/* This TREE_LIST contains any weak symbol declarations waiting
to be emitted. */
static GTY(()) tree weak_decls;
/* Output something to declare an external symbol to the assembler,
and qualifiers such as weakness. (Most assemblers don't need
extern declaration, so we normally output nothing.) Do nothing if
DECL is not external. */
void
assemble_external (tree decl ATTRIBUTE_UNUSED)
{
/* Make sure that the ASM_OUT_FILE is open.
If it's not, we should not be calling this function. */
gcc_assert (asm_out_file);
/* In a perfect world, the following condition would be true.
Sadly, the Java and Go front ends emit assembly *from the front end*,
bypassing the call graph. See PR52739. Fix before GCC 4.8. */
#if 0
/* This function should only be called if we are expanding, or have
expanded, to RTL.
Ideally, only final.c would be calling this function, but it is
not clear whether that would break things somehow. See PR 17982
for further discussion. */
gcc_assert (state == EXPANSION
|| state == FINISHED);
#endif
if (!DECL_P (decl) || !DECL_EXTERNAL (decl) || !TREE_PUBLIC (decl))
return;
/* We want to output annotation for weak and external symbols at
very last to check if they are references or not. */
if (TARGET_SUPPORTS_WEAK
&& DECL_WEAK (decl)
/* TREE_STATIC is a weird and abused creature which is not
generally the right test for whether an entity has been
locally emitted, inlined or otherwise not-really-extern, but
for declarations that can be weak, it happens to be
match. */
&& !TREE_STATIC (decl)
&& lookup_attribute ("weak", DECL_ATTRIBUTES (decl))
&& value_member (decl, weak_decls) == NULL_TREE)
weak_decls = tree_cons (NULL, decl, weak_decls);
#ifdef ASM_OUTPUT_EXTERNAL
if (pending_assemble_externals_processed)
{
assemble_external_real (decl);
return;
}
if (! pending_assemble_externals_set->add (decl))
pending_assemble_externals = tree_cons (NULL, decl,
pending_assemble_externals);
#endif
}
/* Similar, for calling a library function FUN. */
void
assemble_external_libcall (rtx fun)
{
/* Declare library function name external when first used, if nec. */
if (! SYMBOL_REF_USED (fun))
{
SYMBOL_REF_USED (fun) = 1;
targetm.asm_out.external_libcall (fun);
}
}
/* Assemble a label named NAME. */
void
assemble_label (FILE *file, const char *name)
{
ASM_OUTPUT_LABEL (file, name);
}
/* Set the symbol_referenced flag for ID. */
void
mark_referenced (tree id)
{
TREE_SYMBOL_REFERENCED (id) = 1;
}
/* Set the symbol_referenced flag for DECL and notify callgraph. */
void
mark_decl_referenced (tree decl)
{
if (TREE_CODE (decl) == FUNCTION_DECL)
{
/* Extern inline functions don't become needed when referenced.
If we know a method will be emitted in other TU and no new
functions can be marked reachable, just use the external
definition. */
struct cgraph_node *node = cgraph_node::get_create (decl);
if (!DECL_EXTERNAL (decl)
&& !node->definition)
node->mark_force_output ();
}
else if (TREE_CODE (decl) == VAR_DECL)
{
varpool_node *node = varpool_node::get_create (decl);
/* C++ frontend use mark_decl_references to force COMDAT variables
to be output that might appear dead otherwise. */
node->force_output = true;
}
/* else do nothing - we can get various sorts of CST nodes here,
which do not need to be marked. */
}
/* Output to FILE (an assembly file) a reference to NAME. If NAME
starts with a *, the rest of NAME is output verbatim. Otherwise
NAME is transformed in a target-specific way (usually by the
addition of an underscore). */
void
assemble_name_raw (FILE *file, const char *name)
{
if (name[0] == '*')
fputs (&name[1], file);
else
ASM_OUTPUT_LABELREF (file, name);
}
/* 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)
{
const char *real_name;
tree id;
real_name = targetm.strip_name_encoding (name);
id = maybe_get_identifier (real_name);
if (id)
{
tree id_orig = id;
mark_referenced (id);
ultimate_transparent_alias_target (&id);
if (id != id_orig)
name = IDENTIFIER_POINTER (id);
gcc_assert (! TREE_CHAIN (id));
}
assemble_name_raw (file, name);
}
/* Allocate SIZE bytes writable static space with a gensym name
and return an RTX to refer to its address. */
rtx
assemble_static_space (unsigned HOST_WIDE_INT size)
{
char name[12];
const char *namestring;
rtx x;
ASM_GENERATE_INTERNAL_LABEL (name, "LF", const_labelno);
++const_labelno;
namestring = ggc_strdup (name);
x = gen_rtx_SYMBOL_REF (Pmode, namestring);
SYMBOL_REF_FLAGS (x) = SYMBOL_FLAG_LOCAL;
#ifdef ASM_OUTPUT_ALIGNED_DECL_LOCAL
ASM_OUTPUT_ALIGNED_DECL_LOCAL (asm_out_file, NULL_TREE, name, size,
BIGGEST_ALIGNMENT);
#else
#ifdef ASM_OUTPUT_ALIGNED_LOCAL
ASM_OUTPUT_ALIGNED_LOCAL (asm_out_file, name, size, BIGGEST_ALIGNMENT);
#else
{
/* Round size up to multiple of BIGGEST_ALIGNMENT bits
so that each uninitialized object starts on such a boundary. */
/* Variable `rounded' might or might not be used in ASM_OUTPUT_LOCAL. */
unsigned HOST_WIDE_INT rounded ATTRIBUTE_UNUSED
= ((size + (BIGGEST_ALIGNMENT / BITS_PER_UNIT) - 1)
/ (BIGGEST_ALIGNMENT / BITS_PER_UNIT)
* (BIGGEST_ALIGNMENT / BITS_PER_UNIT));
ASM_OUTPUT_LOCAL (asm_out_file, name, size, rounded);
}
#endif
#endif
return x;
}
/* Assemble the static constant template for function entry trampolines.
This is done at most once per compilation.
Returns an RTX for the address of the template. */
static GTY(()) rtx initial_trampoline;
rtx
assemble_trampoline_template (void)
{
char label[256];
const char *name;
int align;
rtx symbol;
gcc_assert (targetm.asm_out.trampoline_template != NULL);
if (initial_trampoline)
return initial_trampoline;
/* By default, put trampoline templates in read-only data section. */
#ifdef TRAMPOLINE_SECTION
switch_to_section (TRAMPOLINE_SECTION);
#else
switch_to_section (readonly_data_section);
#endif
/* Write the assembler code to define one. */
align = floor_log2 (TRAMPOLINE_ALIGNMENT / BITS_PER_UNIT);
if (align > 0)
ASM_OUTPUT_ALIGN (asm_out_file, align);
targetm.asm_out.internal_label (asm_out_file, "LTRAMP", 0);
targetm.asm_out.trampoline_template (asm_out_file);
/* Record the rtl to refer to it. */
ASM_GENERATE_INTERNAL_LABEL (label, "LTRAMP", 0);
name = ggc_strdup (label);
symbol = gen_rtx_SYMBOL_REF (Pmode, name);
SYMBOL_REF_FLAGS (symbol) = SYMBOL_FLAG_LOCAL;
initial_trampoline = gen_const_mem (BLKmode, symbol);
set_mem_align (initial_trampoline, TRAMPOLINE_ALIGNMENT);
set_mem_size (initial_trampoline, TRAMPOLINE_SIZE);
return initial_trampoline;
}
/* A and B are either alignments or offsets. Return the minimum alignment
that may be assumed after adding the two together. */
static inline unsigned
min_align (unsigned int a, unsigned int b)
{
return (a | b) & -(a | b);
}
/* Return the assembler directive for creating a given kind of integer
object. SIZE is the number of bytes in the object and ALIGNED_P
indicates whether it is known to be aligned. Return NULL if the
assembly dialect has no such directive.
The returned string should be printed at the start of a new line and
be followed immediately by the object's initial value. */
const char *
integer_asm_op (int size, int aligned_p)
{
struct asm_int_op *ops;
if (aligned_p)
ops = &targetm.asm_out.aligned_op;
else
ops = &targetm.asm_out.unaligned_op;
switch (size)
{
case 1:
return targetm.asm_out.byte_op;
case 2:
return ops->hi;
case 4:
return ops->si;
case 8:
return ops->di;
case 16:
return ops->ti;
default:
return NULL;
}
}
/* Use directive OP to assemble an integer object X. Print OP at the
start of the line, followed immediately by the value of X. */
void
assemble_integer_with_op (const char *op, rtx x)
{
fputs (op, asm_out_file);
output_addr_const (asm_out_file, x);
fputc ('\n', asm_out_file);
}
/* The default implementation of the asm_out.integer target hook. */
bool
default_assemble_integer (rtx x ATTRIBUTE_UNUSED,
unsigned int size ATTRIBUTE_UNUSED,
int aligned_p ATTRIBUTE_UNUSED)
{
const char *op = integer_asm_op (size, aligned_p);
/* Avoid GAS bugs for large values. Specifically negative values whose
absolute value fits in a bfd_vma, but not in a bfd_signed_vma. */
if (size > UNITS_PER_WORD && size > POINTER_SIZE_UNITS)
return false;
return op && (assemble_integer_with_op (op, x), true);
}
/* Assemble the integer constant X into an object of SIZE bytes. ALIGN is
the alignment of the integer in bits. Return 1 if we were able to output
the constant, otherwise 0. We must be able to output the constant,
if FORCE is nonzero. */
bool
assemble_integer (rtx x, unsigned int size, unsigned int align, int force)
{
int aligned_p;
aligned_p = (align >= MIN (size * BITS_PER_UNIT, BIGGEST_ALIGNMENT));
/* See if the target hook can handle this kind of object. */
if (targetm.asm_out.integer (x, size, aligned_p))
return true;
/* If the object is a multi-byte one, try splitting it up. Split
it into words it if is multi-word, otherwise split it into bytes. */
if (size > 1)
{
machine_mode omode, imode;
unsigned int subalign;
unsigned int subsize, i;
enum mode_class mclass;
subsize = size > UNITS_PER_WORD? UNITS_PER_WORD : 1;
subalign = MIN (align, subsize * BITS_PER_UNIT);
if (GET_CODE (x) == CONST_FIXED)
mclass = GET_MODE_CLASS (GET_MODE (x));
else
mclass = MODE_INT;
omode = mode_for_size (subsize * BITS_PER_UNIT, mclass, 0);
imode = mode_for_size (size * BITS_PER_UNIT, mclass, 0);
for (i = 0; i < size; i += subsize)
{
rtx partial = simplify_subreg (omode, x, imode, i);
if (!partial || !assemble_integer (partial, subsize, subalign, 0))
break;
}
if (i == size)
return true;
/* If we've printed some of it, but not all of it, there's no going
back now. */
gcc_assert (!i);
}
gcc_assert (!force);
return false;
}
void
assemble_real (REAL_VALUE_TYPE d, machine_mode mode, unsigned int align)
{
long data[4] = {0, 0, 0, 0};
int i;
int bitsize, nelts, nunits, units_per;
/* This is hairy. We have a quantity of known size. real_to_target
will put it into an array of *host* longs, 32 bits per element
(even if long is more than 32 bits). We need to determine the
number of array elements that are occupied (nelts) and the number
of *target* min-addressable units that will be occupied in the
object file (nunits). We cannot assume that 32 divides the
mode's bitsize (size * BITS_PER_UNIT) evenly.
size * BITS_PER_UNIT is used here to make sure that padding bits
(which might appear at either end of the value; real_to_target
will include the padding bits in its output array) are included. */
nunits = GET_MODE_SIZE (mode);
bitsize = nunits * BITS_PER_UNIT;
nelts = CEIL (bitsize, 32);
units_per = 32 / BITS_PER_UNIT;
real_to_target (data, &d, mode);
/* Put out the first word with the specified alignment. */
assemble_integer (GEN_INT (data[0]), MIN (nunits, units_per), align, 1);
nunits -= units_per;
/* Subsequent words need only 32-bit alignment. */
align = min_align (align, 32);
for (i = 1; i < nelts; i++)
{
assemble_integer (GEN_INT (data[i]), MIN (nunits, units_per), align, 1);
nunits -= units_per;
}
}
/* Given an expression EXP with a constant value,
reduce it to the sum of an assembler symbol and an integer.
Store them both in the structure *VALUE.
EXP must be reducible. */
struct addr_const {
rtx base;
HOST_WIDE_INT offset;
};
static void
decode_addr_const (tree exp, struct addr_const *value)
{
tree target = TREE_OPERAND (exp, 0);
int offset = 0;
rtx x;
while (1)
{
if (TREE_CODE (target) == COMPONENT_REF
&& tree_fits_shwi_p (byte_position (TREE_OPERAND (target, 1))))
{
offset += int_byte_position (TREE_OPERAND (target, 1));
target = TREE_OPERAND (target, 0);
}
else if (TREE_CODE (target) == ARRAY_REF
|| TREE_CODE (target) == ARRAY_RANGE_REF)
{
offset += (tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (target)))
* tree_to_shwi (TREE_OPERAND (target, 1)));
target = TREE_OPERAND (target, 0);
}
else if (TREE_CODE (target) == MEM_REF
&& TREE_CODE (TREE_OPERAND (target, 0)) == ADDR_EXPR)
{
offset += mem_ref_offset (target).to_short_addr ();
target = TREE_OPERAND (TREE_OPERAND (target, 0), 0);
}
else if (TREE_CODE (target) == INDIRECT_REF
&& TREE_CODE (TREE_OPERAND (target, 0)) == NOP_EXPR
&& TREE_CODE (TREE_OPERAND (TREE_OPERAND (target, 0), 0))
== ADDR_EXPR)
target = TREE_OPERAND (TREE_OPERAND (TREE_OPERAND (target, 0), 0), 0);
else
break;
}
switch (TREE_CODE (target))
{
case VAR_DECL:
case FUNCTION_DECL:
x = DECL_RTL (target);
break;
case LABEL_DECL:
x = gen_rtx_MEM (FUNCTION_MODE,
gen_rtx_LABEL_REF (Pmode, force_label_rtx (target)));
break;
case REAL_CST:
case FIXED_CST:
case STRING_CST:
case COMPLEX_CST:
case CONSTRUCTOR:
case INTEGER_CST:
x = output_constant_def (target, 1);
break;
default:
gcc_unreachable ();
}
gcc_assert (MEM_P (x));
x = XEXP (x, 0);
value->base = x;
value->offset = offset;
}
static GTY(()) hash_table<tree_descriptor_hasher> *const_desc_htab;
static void maybe_output_constant_def_contents (struct constant_descriptor_tree *, int);
/* Constant pool accessor function. */
hash_table<tree_descriptor_hasher> *
constant_pool_htab (void)
{
return const_desc_htab;
}
/* Compute a hash code for a constant expression. */
hashval_t
tree_descriptor_hasher::hash (constant_descriptor_tree *ptr)
{
return ptr->hash;
}
static hashval_t
const_hash_1 (const tree exp)
{
const char *p;
hashval_t hi;
int len, i;
enum tree_code code = TREE_CODE (exp);
/* Either set P and LEN to the address and len of something to hash and
exit the switch or return a value. */
switch (code)
{
case INTEGER_CST:
p = (char *) &TREE_INT_CST_ELT (exp, 0);
len = TREE_INT_CST_NUNITS (exp) * sizeof (HOST_WIDE_INT);
break;
case REAL_CST:
return real_hash (TREE_REAL_CST_PTR (exp));
case FIXED_CST:
return fixed_hash (TREE_FIXED_CST_PTR (exp));
case STRING_CST:
p = TREE_STRING_POINTER (exp);
len = TREE_STRING_LENGTH (exp);
break;
case COMPLEX_CST:
return (const_hash_1 (TREE_REALPART (exp)) * 5
+ const_hash_1 (TREE_IMAGPART (exp)));
case VECTOR_CST:
{
unsigned i;
hi = 7 + VECTOR_CST_NELTS (exp);
for (i = 0; i < VECTOR_CST_NELTS (exp); ++i)
hi = hi * 563 + const_hash_1 (VECTOR_CST_ELT (exp, i));
return hi;
}
case CONSTRUCTOR:
{
unsigned HOST_WIDE_INT idx;
tree value;
hi = 5 + int_size_in_bytes (TREE_TYPE (exp));
FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (exp), idx, value)
if (value)
hi = hi * 603 + const_hash_1 (value);
return hi;
}
case ADDR_EXPR:
case FDESC_EXPR:
{
struct addr_const value;
decode_addr_const (exp, &value);
switch (GET_CODE (value.base))
{
case SYMBOL_REF:
/* Don't hash the address of the SYMBOL_REF;
only use the offset and the symbol name. */
hi = value.offset;
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 (LABEL_REF_LABEL (value.base)) * 13);
break;
default:
gcc_unreachable ();
}
}
return hi;
case PLUS_EXPR:
case POINTER_PLUS_EXPR:
case MINUS_EXPR:
return (const_hash_1 (TREE_OPERAND (exp, 0)) * 9
+ const_hash_1 (TREE_OPERAND (exp, 1)));
CASE_CONVERT:
return const_hash_1 (TREE_OPERAND (exp, 0)) * 7 + 2;
default:
/* A language specific constant. Just hash the code. */
return code;
}
/* Compute hashing function. */
hi = len;
for (i = 0; i < len; i++)
hi = ((hi * 613) + (unsigned) (p[i]));
return hi;
}
/* Wrapper of compare_constant, for the htab interface. */
bool
tree_descriptor_hasher::equal (constant_descriptor_tree *c1,
constant_descriptor_tree *c2)
{
if (c1->hash != c2->hash)
return 0;
return compare_constant (c1->value, c2->value);
}
/* Compare t1 and t2, and return 1 only if they are known to result in
the same bit pattern on output. */
static int
compare_constant (const tree t1, const tree t2)
{
enum tree_code typecode;
if (t1 == NULL_TREE)
return t2 == NULL_TREE;
if (t2 == NULL_TREE)
return 0;
if (TREE_CODE (t1) != TREE_CODE (t2))
return 0;
switch (TREE_CODE (t1))
{
case INTEGER_CST:
/* Integer constants are the same only if the same width of type. */
if (TYPE_PRECISION (TREE_TYPE (t1)) != TYPE_PRECISION (TREE_TYPE (t2)))
return 0;
if (TYPE_MODE (TREE_TYPE (t1)) != TYPE_MODE (TREE_TYPE (t2)))
return 0;
return tree_int_cst_equal (t1, t2);
case REAL_CST:
/* Real constants are the same only if the same width of type. */
if (TYPE_PRECISION (TREE_TYPE (t1)) != TYPE_PRECISION (TREE_TYPE (t2)))
return 0;
return REAL_VALUES_IDENTICAL (TREE_REAL_CST (t1), TREE_REAL_CST (t2));
case FIXED_CST:
/* Fixed constants are the same only if the same width of type. */
if (TYPE_PRECISION (TREE_TYPE (t1)) != TYPE_PRECISION (TREE_TYPE (t2)))
return 0;
return FIXED_VALUES_IDENTICAL (TREE_FIXED_CST (t1), TREE_FIXED_CST (t2));
case STRING_CST:
if (TYPE_MODE (TREE_TYPE (t1)) != TYPE_MODE (TREE_TYPE (t2)))
return 0;
return (TREE_STRING_LENGTH (t1) == TREE_STRING_LENGTH (t2)
&& ! memcmp (TREE_STRING_POINTER (t1), TREE_STRING_POINTER (t2),
TREE_STRING_LENGTH (t1)));
case COMPLEX_CST:
return (compare_constant (TREE_REALPART (t1), TREE_REALPART (t2))
&& compare_constant (TREE_IMAGPART (t1), TREE_IMAGPART (t2)));
case VECTOR_CST:
{
unsigned i;
if (VECTOR_CST_NELTS (t1) != VECTOR_CST_NELTS (t2))
return 0;
for (i = 0; i < VECTOR_CST_NELTS (t1); ++i)
if (!compare_constant (VECTOR_CST_ELT (t1, i),
VECTOR_CST_ELT (t2, i)))
return 0;
return 1;
}
case CONSTRUCTOR:
{
vec<constructor_elt, va_gc> *v1, *v2;
unsigned HOST_WIDE_INT idx;
typecode = TREE_CODE (TREE_TYPE (t1));
if (typecode != TREE_CODE (TREE_TYPE (t2)))
return 0;
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;
}
v1 = CONSTRUCTOR_ELTS (t1);
v2 = CONSTRUCTOR_ELTS (t2);
if (vec_safe_length (v1) != vec_safe_length (v2))
return 0;
for (idx = 0; idx < vec_safe_length (v1); ++idx)
{
constructor_elt *c1 = &(*v1)[idx];
constructor_elt *c2 = &(*v2)[idx];
/* Check that each value is the same... */
if (!compare_constant (c1->value, c2->value))
return 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 1;
}
case ADDR_EXPR:
case FDESC_EXPR:
{
struct addr_const value1, value2;
enum rtx_code code;
int ret;
decode_addr_const (t1, &value1);
decode_addr_const (t2, &value2);
if (value1.offset != value2.offset)
return 0;
code = GET_CODE (value1.base);
if (code != GET_CODE (value2.base))
return 0;
switch (code)
{
case SYMBOL_REF:
ret = (strcmp (XSTR (value1.base, 0), XSTR (value2.base, 0)) == 0);
break;
case LABEL_REF:
ret = (CODE_LABEL_NUMBER (LABEL_REF_LABEL (value1.base))
== CODE_LABEL_NUMBER (LABEL_REF_LABEL (value2.base)));
break;
default:
gcc_unreachable ();
}
return ret;
}
case PLUS_EXPR:
case POINTER_PLUS_EXPR:
case MINUS_EXPR:
case RANGE_EXPR:
return (compare_constant (TREE_OPERAND (t1, 0), TREE_OPERAND (t2, 0))
&& compare_constant (TREE_OPERAND (t1, 1), TREE_OPERAND (t2, 1)));
CASE_CONVERT:
case VIEW_CONVERT_EXPR:
return compare_constant (TREE_OPERAND (t1, 0), TREE_OPERAND (t2, 0));
default:
return 0;
}
gcc_unreachable ();
}
/* Return the section into which constant EXP should be placed. */
static section *
get_constant_section (tree exp, unsigned int align)
{
return targetm.asm_out.select_section (exp,
compute_reloc_for_constant (exp),
align);
}
/* Return the size of constant EXP in bytes. */