/* Output variables, constants and external declarations, for GNU compiler.
Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997,
- 1998, 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+ 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
+ Free Software Foundation, Inc.
This file is part of GCC.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING. If not, write to the Free
-Software Foundation, 59 Temple Place - Suite 330, Boston, MA
-02111-1307, USA. */
+Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301, USA. */
/* This file handles generation of all the assembler code
#include "toplev.h"
#include "hashtab.h"
#include "c-pragma.h"
-#include "c-tree.h"
#include "ggc.h"
#include "langhooks.h"
#include "tm_p.h"
#include "debug.h"
#include "target.h"
+#include "tree-mudflap.h"
+#include "cgraph.h"
+#include "cfglayout.h"
+#include "basic-block.h"
#ifdef XCOFF_DEBUGGING_INFO
#include "xcoffout.h" /* Needed for external data
declarations for e.g. AIX 4.x. */
#endif
-#ifndef TRAMPOLINE_ALIGNMENT
-#define TRAMPOLINE_ALIGNMENT FUNCTION_BOUNDARY
-#endif
-
-#ifndef ASM_STABS_OP
-#define ASM_STABS_OP "\t.stabs\t"
-#endif
-
/* The (assembler) name of the first globally-visible object output. */
+extern GTY(()) const char *first_global_object_name;
+extern GTY(()) const char *weak_global_object_name;
+
const char *first_global_object_name;
const char *weak_global_object_name;
struct addr_const;
struct constant_descriptor_rtx;
-struct rtx_const;
-struct pool_constant;
-
-#define MAX_RTX_HASH_TABLE 61
+struct rtx_constant_pool;
struct varasm_status GTY(())
{
- /* Hash facility for making memory-constants
- from constant rtl-expressions. It is used on RISC machines
- where immediate integer arguments and constant addresses are restricted
- so that such constants must be stored in memory.
-
- This pool of constants is reinitialized for each function
- so each function gets its own constants-pool that comes right before
- it. */
- struct constant_descriptor_rtx ** GTY ((length ("MAX_RTX_HASH_TABLE")))
- x_const_rtx_hash_table;
- struct pool_constant ** GTY ((length ("MAX_RTX_HASH_TABLE")))
- x_const_rtx_sym_hash_table;
-
- /* Pointers to first and last constant in pool. */
- struct pool_constant *x_first_pool;
- struct pool_constant *x_last_pool;
-
- /* Current offset in constant pool (does not include any machine-specific
- header). */
- HOST_WIDE_INT x_pool_offset;
+ /* If we're using a per-function constant pool, this is it. */
+ struct rtx_constant_pool *pool;
/* Number of tree-constants deferred during the expansion of this
function. */
unsigned int deferred_constants;
};
-#define const_rtx_hash_table (cfun->varasm->x_const_rtx_hash_table)
-#define const_rtx_sym_hash_table (cfun->varasm->x_const_rtx_sym_hash_table)
-#define first_pool (cfun->varasm->x_first_pool)
-#define last_pool (cfun->varasm->x_last_pool)
-#define pool_offset (cfun->varasm->x_pool_offset)
#define n_deferred_constants (cfun->varasm->deferred_constants)
/* Number for making the label on the next
static GTY(()) int const_labelno;
-/* Number for making the label on the next
- static variable internal to a function. */
-
-static GTY(()) int var_labelno;
-
/* Carry information from ASM_DECLARE_OBJECT_NAME
to ASM_FINISH_DECLARE_OBJECT. */
tree last_assemble_variable_decl;
-/* RTX_UNCHANGING_P in a MEM can mean it is stored into, for initialization.
- So giving constant the alias set for the type will allow such
- initializations to appear to conflict with the load of the constant. We
- avoid this by giving all constants an alias set for just constants.
- Since there will be no stores to that alias set, nothing will ever
- conflict with them. */
+/* The following global variable indicates if the first basic block
+ in a function belongs to the cold partition or not. */
+
+bool first_function_block_is_cold;
+
+/* We give all constants their own alias set. Perhaps redundant with
+ MEM_READONLY_P, but pre-dates it. */
static HOST_WIDE_INT const_alias_set;
-static const char *strip_reg_name PARAMS ((const char *));
-static int contains_pointers_p PARAMS ((tree));
-static void decode_addr_const PARAMS ((tree, struct addr_const *));
-static hashval_t const_desc_hash PARAMS ((const void *));
-static int const_desc_eq PARAMS ((const void *, const void *));
-static hashval_t const_hash_1 PARAMS ((const tree));
-static int compare_constant PARAMS ((const tree, const tree));
-static tree copy_constant PARAMS ((tree));
-static void output_constant_def_contents PARAMS ((rtx));
-static void decode_rtx_const PARAMS ((enum machine_mode, rtx,
- struct rtx_const *));
-static unsigned int const_hash_rtx PARAMS ((enum machine_mode, rtx));
-static int compare_constant_rtx
- PARAMS ((enum machine_mode, rtx, struct constant_descriptor_rtx *));
-static struct constant_descriptor_rtx * record_constant_rtx
- PARAMS ((enum machine_mode, rtx));
-static struct pool_constant *find_pool_constant PARAMS ((struct function *, rtx));
-static void mark_constant_pool PARAMS ((void));
-static void mark_constants PARAMS ((rtx));
-static int mark_constant PARAMS ((rtx *current_rtx, void *data));
-static int output_addressed_constants PARAMS ((tree));
-static unsigned HOST_WIDE_INT array_size_for_constructor PARAMS ((tree));
-static unsigned min_align PARAMS ((unsigned, unsigned));
-static void output_constructor PARAMS ((tree, unsigned HOST_WIDE_INT,
- unsigned int));
-static void globalize_decl PARAMS ((tree));
-static void maybe_assemble_visibility PARAMS ((tree));
-static int in_named_entry_eq PARAMS ((const PTR, const PTR));
-static hashval_t in_named_entry_hash PARAMS ((const PTR));
-#ifdef ASM_OUTPUT_BSS
-static void asm_output_bss PARAMS ((FILE *, tree, const char *,
- unsigned HOST_WIDE_INT,
- unsigned HOST_WIDE_INT));
+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_desc_hash (const void *);
+static int const_desc_eq (const void *, const void *);
+static hashval_t const_hash_1 (const tree);
+static int compare_constant (const tree, const tree);
+static tree copy_constant (tree);
+static void output_constant_def_contents (rtx);
+static void output_addressed_constants (tree);
+static unsigned HOST_WIDE_INT array_size_for_constructor (tree);
+static unsigned min_align (unsigned, unsigned);
+static void output_constructor (tree, unsigned HOST_WIDE_INT, unsigned int);
+static void globalize_decl (tree);
+static void maybe_assemble_visibility (tree);
+static int in_named_entry_eq (const void *, const void *);
+static hashval_t in_named_entry_hash (const void *);
+static void initialize_cold_section_name (void);
#ifdef BSS_SECTION_ASM_OP
+#ifdef ASM_OUTPUT_BSS
+static void asm_output_bss (FILE *, tree, const char *,
+ unsigned HOST_WIDE_INT, unsigned HOST_WIDE_INT);
+#endif
#ifdef ASM_OUTPUT_ALIGNED_BSS
-static void asm_output_aligned_bss
- PARAMS ((FILE *, tree, const char *,
- unsigned HOST_WIDE_INT, int)) ATTRIBUTE_UNUSED;
+static void asm_output_aligned_bss (FILE *, tree, const char *,
+ unsigned HOST_WIDE_INT, int)
+ ATTRIBUTE_UNUSED;
#endif
#endif /* BSS_SECTION_ASM_OP */
-static bool asm_emit_uninitialised PARAMS ((tree, const char*,
- unsigned HOST_WIDE_INT,
- unsigned HOST_WIDE_INT));
-static void resolve_unique_section PARAMS ((tree, int, int));
-static void mark_weak PARAMS ((tree));
+static bool asm_emit_uninitialised (tree, const char*,
+ unsigned HOST_WIDE_INT,
+ unsigned HOST_WIDE_INT);
+static void mark_weak (tree);
\f
-enum in_section { no_section, in_text, in_data, in_named
-#ifdef BSS_SECTION_ASM_OP
- , in_bss
-#endif
-#ifdef CTORS_SECTION_ASM_OP
- , in_ctors
-#endif
-#ifdef DTORS_SECTION_ASM_OP
- , in_dtors
-#endif
-#ifdef READONLY_DATA_SECTION_ASM_OP
- , in_readonly_data
-#endif
-#ifdef EXTRA_SECTIONS
- , EXTRA_SECTIONS
-#endif
-};
static GTY(()) enum in_section in_section = no_section;
+enum in_section last_text_section;
/* Return a nonzero value if DECL has a section attribute. */
#ifndef IN_NAMED_SECTION
/* Text of section name when in_section == in_named. */
static GTY(()) const char *in_named_name;
+const char *last_text_section_name;
/* Hash table of flags that have been used for a particular named section. */
EXTRA_SECTION_FUNCTIONS
#endif
+static void
+initialize_cold_section_name (void)
+{
+ const char *stripped_name;
+ char *name, *buffer;
+ tree dsn;
+
+ gcc_assert (cfun && current_function_decl);
+ if (cfun->unlikely_text_section_name)
+ return;
+
+ dsn = DECL_SECTION_NAME (current_function_decl);
+ if (flag_function_sections && dsn)
+ {
+ name = alloca (TREE_STRING_LENGTH (dsn) + 1);
+ memcpy (name, TREE_STRING_POINTER (dsn), TREE_STRING_LENGTH (dsn) + 1);
+
+ stripped_name = targetm.strip_name_encoding (name);
+
+ buffer = ACONCAT ((stripped_name, "_unlikely", NULL));
+ cfun->unlikely_text_section_name = ggc_strdup (buffer);
+ }
+ else
+ cfun->unlikely_text_section_name = UNLIKELY_EXECUTED_TEXT_SECTION_NAME;
+}
+
/* Tell assembler to switch to text section. */
void
-text_section ()
+text_section (void)
{
if (in_section != in_text)
{
in_section = in_text;
-#ifdef TEXT_SECTION
- TEXT_SECTION ();
-#else
+ last_text_section = in_text;
fprintf (asm_out_file, "%s\n", TEXT_SECTION_ASM_OP);
-#endif
+ }
+}
+
+/* Tell assembler to switch to unlikely-to-be-executed text section. */
+
+void
+unlikely_text_section (void)
+{
+ if (cfun)
+ {
+ if (!cfun->unlikely_text_section_name)
+ initialize_cold_section_name ();
+
+ if (flag_function_sections
+ || ((in_section != in_unlikely_executed_text)
+ && (in_section != in_named
+ || (strcmp (in_named_name, cfun->unlikely_text_section_name)
+ != 0))))
+ {
+ named_section (NULL_TREE, cfun->unlikely_text_section_name, 0);
+ in_section = in_unlikely_executed_text;
+ last_text_section = in_unlikely_executed_text;
+ }
+ }
+ else
+ {
+ named_section (NULL_TREE, UNLIKELY_EXECUTED_TEXT_SECTION_NAME, 0);
+ in_section = in_unlikely_executed_text;
+ last_text_section = in_unlikely_executed_text;
}
}
/* Tell assembler to switch to data section. */
void
-data_section ()
+data_section (void)
{
if (in_section != in_data)
{
in_section = in_data;
- if (flag_shared_data)
- {
-#ifdef SHARED_SECTION_ASM_OP
- fprintf (asm_out_file, "%s\n", SHARED_SECTION_ASM_OP);
-#else
- fprintf (asm_out_file, "%s\n", DATA_SECTION_ASM_OP);
-#endif
- }
- else
- fprintf (asm_out_file, "%s\n", DATA_SECTION_ASM_OP);
+ fprintf (asm_out_file, "%s\n", DATA_SECTION_ASM_OP);
}
}
the text section. */
void
-readonly_data_section ()
+readonly_data_section (void)
{
#ifdef READONLY_DATA_SECTION
READONLY_DATA_SECTION (); /* Note this can call data_section. */
/* Determine if we're in the text section. */
int
-in_text_section ()
+in_text_section (void)
{
return in_section == in_text;
}
+/* Determine if we're in the unlikely-to-be-executed text section. */
+
+int
+in_unlikely_text_section (void)
+{
+ bool ret_val;
+
+ if (cfun)
+ {
+ ret_val = ((in_section == in_unlikely_executed_text)
+ || (in_section == in_named
+ && cfun->unlikely_text_section_name
+ && strcmp (in_named_name,
+ cfun->unlikely_text_section_name) == 0));
+ }
+ else
+ {
+ ret_val = ((in_section == in_unlikely_executed_text)
+ || (in_section == in_named
+ && strcmp (in_named_name,
+ UNLIKELY_EXECUTED_TEXT_SECTION_NAME) == 0));
+ }
+
+ return ret_val;
+}
+
/* Determine if we're in the data section. */
int
-in_data_section ()
+in_data_section (void)
{
return in_section == in_data;
}
/* Helper routines for maintaining in_named_htab. */
static int
-in_named_entry_eq (p1, p2)
- const PTR p1;
- const PTR p2;
+in_named_entry_eq (const void *p1, const void *p2)
{
const struct in_named_entry *old = p1;
const char *new = p2;
}
static hashval_t
-in_named_entry_hash (p)
- const PTR p;
+in_named_entry_hash (const void *p)
{
const struct in_named_entry *old = p;
return htab_hash_string (old->name);
set of flags for a section to have, so 0 does not mean that the section
has not been seen. */
-unsigned int
-get_named_section_flags (section)
- const char *section;
+static unsigned int
+get_named_section_flags (const char *section)
{
struct in_named_entry **slot;
section will return false. */
bool
-named_section_first_declaration (name)
- const char *name;
+named_section_first_declaration (const char *name)
{
struct in_named_entry **slot;
different set of flags, return false. */
bool
-set_named_section_flags (section, flags)
- const char *section;
- unsigned int flags;
+set_named_section_flags (const char *section, unsigned int flags)
{
struct in_named_entry **slot, *entry;
if (!entry)
{
- entry = (struct in_named_entry *) ggc_alloc (sizeof (*entry));
+ entry = ggc_alloc (sizeof (*entry));
*slot = entry;
entry->name = ggc_strdup (section);
entry->flags = flags;
return true;
}
-/* Tell assembler to change to section NAME with attributes FLAGS. */
+/* Tell assembler to change to section NAME with attributes FLAGS. If
+ DECL is non-NULL, it is the VAR_DECL or FUNCTION_DECL with which
+ this section is associated. */
void
-named_section_flags (name, flags)
- const char *name;
- unsigned int flags;
+named_section_real (const char *name, unsigned int flags, tree decl)
{
if (in_section != in_named || strcmp (name, in_named_name) != 0)
{
- if (! set_named_section_flags (name, flags))
- abort ();
+ bool unchanged = set_named_section_flags (name, flags);
+
+ gcc_assert (unchanged);
- (*targetm.asm_out.named_section) (name, flags);
+ targetm.asm_out.named_section (name, flags, decl);
if (flags & SECTION_FORGET)
in_section = no_section;
in_section = in_named;
}
}
+
+ if (in_text_section () || in_unlikely_text_section ())
+ {
+ last_text_section = in_section;
+ last_text_section_name = name;
+ }
}
/* Tell assembler to change to section NAME for DECL.
If RELOC is 1, the initializer for DECL contains relocs. */
void
-named_section (decl, name, reloc)
- tree decl;
- const char *name;
- int reloc;
+named_section (tree decl, const char *name, int reloc)
{
unsigned int flags;
- if (decl != NULL_TREE && !DECL_P (decl))
- abort ();
+ gcc_assert (!decl || DECL_P (decl));
if (name == NULL)
name = TREE_STRING_POINTER (DECL_SECTION_NAME (decl));
- flags = (* targetm.section_type_flags) (decl, name, reloc);
+ if (strcmp (name, UNLIKELY_EXECUTED_TEXT_SECTION_NAME) == 0
+ && cfun
+ && ! cfun->unlikely_text_section_name)
+ cfun->unlikely_text_section_name = UNLIKELY_EXECUTED_TEXT_SECTION_NAME;
+
+ flags = targetm.section_type_flags (decl, name, reloc);
/* Sanity check user variables for flag changes. Non-user
- section flag changes will abort in named_section_flags.
+ section flag changes will die in named_section_flags.
However, don't complain if SECTION_OVERRIDE is set.
We trust that the setter knows that it is safe to ignore
the default flags for this decl. */
{
flags = get_named_section_flags (name);
if ((flags & SECTION_OVERRIDE) == 0)
- error_with_decl (decl, "%s causes a section type conflict");
+ error ("%+D causes a section type conflict", decl);
}
- named_section_flags (name, flags);
+ named_section_real (name, flags, decl);
}
/* If required, set DECL_SECTION_NAME to a unique name. */
-static void
-resolve_unique_section (decl, reloc, flag_function_or_data_sections)
- tree decl;
- int reloc ATTRIBUTE_UNUSED;
- int flag_function_or_data_sections;
+void
+resolve_unique_section (tree decl, int reloc ATTRIBUTE_UNUSED,
+ int flag_function_or_data_sections)
{
if (DECL_SECTION_NAME (decl) == NULL_TREE
&& targetm.have_named_sections
&& (flag_function_or_data_sections
|| DECL_ONE_ONLY (decl)))
- (*targetm.asm_out.unique_section) (decl, reloc);
+ targetm.asm_out.unique_section (decl, reloc);
}
#ifdef BSS_SECTION_ASM_OP
/* Tell the assembler to switch to the bss section. */
void
-bss_section ()
+bss_section (void)
{
if (in_section != in_bss)
{
-#ifdef SHARED_BSS_SECTION_ASM_OP
- if (flag_shared_data)
- fprintf (asm_out_file, "%s\n", SHARED_BSS_SECTION_ASM_OP);
- else
-#endif
- fprintf (asm_out_file, "%s\n", BSS_SECTION_ASM_OP);
-
+ fprintf (asm_out_file, "%s\n", BSS_SECTION_ASM_OP);
in_section = in_bss;
}
}
support is localized here. */
static void
-asm_output_bss (file, decl, name, size, rounded)
- FILE *file;
- tree decl ATTRIBUTE_UNUSED;
- const char *name;
- unsigned HOST_WIDE_INT size ATTRIBUTE_UNUSED, rounded;
+asm_output_bss (FILE *file, tree decl ATTRIBUTE_UNUSED,
+ const char *name,
+ unsigned HOST_WIDE_INT size ATTRIBUTE_UNUSED,
+ unsigned HOST_WIDE_INT rounded)
{
- (*targetm.asm_out.globalize_label) (file, name);
+ targetm.asm_out.globalize_label (file, name);
bss_section ();
#ifdef ASM_DECLARE_OBJECT_NAME
last_assemble_variable_decl = decl;
support is localized here. */
static void
-asm_output_aligned_bss (file, decl, name, size, align)
- FILE *file;
- tree decl ATTRIBUTE_UNUSED;
- const char *name;
- unsigned HOST_WIDE_INT size;
- int align;
+asm_output_aligned_bss (FILE *file, tree decl ATTRIBUTE_UNUSED,
+ const char *name, unsigned HOST_WIDE_INT size,
+ int align)
{
bss_section ();
ASM_OUTPUT_ALIGN (file, floor_log2 (align / BITS_PER_UNIT));
/* Switch to the section for function DECL.
- If DECL is NULL_TREE, switch to the text section.
- ??? It's not clear that we will ever be passed NULL_TREE, but it's
- safer to handle it. */
+ If DECL is NULL_TREE, switch to the text section. We can be passed
+ NULL_TREE under some circumstances by dbxout.c at least. */
void
-function_section (decl)
- tree decl;
+function_section (tree decl)
{
+ int reloc = 0;
+
+ if (first_function_block_is_cold)
+ reloc = 1;
+
+#ifdef USE_SELECT_SECTION_FOR_FUNCTIONS
+ targetm.asm_out.select_section (decl, reloc, DECL_ALIGN (decl));
+#else
if (decl != NULL_TREE
- && DECL_SECTION_NAME (decl) != NULL_TREE)
+ && DECL_SECTION_NAME (decl) != NULL_TREE
+ && targetm.have_named_sections)
named_section (decl, (char *) 0, 0);
else
text_section ();
+#endif
+}
+
+void
+current_function_section (tree decl)
+{
+#ifdef USE_SELECT_SECTION_FOR_FUNCTIONS
+ int reloc = 0;
+
+ if (in_unlikely_text_section ()
+ || last_text_section == in_unlikely_executed_text)
+ reloc = 1;
+
+ targetm.asm_out.select_section (decl, reloc, DECL_ALIGN (decl));
+#else
+ if (last_text_section == in_unlikely_executed_text)
+ unlikely_text_section ();
+ else if (last_text_section == in_text)
+ text_section ();
+ else if (last_text_section == in_named
+ && targetm.have_named_sections)
+ named_section (NULL_TREE, last_text_section_name, 0);
+ else
+ function_section (decl);
+#endif
+}
+
+/* Switch to read-only data section associated with function DECL. */
+
+void
+default_function_rodata_section (tree decl)
+{
+ if (decl != NULL_TREE && DECL_SECTION_NAME (decl))
+ {
+ const char *name = TREE_STRING_POINTER (DECL_SECTION_NAME (decl));
+
+ if (DECL_ONE_ONLY (decl) && HAVE_COMDAT_GROUP)
+ {
+ size_t len = strlen (name) + 3;
+ char* rname = alloca (len);
+
+ strcpy (rname, ".rodata");
+ strcat (rname, name + 5);
+ named_section_real (rname, SECTION_LINKONCE, decl);
+ return;
+ }
+ /* For .gnu.linkonce.t.foo we want to use .gnu.linkonce.r.foo. */
+ else if (DECL_ONE_ONLY (decl)
+ && strncmp (name, ".gnu.linkonce.t.", 16) == 0)
+ {
+ size_t len = strlen (name) + 1;
+ char *rname = alloca (len);
+
+ memcpy (rname, name, len);
+ rname[14] = 'r';
+ named_section_real (rname, SECTION_LINKONCE, decl);
+ return;
+ }
+ /* For .text.foo we want to use .rodata.foo. */
+ else if (flag_function_sections && flag_data_sections
+ && strncmp (name, ".text.", 6) == 0)
+ {
+ size_t len = strlen (name) + 1;
+ char *rname = alloca (len + 2);
+
+ memcpy (rname, ".rodata", 7);
+ memcpy (rname + 7, name + 5, len - 5);
+ named_section_flags (rname, 0);
+ return;
+ }
+ }
+
+ readonly_data_section ();
+}
+
+/* Switch to read-only data section associated with function DECL
+ for targets where that section should be always the single
+ readonly data section. */
+
+void
+default_no_function_rodata_section (tree decl ATTRIBUTE_UNUSED)
+{
+ readonly_data_section ();
}
/* Switch to section for variable DECL. RELOC is the same as the
argument to SELECT_SECTION. */
void
-variable_section (decl, reloc)
- tree decl;
- int reloc;
+variable_section (tree decl, int reloc)
{
if (IN_NAMED_SECTION (decl))
named_section (decl, NULL, reloc);
else
- (*targetm.asm_out.select_section) (decl, reloc, DECL_ALIGN (decl));
+ targetm.asm_out.select_section (decl, reloc, DECL_ALIGN (decl));
}
/* Tell assembler to switch to the section for string merging. */
void
-mergeable_string_section (decl, align, flags)
- tree decl ATTRIBUTE_UNUSED;
- unsigned HOST_WIDE_INT align ATTRIBUTE_UNUSED;
- unsigned int flags ATTRIBUTE_UNUSED;
+mergeable_string_section (tree decl ATTRIBUTE_UNUSED,
+ unsigned HOST_WIDE_INT align ATTRIBUTE_UNUSED,
+ unsigned int flags ATTRIBUTE_UNUSED)
{
-#ifdef HAVE_GAS_SHF_MERGE
- if (flag_merge_constants
+ if (HAVE_GAS_SHF_MERGE && flag_merge_constants
&& TREE_CODE (decl) == STRING_CST
&& TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE
&& align <= 256
}
}
}
-#endif
+
readonly_data_section ();
}
/* Tell assembler to switch to the section for constant merging. */
void
-mergeable_constant_section (mode, align, flags)
- enum machine_mode mode ATTRIBUTE_UNUSED;
- unsigned HOST_WIDE_INT align ATTRIBUTE_UNUSED;
- unsigned int flags ATTRIBUTE_UNUSED;
+mergeable_constant_section (enum machine_mode mode ATTRIBUTE_UNUSED,
+ unsigned HOST_WIDE_INT align ATTRIBUTE_UNUSED,
+ unsigned int flags ATTRIBUTE_UNUSED)
{
-#ifdef HAVE_GAS_SHF_MERGE
unsigned int modesize = GET_MODE_BITSIZE (mode);
- if (flag_merge_constants
+ if (HAVE_GAS_SHF_MERGE && flag_merge_constants
&& mode != VOIDmode
&& mode != BLKmode
&& modesize <= align
named_section_flags (name, flags);
return;
}
-#endif
+
readonly_data_section ();
}
\f
/* Given NAME, a putative register name, discard any customary prefixes. */
static const char *
-strip_reg_name (name)
- const char *name;
+strip_reg_name (const char *name)
{
#ifdef REGISTER_PREFIX
if (!strncmp (name, REGISTER_PREFIX, strlen (REGISTER_PREFIX)))
return name;
}
\f
+/* The user has asked for a DECL to have a particular name. Set (or
+ change) it in such a way that we don't prefix an underscore to
+ it. */
+void
+set_user_assembler_name (tree decl, const char *name)
+{
+ char *starred = alloca (strlen (name) + 2);
+ starred[0] = '*';
+ strcpy (starred + 1, name);
+ change_decl_assembler_name (decl, get_identifier (starred));
+ SET_DECL_RTL (decl, NULL_RTX);
+}
+\f
/* 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,
Prefixes such as % are optional. */
int
-decode_reg_name (asmspec)
- const char *asmspec;
+decode_reg_name (const char *asmspec)
{
if (asmspec != 0)
{
= ADDITIONAL_REGISTER_NAMES;
for (i = 0; i < (int) ARRAY_SIZE (table); i++)
- if (! strcmp (asmspec, table[i].name))
+ if (table[i].name[0]
+ && ! strcmp (asmspec, table[i].name))
return table[i].number;
}
#endif /* ADDITIONAL_REGISTER_NAMES */
There is, however, one exception: this function handles variables
explicitly placed in a particular register by the user.
- ASMSPEC, if not 0, is the string which the user specified as the
- assembler symbol name.
-
This is never called for PARM_DECL nodes. */
void
-make_decl_rtl (decl, asmspec)
- tree decl;
- const char *asmspec;
+make_decl_rtl (tree decl)
{
- int top_level = (DECL_CONTEXT (decl) == NULL_TREE);
const char *name = 0;
- const char *new_name = 0;
int reg_number;
rtx x;
/* Check that we are not being given an automatic variable. */
+ gcc_assert (TREE_CODE (decl) != PARM_DECL
+ && TREE_CODE (decl) != RESULT_DECL);
+
/* A weak alias has TREE_PUBLIC set but not the other bits. */
- if (TREE_CODE (decl) == PARM_DECL
- || TREE_CODE (decl) == RESULT_DECL
- || (TREE_CODE (decl) == VAR_DECL
- && !TREE_STATIC (decl)
- && !TREE_PUBLIC (decl)
- && !DECL_EXTERNAL (decl)
- && !DECL_REGISTER (decl)))
- abort ();
+ gcc_assert (TREE_CODE (decl) != VAR_DECL
+ || TREE_STATIC (decl)
+ || TREE_PUBLIC (decl)
+ || DECL_EXTERNAL (decl)
+ || DECL_REGISTER (decl));
+
/* And that we were not given a type or a label. */
- else if (TREE_CODE (decl) == TYPE_DECL
- || TREE_CODE (decl) == LABEL_DECL)
- abort ();
+ gcc_assert (TREE_CODE (decl) != TYPE_DECL
+ && TREE_CODE (decl) != LABEL_DECL);
/* For a duplicate declaration, we can be called twice on the
same DECL node. Don't discard the RTL already made. */
SET_DECL_RTL (decl, adjust_address_nv (DECL_RTL (decl),
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
/* 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);
+ targetm.encode_section_info (decl, DECL_RTL (decl), false);
+
+ /* Make this function static known to the mudflap runtime. */
+ if (flag_mudflap && TREE_CODE (decl) == VAR_DECL)
+ mudflap_enqueue_decl (decl);
+
return;
}
- new_name = name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl));
-
- reg_number = decode_reg_name (asmspec);
- if (reg_number == -2)
+ name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl));
+
+ if (name[0] != '*' && TREE_CODE (decl) != FUNCTION_DECL
+ && DECL_REGISTER (decl))
{
- /* ASMSPEC is given, and not the name of a register. Mark the
- name with a star so assemble_name won't munge it. */
- char *starred = alloca (strlen (asmspec) + 2);
- starred[0] = '*';
- strcpy (starred + 1, asmspec);
- new_name = starred;
+ error ("register name not specified for %q+D", decl);
}
-
- if (TREE_CODE (decl) != FUNCTION_DECL && DECL_REGISTER (decl))
+ else if (TREE_CODE (decl) != FUNCTION_DECL && DECL_REGISTER (decl))
{
+ const char *asmspec = name+1;
+ reg_number = decode_reg_name (asmspec);
/* First detect errors in declaring global registers. */
if (reg_number == -1)
- error_with_decl (decl, "register name not specified for `%s'");
+ error ("register name not specified for %q+D", decl);
else if (reg_number < 0)
- error_with_decl (decl, "invalid register name for `%s'");
+ error ("invalid register name for %q+D", decl);
else if (TYPE_MODE (TREE_TYPE (decl)) == BLKmode)
- error_with_decl (decl,
- "data type of `%s' isn't suitable for a register");
+ error ("data type of %q+D isn%'t suitable for a register",
+ decl);
else if (! HARD_REGNO_MODE_OK (reg_number, TYPE_MODE (TREE_TYPE (decl))))
- error_with_decl (decl,
- "register specified for `%s' isn't suitable for data type");
+ error ("register specified for %q+D isn%'t suitable for data type",
+ decl);
/* Now handle properly declared static register variables. */
else
{
error ("global register variable has initial value");
}
if (TREE_THIS_VOLATILE (decl))
- warning ("volatile register variables don't work as you might wish");
+ warning (0, "volatile register variables don%'t "
+ "work as you might wish");
/* If the user specified one of the eliminables registers here,
e.g., FRAME_POINTER_REGNUM, we don't want to get this variable
/* Make this register global, so not usable for anything
else. */
#ifdef ASM_DECLARE_REGISTER_GLOBAL
+ name = IDENTIFIER_POINTER (DECL_NAME (decl));
ASM_DECLARE_REGISTER_GLOBAL (asm_out_file, decl, reg_number, name);
#endif
- nregs = HARD_REGNO_NREGS (reg_number, DECL_MODE (decl));
+ nregs = hard_regno_nregs[reg_number][DECL_MODE (decl)];
while (nregs > 0)
globalize_reg (reg_number + --nregs);
}
return;
}
}
-
/* Now handle ordinary static variables and functions (in memory).
Also handle vars declared register invalidly. */
-
- if (reg_number >= 0 || reg_number == -3)
- error_with_decl (decl,
- "register name given for non-register variable `%s'");
+ 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. */
if (TREE_CODE (decl) == VAR_DECL && DECL_WEAK (decl))
DECL_COMMON (decl) = 0;
- /* Can't use just the variable's own name for a variable
- whose scope is less than the whole file, unless it's a member
- of a local class (which will already be unambiguous).
- Concatenate a distinguishing number. */
- if (!top_level && !TREE_PUBLIC (decl)
- && ! (DECL_CONTEXT (decl) && TYPE_P (DECL_CONTEXT (decl)))
- && asmspec == 0
- && name == IDENTIFIER_POINTER (DECL_NAME (decl)))
- {
- char *label;
-
- ASM_FORMAT_PRIVATE_NAME (label, name, var_labelno);
- var_labelno++;
- new_name = label;
- }
-
- if (name != new_name)
- {
- SET_DECL_ASSEMBLER_NAME (decl, get_identifier (new_name));
- name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl));
- }
-
x = gen_rtx_SYMBOL_REF (Pmode, name);
SYMBOL_REF_WEAK (x) = DECL_WEAK (decl);
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);
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);
+ targetm.encode_section_info (decl, DECL_RTL (decl), true);
+
+ /* Make this function static known to the mudflap runtime. */
+ if (flag_mudflap && TREE_CODE (decl) == VAR_DECL)
+ mudflap_enqueue_decl (decl);
}
/* Make the rtl for variable VAR be volatile.
Use this only for static variables. */
void
-make_var_volatile (var)
- tree var;
+make_var_volatile (tree var)
{
- if (GET_CODE (DECL_RTL (var)) != MEM)
- abort ();
+ gcc_assert (MEM_P (DECL_RTL (var)));
MEM_VOLATILE_P (DECL_RTL (var)) = 1;
}
for an `asm' keyword used between functions. */
void
-assemble_asm (string)
- tree string;
+assemble_asm (tree string)
{
app_enable ();
between 0 and MAX_INIT_PRIORITY. */
void
-default_stabs_asm_out_destructor (symbol, priority)
- rtx symbol;
- int priority ATTRIBUTE_UNUSED;
+default_stabs_asm_out_destructor (rtx symbol ATTRIBUTE_UNUSED,
+ int priority ATTRIBUTE_UNUSED)
{
+#if defined DBX_DEBUGGING_INFO || defined XCOFF_DEBUGGING_INFO
/* Tell GNU LD that this is part of the static destructor set.
This will work for any system that uses stabs, most usefully
aout systems. */
- fprintf (asm_out_file, "%s\"___DTOR_LIST__\",22,0,0,", ASM_STABS_OP);
- assemble_name (asm_out_file, XSTR (symbol, 0));
- fputc ('\n', asm_out_file);
+ dbxout_begin_simple_stabs ("___DTOR_LIST__", 22 /* N_SETT */);
+ dbxout_stab_value_label (XSTR (symbol, 0));
+#else
+ sorry ("global destructors not supported on this target");
+#endif
}
void
-default_named_section_asm_out_destructor (symbol, priority)
- rtx symbol;
- int priority;
+default_named_section_asm_out_destructor (rtx symbol, int priority)
{
const char *section = ".dtors";
char buf[16];
#ifdef DTORS_SECTION_ASM_OP
void
-dtors_section ()
+dtors_section (void)
{
if (in_section != in_dtors)
{
}
void
-default_dtor_section_asm_out_destructor (symbol, priority)
- rtx symbol;
- int priority ATTRIBUTE_UNUSED;
+default_dtor_section_asm_out_destructor (rtx symbol,
+ int priority ATTRIBUTE_UNUSED)
{
dtors_section ();
assemble_align (POINTER_SIZE);
/* Likewise for global constructors. */
void
-default_stabs_asm_out_constructor (symbol, priority)
- rtx symbol;
- int priority ATTRIBUTE_UNUSED;
+default_stabs_asm_out_constructor (rtx symbol ATTRIBUTE_UNUSED,
+ int priority ATTRIBUTE_UNUSED)
{
+#if defined DBX_DEBUGGING_INFO || defined XCOFF_DEBUGGING_INFO
/* Tell GNU LD that this is part of the static destructor set.
This will work for any system that uses stabs, most usefully
aout systems. */
- fprintf (asm_out_file, "%s\"___CTOR_LIST__\",22,0,0,", ASM_STABS_OP);
- assemble_name (asm_out_file, XSTR (symbol, 0));
- fputc ('\n', asm_out_file);
+ dbxout_begin_simple_stabs ("___CTOR_LIST__", 22 /* N_SETT */);
+ dbxout_stab_value_label (XSTR (symbol, 0));
+#else
+ sorry ("global constructors not supported on this target");
+#endif
}
void
-default_named_section_asm_out_constructor (symbol, priority)
- rtx symbol;
- int priority;
+default_named_section_asm_out_constructor (rtx symbol, int priority)
{
const char *section = ".ctors";
char buf[16];
#ifdef CTORS_SECTION_ASM_OP
void
-ctors_section ()
+ctors_section (void)
{
if (in_section != in_ctors)
{
}
void
-default_ctor_section_asm_out_constructor (symbol, priority)
- rtx symbol;
- int priority ATTRIBUTE_UNUSED;
+default_ctor_section_asm_out_constructor (rtx symbol,
+ int priority ATTRIBUTE_UNUSED)
{
ctors_section ();
assemble_align (POINTER_SIZE);
#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 **type = &first_global_object_name;
+
+ if (first_global_object_name
+ || !TREE_PUBLIC (decl) || DECL_EXTERNAL (decl)
+ || !DECL_NAME (decl)
+ || (TREE_CODE (decl) != FUNCTION_DECL
+ && (TREE_CODE (decl) != VAR_DECL
+ || (DECL_COMMON (decl)
+ && (DECL_INITIAL (decl) == 0
+ || DECL_INITIAL (decl) == error_mark_node))))
+ || !MEM_P (DECL_RTL (decl)))
+ 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))
+ type = &weak_global_object_name;
+
+ if (!*type)
+ {
+ const char *p;
+ const char *name;
+ rtx decl_rtl = DECL_RTL (decl);
+
+ p = targetm.strip_name_encoding (XSTR (XEXP (decl_rtl, 0), 0));
+ name = ggc_strdup (p);
+
+ *type = name;
+ }
+}
+
/* 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 (decl, fnname)
- tree decl;
- const char *fnname;
+assemble_start_function (tree decl, const char *fnname)
{
int align;
+ char tmp_label[100];
+ bool hot_label_written = false;
+
+ cfun->unlikely_text_section_name = NULL;
+
+ first_function_block_is_cold = false;
+ if (flag_reorder_blocks_and_partition)
+ {
+ ASM_GENERATE_INTERNAL_LABEL (tmp_label, "LHOTB", const_labelno);
+ cfun->hot_section_label = ggc_strdup (tmp_label);
+ ASM_GENERATE_INTERNAL_LABEL (tmp_label, "LCOLDB", const_labelno);
+ cfun->cold_section_label = ggc_strdup (tmp_label);
+ ASM_GENERATE_INTERNAL_LABEL (tmp_label, "LHOTE", const_labelno);
+ cfun->hot_section_end_label = ggc_strdup (tmp_label);
+ ASM_GENERATE_INTERNAL_LABEL (tmp_label, "LCOLDE", const_labelno);
+ cfun->cold_section_end_label = ggc_strdup (tmp_label);
+ const_labelno++;
+ }
+ else
+ {
+ cfun->hot_section_label = NULL;
+ cfun->cold_section_label = NULL;
+ cfun->hot_section_end_label = NULL;
+ cfun->cold_section_end_label = NULL;
+ }
/* The following code does not need preprocessing in the assembler. */
output_constant_pool (fnname, decl);
resolve_unique_section (decl, 0, flag_function_sections);
+
+ /* 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)
+ {
+ unlikely_text_section ();
+ assemble_align (FUNCTION_BOUNDARY);
+ ASM_OUTPUT_LABEL (asm_out_file, cfun->cold_section_label);
+
+ /* When the function starts with a cold section, we need to explicitly
+ align the hot section and write out the hot section label.
+ But if the current function is a thunk, we do not have a CFG. */
+ if (!current_function_is_thunk
+ && BB_PARTITION (ENTRY_BLOCK_PTR->next_bb) == BB_COLD_PARTITION)
+ {
+ text_section ();
+ assemble_align (FUNCTION_BOUNDARY);
+ ASM_OUTPUT_LABEL (asm_out_file, cfun->hot_section_label);
+ hot_label_written = true;
+ first_function_block_is_cold = true;
+ }
+ }
+ else if (DECL_SECTION_NAME (decl))
+ {
+ /* Calls to function_section rely on first_function_block_is_cold
+ being accurate. The first block may be cold even if we aren't
+ doing partitioning, if the entire function was decided by
+ choose_function_section (predict.c) to be cold. */
+
+ initialize_cold_section_name ();
+
+ if (cfun->unlikely_text_section_name
+ && strcmp (TREE_STRING_POINTER (DECL_SECTION_NAME (decl)),
+ cfun->unlikely_text_section_name) == 0)
+ first_function_block_is_cold = true;
+ }
+
+ last_text_section = no_section;
+
+ /* Switch to the correct text section for the start of the function. */
+
function_section (decl);
+ if (flag_reorder_blocks_and_partition
+ && !hot_label_written)
+ ASM_OUTPUT_LABEL (asm_out_file, cfun->hot_section_label);
/* Tell assembler to move to target machine's alignment for functions. */
align = floor_log2 (FUNCTION_BOUNDARY / BITS_PER_UNIT);
if (TREE_PUBLIC (decl))
{
- if (! first_global_object_name)
- {
- const char *p;
- char *name;
-
- p = (* targetm.strip_name_encoding) (fnname);
- name = xstrdup (p);
-
- if (! DECL_WEAK (decl) && ! DECL_ONE_ONLY (decl))
- first_global_object_name = name;
- else
- weak_global_object_name = name;
- }
+ notice_global_symbol (decl);
globalize_decl (decl);
maybe_assemble_visibility (decl);
}
- /* Do any machine/system dependent processing of the function name */
+ 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
function. DECL describes the function. NAME is the function's name. */
void
-assemble_end_function (decl, fnname)
- tree decl;
- const char *fnname;
+assemble_end_function (tree decl, const char *fnname)
{
#ifdef ASM_DECLARE_FUNCTION_SIZE
+ /* We could have switched section in the middle of the function. */
+ if (flag_reorder_blocks_and_partition)
+ function_section (decl);
ASM_DECLARE_FUNCTION_SIZE (asm_out_file, fnname, decl);
#endif
if (! CONSTANT_POOL_BEFORE_FUNCTION)
output_constant_pool (fnname, decl);
function_section (decl); /* need to switch back */
}
+ /* Output labels for end of hot/cold text sections (to be used by
+ debug info.) */
+ if (flag_reorder_blocks_and_partition)
+ {
+ enum in_section save_text_section;
+
+ save_text_section = in_section;
+ unlikely_text_section ();
+ ASM_OUTPUT_LABEL (asm_out_file, cfun->cold_section_end_label);
+ if (first_function_block_is_cold)
+ text_section ();
+ else
+ function_section (decl);
+ ASM_OUTPUT_LABEL (asm_out_file, cfun->hot_section_end_label);
+ if (save_text_section == in_unlikely_executed_text)
+ unlikely_text_section ();
+ }
}
\f
/* Assemble code to leave SIZE bytes of zeros. */
void
-assemble_zeros (size)
- unsigned HOST_WIDE_INT size;
+assemble_zeros (unsigned HOST_WIDE_INT size)
{
/* Do no output if -fsyntax-only. */
if (flag_syntax_only)
#ifdef ASM_NO_SKIP_IN_TEXT
/* The `space' pseudo in the text section outputs nop insns rather than 0s,
so we must output 0s explicitly in the text section. */
- if (ASM_NO_SKIP_IN_TEXT && in_text_section ())
+ if ((ASM_NO_SKIP_IN_TEXT && in_text_section ())
+ || (ASM_NO_SKIP_IN_TEXT && in_unlikely_text_section ()))
{
unsigned HOST_WIDE_INT i;
for (i = 0; i < size; i++)
/* Assemble an alignment pseudo op for an ALIGN-bit boundary. */
void
-assemble_align (align)
- int align;
+assemble_align (int align)
{
if (align > BITS_PER_UNIT)
{
/* Assemble a string constant with the specified C string as contents. */
void
-assemble_string (p, size)
- const char *p;
- int size;
+assemble_string (const char *p, int size)
{
int pos = 0;
int maximum = 2000;
#endif
static bool
-asm_emit_uninitialised (decl, name, size, rounded)
- tree decl;
- const char *name;
- unsigned HOST_WIDE_INT size ATTRIBUTE_UNUSED;
- unsigned HOST_WIDE_INT rounded ATTRIBUTE_UNUSED;
+asm_emit_uninitialised (tree decl, const char *name,
+ unsigned HOST_WIDE_INT size ATTRIBUTE_UNUSED,
+ unsigned HOST_WIDE_INT rounded ATTRIBUTE_UNUSED)
{
enum
{
destination = asm_dest_common;
}
+ if (destination != asm_dest_common)
+ {
+ resolve_unique_section (decl, 0, flag_data_sections);
+ /* Custom sections don't belong here. */
+ if (DECL_SECTION_NAME (decl))
+ return false;
+ }
+
if (destination == asm_dest_bss)
globalize_decl (decl);
- resolve_unique_section (decl, 0, flag_data_sections);
if (flag_shared_data)
{
ASM_EMIT_LOCAL (decl, name, size, rounded);
break;
default:
- abort ();
+ gcc_unreachable ();
}
return true;
initial value (that will be done by the caller). */
void
-assemble_variable (decl, top_level, at_end, dont_output_data)
- tree decl;
- int top_level ATTRIBUTE_UNUSED;
- int at_end ATTRIBUTE_UNUSED;
- int dont_output_data;
+assemble_variable (tree decl, int top_level ATTRIBUTE_UNUSED,
+ int at_end ATTRIBUTE_UNUSED, int dont_output_data)
{
const char *name;
unsigned int align;
int reloc = 0;
rtx decl_rtl;
+ if (lang_hooks.decls.prepare_assemble_variable)
+ lang_hooks.decls.prepare_assemble_variable (decl);
+
last_assemble_variable_decl = 0;
/* Normally no need to say anything here for external references,
return;
/* Do nothing for global register variables. */
- if (DECL_RTL_SET_P (decl) && GET_CODE (DECL_RTL (decl)) == REG)
+ if (DECL_RTL_SET_P (decl) && REG_P (DECL_RTL (decl)))
{
TREE_ASM_WRITTEN (decl) = 1;
return;
if (!dont_output_data && DECL_SIZE (decl) == 0)
{
- error ("%Hstorage size of `%s' isn't known",
- &DECL_SOURCE_LOCATION (decl),
- IDENTIFIER_POINTER (DECL_NAME (decl)));
+ error ("storage size of %q+D isn%'t known", decl);
TREE_ASM_WRITTEN (decl) = 1;
return;
}
if (! dont_output_data
&& ! host_integerp (DECL_SIZE_UNIT (decl), 1))
{
- error_with_decl (decl, "size of variable `%s' is too large");
+ error ("size of variable %q+D is too large", decl);
return;
}
name = XSTR (XEXP (decl_rtl, 0), 0);
- if (TREE_PUBLIC (decl) && DECL_NAME (decl)
- && ! first_global_object_name
- && ! (DECL_COMMON (decl) && (DECL_INITIAL (decl) == 0
- || DECL_INITIAL (decl) == error_mark_node))
- && ! DECL_WEAK (decl)
- && ! DECL_ONE_ONLY (decl))
- {
- const char *p;
- char *xname;
-
- p = (* targetm.strip_name_encoding) (name);
- xname = xstrdup (p);
- first_global_object_name = xname;
- }
+ if (TREE_PUBLIC (decl) && DECL_NAME (decl))
+ notice_global_symbol (decl);
/* Compute the alignment of this data. */
/* Some object file formats have a maximum alignment which they support.
In particular, a.out format supports a maximum alignment of 4. */
-#ifndef MAX_OFILE_ALIGNMENT
-#define MAX_OFILE_ALIGNMENT BIGGEST_ALIGNMENT
-#endif
if (align > MAX_OFILE_ALIGNMENT)
{
- warning_with_decl (decl,
- "alignment of `%s' is greater than maximum object file alignment. Using %d",
- MAX_OFILE_ALIGNMENT/BITS_PER_UNIT);
+ warning (0, "alignment of %q+D is greater than maximum object "
+ "file alignment. Using %d", decl,
+ MAX_OFILE_ALIGNMENT/BITS_PER_UNIT);
align = MAX_OFILE_ALIGNMENT;
}
if (TREE_PUBLIC (decl))
maybe_assemble_visibility (decl);
- /* Output any data that we will need to use the address of. */
- if (DECL_INITIAL (decl) == error_mark_node)
- reloc = contains_pointers_p (TREE_TYPE (decl)) ? 3 : 0;
- else if (DECL_INITIAL (decl))
- reloc = output_addressed_constants (DECL_INITIAL (decl));
- resolve_unique_section (decl, reloc, flag_data_sections);
+ if (DECL_PRESERVE_P (decl))
+ targetm.asm_out.mark_decl_preserved (name);
/* Handle uninitialized definitions. */
if (DECL_SECTION_NAME (decl) || dont_output_data)
;
/* We don't implement common thread-local data at present. */
- else if (DECL_THREAD_LOCAL (decl))
+ else if (DECL_THREAD_LOCAL_P (decl))
{
if (DECL_COMMON (decl))
sorry ("thread-local COMMON data not implemented");
* (BIGGEST_ALIGNMENT / BITS_PER_UNIT));
#if !defined(ASM_OUTPUT_ALIGNED_COMMON) && !defined(ASM_OUTPUT_ALIGNED_DECL_COMMON) && !defined(ASM_OUTPUT_ALIGNED_BSS)
- if ((unsigned HOST_WIDE_INT) DECL_ALIGN (decl) / BITS_PER_UNIT > rounded)
- warning_with_decl
- (decl, "requested alignment for %s is greater than implemented alignment of %d",rounded);
+ if ((unsigned HOST_WIDE_INT) DECL_ALIGN_UNIT (decl) > rounded)
+ warning (0, "requested alignment for %q+D is greater than "
+ "implemented alignment of %wu", decl, rounded);
#endif
/* If the target cannot output uninitialized but not common global data
if (TREE_PUBLIC (decl) && DECL_NAME (decl))
globalize_decl (decl);
+ /* Output any data that we will need to use the address of. */
+ if (DECL_INITIAL (decl) == error_mark_node)
+ reloc = contains_pointers_p (TREE_TYPE (decl)) ? 3 : 0;
+ else if (DECL_INITIAL (decl))
+ {
+ reloc = compute_reloc_for_constant (DECL_INITIAL (decl));
+ output_addressed_constants (DECL_INITIAL (decl));
+ }
+
/* Switch to the appropriate section. */
+ resolve_unique_section (decl, reloc, flag_data_sections);
variable_section (decl, reloc);
/* dbxout.c needs to know this. */
- if (in_text_section ())
+ if (in_text_section () || in_unlikely_text_section ())
DECL_IN_TEXT_SECTION (decl) = 1;
/* Output the alignment of this data. */
if (align > BITS_PER_UNIT)
- {
- ASM_OUTPUT_ALIGN (asm_out_file,
- floor_log2 (DECL_ALIGN (decl) / BITS_PER_UNIT));
- }
+ ASM_OUTPUT_ALIGN (asm_out_file, floor_log2 (DECL_ALIGN_UNIT (decl)));
/* Do any machine/system dependent processing of the object. */
#ifdef ASM_DECLARE_OBJECT_NAME
if (!dont_output_data)
{
- if (DECL_INITIAL (decl) && 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_low_cst (DECL_SIZE_UNIT (decl), 1),
/* Return 1 if type TYPE contains any pointers. */
static int
-contains_pointers_p (type)
- tree type;
+contains_pointers_p (tree type)
{
switch (TREE_CODE (type))
{
}
}
-/* Output something to declare an external symbol to the assembler.
- (Most assemblers don't need this, so we normally output nothing.)
- Do nothing if DECL is not external. */
-
-void
-assemble_external (decl)
- tree decl ATTRIBUTE_UNUSED;
-{
- /* Because most platforms do not define ASM_OUTPUT_EXTERNAL, the
- main body of this code is only rarely exercised. To provide some
- testing, on all platforms, we make sure that the ASM_OUT_FILE is
- open. If it's not, we should not be calling this function. */
- if (!asm_out_file)
- abort ();
+/* In unit-at-a-time mode, we delay assemble_external processing until
+ the compilation unit is finalized. This is the best we can do for
+ right now (i.e. stage 3 of GCC 4.0) - the right thing is to delay
+ it all the way to final. See PR 17982 for further discussion. */
+static GTY(()) tree pending_assemble_externals;
#ifdef ASM_OUTPUT_EXTERNAL
- if (DECL_P (decl) && DECL_EXTERNAL (decl) && TREE_PUBLIC (decl))
+/* 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))
{
- rtx rtl = DECL_RTL (decl);
+ const char *name;
- if (GET_CODE (rtl) == MEM && GET_CODE (XEXP (rtl, 0)) == SYMBOL_REF
- && ! SYMBOL_REF_USED (XEXP (rtl, 0)))
- {
- /* Some systems do require some output. */
- SYMBOL_REF_USED (XEXP (rtl, 0)) = 1;
- ASM_OUTPUT_EXTERNAL (asm_out_file, decl, XSTR (XEXP (rtl, 0), 0));
- }
+ if (DECL_BUILT_IN_CLASS (decl) == BUILT_IN_NORMAL
+ && DECL_FUNCTION_CODE (decl) == BUILT_IN_ALLOCA)
+ return true;
+
+ name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl));
+ if (strncmp (name, "__builtin_", strlen ("__builtin_")) == 0)
+ return true;
}
-#endif
+ return false;
}
-/* Similar, for calling a library function FUN. */
-
+/* Actually do the tests to determine if this is necessary, and invoke
+ ASM_OUTPUT_EXTERNAL. */
+static void
+assemble_external_real (tree decl)
+{
+ rtx rtl = DECL_RTL (decl);
+
+ if (MEM_P (rtl) && GET_CODE (XEXP (rtl, 0)) == SYMBOL_REF
+ && !SYMBOL_REF_USED (XEXP (rtl, 0))
+ && !incorporeal_function_p (decl))
+ {
+ /* Some systems do require some output. */
+ SYMBOL_REF_USED (XEXP (rtl, 0)) = 1;
+ ASM_OUTPUT_EXTERNAL (asm_out_file, decl, XSTR (XEXP (rtl, 0), 0));
+ }
+}
+#endif
+
+void
+process_pending_assemble_externals (void)
+{
+#ifdef ASM_OUTPUT_EXTERNAL
+ tree list;
+ for (list = pending_assemble_externals; list; list = TREE_CHAIN (list))
+ assemble_external_real (TREE_VALUE (list));
+
+ pending_assemble_externals = 0;
+#endif
+}
+
+/* Output something to declare an external symbol to the assembler.
+ (Most assemblers don't need this, so we normally output nothing.)
+ Do nothing if DECL is not external. */
+
+void
+assemble_external (tree decl ATTRIBUTE_UNUSED)
+{
+ /* Because most platforms do not define ASM_OUTPUT_EXTERNAL, the
+ main body of this code is only rarely exercised. To provide some
+ testing, on all platforms, we make sure that the ASM_OUT_FILE is
+ open. If it's not, we should not be calling this function. */
+ gcc_assert (asm_out_file);
+
+#ifdef ASM_OUTPUT_EXTERNAL
+ if (!DECL_P (decl) || !DECL_EXTERNAL (decl) || !TREE_PUBLIC (decl))
+ return;
+
+ if (flag_unit_at_a_time)
+ pending_assemble_externals = tree_cons (0, decl,
+ pending_assemble_externals);
+ else
+ assemble_external_real (decl);
+#endif
+}
+
+/* Similar, for calling a library function FUN. */
+
void
-assemble_external_libcall (fun)
- rtx fun ATTRIBUTE_UNUSED;
+assemble_external_libcall (rtx fun)
{
-#ifdef ASM_OUTPUT_EXTERNAL_LIBCALL
/* Declare library function name external when first used, if nec. */
if (! SYMBOL_REF_USED (fun))
{
SYMBOL_REF_USED (fun) = 1;
- ASM_OUTPUT_EXTERNAL_LIBCALL (asm_out_file, fun);
+ targetm.asm_out.external_libcall (fun);
}
-#endif
}
/* Assemble a label named NAME. */
void
-assemble_label (name)
- const char *name;
+assemble_label (const char *name)
{
ASM_OUTPUT_LABEL (asm_out_file, name);
}
-/* Output to FILE a reference to the assembler name of a C-level name NAME.
- If NAME starts with a *, the rest of NAME is output verbatim.
- Otherwise NAME is transformed in an implementation-defined way
- (usually by the addition of an underscore).
- Many macros in the tm file are defined to call this function. */
+/* 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 (decl);
+ if (!DECL_EXTERNAL (decl)
+ && (!node->local.vtable_method || !cgraph_global_info_ready
+ || !node->local.finalized))
+ cgraph_mark_needed_node (node);
+ }
+ else if (TREE_CODE (decl) == VAR_DECL)
+ {
+ struct cgraph_varpool_node *node = cgraph_varpool_node (decl);
+ cgraph_varpool_mark_needed_node (node);
+ /* C++ frontend use mark_decl_references to force COMDAT variables
+ to be output that might appear dead otherwise. */
+ node->force_output = true;
+ }
+ /* else do nothing - we can get various sorts of CST nodes here,
+ which do not need to be marked. */
+}
+
+/* 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, name)
- FILE *file;
- const char *name;
+assemble_name (FILE *file, const char *name)
{
const char *real_name;
tree id;
- real_name = (* targetm.strip_name_encoding) (name);
+ real_name = targetm.strip_name_encoding (name);
id = maybe_get_identifier (real_name);
if (id)
- TREE_SYMBOL_REFERENCED (id) = 1;
+ mark_referenced (id);
- if (name[0] == '*')
- fputs (&name[1], file);
- else
- ASM_OUTPUT_LABELREF (file, name);
+ 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 (size)
- unsigned HOST_WIDE_INT size;
+assemble_static_space (unsigned HOST_WIDE_INT size)
{
char name[12];
const char *namestring;
This is done at most once per compilation.
Returns an RTX for the address of the template. */
+static GTY(()) rtx initial_trampoline;
+
#ifdef TRAMPOLINE_TEMPLATE
rtx
-assemble_trampoline_template ()
+assemble_trampoline_template (void)
{
char label[256];
const char *name;
int align;
rtx symbol;
+ if (initial_trampoline)
+ return initial_trampoline;
+
/* By default, put trampoline templates in read-only data section. */
#ifdef TRAMPOLINE_SECTION
ASM_OUTPUT_ALIGN (asm_out_file, align);
}
- (*targetm.asm_out.internal_label) (asm_out_file, "LTRAMP", 0);
+ targetm.asm_out.internal_label (asm_out_file, "LTRAMP", 0);
TRAMPOLINE_TEMPLATE (asm_out_file);
/* Record the rtl to refer to it. */
symbol = gen_rtx_SYMBOL_REF (Pmode, name);
SYMBOL_REF_FLAGS (symbol) = SYMBOL_FLAG_LOCAL;
- return symbol;
+ initial_trampoline = gen_rtx_MEM (BLKmode, symbol);
+ set_mem_align (initial_trampoline, TRAMPOLINE_ALIGNMENT);
+
+ return initial_trampoline;
}
#endif
\f
that may be assumed after adding the two together. */
static inline unsigned
-min_align (a, b)
- unsigned int a, b;
+min_align (unsigned int a, unsigned int b)
{
return (a | b) & -(a | b);
}
be followed immediately by the object's initial value. */
const char *
-integer_asm_op (size, aligned_p)
- int size;
- int aligned_p;
+integer_asm_op (int size, int aligned_p)
{
struct asm_int_op *ops;
start of the line, followed immediately by the value of X. */
void
-assemble_integer_with_op (op, x)
- const char *op;
- rtx x;
+assemble_integer_with_op (const char *op, rtx x)
{
fputs (op, asm_out_file);
output_addr_const (asm_out_file, x);
/* The default implementation of the asm_out.integer target hook. */
bool
-default_assemble_integer (x, size, aligned_p)
- rtx x ATTRIBUTE_UNUSED;
- unsigned int size ATTRIBUTE_UNUSED;
- int aligned_p ATTRIBUTE_UNUSED;
+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 / BITS_PER_UNIT)
+ return false;
return op && (assemble_integer_with_op (op, x), true);
}
/* Assemble the integer constant X into an object of SIZE bytes. ALIGN is
the alignment of the integer in bits. Return 1 if we were able to output
- the constant, otherwise 0. If FORCE is nonzero, abort if we can't output
- the constant. */
+ the constant, otherwise 0. We must be able to output the constant,
+ if FORCE is nonzero. */
bool
-assemble_integer (x, size, align, force)
- rtx x;
- unsigned int size;
- unsigned int align;
- int force;
+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))
+ if (targetm.asm_out.integer (x, size, aligned_p))
return true;
/* If the object is a multi-byte one, try splitting it up. Split
/* If we've printed some of it, but not all of it, there's no going
back now. */
- if (i > 0)
- abort ();
+ gcc_assert (!i);
}
- if (force)
- abort ();
-
+ gcc_assert (!force);
+
return false;
}
\f
void
-assemble_real (d, mode, align)
- REAL_VALUE_TYPE d;
- enum machine_mode mode;
- unsigned int align;
+assemble_real (REAL_VALUE_TYPE d, enum machine_mode mode, unsigned int align)
{
long data[4];
- long l;
- unsigned int nalign = min_align (align, 32);
+ int i;
+ int bitsize, nelts, nunits, units_per;
- switch (BITS_PER_UNIT)
- {
- case 8:
- switch (mode)
- {
- case SFmode:
- REAL_VALUE_TO_TARGET_SINGLE (d, l);
- assemble_integer (GEN_INT (l), 4, align, 1);
- break;
- case DFmode:
- REAL_VALUE_TO_TARGET_DOUBLE (d, data);
- assemble_integer (GEN_INT (data[0]), 4, align, 1);
- assemble_integer (GEN_INT (data[1]), 4, nalign, 1);
- break;
- case XFmode:
- REAL_VALUE_TO_TARGET_LONG_DOUBLE (d, data);
- assemble_integer (GEN_INT (data[0]), 4, align, 1);
- assemble_integer (GEN_INT (data[1]), 4, nalign, 1);
- assemble_integer (GEN_INT (data[2]), 4, nalign, 1);
- break;
- case TFmode:
- REAL_VALUE_TO_TARGET_LONG_DOUBLE (d, data);
- assemble_integer (GEN_INT (data[0]), 4, align, 1);
- assemble_integer (GEN_INT (data[1]), 4, nalign, 1);
- assemble_integer (GEN_INT (data[2]), 4, nalign, 1);
- assemble_integer (GEN_INT (data[3]), 4, nalign, 1);
- break;
- default:
- abort ();
- }
- break;
+ /* 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.
- case 16:
- switch (mode)
- {
- case HFmode:
- REAL_VALUE_TO_TARGET_SINGLE (d, l);
- assemble_integer (GEN_INT (l), 2, align, 1);
- break;
- case TQFmode:
- REAL_VALUE_TO_TARGET_DOUBLE (d, data);
- assemble_integer (GEN_INT (data[0]), 2, align, 1);
- assemble_integer (GEN_INT (data[1]), 1, nalign, 1);
- break;
- default:
- abort ();
- }
- break;
+ 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. */
- case 32:
- switch (mode)
- {
- case QFmode:
- REAL_VALUE_TO_TARGET_SINGLE (d, l);
- assemble_integer (GEN_INT (l), 1, align, 1);
- break;
- case HFmode:
- REAL_VALUE_TO_TARGET_DOUBLE (d, data);
- assemble_integer (GEN_INT (data[0]), 1, align, 1);
- assemble_integer (GEN_INT (data[1]), 1, nalign, 1);
- break;
- default:
- abort ();
- }
- break;
+ nunits = GET_MODE_SIZE (mode);
+ bitsize = nunits * BITS_PER_UNIT;
+ nelts = CEIL (bitsize, 32);
+ units_per = 32 / BITS_PER_UNIT;
- default:
- abort ();
+ 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;
}
}
\f
/* Given an expression EXP with a constant value,
reduce it to the sum of an assembler symbol and an integer.
Store them both in the structure *VALUE.
- Abort if EXP does not reduce. */
+ EXP must be reducible. */
struct addr_const GTY(())
{
};
static void
-decode_addr_const (exp, value)
- tree exp;
- struct addr_const *value;
+decode_addr_const (tree exp, struct addr_const *value)
{
tree target = TREE_OPERAND (exp, 0);
int offset = 0;
case LABEL_DECL:
x = gen_rtx_MEM (FUNCTION_MODE,
- gen_rtx_LABEL_REF (VOIDmode, force_label_rtx (target)));
+ gen_rtx_LABEL_REF (Pmode, force_label_rtx (target)));
break;
case REAL_CST:
break;
default:
- abort ();
+ gcc_unreachable ();
}
- if (GET_CODE (x) != MEM)
- abort ();
+ gcc_assert (MEM_P (x));
x = XEXP (x, 0);
value->base = x;
value->offset = offset;
}
\f
-/* We do RTX_UNSPEC + XINT (blah), so nothing can go after RTX_UNSPEC. */
-enum kind { RTX_UNKNOWN, RTX_DOUBLE, RTX_VECTOR, RTX_INT, RTX_UNSPEC };
-struct rtx_const GTY(())
-{
- ENUM_BITFIELD(kind) kind : 16;
- ENUM_BITFIELD(machine_mode) mode : 16;
- union rtx_const_un {
- REAL_VALUE_TYPE GTY ((tag ("4"))) du;
- struct rtx_const_u_addr {
- rtx base;
- const char *symbol;
- HOST_WIDE_INT offset;
- } GTY ((tag ("1"))) addr;
- struct rtx_const_u_di {
- HOST_WIDE_INT high;
- HOST_WIDE_INT low;
- } GTY ((tag ("0"))) di;
-
- /* The max vector size we have is 16 wide; two variants for
- integral and floating point vectors. */
- struct rtx_const_int_vec {
- HOST_WIDE_INT high;
- HOST_WIDE_INT low;
- } GTY ((tag ("2"))) int_vec[16];
-
- REAL_VALUE_TYPE GTY ((tag ("3"))) fp_vec[8];
-
- } GTY ((desc ("%1.kind >= RTX_INT"), descbits ("1"))) un;
-};
-
/* Uniquize all constants that appear in memory.
Each constant in memory thus far output is recorded
- in `const_hash_table'. */
+ in `const_desc_table'. */
struct constant_descriptor_tree GTY(())
{
/* The value of the constant. */
tree value;
+
+ /* Hash of value. Computing the hash from value each time
+ hashfn is called can't work properly, as that means recursive
+ use of the hash table during hash table expansion. */
+ hashval_t hash;
};
static GTY((param_is (struct constant_descriptor_tree)))
htab_t const_desc_htab;
-static struct constant_descriptor_tree * build_constant_desc PARAMS ((tree));
-static void maybe_output_constant_def_contents
- PARAMS ((struct constant_descriptor_tree *, int));
+static struct constant_descriptor_tree * build_constant_desc (tree);
+static void maybe_output_constant_def_contents (struct constant_descriptor_tree *, int);
/* Compute a hash code for a constant expression. */
static hashval_t
-const_desc_hash (ptr)
- const void *ptr;
+const_desc_hash (const void *ptr)
{
- return const_hash_1 (((struct constant_descriptor_tree *)ptr)->value);
+ return ((struct constant_descriptor_tree *)ptr)->hash;
}
static hashval_t
-const_hash_1 (exp)
- const tree exp;
+const_hash_1 (const tree exp)
{
const char *p;
hashval_t hi;
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 CONSTRUCTOR:
- if (TREE_CODE (TREE_TYPE (exp)) == SET_TYPE)
- {
- char *tmp;
-
- len = int_size_in_bytes (TREE_TYPE (exp));
- tmp = (char *) alloca (len);
- get_set_constructor_bytes (exp, (unsigned char *) tmp, len);
- p = tmp;
- break;
- }
- else
- {
- tree link;
-
- hi = 5 + int_size_in_bytes (TREE_TYPE (exp));
-
- for (link = CONSTRUCTOR_ELTS (exp); link; link = TREE_CHAIN (link))
- if (TREE_VALUE (link))
- hi = hi * 603 + const_hash_1 (TREE_VALUE (link));
-
- return hi;
- }
+ {
+ 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);
- if (GET_CODE (value.base) == SYMBOL_REF)
+ switch (GET_CODE (value.base))
{
+ case SYMBOL_REF:
/* Don't hash the address of the SYMBOL_REF;
only use the offset and the symbol name. */
hi = value.offset;
p = XSTR (value.base, 0);
for (i = 0; p[i] != 0; i++)
hi = ((hi * 613) + (unsigned) (p[i]));
+ break;
+
+ case LABEL_REF:
+ hi = value.offset + CODE_LABEL_NUMBER (XEXP (value.base, 0)) * 13;
+ break;
+
+ default:
+ gcc_unreachable ();
}
- else if (GET_CODE (value.base) == LABEL_REF)
- hi = value.offset + CODE_LABEL_NUMBER (XEXP (value.base, 0)) * 13;
- else
- abort ();
}
return hi;
return code;
}
- /* Compute hashing function */
+ /* Compute hashing function. */
hi = len;
for (i = 0; i < len; i++)
hi = ((hi * 613) + (unsigned) (p[i]));
/* Wrapper of compare_constant, for the htab interface. */
static int
-const_desc_eq (p1, p2)
- const void *p1;
- const void *p2;
+const_desc_eq (const void *p1, const void *p2)
{
- return compare_constant (((struct constant_descriptor_tree *)p1)->value,
- ((struct constant_descriptor_tree *)p2)->value);
+ const struct constant_descriptor_tree *c1 = p1;
+ const struct constant_descriptor_tree *c2 = p2;
+ if (c1->hash != c2->hash)
+ return 0;
+ return compare_constant (c1->value, c2->value);
}
/* Compare t1 and t2, and return 1 only if they are known to result in
the same bit pattern on output. */
static int
-compare_constant (t1, t2)
- const tree t1;
- const tree t2;
+compare_constant (const tree t1, const tree t2)
{
enum tree_code typecode;
return REAL_VALUES_IDENTICAL (TREE_REAL_CST (t1), TREE_REAL_CST (t2));
case STRING_CST:
- if (flag_writable_strings)
- return 0;
-
if (TYPE_MODE (TREE_TYPE (t1)) != TYPE_MODE (TREE_TYPE (t2)))
return 0;
&& compare_constant (TREE_IMAGPART (t1), TREE_IMAGPART (t2)));
case CONSTRUCTOR:
- typecode = TREE_CODE (TREE_TYPE (t1));
- if (typecode != TREE_CODE (TREE_TYPE (t2)))
- return 0;
-
- if (typecode == SET_TYPE)
- {
- int len = int_size_in_bytes (TREE_TYPE (t2));
- unsigned char *tmp1, *tmp2;
-
- if (int_size_in_bytes (TREE_TYPE (t1)) != len)
- return 0;
+ {
+ VEC(constructor_elt, gc) *v1, *v2;
+ unsigned HOST_WIDE_INT idx;
+
+ typecode = TREE_CODE (TREE_TYPE (t1));
+ if (typecode != TREE_CODE (TREE_TYPE (t2)))
+ return 0;
- tmp1 = (unsigned char *) alloca (len);
- tmp2 = (unsigned char *) alloca (len);
+ if (typecode == ARRAY_TYPE)
+ {
+ HOST_WIDE_INT size_1 = int_size_in_bytes (TREE_TYPE (t1));
+ /* For arrays, check that the sizes all match. */
+ if (TYPE_MODE (TREE_TYPE (t1)) != TYPE_MODE (TREE_TYPE (t2))
+ || size_1 == -1
+ || size_1 != int_size_in_bytes (TREE_TYPE (t2)))
+ return 0;
+ }
+ else
+ {
+ /* For record and union constructors, require exact type
+ equality. */
+ if (TREE_TYPE (t1) != TREE_TYPE (t2))
+ return 0;
+ }
- if (get_set_constructor_bytes (t1, tmp1, len) != NULL_TREE)
+ v1 = CONSTRUCTOR_ELTS (t1);
+ v2 = CONSTRUCTOR_ELTS (t2);
+ if (VEC_length (constructor_elt, v1)
+ != VEC_length (constructor_elt, v2))
return 0;
- if (get_set_constructor_bytes (t2, tmp2, len) != NULL_TREE)
- return 0;
-
- return memcmp (tmp1, tmp2, len) != 0;
- }
- else
- {
- tree l1, l2;
-
- if (typecode == ARRAY_TYPE)
- {
- HOST_WIDE_INT size_1 = int_size_in_bytes (TREE_TYPE (t1));
- /* For arrays, check that the sizes all match. */
- if (TYPE_MODE (TREE_TYPE (t1)) != TYPE_MODE (TREE_TYPE (t2))
- || size_1 == -1
- || size_1 != int_size_in_bytes (TREE_TYPE (t2)))
- return 0;
- }
- else
- {
- /* For record and union constructors, require exact type
- equality. */
- if (TREE_TYPE (t1) != TREE_TYPE (t2))
- return 0;
- }
-
- for (l1 = CONSTRUCTOR_ELTS (t1), l2 = CONSTRUCTOR_ELTS (t2);
- l1 && l2;
- l1 = TREE_CHAIN (l1), l2 = TREE_CHAIN (l2))
- {
- /* Check that each value is the same... */
- if (! compare_constant (TREE_VALUE (l1), TREE_VALUE (l2)))
- return 0;
- /* ... and that they apply to the same fields! */
- if (typecode == ARRAY_TYPE)
- {
- if (! compare_constant (TREE_PURPOSE (l1),
- TREE_PURPOSE (l2)))
- return 0;
- }
- else
- {
- if (TREE_PURPOSE (l1) != TREE_PURPOSE (l2))
- return 0;
- }
- }
- return l1 == NULL_TREE && l2 == NULL_TREE;
- }
+ for (idx = 0; idx < VEC_length (constructor_elt, v1); ++idx)
+ {
+ constructor_elt *c1 = VEC_index (constructor_elt, v1, idx);
+ constructor_elt *c2 = VEC_index (constructor_elt, v2, idx);
+
+ /* Check that each value is the same... */
+ if (!compare_constant (c1->value, c2->value))
+ return 0;
+ /* ... and that they apply to the same fields! */
+ if (typecode == ARRAY_TYPE)
+ {
+ if (!compare_constant (c1->index, c2->index))
+ return 0;
+ }
+ else
+ {
+ if (c1->index != c2->index)
+ return 0;
+ }
+ }
+
+ return 1;
+ }
case ADDR_EXPR:
case FDESC_EXPR:
case NOP_EXPR:
case CONVERT_EXPR:
case NON_LVALUE_EXPR:
+ case VIEW_CONVERT_EXPR:
return compare_constant (TREE_OPERAND (t1, 0), TREE_OPERAND (t2, 0));
default:
{
tree nt1, nt2;
- nt1 = (*lang_hooks.expand_constant) (t1);
- nt2 = (*lang_hooks.expand_constant) (t2);
+ nt1 = lang_hooks.expand_constant (t1);
+ nt2 = lang_hooks.expand_constant (t2);
if (nt1 != t1 || nt2 != t2)
return compare_constant (nt1, nt2);
else
}
}
- /* Should not get here. */
- abort ();
+ gcc_unreachable ();
}
\f
/* Make a copy of the whole tree structure for a constant. This
handles the same types of nodes that compare_constant handles. */
static tree
-copy_constant (exp)
- tree exp;
+copy_constant (tree exp)
{
switch (TREE_CODE (exp))
{
case ADDR_EXPR:
/* For ADDR_EXPR, we do not want to copy the decl whose address
is requested. We do want to copy constants though. */
- if (TREE_CODE_CLASS (TREE_CODE (TREE_OPERAND (exp, 0))) == 'c')
+ if (CONSTANT_CLASS_P (TREE_OPERAND (exp, 0)))
return build1 (TREE_CODE (exp), TREE_TYPE (exp),
copy_constant (TREE_OPERAND (exp, 0)));
else
case PLUS_EXPR:
case MINUS_EXPR:
- return build (TREE_CODE (exp), TREE_TYPE (exp),
- copy_constant (TREE_OPERAND (exp, 0)),
- copy_constant (TREE_OPERAND (exp, 1)));
+ return build2 (TREE_CODE (exp), TREE_TYPE (exp),
+ copy_constant (TREE_OPERAND (exp, 0)),
+ copy_constant (TREE_OPERAND (exp, 1)));
case NOP_EXPR:
case CONVERT_EXPR:
case CONSTRUCTOR:
{
tree copy = copy_node (exp);
- tree list = copy_list (CONSTRUCTOR_ELTS (exp));
- tree tail;
-
- CONSTRUCTOR_ELTS (copy) = list;
- for (tail = list; tail; tail = TREE_CHAIN (tail))
- TREE_VALUE (tail) = copy_constant (TREE_VALUE (tail));
- if (TREE_CODE (TREE_TYPE (exp)) == SET_TYPE)
- for (tail = list; tail; tail = TREE_CHAIN (tail))
- TREE_PURPOSE (tail) = copy_constant (TREE_PURPOSE (tail));
-
+ VEC(constructor_elt, gc) *v;
+ unsigned HOST_WIDE_INT idx;
+ tree purpose, value;
+
+ v = VEC_alloc(constructor_elt, gc, VEC_length(constructor_elt,
+ CONSTRUCTOR_ELTS (exp)));
+ FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (exp), idx, purpose, value)
+ {
+ constructor_elt *ce = VEC_quick_push (constructor_elt, v, NULL);
+ ce->index = purpose;
+ ce->value = copy_constant (value);
+ }
+ CONSTRUCTOR_ELTS (copy) = v;
return copy;
}
default:
{
- tree t;
- t = (*lang_hooks.expand_constant) (exp);
- if (t != exp)
- return copy_constant (t);
- else
- abort ();
+ tree t = lang_hooks.expand_constant (exp);
+
+ gcc_assert (t == exp);
+ return copy_constant (t);
}
}
}
Caller is responsible for updating the hash table. */
static struct constant_descriptor_tree *
-build_constant_desc (exp)
- tree exp;
+build_constant_desc (tree exp)
{
rtx symbol;
rtx rtl;
desc = ggc_alloc (sizeof (*desc));
desc->value = copy_constant (exp);
+ /* Propagate marked-ness to copied constant. */
+ if (flag_mudflap && mf_marked_p (exp))
+ mf_mark (desc->value);
+
/* Create a string containing the label name, in LABEL. */
labelno = const_labelno++;
ASM_GENERATE_INTERNAL_LABEL (label, "LC", labelno);
information. This call might invalidate our local variable
SYMBOL; we can't use it afterward. */
- (*targetm.encode_section_info) (exp, rtl, true);
+ targetm.encode_section_info (exp, rtl, true);
desc->rtl = rtl;
If DEFER is nonzero, this constant can be deferred and output only
if referenced in the function after all optimizations.
- The const_hash_table records which constants already have label strings. */
+ `const_desc_table' records which constants already have label strings. */
rtx
-output_constant_def (exp, defer)
- tree exp;
- int defer;
+output_constant_def (tree exp, int defer)
{
struct constant_descriptor_tree *desc;
struct constant_descriptor_tree key;
/* Look up EXP in the table of constant descriptors. If we didn't find
it, create a new one. */
key.value = exp;
- loc = htab_find_slot (const_desc_htab, &key, INSERT);
+ key.hash = const_hash_1 (exp);
+ loc = htab_find_slot_with_hash (const_desc_htab, &key, key.hash, INSERT);
desc = *loc;
if (desc == 0)
{
desc = build_constant_desc (exp);
+ desc->hash = key.hash;
*loc = desc;
}
/* Subroutine of output_constant_def: Decide whether or not we need to
output the constant DESC now, and if so, do it. */
static void
-maybe_output_constant_def_contents (desc, defer)
- struct constant_descriptor_tree *desc;
- int defer;
+maybe_output_constant_def_contents (struct constant_descriptor_tree *desc,
+ int defer)
{
rtx symbol = XEXP (desc->rtl, 0);
tree exp = desc->value;
/* Already output; don't do it again. */
return;
- /* The only constants that cannot safely be deferred, assuming the
- context allows it, are strings under flag_writable_strings. */
- if (defer && (TREE_CODE (exp) != STRING_CST || !flag_writable_strings))
+ /* We can always defer constants as long as the context allows
+ doing so. */
+ if (defer)
{
/* Increment n_deferred_constants if it exists. It needs to be at
least as large as the number of constants actually referred to
/* We must output the constant data referred to by SYMBOL; do so. */
static void
-output_constant_def_contents (symbol)
- rtx symbol;
+output_constant_def_contents (rtx symbol)
{
tree exp = SYMBOL_REF_DECL (symbol);
const char *label = XSTR (symbol, 0);
+ HOST_WIDE_INT size;
/* Make sure any other constants whose addresses appear in EXP
are assigned label numbers. */
- int reloc = output_addressed_constants (exp);
+ int reloc = compute_reloc_for_constant (exp);
/* Align the location counter as required by EXP's data type. */
- int align = TYPE_ALIGN (TREE_TYPE (exp));
+ unsigned int align = TYPE_ALIGN (TREE_TYPE (exp));
#ifdef CONSTANT_ALIGNMENT
align = CONSTANT_ALIGNMENT (exp, align);
#endif
+ output_addressed_constants (exp);
+
/* We are no longer deferring this constant. */
TREE_ASM_WRITTEN (exp) = 1;
if (IN_NAMED_SECTION (exp))
named_section (exp, NULL, reloc);
else
- (*targetm.asm_out.select_section) (exp, reloc, align);
+ targetm.asm_out.select_section (exp, reloc, align);
if (align > BITS_PER_UNIT)
{
ASM_OUTPUT_ALIGN (asm_out_file, floor_log2 (align / BITS_PER_UNIT));
}
- /* Output the label itself. */
+ size = int_size_in_bytes (TREE_TYPE (exp));
+ if (TREE_CODE (exp) == STRING_CST)
+ size = MAX (TREE_STRING_LENGTH (exp), size);
+
+ /* Do any machine/system dependent processing of the constant. */
+#ifdef ASM_DECLARE_CONSTANT_NAME
+ ASM_DECLARE_CONSTANT_NAME (asm_out_file, label, exp, size);
+#else
+ /* Standard thing is just output label for the constant. */
ASM_OUTPUT_LABEL (asm_out_file, label);
+#endif /* ASM_DECLARE_CONSTANT_NAME */
/* Output the value of EXP. */
- output_constant (exp,
- (TREE_CODE (exp) == STRING_CST
- ? MAX (TREE_STRING_LENGTH (exp),
- int_size_in_bytes (TREE_TYPE (exp)))
- : int_size_in_bytes (TREE_TYPE (exp))),
- align);
-
+ output_constant (exp, size, align);
+ if (flag_mudflap)
+ mudflap_enqueue_constant (exp);
}
-/* A constant which was deferred in its original location has been
- inserted by the RTL inliner into a different function. The
- current function's deferred constant count must be incremented. */
-void
-notice_rtl_inlining_of_deferred_constant ()
+/* Look up EXP in the table of constant descriptors. Return the rtl
+ if it has been emitted, else null. */
+
+rtx
+lookup_constant_def (tree exp)
{
- n_deferred_constants++;
+ struct constant_descriptor_tree *desc;
+ struct constant_descriptor_tree key;
+
+ key.value = exp;
+ key.hash = const_hash_1 (exp);
+ desc = htab_find_with_hash (const_desc_htab, &key, key.hash);
+
+ return (desc ? desc->rtl : NULL_RTX);
}
\f
/* Used in the hash tables to avoid outputting the same constant
twice. Unlike 'struct constant_descriptor_tree', RTX constants
- are output once per function, not once per file; there seems
- to be no reason for the difference. */
-
-struct constant_descriptor_rtx GTY(())
-{
- /* More constant_descriptors with the same hash code. */
- struct constant_descriptor_rtx *next;
-
- /* A MEM for the constant. */
- rtx rtl;
-
- /* The value of the constant. */
- struct rtx_const value;
+ are output once per function, not once per file. */
+/* ??? Only a few targets need per-function constant pools. Most
+ can use one per-file pool. Should add a targetm bit to tell the
+ difference. */
+
+struct rtx_constant_pool GTY(())
+{
+ /* Pointers to first and last constant in pool, as ordered by offset. */
+ struct constant_descriptor_rtx *first;
+ struct constant_descriptor_rtx *last;
+
+ /* Hash facility for making memory-constants from constant rtl-expressions.
+ It is used on RISC machines where immediate integer arguments and
+ constant addresses are restricted so that such constants must be stored
+ in memory. */
+ htab_t GTY((param_is (struct constant_descriptor_rtx))) const_rtx_htab;
+ htab_t GTY((param_is (struct constant_descriptor_rtx))) const_rtx_sym_htab;
+
+ /* Current offset in constant pool (does not include any
+ machine-specific header). */
+ HOST_WIDE_INT offset;
};
-/* Structure to represent sufficient information about a constant so that
- it can be output when the constant pool is output, so that function
- integration can be done, and to simplify handling on machines that reference
- constant pool as base+displacement. */
-
-struct pool_constant GTY(())
+struct constant_descriptor_rtx GTY((chain_next ("%h.next")))
{
- struct constant_descriptor_rtx *desc;
- struct pool_constant *next;
- struct pool_constant *next_sym;
+ struct constant_descriptor_rtx *next;
+ rtx mem;
+ rtx sym;
rtx constant;
+ HOST_WIDE_INT offset;
+ hashval_t hash;
enum machine_mode mode;
- int labelno;
unsigned int align;
- HOST_WIDE_INT offset;
+ int labelno;
int mark;
};
-/* Hash code for a SYMBOL_REF with CONSTANT_POOL_ADDRESS_P true.
- The argument is XSTR (... , 0) */
+/* Hash and compare functions for const_rtx_htab. */
-#define SYMHASH(LABEL) (((unsigned long) (LABEL)) % MAX_RTX_HASH_TABLE)
-\f
-/* Initialize constant pool hashing for a new function. */
+static hashval_t
+const_desc_rtx_hash (const void *ptr)
+{
+ const struct constant_descriptor_rtx *desc = ptr;
+ return desc->hash;
+}
-void
-init_varasm_status (f)
- struct function *f;
+static int
+const_desc_rtx_eq (const void *a, const void *b)
{
- struct varasm_status *p;
- p = (struct varasm_status *) ggc_alloc (sizeof (struct varasm_status));
- f->varasm = p;
- p->x_const_rtx_hash_table
- = ((struct constant_descriptor_rtx **)
- ggc_alloc_cleared (MAX_RTX_HASH_TABLE
- * sizeof (struct constant_descriptor_rtx *)));
- p->x_const_rtx_sym_hash_table
- = ((struct pool_constant **)
- ggc_alloc_cleared (MAX_RTX_HASH_TABLE
- * sizeof (struct pool_constant *)));
-
- p->x_first_pool = p->x_last_pool = 0;
- p->x_pool_offset = 0;
- p->deferred_constants = 0;
+ const struct constant_descriptor_rtx *x = a;
+ const struct constant_descriptor_rtx *y = b;
+
+ if (x->mode != y->mode)
+ return 0;
+ return rtx_equal_p (x->constant, y->constant);
}
-\f
-/* Express an rtx for a constant integer (perhaps symbolic)
- as the sum of a symbol or label plus an explicit integer.
- They are stored into VALUE. */
+/* Hash and compare functions for const_rtx_sym_htab. */
-static void
-decode_rtx_const (mode, x, value)
- enum machine_mode mode;
- rtx x;
- struct rtx_const *value;
+static hashval_t
+const_desc_rtx_sym_hash (const void *ptr)
{
- /* Clear the whole structure, including any gaps. */
- memset (value, 0, sizeof (struct rtx_const));
+ const struct constant_descriptor_rtx *desc = ptr;
+ return htab_hash_string (XSTR (desc->sym, 0));
+}
- value->kind = RTX_INT; /* Most usual kind. */
- value->mode = mode;
+static int
+const_desc_rtx_sym_eq (const void *a, const void *b)
+{
+ const struct constant_descriptor_rtx *x = a;
+ const struct constant_descriptor_rtx *y = b;
+ return XSTR (x->sym, 0) == XSTR (y->sym, 0);
+}
- switch (GET_CODE (x))
+/* This is the worker function for const_rtx_hash, called via for_each_rtx. */
+
+static int
+const_rtx_hash_1 (rtx *xp, void *data)
+{
+ unsigned HOST_WIDE_INT hwi;
+ enum machine_mode mode;
+ enum rtx_code code;
+ hashval_t h, *hp;
+ rtx x;
+
+ x = *xp;
+ code = GET_CODE (x);
+ mode = GET_MODE (x);
+ h = (hashval_t) code * 1048573 + mode;
+
+ switch (code)
{
- case CONST_DOUBLE:
- value->kind = RTX_DOUBLE;
- if (GET_MODE (x) != VOIDmode)
- {
- const REAL_VALUE_TYPE *r = CONST_DOUBLE_REAL_VALUE (x);
+ case CONST_INT:
+ hwi = INTVAL (x);
+ fold_hwi:
+ {
+ const int shift = sizeof (hashval_t) * CHAR_BIT;
+ const int n = sizeof (HOST_WIDE_INT) / sizeof (hashval_t);
+ int i;
- value->mode = GET_MODE (x);
+ h ^= (hashval_t) hwi;
+ for (i = 1; i < n; ++i)
+ {
+ hwi >>= shift;
+ h ^= (hashval_t) hwi;
+ }
+ }
+ break;
- /* Copy the REAL_VALUE_TYPE by members so that we don't
- copy garbage from the original structure into our
- carefully cleaned hashing structure. */
- value->un.du.class = r->class;
- value->un.du.sign = r->sign;
- switch (r->class)
- {
- case rvc_zero:
- case rvc_inf:
- break;
- case rvc_normal:
- value->un.du.exp = r->exp;
- /* FALLTHRU */
- case rvc_nan:
- memcpy (value->un.du.sig, r->sig, sizeof (r->sig));
- break;
- default:
- abort ();
- }
- }
- else
+ case CONST_DOUBLE:
+ if (mode == VOIDmode)
{
- value->un.di.low = CONST_DOUBLE_LOW (x);
- value->un.di.high = CONST_DOUBLE_HIGH (x);
+ hwi = CONST_DOUBLE_LOW (x) ^ CONST_DOUBLE_HIGH (x);
+ goto fold_hwi;
}
+ else
+ h ^= real_hash (CONST_DOUBLE_REAL_VALUE (x));
break;
case CONST_VECTOR:
{
- int units, i;
-
- units = CONST_VECTOR_NUNITS (x);
- value->kind = RTX_VECTOR;
- value->mode = mode;
-
- if (GET_MODE_CLASS (mode) == MODE_VECTOR_INT)
- {
- for (i = 0; i < units; ++i)
- {
- rtx elt = CONST_VECTOR_ELT (x, i);
- if (GET_CODE (elt) == CONST_INT)
- {
- value->un.int_vec[i].low = INTVAL (elt);
- value->un.int_vec[i].high = 0;
- }
- else
- {
- value->un.int_vec[i].low = CONST_DOUBLE_LOW (elt);
- value->un.int_vec[i].high = CONST_DOUBLE_HIGH (elt);
- }
- }
- }
- else if (GET_MODE_CLASS (mode) == MODE_VECTOR_FLOAT)
- {
- for (i = 0; i < units; ++i)
- {
- const REAL_VALUE_TYPE *r
- = CONST_DOUBLE_REAL_VALUE (CONST_VECTOR_ELT (x, i));
- REAL_VALUE_TYPE *d = &value->un.fp_vec[i];
-
- /* Copy the REAL_VALUE_TYPE by members so that we don't
- copy garbage from the original structure into our
- carefully cleaned hashing structure. */
- d->class = r->class;
- d->sign = r->sign;
- switch (r->class)
- {
- case rvc_zero:
- case rvc_inf:
- break;
- case rvc_normal:
- d->exp = r->exp;
- /* FALLTHRU */
- case rvc_nan:
- memcpy (d->sig, r->sig, sizeof (r->sig));
- break;
- default:
- abort ();
- }
- }
- }
- else
- abort ();
+ int i;
+ for (i = XVECLEN (x, 0); i-- > 0; )
+ h = h * 251 + const_rtx_hash_1 (&XVECEXP (x, 0, i), data);
}
break;
- case CONST_INT:
- value->un.addr.offset = INTVAL (x);
+ case SYMBOL_REF:
+ h ^= htab_hash_string (XSTR (x, 0));
break;
- case SYMBOL_REF:
case LABEL_REF:
- case PC:
- value->un.addr.base = x;
+ h = h * 251 + CODE_LABEL_NUMBER (XEXP (x, 0));
break;
- case CONST:
- x = XEXP (x, 0);
- if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 1)) == CONST_INT)
- {
- value->un.addr.base = XEXP (x, 0);
- value->un.addr.offset = INTVAL (XEXP (x, 1));
- }
- else if (GET_CODE (x) == MINUS && GET_CODE (XEXP (x, 1)) == CONST_INT)
- {
- value->un.addr.base = XEXP (x, 0);
- value->un.addr.offset = - INTVAL (XEXP (x, 1));
- }
- else
- {
- value->un.addr.base = x;
- value->un.addr.offset = 0;
- }
+ case UNSPEC:
+ case UNSPEC_VOLATILE:
+ h = h * 251 + XINT (x, 1);
break;
default:
- value->kind = RTX_UNKNOWN;
break;
}
- if (value->kind == RTX_INT && value->un.addr.base != 0
- && GET_CODE (value->un.addr.base) == UNSPEC)
- {
- /* For a simple UNSPEC, the base is set to the
- operand, the kind field is set to the index of
- the unspec expression.
- Together with the code below, in case that
- the operand is a SYMBOL_REF or LABEL_REF,
- the address of the string or the code_label
- is taken as base. */
- if (XVECLEN (value->un.addr.base, 0) == 1)
- {
- value->kind = RTX_UNSPEC + XINT (value->un.addr.base, 1);
- value->un.addr.base = XVECEXP (value->un.addr.base, 0, 0);
- }
- }
-
- if (value->kind >= RTX_INT && value->un.addr.base != 0)
- switch (GET_CODE (value->un.addr.base))
- {
- case SYMBOL_REF:
- /* Use the string's address, not the SYMBOL_REF's address,
- for the sake of addresses of library routines. */
- value->un.addr.symbol = XSTR (value->un.addr.base, 0);
- value->un.addr.base = NULL_RTX;
- break;
-
- case LABEL_REF:
- /* For a LABEL_REF, compare labels. */
- value->un.addr.base = XEXP (value->un.addr.base, 0);
-
- default:
- break;
- }
-}
-
-/* Given a MINUS expression, simplify it if both sides
- include the same symbol. */
-
-rtx
-simplify_subtraction (x)
- rtx x;
-{
- struct rtx_const val0, val1;
-
- decode_rtx_const (GET_MODE (x), XEXP (x, 0), &val0);
- decode_rtx_const (GET_MODE (x), XEXP (x, 1), &val1);
-
- if (val0.kind >= RTX_INT
- && val0.kind == val1.kind
- && val0.un.addr.base == val1.un.addr.base
- && val0.un.addr.symbol == val1.un.addr.symbol)
- return GEN_INT (val0.un.addr.offset - val1.un.addr.offset);
-
- return x;
+ hp = data;
+ *hp = *hp * 509 + h;
+ return 0;
}
-/* Compute a hash code for a constant RTL expression. */
+/* Compute a hash value for X, which should be a constant. */
-static unsigned int
-const_hash_rtx (mode, x)
- enum machine_mode mode;
- rtx x;
+static hashval_t
+const_rtx_hash (rtx x)
{
- union {
- struct rtx_const value;
- unsigned int data[sizeof(struct rtx_const) / sizeof (unsigned int)];
- } u;
-
- unsigned int hi;
- size_t i;
-
- decode_rtx_const (mode, x, &u.value);
-
- /* Compute hashing function */
- hi = 0;
- for (i = 0; i < ARRAY_SIZE (u.data); i++)
- hi = hi * 613 + u.data[i];
-
- return hi % MAX_RTX_HASH_TABLE;
+ hashval_t h = 0;
+ for_each_rtx (&x, const_rtx_hash_1, &h);
+ return h;
}
-/* Compare a constant rtl object X with a constant-descriptor DESC.
- Return 1 if DESC describes a constant with the same value as X. */
+\f
+/* Initialize constant pool hashing for a new function. */
-static int
-compare_constant_rtx (mode, x, desc)
- enum machine_mode mode;
- rtx x;
- struct constant_descriptor_rtx *desc;
+void
+init_varasm_status (struct function *f)
{
- struct rtx_const value;
-
- decode_rtx_const (mode, x, &value);
-
- /* Compare constant contents. */
- return memcmp (&value, &desc->value, sizeof (struct rtx_const)) == 0;
-}
-
-/* Construct a constant descriptor for the rtl-expression X.
- It is up to the caller to enter the descriptor in the hash table. */
+ struct varasm_status *p;
+ struct rtx_constant_pool *pool;
-static struct constant_descriptor_rtx *
-record_constant_rtx (mode, x)
- enum machine_mode mode;
- rtx x;
-{
- struct constant_descriptor_rtx *ptr;
+ p = ggc_alloc (sizeof (struct varasm_status));
+ f->varasm = p;
- ptr = (struct constant_descriptor_rtx *) ggc_alloc (sizeof (*ptr));
- decode_rtx_const (mode, x, &ptr->value);
+ pool = ggc_alloc (sizeof (struct rtx_constant_pool));
+ p->pool = pool;
+ p->deferred_constants = 0;
- return ptr;
+ pool->const_rtx_htab = htab_create_ggc (31, const_desc_rtx_hash,
+ const_desc_rtx_eq, NULL);
+ pool->const_rtx_sym_htab = htab_create_ggc (31, const_desc_rtx_sym_hash,
+ const_desc_rtx_sym_eq, NULL);
+ pool->first = pool->last = NULL;
+ pool->offset = 0;
}
\f
-/* Given a constant rtx X, return a MEM for the location in memory at which
- this constant has been placed. Return 0 if it not has been placed yet. */
+/* Given a MINUS expression, simplify it if both sides
+ include the same symbol. */
rtx
-mem_for_const_double (x)
- rtx x;
+simplify_subtraction (rtx x)
{
- enum machine_mode mode = GET_MODE (x);
- struct constant_descriptor_rtx *desc;
-
- for (desc = const_rtx_hash_table[const_hash_rtx (mode, x)]; desc;
- desc = desc->next)
- if (compare_constant_rtx (mode, x, desc))
- return desc->rtl;
-
- return 0;
+ rtx r = simplify_rtx (x);
+ return r ? r : x;
}
-
+\f
/* Given a constant rtx X, make (or find) a memory constant for its value
and return a MEM rtx to refer to it in memory. */
rtx
-force_const_mem (mode, x)
- enum machine_mode mode;
- rtx x;
+force_const_mem (enum machine_mode mode, rtx x)
{
- int hash;
- struct constant_descriptor_rtx *desc;
+ struct constant_descriptor_rtx *desc, tmp;
+ struct rtx_constant_pool *pool = cfun->varasm->pool;
char label[256];
rtx def, symbol;
- struct pool_constant *pool;
+ hashval_t hash;
unsigned int align;
+ void **slot;
/* If we're not allowed to drop X into the constant pool, don't. */
- if ((*targetm.cannot_force_const_mem) (x))
+ if (targetm.cannot_force_const_mem (x))
return NULL_RTX;
- /* Compute hash code of X. Search the descriptors for that hash code
- to see if any of them describes X. If yes, we have an rtx to use. */
- hash = const_hash_rtx (mode, x);
- for (desc = const_rtx_hash_table[hash]; desc; desc = desc->next)
- if (compare_constant_rtx (mode, x, desc))
- return desc->rtl;
+ /* Lookup the value in the hashtable. */
+ tmp.constant = x;
+ tmp.mode = mode;
+ hash = const_rtx_hash (x);
+ slot = htab_find_slot_with_hash (pool->const_rtx_htab, &tmp, hash, INSERT);
+ desc = *slot;
+
+ /* If the constant was already present, return its memory. */
+ if (desc)
+ return copy_rtx (desc->mem);
- /* No constant equal to X is known to have been output.
- Make a constant descriptor to enter X in the hash table
- and make a MEM for it. */
- desc = record_constant_rtx (mode, x);
- desc->next = const_rtx_hash_table[hash];
- const_rtx_hash_table[hash] = desc;
+ /* Otherwise, create a new descriptor. */
+ desc = ggc_alloc (sizeof (*desc));
+ *slot = desc;
/* Align the location counter as required by EXP's data type. */
align = GET_MODE_ALIGNMENT (mode == VOIDmode ? word_mode : mode);
#ifdef CONSTANT_ALIGNMENT
- align = CONSTANT_ALIGNMENT (make_tree ((*lang_hooks.types.type_for_mode)
- (mode, 0), x), align);
+ {
+ tree type = lang_hooks.types.type_for_mode (mode, 0);
+ if (type != NULL_TREE)
+ align = CONSTANT_ALIGNMENT (make_tree (type, x), align);
+ }
#endif
- pool_offset += (align / BITS_PER_UNIT) - 1;
- pool_offset &= ~ ((align / BITS_PER_UNIT) - 1);
-
- if (GET_CODE (x) == LABEL_REF)
- LABEL_PRESERVE_P (XEXP (x, 0)) = 1;
-
- /* Allocate a pool constant descriptor, fill it in, and chain it in. */
- pool = (struct pool_constant *) ggc_alloc (sizeof (struct pool_constant));
- pool->desc = desc;
- pool->constant = x;
- pool->mode = mode;
- pool->labelno = const_labelno;
- pool->align = align;
- pool->offset = pool_offset;
- pool->mark = 1;
- pool->next = 0;
-
- if (last_pool == 0)
- first_pool = pool;
+ pool->offset += (align / BITS_PER_UNIT) - 1;
+ pool->offset &= ~ ((align / BITS_PER_UNIT) - 1);
+
+ desc->next = NULL;
+ desc->constant = tmp.constant;
+ desc->offset = pool->offset;
+ desc->hash = hash;
+ desc->mode = mode;
+ desc->align = align;
+ desc->labelno = const_labelno;
+ desc->mark = 0;
+
+ pool->offset += GET_MODE_SIZE (mode);
+ if (pool->last)
+ pool->last->next = desc;
else
- last_pool->next = pool;
-
- last_pool = pool;
- pool_offset += GET_MODE_SIZE (mode);
+ pool->first = pool->last = desc;
+ pool->last = desc;
/* Create a string containing the label name, in LABEL. */
ASM_GENERATE_INTERNAL_LABEL (label, "LC", const_labelno);
-
++const_labelno;
- /* Construct the SYMBOL_REF and the MEM. */
-
- symbol = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (label));
+ /* Construct the SYMBOL_REF. Make sure to mark it as belonging to
+ the constants pool. */
+ desc->sym = symbol = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (label));
SYMBOL_REF_FLAGS (symbol) = SYMBOL_FLAG_LOCAL;
+ CONSTANT_POOL_ADDRESS_P (symbol) = 1;
+ current_function_uses_const_pool = 1;
- pool->desc->rtl = def = gen_rtx_MEM (mode, symbol);
- set_mem_attributes (def, (*lang_hooks.types.type_for_mode) (mode, 0), 1);
- RTX_UNCHANGING_P (def) = 1;
+ /* Insert the descriptor into the symbol cross-reference table too. */
+ slot = htab_find_slot (pool->const_rtx_sym_htab, desc, INSERT);
+ gcc_assert (!*slot);
+ *slot = desc;
- /* Add label to symbol hash table. */
- hash = SYMHASH (XSTR (symbol, 0));
- pool->next_sym = const_rtx_sym_hash_table[hash];
- const_rtx_sym_hash_table[hash] = pool;
+ /* Construct the MEM. */
+ desc->mem = def = gen_const_mem (mode, symbol);
+ set_mem_attributes (def, lang_hooks.types.type_for_mode (mode, 0), 1);
+ set_mem_align (def, align);
- /* Mark the symbol_ref as belonging to this constants pool. */
- CONSTANT_POOL_ADDRESS_P (symbol) = 1;
- SYMBOL_REF_FLAGS (symbol) = SYMBOL_FLAG_LOCAL;
- current_function_uses_const_pool = 1;
+ /* If we're dropping a label to the constant pool, make sure we
+ don't delete it. */
+ if (GET_CODE (x) == LABEL_REF)
+ LABEL_PRESERVE_P (XEXP (x, 0)) = 1;
- return def;
+ return copy_rtx (def);
}
\f
/* Given a SYMBOL_REF with CONSTANT_POOL_ADDRESS_P true, return a pointer to
- the corresponding pool_constant structure. */
+ the corresponding constant_descriptor_rtx structure. */
-static struct pool_constant *
-find_pool_constant (f, addr)
- struct function *f;
- rtx addr;
+static struct constant_descriptor_rtx *
+find_pool_constant (struct rtx_constant_pool *pool, rtx sym)
{
- struct pool_constant *pool;
- const char *label = XSTR (addr, 0);
-
- for (pool = f->varasm->x_const_rtx_sym_hash_table[SYMHASH (label)]; pool;
- pool = pool->next_sym)
- if (XSTR (XEXP (pool->desc->rtl, 0), 0) == label)
- return pool;
-
- abort ();
+ struct constant_descriptor_rtx tmp;
+ tmp.sym = sym;
+ return htab_find (pool->const_rtx_sym_htab, &tmp);
}
/* Given a constant pool SYMBOL_REF, return the corresponding constant. */
rtx
-get_pool_constant (addr)
- rtx addr;
+get_pool_constant (rtx addr)
{
- return (find_pool_constant (cfun, addr))->constant;
+ return find_pool_constant (cfun->varasm->pool, addr)->constant;
}
/* Given a constant pool SYMBOL_REF, return the corresponding constant
and whether it has been output or not. */
rtx
-get_pool_constant_mark (addr, pmarked)
- rtx addr;
- bool *pmarked;
+get_pool_constant_mark (rtx addr, bool *pmarked)
{
- struct pool_constant *pool = find_pool_constant (cfun, addr);
- *pmarked = (pool->mark != 0);
- return pool->constant;
+ struct constant_descriptor_rtx *desc;
+
+ desc = find_pool_constant (cfun->varasm->pool, addr);
+ *pmarked = (desc->mark != 0);
+ return desc->constant;
}
/* Likewise, but for the constant pool of a specific function. */
rtx
-get_pool_constant_for_function (f, addr)
- struct function *f;
- rtx addr;
+get_pool_constant_for_function (struct function *f, rtx addr)
{
- return (find_pool_constant (f, addr))->constant;
+ return find_pool_constant (f->varasm->pool, addr)->constant;
}
/* Similar, return the mode. */
enum machine_mode
-get_pool_mode (addr)
- rtx addr;
-{
- return (find_pool_constant (cfun, addr))->mode;
-}
-
-enum machine_mode
-get_pool_mode_for_function (f, addr)
- struct function *f;
- rtx addr;
-{
- return (find_pool_constant (f, addr))->mode;
-}
-
-/* Similar, return the offset in the constant pool. */
-
-int
-get_pool_offset (addr)
- rtx addr;
+get_pool_mode (rtx addr)
{
- return (find_pool_constant (cfun, addr))->offset;
+ return find_pool_constant (cfun->varasm->pool, addr)->mode;
}
/* Return the size of the constant pool. */
int
-get_pool_size ()
+get_pool_size (void)
{
- return pool_offset;
+ return cfun->varasm->pool->offset;
}
\f
-/* Write all the constants in the constant pool. */
+/* Worker function for output_constant_pool_1. Emit assembly for X
+ in MODE with known alignment ALIGN. */
-void
-output_constant_pool (fnname, fndecl)
- const char *fnname ATTRIBUTE_UNUSED;
- tree fndecl ATTRIBUTE_UNUSED;
+static void
+output_constant_pool_2 (enum machine_mode mode, rtx x, unsigned int align)
{
- struct pool_constant *pool;
- rtx x;
- REAL_VALUE_TYPE r;
-
- /* It is possible for gcc to call force_const_mem and then to later
- discard the instructions which refer to the constant. In such a
- case we do not need to output the constant. */
- mark_constant_pool ();
-
-#ifdef ASM_OUTPUT_POOL_PROLOGUE
- ASM_OUTPUT_POOL_PROLOGUE (asm_out_file, fnname, fndecl, pool_offset);
-#endif
-
- for (pool = first_pool; pool; pool = pool->next)
+ switch (GET_MODE_CLASS (mode))
{
- rtx tmp;
+ case MODE_FLOAT:
+ {
+ REAL_VALUE_TYPE r;
+
+ gcc_assert (GET_CODE (x) == CONST_DOUBLE);
+ REAL_VALUE_FROM_CONST_DOUBLE (r, x);
+ assemble_real (r, mode, align);
+ break;
+ }
+
+ case MODE_INT:
+ case MODE_PARTIAL_INT:
+ assemble_integer (x, GET_MODE_SIZE (mode), align, 1);
+ break;
- x = pool->constant;
+ case MODE_VECTOR_FLOAT:
+ case MODE_VECTOR_INT:
+ {
+ int i, units;
+ enum machine_mode submode = GET_MODE_INNER (mode);
+ unsigned int subalign = MIN (align, GET_MODE_BITSIZE (submode));
- if (! pool->mark)
- continue;
+ gcc_assert (GET_CODE (x) == CONST_VECTOR);
+ units = CONST_VECTOR_NUNITS (x);
- /* See if X is a LABEL_REF (or a CONST referring to a LABEL_REF)
- whose CODE_LABEL has been deleted. This can occur if a jump table
- is eliminated by optimization. If so, write a constant of zero
- instead. Note that this can also happen by turning the
- CODE_LABEL into a NOTE. */
- /* ??? This seems completely and utterly wrong. Certainly it's
- not true for NOTE_INSN_DELETED_LABEL, but I disbelieve proper
- functioning even with INSN_DELETED_P and friends. */
-
- tmp = x;
- switch (GET_CODE (x))
- {
- case CONST:
- if (GET_CODE (XEXP (x, 0)) != PLUS
- || GET_CODE (XEXP (XEXP (x, 0), 0)) != LABEL_REF)
- break;
- tmp = XEXP (XEXP (x, 0), 0);
- /* FALLTHRU */
-
- case LABEL_REF:
- tmp = XEXP (x, 0);
- if (INSN_DELETED_P (tmp)
- || (GET_CODE (tmp) == NOTE
- && NOTE_LINE_NUMBER (tmp) == NOTE_INSN_DELETED))
- {
- abort ();
- x = const0_rtx;
- }
- break;
+ for (i = 0; i < units; i++)
+ {
+ rtx elt = CONST_VECTOR_ELT (x, i);
+ output_constant_pool_2 (submode, elt, i ? subalign : align);
+ }
+ }
+ break;
- default:
- break;
- }
+ default:
+ gcc_unreachable ();
+ }
+}
- /* First switch to correct section. */
- (*targetm.asm_out.select_rtx_section) (pool->mode, x, pool->align);
+/* Worker function for output_constant_pool. Emit POOL. */
-#ifdef ASM_OUTPUT_SPECIAL_POOL_ENTRY
- ASM_OUTPUT_SPECIAL_POOL_ENTRY (asm_out_file, x, pool->mode,
- pool->align, pool->labelno, done);
-#endif
+static void
+output_constant_pool_1 (struct constant_descriptor_rtx *desc)
+{
+ rtx x, tmp;
- assemble_align (pool->align);
+ if (!desc->mark)
+ return;
+ x = desc->constant;
+
+ /* See if X is a LABEL_REF (or a CONST referring to a LABEL_REF)
+ whose CODE_LABEL has been deleted. This can occur if a jump table
+ is eliminated by optimization. If so, write a constant of zero
+ instead. Note that this can also happen by turning the
+ CODE_LABEL into a NOTE. */
+ /* ??? This seems completely and utterly wrong. Certainly it's
+ not true for NOTE_INSN_DELETED_LABEL, but I disbelieve proper
+ functioning even with INSN_DELETED_P and friends. */
+
+ tmp = x;
+ switch (GET_CODE (x))
+ {
+ case CONST:
+ if (GET_CODE (XEXP (x, 0)) != PLUS
+ || GET_CODE (XEXP (XEXP (x, 0), 0)) != LABEL_REF)
+ break;
+ tmp = XEXP (XEXP (x, 0), 0);
+ /* FALLTHRU */
- /* Output the label. */
- (*targetm.asm_out.internal_label) (asm_out_file, "LC", pool->labelno);
+ case LABEL_REF:
+ tmp = XEXP (x, 0);
+ gcc_assert (!INSN_DELETED_P (tmp));
+ gcc_assert (!NOTE_P (tmp)
+ || NOTE_LINE_NUMBER (tmp) != NOTE_INSN_DELETED);
+ break;
- /* Output the value of the constant itself. */
- switch (GET_MODE_CLASS (pool->mode))
- {
- case MODE_FLOAT:
- if (GET_CODE (x) != CONST_DOUBLE)
- abort ();
+ default:
+ break;
+ }
- REAL_VALUE_FROM_CONST_DOUBLE (r, x);
- assemble_real (r, pool->mode, pool->align);
- break;
+ /* First switch to correct section. */
+ targetm.asm_out.select_rtx_section (desc->mode, x, desc->align);
- case MODE_INT:
- case MODE_PARTIAL_INT:
- assemble_integer (x, GET_MODE_SIZE (pool->mode), pool->align, 1);
- break;
+#ifdef ASM_OUTPUT_SPECIAL_POOL_ENTRY
+ ASM_OUTPUT_SPECIAL_POOL_ENTRY (asm_out_file, x, desc->mode,
+ desc->align, desc->labelno, done);
+#endif
- case MODE_VECTOR_FLOAT:
- {
- int i, units;
- rtx elt;
+ assemble_align (desc->align);
- if (GET_CODE (x) != CONST_VECTOR)
- abort ();
+ /* Output the label. */
+ targetm.asm_out.internal_label (asm_out_file, "LC", desc->labelno);
- units = CONST_VECTOR_NUNITS (x);
+ /* Output the data. */
+ output_constant_pool_2 (desc->mode, x, desc->align);
- for (i = 0; i < units; i++)
- {
- elt = CONST_VECTOR_ELT (x, i);
- REAL_VALUE_FROM_CONST_DOUBLE (r, elt);
- assemble_real (r, GET_MODE_INNER (pool->mode), pool->align);
- }
- }
- break;
+ /* Make sure all constants in SECTION_MERGE and not SECTION_STRINGS
+ sections have proper size. */
+ if (desc->align > GET_MODE_BITSIZE (desc->mode)
+ && in_section == in_named
+ && get_named_section_flags (in_named_name) & SECTION_MERGE)
+ assemble_align (desc->align);
- case MODE_VECTOR_INT:
- {
- int i, units;
- rtx elt;
+#ifdef ASM_OUTPUT_SPECIAL_POOL_ENTRY
+ done:
+#endif
+ return;
+}
- if (GET_CODE (x) != CONST_VECTOR)
- abort ();
+/* Given a SYMBOL_REF CURRENT_RTX, mark it and all constants it refers
+ to as used. Emit referenced deferred strings. This function can
+ be used with for_each_rtx to mark all SYMBOL_REFs in an rtx. */
- units = CONST_VECTOR_NUNITS (x);
+static int
+mark_constant (rtx *current_rtx, void *data)
+{
+ struct rtx_constant_pool *pool = data;
+ rtx x = *current_rtx;
- for (i = 0; i < units; i++)
- {
- elt = CONST_VECTOR_ELT (x, i);
- assemble_integer (elt, GET_MODE_UNIT_SIZE (pool->mode),
- pool->align, 1);
- }
- }
- break;
+ if (x == NULL_RTX || GET_CODE (x) != SYMBOL_REF)
+ return 0;
- default:
- abort ();
+ if (CONSTANT_POOL_ADDRESS_P (x))
+ {
+ struct constant_descriptor_rtx *desc = find_pool_constant (pool, x);
+ if (desc->mark == 0)
+ {
+ desc->mark = 1;
+ for_each_rtx (&desc->constant, mark_constant, pool);
+ }
+ }
+ else if (TREE_CONSTANT_POOL_ADDRESS_P (x))
+ {
+ tree exp = SYMBOL_REF_DECL (x);
+ if (!TREE_ASM_WRITTEN (exp))
+ {
+ n_deferred_constants--;
+ output_constant_def_contents (x);
}
+ }
- /* Make sure all constants in SECTION_MERGE and not SECTION_STRINGS
- sections have proper size. */
- if (pool->align > GET_MODE_BITSIZE (pool->mode)
- && in_section == in_named
- && get_named_section_flags (in_named_name) & SECTION_MERGE)
- assemble_align (pool->align);
+ return -1;
+}
-#ifdef ASM_OUTPUT_SPECIAL_POOL_ENTRY
- done: ;
-#endif
- }
+/* Look through appropriate parts of INSN, marking all entries in the
+ constant pool which are actually being used. Entries that are only
+ referenced by other constants are also marked as used. Emit
+ deferred strings that are used. */
-#ifdef ASM_OUTPUT_POOL_EPILOGUE
- ASM_OUTPUT_POOL_EPILOGUE (asm_out_file, fnname, fndecl, pool_offset);
-#endif
+static void
+mark_constants (struct rtx_constant_pool *pool, rtx insn)
+{
+ if (!INSN_P (insn))
+ return;
- /* Done with this pool. */
- first_pool = last_pool = 0;
+ /* Insns may appear inside a SEQUENCE. Only check the patterns of
+ insns, not any notes that may be attached. We don't want to mark
+ a constant just because it happens to appear in a REG_EQUIV note. */
+ if (GET_CODE (PATTERN (insn)) == SEQUENCE)
+ {
+ rtx seq = PATTERN (insn);
+ int i, n = XVECLEN (seq, 0);
+ for (i = 0; i < n; ++i)
+ {
+ rtx subinsn = XVECEXP (seq, 0, i);
+ if (INSN_P (subinsn))
+ for_each_rtx (&PATTERN (subinsn), mark_constant, pool);
+ }
+ }
+ else
+ for_each_rtx (&PATTERN (insn), mark_constant, pool);
}
/* Look through the instructions for this function, and mark all the
- entries in the constant pool which are actually being used. Emit
- deferred constants which have indeed been used. */
+ entries in POOL which are actually being used. Emit deferred constants
+ which have indeed been used. */
static void
-mark_constant_pool ()
+mark_constant_pool (struct rtx_constant_pool *pool)
{
- rtx insn;
- rtx link;
- struct pool_constant *pool;
+ rtx insn, link;
- if (first_pool == 0 && n_deferred_constants == 0)
+ if (pool->first == 0 && n_deferred_constants == 0)
return;
- for (pool = first_pool; pool; pool = pool->next)
- pool->mark = 0;
-
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
- if (INSN_P (insn))
- mark_constants (PATTERN (insn));
+ mark_constants (pool, insn);
for (link = current_function_epilogue_delay_list;
link;
link = XEXP (link, 1))
- {
- insn = XEXP (link, 0);
-
- if (INSN_P (insn))
- mark_constants (PATTERN (insn));
- }
+ mark_constants (pool, XEXP (link, 0));
}
-/* Look through appropriate parts of X, marking all entries in the
- constant pool which are actually being used. Entries that are only
- referenced by other constants are also marked as used. Emit
- deferred strings that are used. */
+/* Write all the constants in the constant pool. */
-static void
-mark_constants (x)
- rtx x;
+void
+output_constant_pool (const char *fnname ATTRIBUTE_UNUSED,
+ tree fndecl ATTRIBUTE_UNUSED)
{
- int i;
- const char *format_ptr;
-
- if (x == 0)
- return;
-
- if (GET_CODE (x) == SYMBOL_REF)
- {
- mark_constant (&x, NULL);
- return;
- }
+ struct rtx_constant_pool *pool = cfun->varasm->pool;
+ struct constant_descriptor_rtx *desc;
- /* Insns may appear inside a SEQUENCE. Only check the patterns of
- insns, not any notes that may be attached. We don't want to mark
- a constant just because it happens to appear in a REG_EQUIV note. */
- if (INSN_P (x))
- {
- mark_constants (PATTERN (x));
- return;
- }
+ /* It is possible for gcc to call force_const_mem and then to later
+ discard the instructions which refer to the constant. In such a
+ case we do not need to output the constant. */
+ mark_constant_pool (pool);
- format_ptr = GET_RTX_FORMAT (GET_CODE (x));
+#ifdef ASM_OUTPUT_POOL_PROLOGUE
+ ASM_OUTPUT_POOL_PROLOGUE (asm_out_file, fnname, fndecl, pool->offset);
+#endif
- for (i = 0; i < GET_RTX_LENGTH (GET_CODE (x)); i++)
- {
- switch (*format_ptr++)
- {
- case 'e':
- mark_constants (XEXP (x, i));
- break;
+ for (desc = pool->first; desc ; desc = desc->next)
+ output_constant_pool_1 (desc);
- case 'E':
- if (XVEC (x, i) != 0)
- {
- int j;
+#ifdef ASM_OUTPUT_POOL_EPILOGUE
+ ASM_OUTPUT_POOL_EPILOGUE (asm_out_file, fnname, fndecl, pool->offset);
+#endif
+}
+\f
+/* Determine what kind of relocations EXP may need. */
- for (j = 0; j < XVECLEN (x, i); j++)
- mark_constants (XVECEXP (x, i, j));
- }
- break;
+int
+compute_reloc_for_constant (tree exp)
+{
+ int reloc = 0, reloc2;
+ tree tem;
- case 'S':
- case 's':
- case '0':
- case 'i':
- case 'w':
- case 'n':
- case 'u':
- case 'B':
- break;
+ /* Give the front-end a chance to convert VALUE to something that
+ looks more like a constant to the back-end. */
+ exp = lang_hooks.expand_constant (exp);
- default:
- abort ();
- }
- }
-}
+ switch (TREE_CODE (exp))
+ {
+ case ADDR_EXPR:
+ case FDESC_EXPR:
+ /* Go inside any operations that get_inner_reference can handle and see
+ if what's inside is a constant: no need to do anything here for
+ addresses of variables or functions. */
+ for (tem = TREE_OPERAND (exp, 0); handled_component_p (tem);
+ tem = TREE_OPERAND (tem, 0))
+ ;
-/* Given a SYMBOL_REF CURRENT_RTX, mark it and all constants it refers
- to as used. Emit referenced deferred strings. This function can
- be used with for_each_rtx to mark all SYMBOL_REFs in an rtx. */
+ if (TREE_PUBLIC (tem))
+ reloc |= 2;
+ else
+ reloc |= 1;
+ break;
-static int
-mark_constant (current_rtx, data)
- rtx *current_rtx;
- void *data ATTRIBUTE_UNUSED;
-{
- rtx x = *current_rtx;
+ case PLUS_EXPR:
+ reloc = compute_reloc_for_constant (TREE_OPERAND (exp, 0));
+ reloc |= compute_reloc_for_constant (TREE_OPERAND (exp, 1));
+ break;
- if (x == NULL_RTX)
- return 0;
+ case MINUS_EXPR:
+ reloc = compute_reloc_for_constant (TREE_OPERAND (exp, 0));
+ reloc2 = compute_reloc_for_constant (TREE_OPERAND (exp, 1));
+ /* The difference of two local labels is computable at link time. */
+ if (reloc == 1 && reloc2 == 1)
+ reloc = 0;
+ else
+ reloc |= reloc2;
+ break;
- else if (GET_CODE (x) == SYMBOL_REF)
- {
- if (CONSTANT_POOL_ADDRESS_P (x))
- {
- struct pool_constant *pool = find_pool_constant (cfun, x);
- if (pool->mark == 0)
- {
- pool->mark = 1;
- for_each_rtx (&(pool->constant), &mark_constant, NULL);
- }
- else
- return -1;
- }
- else if (TREE_CONSTANT_POOL_ADDRESS_P (x))
- {
- tree exp = SYMBOL_REF_DECL (x);
- if (!TREE_ASM_WRITTEN (exp))
- {
- n_deferred_constants--;
- output_constant_def_contents (x);
- }
- }
+ case NOP_EXPR:
+ case CONVERT_EXPR:
+ case NON_LVALUE_EXPR:
+ case VIEW_CONVERT_EXPR:
+ reloc = compute_reloc_for_constant (TREE_OPERAND (exp, 0));
+ break;
+
+ case CONSTRUCTOR:
+ {
+ unsigned HOST_WIDE_INT idx;
+ FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (exp), idx, tem)
+ if (tem != 0)
+ reloc |= compute_reloc_for_constant (tem);
+ }
+ break;
+
+ default:
+ break;
}
- return 0;
+ return reloc;
}
-\f
+
/* Find all the constants whose addresses are referenced inside of EXP,
and make sure assembler code with a label has been output for each one.
Indicate whether an ADDR_EXPR has been encountered. */
-static int
-output_addressed_constants (exp)
- tree exp;
+static void
+output_addressed_constants (tree exp)
{
- int reloc = 0, reloc2;
tree tem;
/* Give the front-end a chance to convert VALUE to something that
looks more like a constant to the back-end. */
- exp = (*lang_hooks.expand_constant) (exp);
+ exp = lang_hooks.expand_constant (exp);
switch (TREE_CODE (exp))
{
tem = TREE_OPERAND (tem, 0))
;
- if (TREE_CODE_CLASS (TREE_CODE (tem)) == 'c'
- || TREE_CODE (tem) == CONSTRUCTOR)
- output_constant_def (tem, 0);
+ /* If we have an initialized CONST_DECL, retrieve the initializer. */
+ if (TREE_CODE (tem) == CONST_DECL && DECL_INITIAL (tem))
+ tem = DECL_INITIAL (tem);
- if (TREE_PUBLIC (tem))
- reloc |= 2;
- else
- reloc |= 1;
+ if (CONSTANT_CLASS_P (tem) || TREE_CODE (tem) == CONSTRUCTOR)
+ output_constant_def (tem, 0);
break;
case PLUS_EXPR:
- reloc = output_addressed_constants (TREE_OPERAND (exp, 0));
- reloc |= output_addressed_constants (TREE_OPERAND (exp, 1));
- break;
-
case MINUS_EXPR:
- reloc = output_addressed_constants (TREE_OPERAND (exp, 0));
- reloc2 = output_addressed_constants (TREE_OPERAND (exp, 1));
- /* The difference of two local labels is computable at link time. */
- if (reloc == 1 && reloc2 == 1)
- reloc = 0;
- else
- reloc |= reloc2;
- break;
+ output_addressed_constants (TREE_OPERAND (exp, 1));
+ /* Fall through. */
case NOP_EXPR:
case CONVERT_EXPR:
case NON_LVALUE_EXPR:
- reloc = output_addressed_constants (TREE_OPERAND (exp, 0));
+ case VIEW_CONVERT_EXPR:
+ output_addressed_constants (TREE_OPERAND (exp, 0));
break;
case CONSTRUCTOR:
- for (tem = CONSTRUCTOR_ELTS (exp); tem; tem = TREE_CHAIN (tem))
- if (TREE_VALUE (tem) != 0)
- reloc |= output_addressed_constants (TREE_VALUE (tem));
-
+ {
+ unsigned HOST_WIDE_INT idx;
+ FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (exp), idx, tem)
+ if (tem != 0)
+ output_addressed_constants (tem);
+ }
break;
default:
break;
}
- return reloc;
}
\f
/* Return nonzero if VALUE is a valid constant-valued expression
arithmetic-combinations of integers. */
tree
-initializer_constant_valid_p (value, endtype)
- tree value;
- tree endtype;
+initializer_constant_valid_p (tree value, tree endtype)
{
/* Give the front-end a chance to convert VALUE to something that
looks more like a constant to the back-end. */
- value = (*lang_hooks.expand_constant) (value);
+ value = lang_hooks.expand_constant (value);
switch (TREE_CODE (value))
{
if ((TREE_CODE (TREE_TYPE (value)) == UNION_TYPE
|| TREE_CODE (TREE_TYPE (value)) == RECORD_TYPE)
&& TREE_CONSTANT (value)
- && CONSTRUCTOR_ELTS (value))
- return
- initializer_constant_valid_p (TREE_VALUE (CONSTRUCTOR_ELTS (value)),
- endtype);
+ && !VEC_empty (constructor_elt, CONSTRUCTOR_ELTS (value)))
+ {
+ unsigned HOST_WIDE_INT idx;
+ tree elt;
+ bool absolute = true;
+
+ FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (value), idx, elt)
+ {
+ tree reloc;
+ reloc = initializer_constant_valid_p (elt, TREE_TYPE (elt));
+ if (!reloc)
+ return NULL_TREE;
+ if (reloc != null_pointer_node)
+ absolute = false;
+ }
+ /* For a non-absolute relocation, there is no single
+ variable that can be "the variable that determines the
+ relocation." */
+ return absolute ? null_pointer_node : error_mark_node;
+ }
- return TREE_STATIC (value) ? null_pointer_node : 0;
+ return TREE_STATIC (value) ? null_pointer_node : NULL_TREE;
case INTEGER_CST:
case VECTOR_CST:
case ADDR_EXPR:
case FDESC_EXPR:
- return staticp (TREE_OPERAND (value, 0)) ? TREE_OPERAND (value, 0) : 0;
+ value = staticp (TREE_OPERAND (value, 0));
+ /* "&(*a).f" is like unto pointer arithmetic. If "a" turns out to
+ be a constant, this is old-skool offsetof-like nonsense. */
+ if (value
+ && TREE_CODE (value) == INDIRECT_REF
+ && TREE_CONSTANT (TREE_OPERAND (value, 0)))
+ return null_pointer_node;
+ /* Taking the address of a nested function involves a trampoline. */
+ if (value
+ && TREE_CODE (value) == FUNCTION_DECL
+ && ((decl_function_context (value) && !DECL_NO_STATIC_CHAIN (value))
+ || DECL_DLLIMPORT_P (value)))
+ return NULL_TREE;
+ return value;
case VIEW_CONVERT_EXPR:
case NON_LVALUE_EXPR:
case CONVERT_EXPR:
case NOP_EXPR:
- /* Allow conversions between pointer types. */
- if (POINTER_TYPE_P (TREE_TYPE (value))
- && POINTER_TYPE_P (TREE_TYPE (TREE_OPERAND (value, 0))))
- return initializer_constant_valid_p (TREE_OPERAND (value, 0), endtype);
-
- /* Allow conversions between real types. */
- if (FLOAT_TYPE_P (TREE_TYPE (value))
- && FLOAT_TYPE_P (TREE_TYPE (TREE_OPERAND (value, 0))))
- return initializer_constant_valid_p (TREE_OPERAND (value, 0), endtype);
-
- /* Allow length-preserving conversions between integer types. */
- if (INTEGRAL_TYPE_P (TREE_TYPE (value))
- && INTEGRAL_TYPE_P (TREE_TYPE (TREE_OPERAND (value, 0)))
- && (TYPE_PRECISION (TREE_TYPE (value))
- == TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (value, 0)))))
- return initializer_constant_valid_p (TREE_OPERAND (value, 0), endtype);
-
- /* Allow conversions between other integer types only if
- explicit value. */
- if (INTEGRAL_TYPE_P (TREE_TYPE (value))
- && INTEGRAL_TYPE_P (TREE_TYPE (TREE_OPERAND (value, 0))))
- {
- tree inner = initializer_constant_valid_p (TREE_OPERAND (value, 0),
- endtype);
- if (inner == null_pointer_node)
- return null_pointer_node;
- break;
- }
+ {
+ tree src;
+ tree src_type;
+ tree dest_type;
+
+ src = TREE_OPERAND (value, 0);
+ src_type = TREE_TYPE (src);
+ dest_type = TREE_TYPE (value);
+
+ /* Allow conversions between pointer types, floating-point
+ types, and offset types. */
+ if ((POINTER_TYPE_P (dest_type) && POINTER_TYPE_P (src_type))
+ || (FLOAT_TYPE_P (dest_type) && FLOAT_TYPE_P (src_type))
+ || (TREE_CODE (dest_type) == OFFSET_TYPE
+ && TREE_CODE (src_type) == OFFSET_TYPE))
+ return initializer_constant_valid_p (src, endtype);
+
+ /* Allow length-preserving conversions between integer types. */
+ if (INTEGRAL_TYPE_P (dest_type) && INTEGRAL_TYPE_P (src_type)
+ && (TYPE_PRECISION (dest_type) == TYPE_PRECISION (src_type)))
+ return initializer_constant_valid_p (src, endtype);
+
+ /* Allow conversions between other integer types only if
+ explicit value. */
+ if (INTEGRAL_TYPE_P (dest_type) && INTEGRAL_TYPE_P (src_type))
+ {
+ tree inner = initializer_constant_valid_p (src, endtype);
+ if (inner == null_pointer_node)
+ return null_pointer_node;
+ break;
+ }
- /* Allow (int) &foo provided int is as wide as a pointer. */
- if (INTEGRAL_TYPE_P (TREE_TYPE (value))
- && POINTER_TYPE_P (TREE_TYPE (TREE_OPERAND (value, 0)))
- && (TYPE_PRECISION (TREE_TYPE (value))
- >= TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (value, 0)))))
- return initializer_constant_valid_p (TREE_OPERAND (value, 0),
- endtype);
-
- /* Likewise conversions from int to pointers, but also allow
- conversions from 0. */
- if (POINTER_TYPE_P (TREE_TYPE (value))
- && INTEGRAL_TYPE_P (TREE_TYPE (TREE_OPERAND (value, 0))))
- {
- if (integer_zerop (TREE_OPERAND (value, 0)))
- return null_pointer_node;
- else if (TYPE_PRECISION (TREE_TYPE (value))
- <= TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (value, 0))))
- return initializer_constant_valid_p (TREE_OPERAND (value, 0),
- endtype);
- }
+ /* Allow (int) &foo provided int is as wide as a pointer. */
+ if (INTEGRAL_TYPE_P (dest_type) && POINTER_TYPE_P (src_type)
+ && (TYPE_PRECISION (dest_type) >= TYPE_PRECISION (src_type)))
+ return initializer_constant_valid_p (src, endtype);
+
+ /* Likewise conversions from int to pointers, but also allow
+ conversions from 0. */
+ if ((POINTER_TYPE_P (dest_type)
+ || TREE_CODE (dest_type) == OFFSET_TYPE)
+ && INTEGRAL_TYPE_P (src_type))
+ {
+ if (integer_zerop (src))
+ return null_pointer_node;
+ else if (TYPE_PRECISION (dest_type) <= TYPE_PRECISION (src_type))
+ return initializer_constant_valid_p (src, endtype);
+ }
- /* Allow conversions to union types if the value inside is okay. */
- if (TREE_CODE (TREE_TYPE (value)) == UNION_TYPE)
- return initializer_constant_valid_p (TREE_OPERAND (value, 0),
- endtype);
+ /* Allow conversions to struct or union types if the value
+ inside is okay. */
+ if (TREE_CODE (dest_type) == RECORD_TYPE
+ || TREE_CODE (dest_type) == UNION_TYPE)
+ return initializer_constant_valid_p (src, endtype);
+ }
break;
case PLUS_EXPR:
/* Since GCC guarantees that string constants are unique in the
generated code, a subtraction between two copies of the same
constant string is absolute. */
- if (valid0 && TREE_CODE (valid0) == STRING_CST &&
- valid1 && TREE_CODE (valid1) == STRING_CST &&
- TREE_STRING_POINTER (valid0) == TREE_STRING_POINTER (valid1))
+ if (valid0 && TREE_CODE (valid0) == STRING_CST
+ && valid1 && TREE_CODE (valid1) == STRING_CST
+ && operand_equal_p (valid0, valid1, 1))
return null_pointer_node;
}
- /* Support differences between labels. */
+ /* Support narrowing differences. */
if (INTEGRAL_TYPE_P (endtype))
{
tree op0, op1;
+
op0 = TREE_OPERAND (value, 0);
op1 = TREE_OPERAND (value, 1);
op1 = inner;
}
- if (TREE_CODE (op0) == ADDR_EXPR
- && TREE_CODE (TREE_OPERAND (op0, 0)) == LABEL_DECL
- && TREE_CODE (op1) == ADDR_EXPR
- && TREE_CODE (TREE_OPERAND (op1, 0)) == LABEL_DECL)
- return null_pointer_node;
+ op0 = initializer_constant_valid_p (op0, endtype);
+ op1 = initializer_constant_valid_p (op1, endtype);
+
+ /* Both initializers must be known. */
+ if (op0 && op1)
+ {
+ if (op0 == op1)
+ return null_pointer_node;
+
+ /* Support differences between labels. */
+ if (TREE_CODE (op0) == LABEL_DECL
+ && TREE_CODE (op1) == LABEL_DECL)
+ return null_pointer_node;
+
+ if (TREE_CODE (op0) == STRING_CST
+ && TREE_CODE (op1) == STRING_CST
+ && operand_equal_p (op0, op1, 1))
+ return null_pointer_node;
+ }
}
break;
ALIGN is the alignment of the data in bits. */
void
-output_constant (exp, size, align)
- tree exp;
- unsigned HOST_WIDE_INT size;
- unsigned int align;
+output_constant (tree exp, unsigned HOST_WIDE_INT size, unsigned int align)
{
enum tree_code code;
unsigned HOST_WIDE_INT thissize;
/* Some front-ends use constants other than the standard language-independent
varieties, but which may still be output directly. Give the front-end a
chance to convert EXP to a language-independent representation. */
- exp = (*lang_hooks.expand_constant) (exp);
+ exp = lang_hooks.expand_constant (exp);
if (size == 0 || flag_syntax_only)
return;
+ /* See if we're trying to initialize a pointer in a non-default mode
+ to the address of some declaration somewhere. If the target says
+ the mode is valid for pointers, assume the target has a way of
+ resolving it. */
+ if (TREE_CODE (exp) == NOP_EXPR
+ && POINTER_TYPE_P (TREE_TYPE (exp))
+ && targetm.valid_pointer_mode (TYPE_MODE (TREE_TYPE (exp))))
+ {
+ tree saved_type = TREE_TYPE (exp);
+
+ /* Peel off any intermediate conversions-to-pointer for valid
+ pointer modes. */
+ while (TREE_CODE (exp) == NOP_EXPR
+ && POINTER_TYPE_P (TREE_TYPE (exp))
+ && targetm.valid_pointer_mode (TYPE_MODE (TREE_TYPE (exp))))
+ exp = TREE_OPERAND (exp, 0);
+
+ /* If what we're left with is the address of something, we can
+ convert the address to the final type and output it that
+ way. */
+ if (TREE_CODE (exp) == ADDR_EXPR)
+ exp = build1 (ADDR_EXPR, saved_type, TREE_OPERAND (exp, 0));
+ }
+
/* Eliminate any conversions since we'll be outputting the underlying
constant. */
while (TREE_CODE (exp) == NOP_EXPR || TREE_CODE (exp) == CONVERT_EXPR
|| TREE_CODE (exp) == NON_LVALUE_EXPR
|| TREE_CODE (exp) == VIEW_CONVERT_EXPR)
- exp = TREE_OPERAND (exp, 0);
+ {
+ HOST_WIDE_INT type_size = int_size_in_bytes (TREE_TYPE (exp));
+ HOST_WIDE_INT op_size = int_size_in_bytes (TREE_TYPE (TREE_OPERAND (exp, 0)));
+
+ /* Make sure eliminating the conversion is really a no-op, except with
+ VIEW_CONVERT_EXPRs to allow for wild Ada unchecked conversions and
+ union types to allow for Ada unchecked unions. */
+ if (type_size > op_size
+ && TREE_CODE (exp) != VIEW_CONVERT_EXPR
+ && TREE_CODE (TREE_TYPE (exp)) != UNION_TYPE)
+ internal_error ("no-op convert from %wd to %wd bytes in initializer",
+ op_size, type_size);
+
+ exp = TREE_OPERAND (exp, 0);
+ }
code = TREE_CODE (TREE_TYPE (exp));
thissize = int_size_in_bytes (TREE_TYPE (exp));
/* Allow a constructor with no elements for any data type.
This means to fill the space with zeros. */
- if (TREE_CODE (exp) == CONSTRUCTOR && CONSTRUCTOR_ELTS (exp) == 0)
+ if (TREE_CODE (exp) == CONSTRUCTOR
+ && VEC_empty (constructor_elt, CONSTRUCTOR_ELTS (exp)))
{
assemble_zeros (size);
return;
tree decl = TREE_OPERAND (exp, 0);
ASM_OUTPUT_FDESC (asm_out_file, decl, part);
#else
- abort ();
+ gcc_unreachable ();
#endif
return;
}
/* Now output the underlying data. If we've handling the padding, return.
- Otherwise, break and ensure THISSIZE is the size written. */
+ Otherwise, break and ensure SIZE is the size written. */
switch (code)
{
case CHAR_TYPE:
case ENUMERAL_TYPE:
case POINTER_TYPE:
case REFERENCE_TYPE:
+ case OFFSET_TYPE:
if (! assemble_integer (expand_expr (exp, NULL_RTX, VOIDmode,
EXPAND_INITIALIZER),
- size, align, 0))
+ MIN (size, thissize), align, 0))
error ("initializer for integer value is too complicated");
break;
if (TREE_CODE (exp) != REAL_CST)
error ("initializer for floating value is not a floating constant");
- assemble_real (TREE_REAL_CST (exp),
- mode_for_size (size * BITS_PER_UNIT, MODE_FLOAT, 0),
- align);
+ assemble_real (TREE_REAL_CST (exp), TYPE_MODE (TREE_TYPE (exp)), align);
break;
case COMPLEX_TYPE:
case ARRAY_TYPE:
case VECTOR_TYPE:
- if (TREE_CODE (exp) == CONSTRUCTOR)
+ switch (TREE_CODE (exp))
{
+ case CONSTRUCTOR:
output_constructor (exp, size, align);
return;
- }
- else if (TREE_CODE (exp) == STRING_CST)
- {
+ case STRING_CST:
thissize = MIN ((unsigned HOST_WIDE_INT)TREE_STRING_LENGTH (exp),
size);
assemble_string (TREE_STRING_POINTER (exp), thissize);
- }
- else if (TREE_CODE (exp) == VECTOR_CST)
- {
- int elt_size;
- tree link;
- unsigned int nalign;
- enum machine_mode inner;
-
- inner = GET_MODE_INNER (TYPE_MODE (TREE_TYPE (exp)));
- nalign = MIN (align, GET_MODE_ALIGNMENT (inner));
-
- elt_size = GET_MODE_UNIT_SIZE (TYPE_MODE (TREE_TYPE (exp)));
+ break;
- link = TREE_VECTOR_CST_ELTS (exp);
- output_constant (TREE_VALUE (link), elt_size, align);
- while ((link = TREE_CHAIN (link)) != NULL)
- output_constant (TREE_VALUE (link), elt_size, nalign);
+ case VECTOR_CST:
+ {
+ int elt_size;
+ tree link;
+ unsigned int nalign;
+ enum machine_mode inner;
+
+ inner = TYPE_MODE (TREE_TYPE (TREE_TYPE (exp)));
+ nalign = MIN (align, GET_MODE_ALIGNMENT (inner));
+
+ elt_size = GET_MODE_SIZE (inner);
+
+ link = TREE_VECTOR_CST_ELTS (exp);
+ output_constant (TREE_VALUE (link), elt_size, align);
+ while ((link = TREE_CHAIN (link)) != NULL)
+ output_constant (TREE_VALUE (link), elt_size, nalign);
+ break;
+ }
+ default:
+ gcc_unreachable ();
}
- else
- abort ();
break;
case RECORD_TYPE:
case UNION_TYPE:
- if (TREE_CODE (exp) == CONSTRUCTOR)
- output_constructor (exp, size, align);
- else
- abort ();
- return;
-
- case SET_TYPE:
- if (TREE_CODE (exp) == INTEGER_CST)
- assemble_integer (expand_expr (exp, NULL_RTX,
- VOIDmode, EXPAND_INITIALIZER),
- thissize, align, 1);
- else if (TREE_CODE (exp) == CONSTRUCTOR)
- {
- unsigned char *buffer = (unsigned char *) alloca (thissize);
- if (get_set_constructor_bytes (exp, buffer, thissize))
- abort ();
- assemble_string ((char *) buffer, thissize);
- }
- else
- error ("unknown set constructor type");
+ gcc_assert (TREE_CODE (exp) == CONSTRUCTOR);
+ output_constructor (exp, size, align);
return;
case ERROR_MARK:
return;
default:
- abort ();
+ gcc_unreachable ();
}
if (size > thissize)
type with an unspecified upper bound. */
static unsigned HOST_WIDE_INT
-array_size_for_constructor (val)
- tree val;
+array_size_for_constructor (tree val)
{
tree max_index, i;
+ unsigned HOST_WIDE_INT cnt;
+ tree index, value;
/* This code used to attempt to handle string constants that are not
arrays of single-bytes, but nothing else does, so there's no point in
return TREE_STRING_LENGTH (val);
max_index = NULL_TREE;
- for (i = CONSTRUCTOR_ELTS (val); i; i = TREE_CHAIN (i))
+ FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (val), cnt, index, value)
{
- tree index = TREE_PURPOSE (i);
-
if (TREE_CODE (index) == RANGE_EXPR)
index = TREE_OPERAND (index, 1);
if (max_index == NULL_TREE || tree_int_cst_lt (max_index, index))
Generate at least SIZE bytes, padding if necessary. */
static void
-output_constructor (exp, size, align)
- tree exp;
- unsigned HOST_WIDE_INT size;
- unsigned int align;
+output_constructor (tree exp, unsigned HOST_WIDE_INT size,
+ unsigned int align)
{
tree type = TREE_TYPE (exp);
- tree link, field = 0;
+ tree field = 0;
tree min_index = 0;
/* Number of bytes output or skipped so far.
In other words, current position within the constructor. */
/* Nonzero means BYTE contains part of a byte, to be output. */
int byte_buffer_in_use = 0;
int byte = 0;
+ unsigned HOST_WIDE_INT cnt;
+ constructor_elt *ce;
- if (HOST_BITS_PER_WIDE_INT < BITS_PER_UNIT)
- abort ();
+ gcc_assert (HOST_BITS_PER_WIDE_INT >= BITS_PER_UNIT);
if (TREE_CODE (type) == RECORD_TYPE)
field = TYPE_FIELDS (type);
There is always a maximum of one element in the chain LINK for unions
(even if the initializer in a source program incorrectly contains
more one). */
- for (link = CONSTRUCTOR_ELTS (exp);
- link;
- link = TREE_CHAIN (link),
- field = field ? TREE_CHAIN (field) : 0)
+ for (cnt = 0;
+ VEC_iterate (constructor_elt, CONSTRUCTOR_ELTS (exp), cnt, ce);
+ cnt++, field = field ? TREE_CHAIN (field) : 0)
{
- tree val = TREE_VALUE (link);
+ tree val = ce->value;
tree index = 0;
/* The element in a union constructor specifies the proper field
or index. */
if ((TREE_CODE (type) == RECORD_TYPE || TREE_CODE (type) == UNION_TYPE
|| TREE_CODE (type) == QUAL_UNION_TYPE)
- && TREE_PURPOSE (link) != 0)
- field = TREE_PURPOSE (link);
+ && ce->index != 0)
+ field = ce->index;
else if (TREE_CODE (type) == ARRAY_TYPE)
- index = TREE_PURPOSE (link);
+ index = ce->index;
+
+#ifdef ASM_COMMENT_START
+ if (field && flag_verbose_asm)
+ fprintf (asm_out_file, "%s %s:\n",
+ ASM_COMMENT_START,
+ DECL_NAME (field)
+ ? IDENTIFIER_POINTER (DECL_NAME (field))
+ : "<anonymous>");
+#endif
/* Eliminate the marker that makes a cast not be an lvalue. */
if (val != 0)
if each element has the proper size. */
if ((field != 0 || index != 0) && pos != total_bytes)
{
+ gcc_assert (pos >= total_bytes);
assemble_zeros (pos - total_bytes);
total_bytes = pos;
}
fieldsize = array_size_for_constructor (val);
/* Given a non-empty initialization, this field had
better be last. */
- if (fieldsize != 0 && TREE_CHAIN (field) != NULL_TREE)
- abort ();
+ gcc_assert (!fieldsize || !TREE_CHAIN (field));
}
else if (DECL_SIZE_UNIT (field))
{
total_bytes += fieldsize;
}
else if (val != 0 && TREE_CODE (val) != INTEGER_CST)
- error ("invalid initial value for member `%s'",
+ error ("invalid initial value for member %qs",
IDENTIFIER_POINTER (DECL_NAME (field)));
else
{
/* If still not at proper byte, advance to there. */
if (next_offset / BITS_PER_UNIT != total_bytes)
{
+ gcc_assert (next_offset / BITS_PER_UNIT >= total_bytes);
assemble_zeros (next_offset / BITS_PER_UNIT - total_bytes);
total_bytes = next_offset / BITS_PER_UNIT;
}
/* Now get the bits from the appropriate constant word. */
if (shift < HOST_BITS_PER_WIDE_INT)
value = TREE_INT_CST_LOW (val);
- else if (shift < 2 * HOST_BITS_PER_WIDE_INT)
+ else
{
+ gcc_assert (shift < 2 * HOST_BITS_PER_WIDE_INT);
value = TREE_INT_CST_HIGH (val);
shift -= HOST_BITS_PER_WIDE_INT;
}
- else
- abort ();
/* Get the result. This works only when:
1 <= this_time <= HOST_BITS_PER_WIDE_INT. */
/* Now get the bits from the appropriate constant word. */
if (shift < HOST_BITS_PER_WIDE_INT)
value = TREE_INT_CST_LOW (val);
- else if (shift < 2 * HOST_BITS_PER_WIDE_INT)
+ else
{
+ gcc_assert (shift < 2 * HOST_BITS_PER_WIDE_INT);
value = TREE_INT_CST_HIGH (val);
shift -= HOST_BITS_PER_WIDE_INT;
}
- else
- abort ();
/* Get the result. This works only when:
1 <= this_time <= HOST_BITS_PER_WIDE_INT. */
/* Mark DECL as weak. */
static void
-mark_weak (decl)
- tree decl;
+mark_weak (tree decl)
{
DECL_WEAK (decl) = 1;
if (DECL_RTL_SET_P (decl)
- && GET_CODE (DECL_RTL (decl)) == MEM
+ && MEM_P (DECL_RTL (decl))
&& XEXP (DECL_RTL (decl), 0)
&& GET_CODE (XEXP (DECL_RTL (decl), 0)) == SYMBOL_REF)
SYMBOL_REF_WEAK (XEXP (DECL_RTL (decl), 0)) = 1;
/* Merge weak status between NEWDECL and OLDDECL. */
void
-merge_weak (newdecl, olddecl)
- tree newdecl;
- tree olddecl;
+merge_weak (tree newdecl, tree olddecl)
{
if (DECL_WEAK (newdecl) == DECL_WEAK (olddecl))
- return;
+ {
+ if (DECL_WEAK (newdecl) && SUPPORTS_WEAK)
+ {
+ tree *pwd;
+ /* We put the NEWDECL on the weak_decls list at some point
+ and OLDDECL as well. Keep just OLDDECL on the list. */
+ for (pwd = &weak_decls; *pwd; pwd = &TREE_CHAIN (*pwd))
+ if (TREE_VALUE (*pwd) == newdecl)
+ {
+ *pwd = TREE_CHAIN (*pwd);
+ break;
+ }
+ }
+ return;
+ }
if (DECL_WEAK (newdecl))
{
declare_weak because the NEWDECL and OLDDECL was not yet
been merged; therefore, TREE_ASM_WRITTEN was not set. */
if (TREE_ASM_WRITTEN (olddecl))
- error_with_decl (newdecl,
- "weak declaration of `%s' must precede definition");
+ error ("weak declaration of %q+D must precede definition",
+ newdecl);
/* If we've already generated rtl referencing OLDDECL, we may
have done so in a way that will not function properly with
a weak symbol. */
else if (TREE_USED (olddecl)
&& TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (olddecl)))
- warning_with_decl (newdecl, "weak declaration of `%s' after first use results in unspecified behavior");
+ warning (0, "weak declaration of %q+D after first use results "
+ "in unspecified behavior", newdecl);
if (SUPPORTS_WEAK)
{
/* Declare DECL to be a weak symbol. */
void
-declare_weak (decl)
- tree decl;
+declare_weak (tree decl)
{
if (! TREE_PUBLIC (decl))
- error_with_decl (decl, "weak declaration of `%s' must be public");
+ error ("weak declaration of %q+D must be public", decl);
else if (TREE_CODE (decl) == FUNCTION_DECL && TREE_ASM_WRITTEN (decl))
- error_with_decl (decl, "weak declaration of `%s' must precede definition");
+ error ("weak declaration of %q+D must precede definition", decl);
else if (SUPPORTS_WEAK)
{
if (! DECL_WEAK (decl))
weak_decls = tree_cons (NULL, decl, weak_decls);
}
else
- warning_with_decl (decl, "weak declaration of `%s' not supported");
+ warning (0, "weak declaration of %q+D not supported", decl);
mark_weak (decl);
}
/* Emit any pending weak declarations. */
void
-weak_finish ()
+weak_finish (void)
{
tree t;
ASM_WEAKEN_LABEL (asm_out_file, name);
#else
#ifdef ASM_OUTPUT_WEAK_ALIAS
- warning ("only weak aliases are supported in this configuration");
+ warning (0, "only weak aliases are supported in this configuration");
return;
#endif
#endif
/* Emit the assembly bits to indicate that DECL is globally visible. */
static void
-globalize_decl (decl)
- tree decl;
+globalize_decl (tree decl)
{
const char *name = XSTR (XEXP (DECL_RTL (decl), 0), 0);
}
return;
}
+#elif defined(ASM_MAKE_LABEL_LINKONCE)
+ if (DECL_ONE_ONLY (decl))
+ ASM_MAKE_LABEL_LINKONCE (asm_out_file, name);
#endif
- (*targetm.asm_out.globalize_label) (asm_out_file, name);
+ targetm.asm_out.globalize_label (asm_out_file, name);
}
-/* Emit an assembler directive to make the symbol for DECL an alias to
- the symbol for TARGET. */
+/* We have to be able to tell cgraph about the needed-ness of the target
+ of an alias. This requires that the decl have been defined. Aliases
+ that precede their definition have to be queued for later processing. */
-void
-assemble_alias (decl, target)
- tree decl, target ATTRIBUTE_UNUSED;
+typedef struct alias_pair GTY(())
{
- const char *name;
+ tree decl;
+ tree target;
+} alias_pair;
- /* We must force creation of DECL_RTL for debug info generation, even though
- we don't use it here. */
- make_decl_rtl (decl, NULL);
+/* Define gc'd vector type. */
+DEF_VEC_O(alias_pair);
+DEF_VEC_ALLOC_O(alias_pair,gc);
- name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl));
+static GTY(()) VEC(alias_pair,gc) *alias_pairs;
+
+/* Given an assembly name, find the decl it is associated with. At the
+ same time, mark it needed for cgraph. */
+
+static tree
+find_decl_and_mark_needed (tree decl, tree target)
+{
+ struct cgraph_node *fnode = NULL;
+ struct cgraph_varpool_node *vnode = NULL;
+
+ if (TREE_CODE (decl) == FUNCTION_DECL)
+ {
+ fnode = cgraph_node_for_asm (target);
+ if (fnode == NULL)
+ vnode = cgraph_varpool_node_for_asm (target);
+ }
+ else
+ {
+ vnode = cgraph_varpool_node_for_asm (target);
+ if (vnode == NULL)
+ fnode = cgraph_node_for_asm (target);
+ }
+
+ if (fnode)
+ {
+ /* We can't mark function nodes as used after cgraph global info
+ is finished. This wouldn't generally be necessary, but C++
+ virtual table thunks are introduced late in the game and
+ might seem like they need marking, although in fact they
+ don't. */
+ if (! cgraph_global_info_ready)
+ cgraph_mark_needed_node (fnode);
+ return fnode->decl;
+ }
+ else if (vnode)
+ {
+ cgraph_varpool_mark_needed_node (vnode);
+ return vnode->decl;
+ }
+ else
+ return NULL_TREE;
+}
+
+/* Output the assembler code for a define (equate) using ASM_OUTPUT_DEF
+ or ASM_OUTPUT_DEF_FROM_DECLS. The function defines the symbol whose
+ tree node is DECL to have the value of the tree node TARGET. */
+
+static void
+do_assemble_alias (tree decl, tree target ATTRIBUTE_UNUSED)
+{
+ if (TREE_ASM_WRITTEN (decl))
+ return;
+
+ TREE_ASM_WRITTEN (decl) = 1;
+ TREE_ASM_WRITTEN (DECL_ASSEMBLER_NAME (decl)) = 1;
#ifdef ASM_OUTPUT_DEF
/* Make name accessible from other files, if appropriate. */
maybe_assemble_visibility (decl);
}
-#ifdef ASM_OUTPUT_DEF_FROM_DECLS
+# ifdef ASM_OUTPUT_DEF_FROM_DECLS
ASM_OUTPUT_DEF_FROM_DECLS (asm_out_file, decl, target);
-#else
- ASM_OUTPUT_DEF (asm_out_file, name, IDENTIFIER_POINTER (target));
+# else
+ ASM_OUTPUT_DEF (asm_out_file,
+ IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)),
+ IDENTIFIER_POINTER (target));
+# endif
+#elif defined (ASM_OUTPUT_WEAK_ALIAS) || defined (ASM_WEAKEN_DECL)
+ {
+ const char *name;
+ tree *p, t;
+
+ name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl));
+# ifdef ASM_WEAKEN_DECL
+ ASM_WEAKEN_DECL (asm_out_file, decl, name, IDENTIFIER_POINTER (target));
+# else
+ ASM_OUTPUT_WEAK_ALIAS (asm_out_file, name, IDENTIFIER_POINTER (target));
+# endif
+ /* Remove this function from the pending weak list so that
+ we do not emit multiple .weak directives for it. */
+ for (p = &weak_decls; (t = *p) ; )
+ if (DECL_ASSEMBLER_NAME (decl) == DECL_ASSEMBLER_NAME (TREE_VALUE (t)))
+ *p = TREE_CHAIN (t);
+ else
+ p = &TREE_CHAIN (t);
+ }
#endif
-#else /* !ASM_OUTPUT_DEF */
-#if defined (ASM_OUTPUT_WEAK_ALIAS) || defined (ASM_WEAKEN_DECL)
- if (! DECL_WEAK (decl))
- warning ("only weak aliases are supported in this configuration");
+}
-#ifdef ASM_WEAKEN_DECL
- ASM_WEAKEN_DECL (asm_out_file, decl, name, IDENTIFIER_POINTER (target));
-#else
- ASM_OUTPUT_WEAK_ALIAS (asm_out_file, name, IDENTIFIER_POINTER (target));
-#endif
-#else
- warning ("alias definitions not supported in this configuration; ignored");
-#endif
+/* First pass of completing pending aliases. Make sure that cgraph knows
+ which symbols will be required. */
+
+void
+finish_aliases_1 (void)
+{
+ unsigned i;
+ alias_pair *p;
+
+ for (i = 0; VEC_iterate (alias_pair, alias_pairs, i, p); i++)
+ {
+ tree target_decl;
+
+ target_decl = find_decl_and_mark_needed (p->decl, p->target);
+ if (target_decl == NULL)
+ error ("%q+D aliased to undefined symbol %qs",
+ p->decl, IDENTIFIER_POINTER (p->target));
+ else if (DECL_EXTERNAL (target_decl))
+ error ("%q+D aliased to external symbol %qs",
+ p->decl, IDENTIFIER_POINTER (p->target));
+ }
+}
+
+/* Second pass of completing pending aliases. Emit the actual assembly.
+ This happens at the end of compilation and thus it is assured that the
+ target symbol has been emitted. */
+
+void
+finish_aliases_2 (void)
+{
+ unsigned i;
+ alias_pair *p;
+
+ for (i = 0; VEC_iterate (alias_pair, alias_pairs, i, p); i++)
+ do_assemble_alias (p->decl, p->target);
+
+ VEC_truncate (alias_pair, alias_pairs, 0);
+}
+
+/* Emit an assembler directive to make the symbol for DECL an alias to
+ the symbol for TARGET. */
+
+void
+assemble_alias (tree decl, tree target)
+{
+ tree target_decl;
+
+#if !defined (ASM_OUTPUT_DEF)
+# if !defined(ASM_OUTPUT_WEAK_ALIAS) && !defined (ASM_WEAKEN_DECL)
+ error ("%Jalias definitions not supported in this configuration", decl);
+ return;
+# else
+ if (!DECL_WEAK (decl))
+ {
+ error ("%Jonly weak aliases are supported in this configuration", decl);
+ return;
+ }
+# endif
#endif
+ /* We must force creation of DECL_RTL for debug info generation, even though
+ we don't use it here. */
+ make_decl_rtl (decl);
TREE_USED (decl) = 1;
- TREE_ASM_WRITTEN (decl) = 1;
- TREE_ASM_WRITTEN (DECL_ASSEMBLER_NAME (decl)) = 1;
+
+ /* A quirk of the initial implementation of aliases required that the user
+ add "extern" to all of them. Which is silly, but now historical. Do
+ note that the symbol is in fact locally defined. */
+ DECL_EXTERNAL (decl) = 0;
+
+ /* Allow aliases to aliases. */
+ if (TREE_CODE (decl) == FUNCTION_DECL)
+ cgraph_node (decl)->alias = true;
+ else
+ cgraph_varpool_node (decl)->alias = true;
+
+ /* If the target has already been emitted, we don't have to queue the
+ alias. This saves a tad o memory. */
+ target_decl = find_decl_and_mark_needed (decl, target);
+ if (target_decl && TREE_ASM_WRITTEN (target_decl))
+ do_assemble_alias (decl, target);
+ else
+ {
+ alias_pair *p = VEC_safe_push (alias_pair, gc, alias_pairs, NULL);
+ p->decl = decl;
+ p->target = target;
+ }
}
/* Emit an assembler directive to set symbol for DECL visibility to
the visibility type VIS, which must not be VISIBILITY_DEFAULT. */
void
-default_assemble_visibility (decl, vis)
- tree decl;
- int vis;
+default_assemble_visibility (tree decl, int vis)
{
static const char * const visibility_types[] = {
NULL, "internal", "hidden", "protected"
const char *name, *type;
- name = (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)));
+ name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl));
type = visibility_types[vis];
#ifdef HAVE_GAS_HIDDEN
assemble_name (asm_out_file, name);
fprintf (asm_out_file, "\n");
#else
- warning ("visibility attribute not supported in this configuration; ignored");
+ warning (OPT_Wattributes, "visibility attribute not supported "
+ "in this configuration; ignored");
#endif
}
/* A helper function to call assemble_visibility when needed for a decl. */
static void
-maybe_assemble_visibility (decl)
- tree decl;
+maybe_assemble_visibility (tree decl)
{
- enum symbol_visibility vis = decl_visibility (decl);
+ enum symbol_visibility vis = DECL_VISIBILITY (decl);
if (vis != VISIBILITY_DEFAULT)
- (* targetm.asm_out.visibility) (decl, vis);
+ targetm.asm_out.visibility (decl, vis);
}
/* Returns 1 if the target configuration supports defining public symbols
a target-specific mechanism for having duplicates discarded. */
int
-supports_one_only ()
+supports_one_only (void)
{
if (SUPPORTS_ONE_ONLY)
return 1;
translation units without generating a linker error. */
void
-make_decl_one_only (decl)
- tree decl;
+make_decl_one_only (tree decl)
{
- if (TREE_CODE (decl) != VAR_DECL && TREE_CODE (decl) != FUNCTION_DECL)
- abort ();
+ gcc_assert (TREE_CODE (decl) == VAR_DECL
+ || TREE_CODE (decl) == FUNCTION_DECL);
TREE_PUBLIC (decl) = 1;
- if (TREE_CODE (decl) == VAR_DECL
- && (DECL_INITIAL (decl) == 0 || DECL_INITIAL (decl) == error_mark_node))
- DECL_COMMON (decl) = 1;
- else if (SUPPORTS_ONE_ONLY)
+ if (SUPPORTS_ONE_ONLY)
{
#ifdef MAKE_DECL_ONE_ONLY
MAKE_DECL_ONE_ONLY (decl);
#endif
DECL_ONE_ONLY (decl) = 1;
}
- else if (SUPPORTS_WEAK)
- DECL_WEAK (decl) = 1;
+ else if (TREE_CODE (decl) == VAR_DECL
+ && (DECL_INITIAL (decl) == 0 || DECL_INITIAL (decl) == error_mark_node))
+ DECL_COMMON (decl) = 1;
else
- abort ();
+ {
+ gcc_assert (SUPPORTS_WEAK);
+ DECL_WEAK (decl) = 1;
+ }
}
void
-init_varasm_once ()
+init_varasm_once (void)
{
in_named_htab = htab_create_ggc (31, in_named_entry_hash,
in_named_entry_eq, NULL);
}
enum tls_model
-decl_tls_model (decl)
- tree decl;
+decl_default_tls_model (tree decl)
{
enum tls_model kind;
- tree attr = lookup_attribute ("tls_model", DECL_ATTRIBUTES (decl));
bool is_local;
- if (attr)
- {
- attr = TREE_VALUE (TREE_VALUE (attr));
- if (TREE_CODE (attr) != STRING_CST)
- abort ();
- if (!strcmp (TREE_STRING_POINTER (attr), "local-exec"))
- kind = TLS_MODEL_LOCAL_EXEC;
- else if (!strcmp (TREE_STRING_POINTER (attr), "initial-exec"))
- kind = TLS_MODEL_INITIAL_EXEC;
- else if (!strcmp (TREE_STRING_POINTER (attr), "local-dynamic"))
- kind = optimize ? TLS_MODEL_LOCAL_DYNAMIC : TLS_MODEL_GLOBAL_DYNAMIC;
- else if (!strcmp (TREE_STRING_POINTER (attr), "global-dynamic"))
- kind = TLS_MODEL_GLOBAL_DYNAMIC;
- else
- abort ();
- return kind;
- }
-
- is_local = (*targetm.binds_local_p) (decl);
- if (!flag_pic)
+ is_local = targetm.binds_local_p (decl);
+ if (!flag_shlib)
{
if (is_local)
kind = TLS_MODEL_LOCAL_EXEC;
else
kind = TLS_MODEL_INITIAL_EXEC;
}
+
/* Local dynamic is inefficient when we're not combining the
parts of the address. */
else if (optimize && is_local)
return kind;
}
-enum symbol_visibility
-decl_visibility (decl)
- tree decl;
-{
- tree attr = lookup_attribute ("visibility", DECL_ATTRIBUTES (decl));
-
- if (attr)
- {
- const char *which = TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (attr)));
-
- if (strcmp (which, "default") == 0)
- return VISIBILITY_DEFAULT;
- if (strcmp (which, "internal") == 0)
- return VISIBILITY_INTERNAL;
- if (strcmp (which, "hidden") == 0)
- return VISIBILITY_HIDDEN;
- if (strcmp (which, "protected") == 0)
- return VISIBILITY_PROTECTED;
-
- abort ();
- }
-
- return VISIBILITY_DEFAULT;
-}
-
/* Select a set of attributes for section NAME based on the properties
of DECL and whether or not RELOC indicates that DECL's initializer
might contain runtime relocations.
read-only for a const data decl, and writable for a non-const data decl. */
unsigned int
-default_section_type_flags (decl, name, reloc)
- tree decl;
- const char *name;
- int reloc;
+default_section_type_flags (tree decl, const char *name, int reloc)
{
return default_section_type_flags_1 (decl, name, reloc, flag_pic);
}
unsigned int
-default_section_type_flags_1 (decl, name, reloc, shlib)
- tree decl;
- const char *name;
- int reloc;
- int shlib;
+default_section_type_flags_1 (tree decl, const char *name, int reloc,
+ int shlib)
{
unsigned int flags;
flags = SECTION_CODE;
else if (decl && decl_readonly_section_1 (decl, reloc, shlib))
flags = 0;
+ else if (current_function_decl
+ && cfun
+ && cfun->unlikely_text_section_name
+ && strcmp (name, cfun->unlikely_text_section_name) == 0)
+ flags = SECTION_CODE;
+ else if (!decl
+ && (!current_function_decl || !cfun)
+ && strcmp (name, UNLIKELY_EXECUTED_TEXT_SECTION_NAME) == 0)
+ flags = SECTION_CODE;
else
flags = SECTION_WRITE;
if (decl && DECL_ONE_ONLY (decl))
flags |= SECTION_LINKONCE;
- if (decl && TREE_CODE (decl) == VAR_DECL && DECL_THREAD_LOCAL (decl))
+ if (decl && TREE_CODE (decl) == VAR_DECL && DECL_THREAD_LOCAL_P (decl))
flags |= SECTION_TLS | SECTION_WRITE;
if (strcmp (name, ".bss") == 0
|| strncmp (name, ".gnu.linkonce.b.", 16) == 0
|| strcmp (name, ".sbss") == 0
|| strncmp (name, ".sbss.", 6) == 0
- || strncmp (name, ".gnu.linkonce.sb.", 17) == 0
- || strcmp (name, ".tbss") == 0
- || strncmp (name, ".gnu.linkonce.tb.", 17) == 0)
+ || strncmp (name, ".gnu.linkonce.sb.", 17) == 0)
flags |= SECTION_BSS;
if (strcmp (name, ".tdata") == 0
- || strcmp (name, ".tbss") == 0
- || strncmp (name, ".gnu.linkonce.td.", 17) == 0
- || strncmp (name, ".gnu.linkonce.tb.", 17) == 0)
+ || strncmp (name, ".tdata.", 7) == 0
+ || strncmp (name, ".gnu.linkonce.td.", 17) == 0)
flags |= SECTION_TLS;
+ if (strcmp (name, ".tbss") == 0
+ || strncmp (name, ".tbss.", 6) == 0
+ || strncmp (name, ".gnu.linkonce.tb.", 17) == 0)
+ flags |= SECTION_TLS | SECTION_BSS;
+
/* These three sections have special ELF types. They are neither
SHT_PROGBITS nor SHT_NOBITS, so when changing sections we don't
want to print a section type (@progbits or @nobits). If someone
Four variants for common object file formats. */
void
-default_no_named_section (name, flags)
- const char *name ATTRIBUTE_UNUSED;
- unsigned int flags ATTRIBUTE_UNUSED;
+default_no_named_section (const char *name ATTRIBUTE_UNUSED,
+ unsigned int flags ATTRIBUTE_UNUSED,
+ tree decl ATTRIBUTE_UNUSED)
{
/* Some object formats don't support named sections at all. The
front-end should already have flagged this as an error. */
- abort ();
+ gcc_unreachable ();
}
void
-default_elf_asm_named_section (name, flags)
- const char *name;
- unsigned int flags;
+default_elf_asm_named_section (const char *name, unsigned int flags,
+ tree decl ATTRIBUTE_UNUSED)
{
char flagchars[10], *f = flagchars;
- if (! named_section_first_declaration (name))
+ /* If we have already declared this section, we can use an
+ abbreviated form to switch back to it -- unless this section is
+ part of a COMDAT groups, in which case GAS requires the full
+ declaration every time. */
+ if (!(HAVE_COMDAT_GROUP && (flags & SECTION_LINKONCE))
+ && ! named_section_first_declaration (name))
{
fprintf (asm_out_file, "\t.section\t%s\n", name);
return;
*f++ = 'S';
if (flags & SECTION_TLS)
*f++ = 'T';
+ if (HAVE_COMDAT_GROUP && (flags & SECTION_LINKONCE))
+ *f++ = 'G';
*f = '\0';
fprintf (asm_out_file, "\t.section\t%s,\"%s\"", name, flagchars);
if (!(flags & SECTION_NOTYPE))
{
const char *type;
+ const char *format;
if (flags & SECTION_BSS)
type = "nobits";
else
type = "progbits";
- fprintf (asm_out_file, ",@%s", type);
+ format = ",@%s";
+#ifdef ASM_COMMENT_START
+ /* On platforms that use "@" as the assembly comment character,
+ use "%" instead. */
+ if (strcmp (ASM_COMMENT_START, "@") == 0)
+ format = ",%%%s";
+#endif
+ fprintf (asm_out_file, format, type);
if (flags & SECTION_ENTSIZE)
fprintf (asm_out_file, ",%d", flags & SECTION_ENTSIZE);
+ if (HAVE_COMDAT_GROUP && (flags & SECTION_LINKONCE))
+ fprintf (asm_out_file, ",%s,comdat",
+ lang_hooks.decls.comdat_group (decl));
}
putc ('\n', asm_out_file);
}
void
-default_coff_asm_named_section (name, flags)
- const char *name;
- unsigned int flags;
+default_coff_asm_named_section (const char *name, unsigned int flags,
+ tree decl ATTRIBUTE_UNUSED)
{
char flagchars[8], *f = flagchars;
}
void
-default_pe_asm_named_section (name, flags)
- const char *name;
- unsigned int flags;
+default_pe_asm_named_section (const char *name, unsigned int flags,
+ tree decl)
{
- default_coff_asm_named_section (name, flags);
+ default_coff_asm_named_section (name, flags, decl);
if (flags & SECTION_LINKONCE)
{
}
}
\f
-/* Used for vtable gc in GNU binutils. Record that the pointer at OFFSET
- from SYMBOL is used in all classes derived from SYMBOL. */
-
-void
-assemble_vtable_entry (symbol, offset)
- rtx symbol;
- HOST_WIDE_INT offset;
-{
- fputs ("\t.vtable_entry ", asm_out_file);
- output_addr_const (asm_out_file, symbol);
- fprintf (asm_out_file, ", " HOST_WIDE_INT_PRINT_DEC "\n", offset);
-}
-
-/* Used for vtable gc in GNU binutils. Record the class hierarchy by noting
- that the vtable symbol CHILD is derived from the vtable symbol PARENT. */
-
-void
-assemble_vtable_inherit (child, parent)
- rtx child, parent;
-{
- fputs ("\t.vtable_inherit ", asm_out_file);
- output_addr_const (asm_out_file, child);
- fputs (", ", asm_out_file);
- output_addr_const (asm_out_file, parent);
- fputc ('\n', asm_out_file);
-}
-\f
/* The lame default section selector. */
void
-default_select_section (decl, reloc, align)
- tree decl;
- int reloc;
- unsigned HOST_WIDE_INT align ATTRIBUTE_UNUSED;
+default_select_section (tree decl, int reloc,
+ unsigned HOST_WIDE_INT align ATTRIBUTE_UNUSED)
{
bool readonly = false;
readonly = true;
}
else if (TREE_CODE (decl) == STRING_CST)
- readonly = !flag_writable_strings;
+ readonly = true;
else if (! (flag_pic && reloc))
readonly = true;
data_section ();
}
-/* A helper function for default_elf_select_section and
- default_elf_unique_section. Categorizes the DECL. */
-
enum section_category
-{
- SECCAT_TEXT,
-
- SECCAT_RODATA,
- SECCAT_RODATA_MERGE_STR,
- SECCAT_RODATA_MERGE_STR_INIT,
- SECCAT_RODATA_MERGE_CONST,
- SECCAT_SRODATA,
-
- SECCAT_DATA,
-
- /* To optimize loading of shared programs, define following subsections
- of data section:
- _REL Contains data that has relocations, so they get grouped
- together and dynamic linker will visit fewer pages in memory.
- _RO Contains data that is otherwise read-only. This is useful
- with prelinking as most relocations won't be dynamically
- linked and thus stay read only.
- _LOCAL Marks data containing relocations only to local objects.
- These relocations will get fully resolved by prelinking. */
- SECCAT_DATA_REL,
- SECCAT_DATA_REL_LOCAL,
- SECCAT_DATA_REL_RO,
- SECCAT_DATA_REL_RO_LOCAL,
-
- SECCAT_SDATA,
- SECCAT_TDATA,
-
- SECCAT_BSS,
- SECCAT_SBSS,
- SECCAT_TBSS
-};
-
-static enum section_category
-categorize_decl_for_section PARAMS ((tree, int, int));
-
-static enum section_category
-categorize_decl_for_section (decl, reloc, shlib)
- tree decl;
- int reloc;
- int shlib;
+categorize_decl_for_section (tree decl, int reloc, int shlib)
{
enum section_category ret;
return SECCAT_TEXT;
else if (TREE_CODE (decl) == STRING_CST)
{
- if (flag_writable_strings)
- return SECCAT_DATA;
+ if (flag_mudflap) /* or !flag_merge_constants */
+ return SECCAT_RODATA;
else
return SECCAT_RODATA_MERGE_STR;
}
else if (TREE_CODE (decl) == VAR_DECL)
{
if (DECL_INITIAL (decl) == NULL
- || DECL_INITIAL (decl) == error_mark_node)
+ || DECL_INITIAL (decl) == error_mark_node
+ || (flag_zero_initialized_in_bss
+ /* Leave constant zeroes in .rodata so they can be shared. */
+ && !TREE_READONLY (decl)
+ && initializer_zerop (DECL_INITIAL (decl))))
ret = SECCAT_BSS;
else if (! TREE_READONLY (decl)
|| TREE_SIDE_EFFECTS (decl)
ret = SECCAT_RODATA;
/* There are no read-only thread-local sections. */
- if (TREE_CODE (decl) == VAR_DECL && DECL_THREAD_LOCAL (decl))
+ if (TREE_CODE (decl) == VAR_DECL && DECL_THREAD_LOCAL_P (decl))
{
- if (ret == SECCAT_BSS)
+ /* Note that this would be *just* SECCAT_BSS, except that there's
+ no concept of a read-only thread-local-data section. */
+ if (ret == SECCAT_BSS
+ || (flag_zero_initialized_in_bss
+ && initializer_zerop (DECL_INITIAL (decl))))
ret = SECCAT_TBSS;
else
ret = SECCAT_TDATA;
}
/* If the target uses small data sections, select it. */
- else if ((*targetm.in_small_data_p) (decl))
+ else if (targetm.in_small_data_p (decl))
{
if (ret == SECCAT_BSS)
ret = SECCAT_SBSS;
}
bool
-decl_readonly_section (decl, reloc)
- tree decl;
- int reloc;
+decl_readonly_section (tree decl, int reloc)
{
return decl_readonly_section_1 (decl, reloc, flag_pic);
}
bool
-decl_readonly_section_1 (decl, reloc, shlib)
- tree decl;
- int reloc;
- int shlib;
+decl_readonly_section_1 (tree decl, int reloc, int shlib)
{
switch (categorize_decl_for_section (decl, reloc, shlib))
{
/* Select a section based on the above categorization. */
void
-default_elf_select_section (decl, reloc, align)
- tree decl;
- int reloc;
- unsigned HOST_WIDE_INT align;
+default_elf_select_section (tree decl, int reloc,
+ unsigned HOST_WIDE_INT align)
{
default_elf_select_section_1 (decl, reloc, align, flag_pic);
}
void
-default_elf_select_section_1 (decl, reloc, align, shlib)
- tree decl;
- int reloc;
- unsigned HOST_WIDE_INT align;
- int shlib;
+default_elf_select_section_1 (tree decl, int reloc,
+ unsigned HOST_WIDE_INT align, int shlib)
{
+ const char *sname;
switch (categorize_decl_for_section (decl, reloc, shlib))
{
case SECCAT_TEXT:
/* We're not supposed to be called on FUNCTION_DECLs. */
- abort ();
+ gcc_unreachable ();
case SECCAT_RODATA:
readonly_data_section ();
- break;
+ return;
case SECCAT_RODATA_MERGE_STR:
mergeable_string_section (decl, align, 0);
- break;
+ return;
case SECCAT_RODATA_MERGE_STR_INIT:
mergeable_string_section (DECL_INITIAL (decl), align, 0);
- break;
+ return;
case SECCAT_RODATA_MERGE_CONST:
mergeable_constant_section (DECL_MODE (decl), align, 0);
- break;
+ return;
case SECCAT_SRODATA:
- named_section (NULL_TREE, ".sdata2", reloc);
+ sname = ".sdata2";
break;
case SECCAT_DATA:
data_section ();
- break;
+ return;
case SECCAT_DATA_REL:
- named_section (NULL_TREE, ".data.rel", reloc);
+ sname = ".data.rel";
break;
case SECCAT_DATA_REL_LOCAL:
- named_section (NULL_TREE, ".data.rel.local", reloc);
+ sname = ".data.rel.local";
break;
case SECCAT_DATA_REL_RO:
- named_section (NULL_TREE, ".data.rel.ro", reloc);
+ sname = ".data.rel.ro";
break;
case SECCAT_DATA_REL_RO_LOCAL:
- named_section (NULL_TREE, ".data.rel.ro.local", reloc);
+ sname = ".data.rel.ro.local";
break;
case SECCAT_SDATA:
- named_section (NULL_TREE, ".sdata", reloc);
+ sname = ".sdata";
break;
case SECCAT_TDATA:
- named_section (NULL_TREE, ".tdata", reloc);
+ sname = ".tdata";
break;
case SECCAT_BSS:
#ifdef BSS_SECTION_ASM_OP
bss_section ();
+ return;
#else
- named_section (NULL_TREE, ".bss", reloc);
-#endif
+ sname = ".bss";
break;
+#endif
case SECCAT_SBSS:
- named_section (NULL_TREE, ".sbss", reloc);
+ sname = ".sbss";
break;
case SECCAT_TBSS:
- named_section (NULL_TREE, ".tbss", reloc);
+ sname = ".tbss";
break;
default:
- abort ();
+ gcc_unreachable ();
}
+
+ if (!DECL_P (decl))
+ decl = NULL_TREE;
+ named_section (decl, sname, reloc);
}
/* Construct a unique section name based on the decl name and the
categorization performed above. */
void
-default_unique_section (decl, reloc)
- tree decl;
- int reloc;
+default_unique_section (tree decl, int reloc)
{
default_unique_section_1 (decl, reloc, flag_pic);
}
void
-default_unique_section_1 (decl, reloc, shlib)
- tree decl;
- int reloc;
- int shlib;
+default_unique_section_1 (tree decl, int reloc, int shlib)
{
- bool one_only = DECL_ONE_ONLY (decl);
+ /* We only need to use .gnu.linkonce if we don't have COMDAT groups. */
+ bool one_only = DECL_ONE_ONLY (decl) && !HAVE_COMDAT_GROUP;
const char *prefix, *name;
size_t nlen, plen;
char *string;
prefix = one_only ? ".gnu.linkonce.tb." : ".tbss.";
break;
default:
- abort ();
+ gcc_unreachable ();
}
plen = strlen (prefix);
name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl));
- name = (* targetm.strip_name_encoding) (name);
+ name = targetm.strip_name_encoding (name);
nlen = strlen (name);
string = alloca (nlen + plen + 1);
}
void
-default_select_rtx_section (mode, x, align)
- enum machine_mode mode ATTRIBUTE_UNUSED;
- rtx x;
- unsigned HOST_WIDE_INT align ATTRIBUTE_UNUSED;
+default_select_rtx_section (enum machine_mode mode ATTRIBUTE_UNUSED,
+ rtx x,
+ unsigned HOST_WIDE_INT align ATTRIBUTE_UNUSED)
{
if (flag_pic)
switch (GET_CODE (x))
}
void
-default_elf_select_rtx_section (mode, x, align)
- enum machine_mode mode;
- rtx x;
- unsigned HOST_WIDE_INT align;
+default_elf_select_rtx_section (enum machine_mode mode, rtx x,
+ unsigned HOST_WIDE_INT align)
{
/* ??? Handle small data here somehow. */
/* Set the generally applicable flags on the SYMBOL_REF for EXP. */
void
-default_encode_section_info (decl, rtl, first)
- tree decl;
- rtx rtl;
- int first ATTRIBUTE_UNUSED;
+default_encode_section_info (tree decl, rtx rtl, int first ATTRIBUTE_UNUSED)
{
rtx symbol;
int flags;
/* Careful not to prod global register variables. */
- if (GET_CODE (rtl) != MEM)
+ if (!MEM_P (rtl))
return;
symbol = XEXP (rtl, 0);
if (GET_CODE (symbol) != SYMBOL_REF)
flags = 0;
if (TREE_CODE (decl) == FUNCTION_DECL)
flags |= SYMBOL_FLAG_FUNCTION;
- if ((*targetm.binds_local_p) (decl))
+ if (targetm.binds_local_p (decl))
flags |= SYMBOL_FLAG_LOCAL;
- if ((*targetm.in_small_data_p) (decl))
+ if (TREE_CODE (decl) == VAR_DECL && DECL_THREAD_LOCAL_P (decl))
+ flags |= DECL_TLS_MODEL (decl) << SYMBOL_FLAG_TLS_SHIFT;
+ else if (targetm.in_small_data_p (decl))
flags |= SYMBOL_FLAG_SMALL;
- if (TREE_CODE (decl) == VAR_DECL && DECL_THREAD_LOCAL (decl))
- flags |= decl_tls_model (decl) << SYMBOL_FLAG_TLS_SHIFT;
/* ??? Why is DECL_EXTERNAL ever set for non-PUBLIC names? Without
being PUBLIC, the thing *must* be defined in this translation unit.
Prevent this buglet from being propagated into rtl code as well. */
do anything but discard the '*' marker. */
const char *
-default_strip_name_encoding (str)
- const char *str;
+default_strip_name_encoding (const char *str)
{
return str + (*str == '*');
}
wrt cross-module name binding. */
bool
-default_binds_local_p (exp)
- tree exp;
+default_binds_local_p (tree exp)
{
- return default_binds_local_p_1 (exp, flag_pic);
+ return default_binds_local_p_1 (exp, flag_shlib);
}
bool
-default_binds_local_p_1 (exp, shlib)
- tree exp;
- int shlib;
+default_binds_local_p_1 (tree exp, int shlib)
{
bool local_p;
/* Static variables are always local. */
else if (! TREE_PUBLIC (exp))
local_p = true;
- /* A variable is local if the user tells us so. */
- else if (decl_visibility (exp) != VISIBILITY_DEFAULT)
+ /* A variable is local if the user explicitly tells us so. */
+ else if (DECL_VISIBILITY_SPECIFIED (exp) && DECL_VISIBILITY (exp) != VISIBILITY_DEFAULT)
local_p = true;
/* Otherwise, variables defined outside this object may not be local. */
else if (DECL_EXTERNAL (exp))
/* Linkonce and weak data are never local. */
else if (DECL_ONE_ONLY (exp) || DECL_WEAK (exp))
local_p = false;
+ /* If none of the above and visibility is not default, make local. */
+ else if (DECL_VISIBILITY (exp) != VISIBILITY_DEFAULT)
+ local_p = true;
/* If PIC, then assume that any global name can be overridden by
symbols resolved from other modules. */
else if (shlib)
/* Determine whether or not a pointer mode is valid. Assume defaults
of ptr_mode or Pmode - can be overridden. */
bool
-default_valid_pointer_mode (mode)
- enum machine_mode mode;
+default_valid_pointer_mode (enum machine_mode mode)
{
return (mode == ptr_mode || mode == Pmode);
}
/* Default function to output code that will globalize a label. A
- target must define GLOBAL_ASM_OP or provide it's own function to
+ target must define GLOBAL_ASM_OP or provide its own function to
globalize a label. */
#ifdef GLOBAL_ASM_OP
void
-default_globalize_label (stream, name)
- FILE * stream;
- const char *name;
+default_globalize_label (FILE * stream, const char *name)
{
fputs (GLOBAL_ASM_OP, stream);
assemble_name (stream, name);
putc ('\n', stream);
}
#endif /* GLOBAL_ASM_OP */
-
+
+/* Default function to output a label for unwind information. The
+ default is to do nothing. A target that needs nonlocal labels for
+ unwind information must provide its own function to do this. */
+void
+default_emit_unwind_label (FILE * stream ATTRIBUTE_UNUSED,
+ tree decl ATTRIBUTE_UNUSED,
+ int for_eh ATTRIBUTE_UNUSED,
+ int empty ATTRIBUTE_UNUSED)
+{
+}
+
/* This is how to output an internal numbered label where PREFIX is
the class of label and LABELNO is the number within the class. */
void
-default_internal_label (stream, prefix, labelno)
- FILE *stream;
- const char *prefix;
- unsigned long labelno;
+default_internal_label (FILE *stream, const char *prefix,
+ unsigned long labelno)
{
char *const buf = alloca (40 + strlen (prefix));
ASM_GENERATE_INTERNAL_LABEL (buf, prefix, labelno);
- ASM_OUTPUT_LABEL (stream, buf);
+ ASM_OUTPUT_INTERNAL_LABEL (stream, buf);
+}
+
+/* This is the default behavior at the beginning of a file. It's
+ controlled by two other target-hook toggles. */
+void
+default_file_start (void)
+{
+ if (targetm.file_start_app_off && !flag_verbose_asm)
+ fputs (ASM_APP_OFF, asm_out_file);
+
+ if (targetm.file_start_file_directive)
+ output_file_directive (asm_out_file, main_input_filename);
+}
+
+/* This is a generic routine suitable for use as TARGET_ASM_FILE_END
+ which emits a special section directive used to indicate whether or
+ not this object file needs an executable stack. This is primarily
+ a GNU extension to ELF but could be used on other targets. */
+
+int trampolines_created;
+
+void
+file_end_indicate_exec_stack (void)
+{
+ unsigned int flags = SECTION_DEBUG;
+ if (trampolines_created)
+ flags |= SECTION_CODE;
+
+ named_section_flags (".note.GNU-stack", flags);
}
#include "gt-varasm.h"